From bbc25cd694e902436891858e2f0ba36de23dcb69 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 14 Apr 2018 15:58:01 +0100 Subject: [PATCH] Add hole-to-hole clearance testing to DRC. Also move courtyard testing flags to BoardSettings API. Both are still stored in the config until we decide where they go in order to prevent prematurely disturbing the board file format. Fixes: lp:684067 * https://bugs.launchpad.net/kicad/+bug/684067 (cherry picked from commit 5399f60) --- include/board_design_settings.h | 39 +++++++++++++ pcbnew/board_design_settings.cpp | 77 ++++++++++++++++++++++++-- pcbnew/dialogs/dialog_drc.cpp | 4 -- pcbnew/drc.cpp | 94 +++++++++++++++++++++++++++++--- pcbnew/drc.h | 15 ++--- pcbnew/drc_item.cpp | 2 + 6 files changed, 205 insertions(+), 26 deletions(-) diff --git a/include/board_design_settings.h b/include/board_design_settings.h index 2d7cc9ec93..9e56670837 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -52,6 +52,7 @@ #define DEFAULT_VIASMINDRILL 0.3 // vias (not micro vias) min drill diameter #define DEFAULT_MICROVIASMINSIZE 0.2 // micro vias (not vias) min diameter #define DEFAULT_MICROVIASMINDRILL 0.1 // micro vias (not vias) min drill diameter +#define DEFAULT_HOLETOHOLEMIN 0.25 // separation between drilled hole edges /** * Struct VIA_DIMENSION @@ -413,6 +414,44 @@ public: return m_useCustomTrackVia; } + /** + * Function GetMinHoleSeparation + * @return The minimum distance between the edges of two holes or 0, which indicates that + * hole-to-hole separation should not be checked. + */ + int GetMinHoleSeparation() const; + + /** + * Function SetMinHoleSeparation + * @param aValue The minimum distance between the edges of two holes or 0 to disable + * hole-to-hole separation checking. + */ + void SetMinHoleSeparation( int aDistance ); + + /** + * Function RequireCourtyardDefinitions + * @return True if footprints without courtyard definitions are considered DRC violations. + */ + bool RequireCourtyardDefinitions() const; + + /** + * Function SetRequireCourtyardDefinitions + * @param aRequire Set to true to generate DRC violations from missing courtyards. + */ + void SetRequireCourtyardDefinitions( bool aRequire ); + + /** + * Function ProhibitOverlappingCourtyards + * @return True if overlapping courtyards are considered DRC violations. + */ + bool ProhibitOverlappingCourtyards() const; + + /** + * Function SetProhibitOverlappingCourtyards + * @param aRequire Set to true to generate DRC violations from overlapping courtyards. + */ + void SetProhibitOverlappingCourtyards( bool aRequire ); + /** * Function GetVisibleLayers * returns a bit-mask of all the layers that are visible diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index fd91a9c4a8..35c16c53ab 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -35,6 +35,11 @@ #include #include +#include + +#define TestMissingCourtyardKey wxT( "TestMissingCourtyard" ) +#define TestFootprintCourtyardKey wxT( "TestFootprintCourtyard" ) +#define MinHoleSeparationKey wxT( "MinHoleSeparation" ) BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : @@ -58,12 +63,12 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : m_UseConnectedTrackWidth = false; m_BlindBuriedViaAllowed = false; // true to allow blind/buried vias - m_MicroViasAllowed = false; // true to allow micro vias + m_MicroViasAllowed = false; // true to allow micro vias - m_DrawSegmentWidth = Millimeter2iu( DEFAULT_GRAPHIC_THICKNESS ); // current graphic line width (not EDGE layer) + m_DrawSegmentWidth = Millimeter2iu( DEFAULT_GRAPHIC_THICKNESS ); // current graphic line width (not EDGE layer) - m_EdgeSegmentWidth = Millimeter2iu( DEFAULT_PCB_EDGE_THICKNESS ); // current graphic line width (EDGE layer only) - m_PcbTextWidth = Millimeter2iu( DEFAULT_TEXT_PCB_THICKNESS ); // current Pcb (not module) Text width + m_EdgeSegmentWidth = Millimeter2iu( DEFAULT_PCB_EDGE_THICKNESS ); // current graphic line width (EDGE layer only) + m_PcbTextWidth = Millimeter2iu( DEFAULT_TEXT_PCB_THICKNESS ); // current Pcb (not module) Text width m_PcbTextSize = wxSize( Millimeter2iu( DEFAULT_TEXT_PCB_SIZE ), Millimeter2iu( DEFAULT_TEXT_PCB_SIZE ) ); // current Pcb (not module) Text size @@ -315,6 +320,70 @@ void BOARD_DESIGN_SETTINGS::SetTrackWidthIndex( unsigned aIndex ) } +int BOARD_DESIGN_SETTINGS::GetMinHoleSeparation() const +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + int value; + + config->Read( MinHoleSeparationKey, &value, Millimeter2iu( DEFAULT_HOLETOHOLEMIN ) ); + return value; +} + + +void BOARD_DESIGN_SETTINGS::SetMinHoleSeparation( int aDistance ) +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + + config->Write( MinHoleSeparationKey, aDistance ); +} + + +bool BOARD_DESIGN_SETTINGS::RequireCourtyardDefinitions() const +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + bool value; + + config->Read( TestMissingCourtyardKey, &value, false ); + return value; +} +void BOARD_DESIGN_SETTINGS::SetRequireCourtyardDefinitions( bool aRequire ) +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + + config->Write( TestMissingCourtyardKey, aRequire ); +} + + +bool BOARD_DESIGN_SETTINGS::ProhibitOverlappingCourtyards() const +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + bool value; + + config->Read( TestFootprintCourtyardKey, &value, false ); + return value; +} + + +void BOARD_DESIGN_SETTINGS::SetProhibitOverlappingCourtyards( bool aRequire ) +{ + // 6.0 TODO: we need to decide where these go, but until then don't disturb the + // file format unnecessarily. + wxConfigBase* config = Kiface().KifaceSettings(); + + config->Write( TestFootprintCourtyardKey, aRequire ); +} + + void BOARD_DESIGN_SETTINGS::SetVisibleAlls() { SetVisibleLayers( LSET().set() ); diff --git a/pcbnew/dialogs/dialog_drc.cpp b/pcbnew/dialogs/dialog_drc.cpp index 83142d47e4..04099b96cc 100644 --- a/pcbnew/dialogs/dialog_drc.cpp +++ b/pcbnew/dialogs/dialog_drc.cpp @@ -251,8 +251,6 @@ void DIALOG_DRC_CONTROL::OnStartdrcClick( wxCommandEvent& event ) true, // DRC test for zones enabled true, // DRC test for keepout areas enabled m_cbRefillZones->GetValue(), - m_cbCourtyardOverlap->GetValue(), - m_cbCourtyardMissing->GetValue(), m_cbReportAllTrackErrors->GetValue(), reportName, make_report ); @@ -325,8 +323,6 @@ void DIALOG_DRC_CONTROL::OnListUnconnectedClick( wxCommandEvent& event ) true, // DRC test for zones enabled true, // DRC test for keepout areas enabled m_cbRefillZones->GetValue(), - m_cbCourtyardOverlap->GetValue(), - m_cbCourtyardMissing->GetValue(), m_cbReportAllTrackErrors->GetValue(), reportName, make_report ); diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 1ca7d25ffd..d2ceb56556 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -138,10 +138,6 @@ DRC::DRC( PCB_EDIT_FRAME* aPcbWindow ) m_doUnconnectedTest = true; // enable unconnected tests m_doZonesTest = true; // enable zone to items clearance tests m_doKeepoutTest = true; // enable keepout areas to items clearance tests - m_doFootprintOverlapping = true; // enable courtyards areas overlap tests - m_doNoCourtyardDefined = true; // enable missing courtyard in footprint warning - m_abortDRC = false; - m_drcInProgress = false; m_refillZones = false; // Only fill zones if requested by user. m_reportAllTrackErrors = false; m_doCreateRptFile = false; @@ -423,6 +419,15 @@ void DRC::RunTests( wxTextCtrl* aMessages ) testPad2Pad(); } + // test clearances between drilled holes + if( aMessages ) + { + aMessages->AppendText( _( "Drill clearances...\n" ) ); + wxSafeYield(); + } + + testDrilledHoles(); + // test track and via clearances to other tracks, pads, and vias if( aMessages ) { @@ -493,7 +498,8 @@ void DRC::RunTests( wxTextCtrl* aMessages ) testTexts(); // find overlapping courtyard ares. - if( m_doFootprintOverlapping || m_doNoCourtyardDefined ) + if( m_pcb->GetDesignSettings().ProhibitOverlappingCourtyards() + || m_pcb->GetDesignSettings().RequireCourtyardDefinitions() ) { if( aMessages ) { @@ -708,6 +714,78 @@ void DRC::testPad2Pad() } +void DRC::testDrilledHoles() +{ + int holeToHoleMin = m_pcb->GetDesignSettings().GetMinHoleSeparation(); + + if( holeToHoleMin == 0 ) // No min setting turns testing off. + return; + + // Test drilled hole clearances to minimize drill bit breakage. + // + // Notes: slots are milled, so we're only concerned with circular holes + // microvias are laser-drilled, so we're only concerned with standard vias + + struct DRILLED_HOLE + { + wxPoint m_location; + int m_drillRadius; + BOARD_ITEM* m_owner; + }; + + std::vector holes; + DRILLED_HOLE hole; + + for( MODULE* mod : m_pcb->Modules() ) + { + for( D_PAD* pad : mod->Pads( ) ) + { + if( pad->GetDrillSize().x && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) + { + hole.m_location = pad->GetPosition(); + hole.m_drillRadius = pad->GetDrillSize().x / 2; + hole.m_owner = pad; + holes.push_back( hole ); + } + } + } + + for( TRACK* track : m_pcb->Tracks() ) + { + VIA* via = dynamic_cast( track ); + if( via && via->GetViaType() == VIA_THROUGH ) + { + hole.m_location = via->GetPosition(); + hole.m_drillRadius = via->GetDrillValue() / 2; + hole.m_owner = via; + holes.push_back( hole ); + } + } + + for( size_t ii = 0; ii < holes.size(); ++ii ) + { + const DRILLED_HOLE& refHole = holes[ ii ]; + + for( size_t jj = ii + 1; jj < holes.size(); ++jj ) + { + const DRILLED_HOLE& checkHole = holes[ jj ]; + + // Holes with identical locations are allowable + if( checkHole.m_location == refHole.m_location ) + continue; + + if( KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) ) + < checkHole.m_drillRadius + refHole.m_drillRadius + holeToHoleMin ) + { + addMarkerToPcb( new MARKER_PCB( DRCE_DRILLED_HOLES_TOO_CLOSE, refHole.m_location, + refHole.m_owner, refHole.m_location, + checkHole.m_owner, checkHole.m_location ) ); + } + } + } +} + + void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar ) { wxProgressDialog * progressDialog = NULL; @@ -1243,7 +1321,7 @@ bool DRC::doFootprintOverlappingDrc() { bool is_ok = footprint->BuildPolyCourtyard(); - if( !is_ok && m_doFootprintOverlapping ) + if( !is_ok && m_pcb->GetDesignSettings().ProhibitOverlappingCourtyards() ) { m_currentMarker = fillMarker( footprint, footprint->GetPosition(), DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT, @@ -1253,7 +1331,7 @@ bool DRC::doFootprintOverlappingDrc() success = false; } - if( !m_doNoCourtyardDefined ) + if( !m_pcb->GetDesignSettings().RequireCourtyardDefinitions() ) continue; if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 && @@ -1269,7 +1347,7 @@ bool DRC::doFootprintOverlappingDrc() } } - if( !m_doFootprintOverlapping ) + if( !m_pcb->GetDesignSettings().ProhibitOverlappingCourtyards() ) return success; // Now test for overlapping on top layer: diff --git a/pcbnew/drc.h b/pcbnew/drc.h index dfcc61074d..0aa7f16103 100644 --- a/pcbnew/drc.h +++ b/pcbnew/drc.h @@ -91,6 +91,7 @@ #define DRCE_MICRO_VIA_NOT_ALLOWED 47 ///< micro vias are not allowed #define DRCE_BURIED_VIA_NOT_ALLOWED 48 ///< buried vias are not allowed #define DRCE_DISABLED_LAYER_ITEM 49 ///< item on a disabled layer +#define DRCE_DRILLED_HOLES_TOO_CLOSE 50 ///< overlapping drilled holes break drill bits class EDA_DRAW_PANEL; @@ -172,8 +173,6 @@ private: bool m_doZonesTest; bool m_doKeepoutTest; bool m_doCreateRptFile; - bool m_doFootprintOverlapping; - bool m_doNoCourtyardDefined; bool m_refillZones; bool m_reportAllTrackErrors; @@ -181,9 +180,6 @@ private: MARKER_PCB* m_currentMarker; - bool m_abortDRC; - bool m_drcInProgress; - /** * in legacy canvas, when creating a track, the drc test must only display the * error message, and do not create a DRC marker. @@ -307,6 +303,8 @@ private: void testPad2Pad(); + void testDrilledHoles(); + void testUnconnected(); void testZones(); @@ -435,6 +433,7 @@ public: ~DRC(); /** + * Function Drc * tests the current segment and returns the result and displays the error * in the status panel only if one exists. * No marker created or added to the board. Must be used only during track @@ -446,6 +445,7 @@ public: int DrcOnCreatingTrack( TRACK* aRefSeg, TRACK* aList ); /** + * Function Drc * tests the outline segment starting at CornerIndex and returns the result and displays * the error in the status panel only if one exists. * Test Edge inside other areas @@ -503,15 +503,12 @@ public: * @param aZonesTest Tells whether to test zones. * @param aRefillZones Refill zones before performing DRC. * @param aKeepoutTest Tells whether to test keepout areas. - * @param aCourtyardTest Tells whether to test footprint courtyard overlap. - * @param aCourtyardMissingTest Tells whether to test missing courtyard definition in footprint. * @param aReportAllTrackErrors Tells whether or not to stop checking track connections after the first error. * @param aReportName A string telling the disk file report name entered. * @param aSaveReport A boolean telling whether to generate disk file report. */ void SetSettings( bool aPad2PadTest, bool aUnconnectedTest, bool aZonesTest, bool aKeepoutTest, bool aRefillZones, - bool aCourtyardTest, bool aCourtyardMissingTest, bool aReportAllTrackErrors, const wxString& aReportName, bool aSaveReport ) { @@ -521,8 +518,6 @@ public: m_doKeepoutTest = aKeepoutTest; m_rptFilename = aReportName; m_doCreateRptFile = aSaveReport; - m_doFootprintOverlapping = aCourtyardTest; - m_doNoCourtyardDefined = aCourtyardMissingTest; m_refillZones = aRefillZones; m_drcInLegacyRoutingMode = false; m_reportAllTrackErrors = aReportAllTrackErrors; diff --git a/pcbnew/drc_item.cpp b/pcbnew/drc_item.cpp index 5e13422999..6429e0143d 100644 --- a/pcbnew/drc_item.cpp +++ b/pcbnew/drc_item.cpp @@ -97,6 +97,8 @@ wxString DRC_ITEM::GetErrorText() const return wxString( _( "Too small via drill" ) ); case DRCE_TOO_SMALL_MICROVIA_DRILL: return wxString( _( "Too small micro via drill" ) ); + case DRCE_DRILLED_HOLES_TOO_CLOSE: + return wxString( _( "Drilled holes too close together" ) ); // use < since this is text ultimately embedded in HTML case DRCE_NETCLASS_TRACKWIDTH: