From 416d82727fbc40243145559cdad7118a90e74374 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 23 May 2020 22:48:24 +0100 Subject: [PATCH] Redo DRC rules to get ready for new system. --- common/drc_rules.keywords | 8 +- include/board_design_settings.h | 4 - pcbnew/board_connected_item.cpp | 108 ++++++------ pcbnew/board_connected_item.h | 9 + pcbnew/board_design_settings.cpp | 74 +-------- pcbnew/class_pad.cpp | 20 +-- pcbnew/class_track.cpp | 86 ++++------ pcbnew/class_track.h | 2 +- pcbnew/class_zone.cpp | 53 +++++- .../dialogs/panel_setup_netclasses_base.cpp | 12 +- .../dialogs/panel_setup_netclasses_base.fbp | 6 +- pcbnew/drc/drc.cpp | 2 + pcbnew/drc/drc.h | 1 + pcbnew/drc/drc_clearance_test_functions.cpp | 21 ++- pcbnew/drc/drc_drilled_hole_tester.cpp | 45 ++--- pcbnew/drc/drc_item.cpp | 1 + pcbnew/drc/drc_rule.cpp | 88 +++++----- pcbnew/drc/drc_rule.h | 56 +++++-- pcbnew/drc/drc_rule_parser.cpp | 157 +++++++++++------- pcbnew/drc/drc_rule_parser.h | 1 + 20 files changed, 385 insertions(+), 369 deletions(-) diff --git a/common/drc_rules.keywords b/common/drc_rules.keywords index 9e197145e2..e06edc0be6 100644 --- a/common/drc_rules.keywords +++ b/common/drc_rules.keywords @@ -1,8 +1,10 @@ -allow annulus_width blind_via board_edge clearance +condition +constraint +disallow track_width graphic hole @@ -10,10 +12,12 @@ match_area match_layer match_netclass match_type +max micro_via +min npth +opt pad -priority pth relaxed rule diff --git a/include/board_design_settings.h b/include/board_design_settings.h index 167f97aef2..599d42a2b4 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -397,10 +397,6 @@ public: */ int GetSmallestClearanceValue(); - int GetRuleClearance( const BOARD_ITEM* aItem, const NETCLASS* aItemNetclass, - const BOARD_ITEM* bItem, const NETCLASS* bItemNetclass, - wxString* aSource ); - /** * Function GetCurrentMicroViaSize * @return the current micro via size, diff --git a/pcbnew/board_connected_item.cpp b/pcbnew/board_connected_item.cpp index 9975f3b4ad..ad21f9887c 100644 --- a/pcbnew/board_connected_item.cpp +++ b/pcbnew/board_connected_item.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,11 +23,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file board_connected_item.cpp - * @brief BOARD_CONNECTED_ITEM class functions. - */ - #include #include @@ -37,9 +32,6 @@ #include -const wxChar* const traceMask = wxT( "BOARD_CONNECTED_ITEM" ); - - BOARD_CONNECTED_ITEM::BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype ) : BOARD_ITEM( aParent, idtype ), m_netinfo( NETINFO_LIST::OrphanedItem() ) { @@ -79,6 +71,27 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert ) } +// This method returns the Default netclass for nets which don't have their own. +NETCLASS* BOARD_CONNECTED_ITEM::GetEffectiveNetclass() const +{ + // NB: we must check the net first, as when it is 0 GetNetClass() will return the + // orphaned net netclass, not the default netclass. + if( m_netinfo->GetNet() == 0 ) + return GetBoard()->GetDesignSettings().GetDefault(); + else + return GetNetClass(); +} + + +/* + * Clearances exist in a hiearchy: + * 1) accumulated board & netclass constraints + * 2) last rule whose condition evaluates to true + * 4) footprint override + * 5) pad override + * + * The base class handles (1) and (2). + */ int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { BOARD* board = GetBoard(); @@ -87,64 +100,51 @@ int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) c if( !board ) return 0; - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - NETCLASS* myNetclass = nullptr; - NETCLASS* itemNetclass = nullptr; + DRC_RULE* rule = GetRule( this, aItem, CLEARANCE_CONSTRAINT ); - // NB: we must check the net first, as when it is 0 GetNetClass() will return the - // orphaned net netclass, not the default netclass. - if( m_netinfo->GetNet() == 0 ) - myNetclass = bds.GetDefault(); - else - myNetclass = GetNetClass(); + if( rule ) + { + if( aSource ) + *aSource = wxString::Format( _( "'%s' rule clearance" ), rule->m_Name ); + + return rule->m_Clearance.Min; + } + + BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + int clearance = bds.m_MinClearance; + + if( aSource ) + *aSource = _( "board minimum" ); + + NETCLASS* netclass = GetEffectiveNetclass(); + + if( netclass && netclass->GetClearance() > clearance ) + { + clearance = netclass->GetClearance(); + + if( aSource ) + *aSource = wxString::Format( _( "'%s' netclass" ), netclass->GetName() ); + } if( aItem && aItem->IsConnected() ) { - if( static_cast( aItem )->GetNet()->GetNet() == 0 ) - itemNetclass = bds.GetDefault(); - else - itemNetclass = static_cast( aItem )->GetNetClass(); - } + netclass = static_cast( aItem )->GetEffectiveNetclass(); - int clearance = bds.GetRuleClearance( this, myNetclass, aItem, itemNetclass, aSource ); - - if( myNetclass ) - { - int myClearance = myNetclass->GetClearance(); - - if( myClearance > clearance ) + if( netclass && netclass->GetClearance() > clearance ) { - clearance = myClearance; + clearance = netclass->GetClearance(); if( aSource ) - *aSource = wxString::Format( _( "'%s' netclass" ), myNetclass->GetName() ); + *aSource = wxString::Format( _( "'%s' netclass" ), netclass->GetName() ); } } - if( itemNetclass ) + if( aItem && aItem->GetLayer() == Edge_Cuts && bds.m_CopperEdgeClearance > clearance ) { - int itemClearance = myNetclass->GetClearance(); + clearance = bds.m_CopperEdgeClearance; - if( itemClearance > clearance ) - { - clearance = itemClearance; - - if( aSource ) - *aSource = wxString::Format( _( "'%s' netclass" ), itemNetclass->GetName() ); - } - } - - if( aItem && aItem->GetLayer() == Edge_Cuts ) - { - int edgeClearance = bds.m_CopperEdgeClearance; - - if( edgeClearance > clearance ) - { - clearance = edgeClearance; - - if( aSource ) - *aSource = _( "board edge" ); - } + if( aSource ) + *aSource = _( "board edge" ); } return clearance; diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index f5ca2732d4..42eafa1f2f 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -172,6 +172,15 @@ public: */ NETCLASS* GetNetClass() const; + /** + * Function GetEffectiveNetclass + * returns the NETCLASS for this item, or the default netclass if none is defined. + * + * Note: do NOT return a std::shared_ptr from this. It is used heavily in DRC, and the + * std::shared_ptr stuff shows up large in performance profiling. + */ + NETCLASS* GetEffectiveNetclass() const; + /** * Function GetNetClassName * returns a pointer to the netclass of the zone. diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index bff06dd5af..92a5dae0a9 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -21,11 +21,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file board_design_settings.cpp - * BOARD_DESIGN_SETTINGS class functions. - */ - #include #include #include @@ -967,7 +962,7 @@ int BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue() clearance = std::max( clearance, netclass.second->GetClearance() ); for( const DRC_RULE* rule : m_DRCRules ) - clearance = std::max( clearance, rule->m_Clearance ); + clearance = std::max( clearance, rule->m_Clearance.Min ); return clearance; } @@ -984,58 +979,6 @@ int BOARD_DESIGN_SETTINGS::GetSmallestClearanceValue() } -int BOARD_DESIGN_SETTINGS::GetRuleClearance( const BOARD_ITEM* aItem, const NETCLASS* aNetclass, - const BOARD_ITEM* bItem, const NETCLASS* bNetclass, - wxString* aSource ) -{ - if( !m_matched.empty() ) - m_matched.clear(); // yes, the difference can be seen in profiles - - if( m_DRCRuleSelectors.size() == 0 ) - return 0; - - MatchSelectors( m_DRCRuleSelectors, aItem, aNetclass, bItem, bNetclass, &m_matched ); - - if( m_matched.size() == 0 ) - return 0; - - std::sort( m_matched.begin(), m_matched.end(), - []( DRC_SELECTOR* a, DRC_SELECTOR* b ) -> bool - { - return a->m_Priority < b->m_Priority; - }); - - int clearance = 0; - - for( DRC_SELECTOR* selector : m_matched ) - { - // ignore hole rules; we're just interested in copper here - for( KICAD_T matchType : selector->m_MatchTypes ) - { - if( BaseType( matchType ) == PCB_LOCATE_HOLE_T ) - continue; - } - - if( selector->m_Rule->m_Clearance > 0 ) - { - clearance = std::max( clearance, selector->m_Rule->m_Clearance ); - - if( aSource ) - *aSource = wxString::Format( _( "'%s' rule clearance" ), selector->m_Rule->m_Name ); - } - else if( selector->m_Rule->m_Clearance < 0 ) - { - clearance = std::min( clearance, abs( selector->m_Rule->m_Clearance ) ); - - if( aSource ) - *aSource = wxString::Format( _( "'%s' rule clearance" ), selector->m_Rule->m_Name ); - } - } - - return clearance; -} - - int BOARD_DESIGN_SETTINGS::GetCurrentMicroViaSize() { NETCLASSPTR netclass = m_NetClasses.Find( m_currentNetClassName ); @@ -1122,26 +1065,11 @@ void BOARD_DESIGN_SETTINGS::SetElementVisibility( GAL_LAYER_ID aElementCategory, void BOARD_DESIGN_SETTINGS::SetCopperLayerCount( int aNewLayerCount ) { - // if( aNewLayerCount < 2 ) aNewLayerCount = 2; - m_copperLayerCount = aNewLayerCount; - // ensure consistency with the m_EnabledLayers member -#if 0 - // was: - m_enabledLayers &= ~ALL_CU_LAYERS; - m_enabledLayers |= LAYER_BACK; - - if( m_copperLayerCount > 1 ) - m_enabledLayers |= LAYER_FRONT; - - for( LAYER_NUM ii = LAYER_N_2; ii < aNewLayerCount - 1; ++ii ) - m_enabledLayers |= GetLayerSet( ii ); -#else // Update only enabled copper layers mask m_enabledLayers &= ~LSET::AllCuMask(); m_enabledLayers |= LSET::AllCuMask( aNewLayerCount ); -#endif } diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index dee887ec6a..305beff2bf 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -615,33 +615,25 @@ wxPoint D_PAD::ShapePos() const int D_PAD::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const { - int clearance; - // A pad can have specific clearance that overrides its NETCLASS clearance value if( m_LocalClearance ) { - clearance = m_LocalClearance; - if( aSource ) *aSource = wxString::Format( _( "pad %s" ), GetName() ); + + return m_LocalClearance; } // A footprint can have a specific clearance value - else if( GetParent() && GetParent()->GetLocalClearance() ) + if( GetParent() && GetParent()->GetLocalClearance() ) { - clearance = GetParent()->GetLocalClearance(); - if( aSource ) *aSource = wxString::Format( _( "%s footprint" ), GetParent()->GetReference() ); + + return GetParent()->GetLocalClearance(); } - // Otherwise use the baseclass method to fetch the netclass and/or rule setting - else - { - clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, aSource ); - } - - return clearance; + return BOARD_CONNECTED_ITEM::GetClearance( aItem, aSource ); } diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index 683267ea62..e3794b76aa 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -125,75 +125,52 @@ int TRACK::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const } -int TRACK::GetMinWidth( wxString* aSource ) const +/* + * Width constraints exist in a hiearchy: + * 1) accumulated board & netclass constraints + * 2) last rule whose condition evaluates to true + */ +void TRACK::GetWidthConstraints( int* aMin, int* aMax, wxString* aSource ) const { - BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); - NETCLASS* netclass = nullptr; - std::vector matched; + DRC_RULE* rule = GetRule( this, nullptr, TRACK_CONSTRAINT ); - // NB: we must check the net first, as when it is 0 GetNetClass() will return the - // orphaned net netclass, not the default netclass. - if( m_netinfo->GetNet() == 0 ) - netclass = bds.GetDefault(); - else - netclass = GetNetClass(); - - MatchSelectors( bds.m_DRCRuleSelectors, this, netclass, nullptr, nullptr, &matched ); - - int minWidth = bds.m_TrackMinWidth; - - if( aSource ) - *aSource = _( "board minumum" ); - - for( DRC_SELECTOR* selector : matched ) + if( rule ) { - if( selector->m_Rule->m_TrackWidth > minWidth ) - { - minWidth = selector->m_Rule->m_TrackWidth; + *aMin = rule->m_TrackConstraint.Min; + *aMax = rule->m_TrackConstraint.Max; - if( aSource ) - *aSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); - } + if( aSource ) + *aSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); } + else + { + *aMin = GetBoard()->GetDesignSettings().m_TrackMinWidth; + *aMax = INT_MAX / 2; - return minWidth; + if( aSource ) + *aSource = _( "board minumum" ); + } } int VIA::GetMinAnnulus( wxString* aSource ) const { - BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); - NETCLASS* netclass = nullptr; - std::vector matched; + DRC_RULE* rule = GetRule( this, nullptr, ANNULUS_CONSTRAINT ); - // NB: we must check the net first, as when it is 0 GetNetClass() will return the - // orphaned net netclass, not the default netclass. - if( m_netinfo->GetNet() == 0 ) - netclass = bds.GetDefault(); - else - netclass = GetNetClass(); - - MatchSelectors( bds.m_DRCRuleSelectors, this, netclass, nullptr, nullptr, &matched ); - - int minAnnulus = bds.m_ViasMinAnnulus; - - if( aSource ) - *aSource = _( "board minumum" ); - - MatchSelectors( bds.m_DRCRuleSelectors, this, netclass, nullptr, nullptr, &matched ); - - for( DRC_SELECTOR* selector : matched ) + if( rule ) { - if( selector->m_Rule->m_AnnulusWidth > minAnnulus ) - { - minAnnulus = selector->m_Rule->m_AnnulusWidth; + if( aSource ) + *aSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); - if( aSource ) - *aSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); - } + return rule->m_MinAnnulusWidth; } + else + { + if( aSource ) + *aSource = _( "board minumum" ); - return minAnnulus; + return GetBoard()->GetDesignSettings().m_ViasMinAnnulus; + } } @@ -634,7 +611,8 @@ void TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector msg2.Printf( _( "(from %s)" ), source ); aList.emplace_back( msg, msg2, BLACK ); - int minWidth = GetMinWidth( &source ); + int minWidth, maxWidth; + GetWidthConstraints( &minWidth, &maxWidth, &source ); msg.Printf( _( "Min Width: %s" ), MessageTextFromValue( units, minWidth, true ) ); msg2.Printf( _( "(from %s)" ), source ); diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index 596d9ac191..6923d9ce73 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -212,7 +212,7 @@ public: */ int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override; - int GetMinWidth( wxString* aSource ) const; + void GetWidthConstraints( int* aMin, int* aMax, wxString* aSource ) const; wxString GetSelectMenuText( EDA_UNITS aUnits ) const override; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 2bbf4dfd6e..47c7d75419 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -463,7 +463,58 @@ int ZONE_CONTAINER::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const if( m_isKeepout ) return 0; - int clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, aSource ); + BOARD* board = GetBoard(); + + // No clearance if "this" is not (yet) linked to a board + if( !board ) + return 0; + + DRC_RULE* rule = GetRule( this, aItem, CLEARANCE_CONSTRAINT ); + + if( rule ) + { + if( aSource ) + *aSource = wxString::Format( _( "'%s' rule clearance" ), rule->m_Name ); + + return rule->m_Clearance.Min; + } + + BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + int clearance = bds.m_MinClearance; + + if( aSource ) + *aSource = _( "board minimum" ); + + NETCLASS* netclass = GetEffectiveNetclass(); + + if( netclass && netclass->GetClearance() > clearance ) + { + clearance = netclass->GetClearance(); + + if( aSource ) + *aSource = wxString::Format( _( "'%s' netclass" ), netclass->GetName() ); + } + + if( aItem && aItem->IsConnected() ) + { + netclass = static_cast( aItem )->GetEffectiveNetclass(); + + if( netclass && netclass->GetClearance() > clearance ) + { + clearance = netclass->GetClearance(); + + if( aSource ) + *aSource = wxString::Format( _( "'%s' netclass" ), netclass->GetName() ); + } + } + + if( aItem && aItem->GetLayer() == Edge_Cuts && bds.m_CopperEdgeClearance > clearance ) + { + clearance = bds.m_CopperEdgeClearance; + + if( aSource ) + *aSource = _( "board edge" ); + } if( m_ZoneClearance > clearance ) { diff --git a/pcbnew/dialogs/panel_setup_netclasses_base.cpp b/pcbnew/dialogs/panel_setup_netclasses_base.cpp index 0a9b442239..60016555e2 100644 --- a/pcbnew/dialogs/panel_setup_netclasses_base.cpp +++ b/pcbnew/dialogs/panel_setup_netclasses_base.cpp @@ -27,7 +27,7 @@ PANEL_SETUP_NETCLASSES_BASE::PANEL_SETUP_NETCLASSES_BASE( wxWindow* parent, wxWi m_netclassGrid = new WX_GRID( m_netclassesPane, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_DEFAULT|wxHSCROLL|wxTAB_TRAVERSAL|wxVSCROLL ); // Grid - m_netclassGrid->CreateGrid( 1, 9 ); + m_netclassGrid->CreateGrid( 1, 10 ); m_netclassGrid->EnableEditing( true ); m_netclassGrid->EnableGridLines( true ); m_netclassGrid->EnableDragGridSize( false ); @@ -41,8 +41,9 @@ PANEL_SETUP_NETCLASSES_BASE::PANEL_SETUP_NETCLASSES_BASE( wxWindow* parent, wxWi m_netclassGrid->SetColSize( 4, 96 ); m_netclassGrid->SetColSize( 5, 96 ); m_netclassGrid->SetColSize( 6, 96 ); - m_netclassGrid->SetColSize( 7, 96 ); - m_netclassGrid->SetColSize( 8, 96 ); + m_netclassGrid->SetColSize( 7, 60 ); + m_netclassGrid->SetColSize( 8, 60 ); + m_netclassGrid->SetColSize( 9, 60 ); m_netclassGrid->EnableDragColMove( false ); m_netclassGrid->EnableDragColSize( true ); m_netclassGrid->SetColLabelSize( 24 ); @@ -53,8 +54,9 @@ PANEL_SETUP_NETCLASSES_BASE::PANEL_SETUP_NETCLASSES_BASE( wxWindow* parent, wxWi m_netclassGrid->SetColLabelValue( 4, _("Via Drill") ); m_netclassGrid->SetColLabelValue( 5, _("uVia Size") ); m_netclassGrid->SetColLabelValue( 6, _("uVia Drill") ); - m_netclassGrid->SetColLabelValue( 7, _("dPair Width") ); - m_netclassGrid->SetColLabelValue( 8, _("dPair Gap") ); + m_netclassGrid->SetColLabelValue( 7, _("DP Width") ); + m_netclassGrid->SetColLabelValue( 8, _("DP Gap") ); + m_netclassGrid->SetColLabelValue( 9, _("DP Via Gap") ); m_netclassGrid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); // Rows diff --git a/pcbnew/dialogs/panel_setup_netclasses_base.fbp b/pcbnew/dialogs/panel_setup_netclasses_base.fbp index 91a7b6afba..4f1fc8fc32 100644 --- a/pcbnew/dialogs/panel_setup_netclasses_base.fbp +++ b/pcbnew/dialogs/panel_setup_netclasses_base.fbp @@ -155,10 +155,10 @@ 1 wxALIGN_CENTER 24 - "Name" "Clearance" "Track Width" "Via Size" "Via Drill" "uVia Size" "uVia Drill" "dPair Width" "dPair Gap" + "Name" "Clearance" "Track Width" "Via Size" "Via Drill" "uVia Size" "uVia Drill" "DP Width" "DP Gap" "DP Via Gap" wxALIGN_CENTER - 9 - 130,96,96,96,96,96,96,96,96 + 10 + 130,96,96,96,96,96,96,60,60,60 1 0 diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index c359f457bb..df8630f303 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -383,6 +383,8 @@ void DRC::LoadRules() } } + std::reverse( std::begin( m_ruleSelectors ), std::end( m_ruleSelectors ) ); + BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); bds.m_DRCRuleSelectors = m_ruleSelectors; bds.m_DRCRules = m_rules; diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index b827e471e8..5ef7a70efa 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -65,6 +65,7 @@ enum PCB_DRC_CODE { DRCE_HOLE_NEAR_TRACK, ///< hole too close to track DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits DRCE_TOO_SMALL_TRACK_WIDTH, ///< Too small track width + DRCE_TOO_LARGE_TRACK_WIDTH, ///< Too small track width DRCE_TOO_SMALL_VIA, ///< Too small via size DRCE_TOO_SMALL_VIA_ANNULUS, ///< Via size and drill leave annulus too small DRCE_TOO_SMALL_VIA_DRILL, ///< Too small via drill diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index ed0cb9e382..16ef45fb11 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -315,17 +315,32 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS } else // This is a track segment { - int minWidth = aRefSeg->GetMinWidth( &m_clearanceSource ); + int minWidth, maxWidth; + aRefSeg->GetWidthConstraints( &minWidth, &maxWidth, &m_clearanceSource ); + + int errorCode = 0; + int constraintWidth; if( refSegWidth < minWidth ) + { + errorCode = DRCE_TOO_SMALL_TRACK_WIDTH; + constraintWidth = minWidth; + } + else if( refSegWidth > maxWidth ) + { + errorCode = DRCE_TOO_LARGE_TRACK_WIDTH; + constraintWidth = maxWidth; + } + + if( errorCode ) { wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2; - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_TRACK_WIDTH ); + DRC_ITEM* drcItem = new DRC_ITEM( errorCode ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ), m_clearanceSource, - MessageTextFromValue( userUnits(), minWidth, true ), + MessageTextFromValue( userUnits(), constraintWidth, true ), MessageTextFromValue( userUnits(), refSegWidth, true ) ); drcItem->SetErrorMessage( m_msg ); diff --git a/pcbnew/drc/drc_drilled_hole_tester.cpp b/pcbnew/drc/drc_drilled_hole_tester.cpp index dcca4117af..eeca399e5e 100644 --- a/pcbnew/drc/drc_drilled_hole_tester.cpp +++ b/pcbnew/drc/drc_drilled_hole_tester.cpp @@ -91,21 +91,14 @@ bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad ) if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) ) { - NETCLASS* netclass = aPad->GetNet()->GetNet() == 0 ? bds.GetDefault() - : aPad->GetNetClass(); int minHole = bds.m_MinThroughDrill; wxString minHoleSource = _( "board minimum" ); + DRC_RULE* rule = GetRule( aPad, nullptr, HOLE_CONSTRAINT ); - std::vector matched; - MatchSelectors( bds.m_DRCRuleSelectors, aPad, netclass, nullptr, nullptr, &matched ); - - for( DRC_SELECTOR* selector : matched ) + if( rule ) { - if( selector->m_Rule->m_Hole > minHole ) - { - minHole = selector->m_Rule->m_Hole; - minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); - } + minHole = rule->m_MinHole; + minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); } if( holeSize < minHole ) @@ -141,21 +134,14 @@ bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via ) if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) ) { - NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault() - : via->GetNetClass(); int minHole = bds.m_MinThroughDrill; wxString minHoleSource = _( "board minimum" ); + DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT ); - std::vector matched; - MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched ); - - for( DRC_SELECTOR* selector : matched ) + if( rule ) { - if( selector->m_Rule->m_Hole > minHole ) - { - minHole = selector->m_Rule->m_Hole; - minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); - } + minHole = rule->m_MinHole; + minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); } if( via->GetDrillValue() < minHole ) @@ -191,21 +177,14 @@ bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via ) if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) ) { - NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault() - : via->GetNetClass(); int minHole = bds.m_MicroViasMinDrill; wxString minHoleSource = _( "board minimum" ); + DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT ); - std::vector matched; - MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched ); - - for( DRC_SELECTOR* selector : matched ) + if( rule ) { - if( selector->m_Rule->m_Hole > minHole ) - { - minHole = selector->m_Rule->m_Hole; - minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name ); - } + minHole = rule->m_MinHole; + minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); } if( via->GetDrillValue() < minHole ) diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index efc827ed3c..0595f38dac 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -84,6 +84,7 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const case DRCE_HOLE_NEAR_PAD: msg = _HKI( "Hole too close to pad" ); break; case DRCE_HOLE_NEAR_TRACK: msg = _HKI( "Hole too close to track" ); break; case DRCE_TOO_SMALL_TRACK_WIDTH: msg = _HKI( "Track width too small" ); break; + case DRCE_TOO_LARGE_TRACK_WIDTH: msg = _HKI( "Track width too large" ); break; case DRCE_TOO_SMALL_VIA: msg = _HKI( "Via size too small" ); break; case DRCE_TOO_SMALL_VIA_ANNULUS: msg = _HKI( "Via annulus too small" ); break; case DRCE_TOO_SMALL_MICROVIA: msg = _HKI( "Micro via size too small" ); break; diff --git a/pcbnew/drc/drc_rule.cpp b/pcbnew/drc/drc_rule.cpp index 11544aa6ca..36fb938177 100644 --- a/pcbnew/drc/drc_rule.cpp +++ b/pcbnew/drc/drc_rule.cpp @@ -29,31 +29,12 @@ #include /* - * Match tokens: - * match_netclass - * match_type - * match_layer - * match_all - * match_area + * Rule tokens: + * disallow + * constraint + * condition * - * (selector (match_area "$board") (rule "OSHParkClass3") (priority 100)) - * - * (selector (match_netclass "HV") (rule "HV_internal")) - * (selector (match_netclass "HV") (match_layer "F_Cu") (rule "HV_external")) - * (selector (match_netclass "HV") (match_layer "B_Cu") (rule "HV_external")) - * - * (selector (match_netclass "HV") (match_netclass "HV") (rule "HV2HV")) - * (selector (match_netclass "HV") (match_netclass "HV") (match_layer "F_Cu") (rule "HV2HV_external")) - * (selector (match_netclass "HV") (match_netclass "HV") (match_layer "B_Cu") (rule "HV2HV_external")) - * - * TODO: pads for connector pins or wire pads have even larger clearances. How to encode? - * User attributes on parent footprint? - * - * (selector (match_netclass "HV") (match_type "pad") (match_netclass "HV") (match_type "pad") (rule "pad2PadHV")) - * - * (selector (match_netclass "signal") (match_area "BGA") (rule "neckdown")) - * - * Type tokens: + * Disallow types: * track * via * micro_via @@ -62,40 +43,46 @@ * zone * text * graphic - * board_edge * hole - * npth - * pth * - * Rule tokens: - * allow + * Constraint types: * clearance * annulus_width * track_width * hole * - * Rule modifiers: - * relaxed * - * (rule "HV" (clearance 200) (priority 200)) - * (rule "HV_external" (clearance 400) (priority 200)) - * (rule "HV2HV" (clearance 200) (priority 200)) - * (rule "HV2HV_external" (clearance 500) (priority 200)) - * (rule "pad2padHV" (clearance 500) (priority 200)) + * (rule "HV" (constraint clearance (min 200))) + * (rule "HV_external" (constraint clearance (min 400))) + * (rule "HV2HV" (constraint clearance (min 200))) + * (rule "HV2HV_external" (constraint clearance (min 500))) + * (rule "pad2padHV" (constraint clearance (min 500))) * - * (rule "signal" (clearance 20)) // implied priority of 1 - * (rule "neckdown" (clearance relaxed 15) (priority 2)) + * (rule "signal" (constraint clearance (min 20))) + * (rule "neckdown" (constraint clearance (min 15))) * - * (rule "allowMicrovias" (allow microvia)) + * (rule "disallowMicrovias" (disallow micro_via)) */ - -void MatchSelectors( const std::vector& aSelectors, - const BOARD_ITEM* aItem, const NETCLASS* aNetclass, - const BOARD_ITEM* bItem, const NETCLASS* bNetclass, - std::vector* aSelected ) +DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint ) { - for( DRC_SELECTOR* candidate : aSelectors ) + // JEY TODO: the bulk of this will be replaced by Tom's expression evaluator + + BOARD* board = aItem->GetBoard(); + + if( !board ) + return nullptr; + + NETCLASS* aNetclass = nullptr; + NETCLASS* bNetclass = nullptr; + + if( aItem->IsConnected() ) + aNetclass = static_cast( aItem )->GetEffectiveNetclass(); + + if( bItem && bItem->IsConnected() ) + bNetclass = static_cast( bItem )->GetEffectiveNetclass(); + + for( DRC_SELECTOR* candidate : board->GetDesignSettings().m_DRCRuleSelectors ) { if( candidate->m_MatchNetclasses.size() == 2 ) { @@ -145,11 +132,8 @@ void MatchSelectors( const std::vector& aSelectors, { PCB_LAYER_ID matchLayer = candidate->m_MatchLayers[0]; - if( !aItem->GetLayerSet().test( matchLayer ) - || ( bItem && !bItem->GetLayerSet().test( matchLayer ) ) ) - { + if( !aItem->GetLayerSet().test( matchLayer ) ) continue; - } } if( candidate->m_MatchAreas.size() ) @@ -165,8 +149,12 @@ void MatchSelectors( const std::vector& aSelectors, } // All tests done; if we're still here then it matches - aSelected->push_back( candidate ); + + if( ( candidate->m_Rule->m_ConstraintFlags & aConstraint ) > 0 ) + return candidate->m_Rule; } + + return nullptr; } diff --git a/pcbnew/drc/drc_rule.h b/pcbnew/drc/drc_rule.h index fa7a6e1eb9..42ee9a10fa 100644 --- a/pcbnew/drc/drc_rule.h +++ b/pcbnew/drc/drc_rule.h @@ -32,26 +32,55 @@ class BOARD_ITEM; +#define CLEARANCE_CONSTRAINT (1 << 0) +#define ANNULUS_CONSTRAINT (1 << 1) +#define TRACK_CONSTRAINT (1 << 2) +#define HOLE_CONSTRAINT (1 << 3) + +#define DISALLOW_VIAS (1 << 0) +#define DISALLOW_MICRO_VIAS (1 << 1) +#define DISALLOW_BB_VIAS (1 << 2) +#define DISALLOW_TRACKS (1 << 3) +#define DISALLOW_PADS (1 << 4) +#define DISALLOW_ZONES (1 << 5) +#define DISALLOW_TEXTS (1 << 6) +#define DISALLOW_GRAPHICS (1 << 7) +#define DISALLOW_HOLES (1 << 8) +#define DISALLOW_FOOTPRINTS (1 << 9) + + class DRC_RULE { public: DRC_RULE() : - m_Clearance( 0 ), - m_AnnulusWidth( 0 ), - m_TrackWidth( 0 ), - m_Hole( 0 ) + m_ConstraintFlags( 0 ), + m_DisallowFlags( 0 ), + m_Clearance( { 0, 0, INT_MAX / 2 } ), + m_MinAnnulusWidth( 0 ), + m_TrackConstraint( { 0, 0, INT_MAX / 2 } ), + m_MinHole( 0 ) { } + struct MINOPTMAX + { + int Min; + int Opt; + int Max; + }; + public: - wxString m_Name; + wxString m_Name; + int m_ConstraintFlags; + int m_DisallowFlags; // A 0 value means the property is not modified by this rule. - // A positive value is a minimum. A negative value is a relaxed constraint: the minimum - // is reduced to the absolute value of the constraint. - int m_Clearance; - int m_AnnulusWidth; - int m_TrackWidth; - int m_Hole; + // A positive value is a minimum. + MINOPTMAX m_Clearance; + int m_MinAnnulusWidth; + MINOPTMAX m_TrackConstraint; + int m_MinHole; + + wxString m_Condition; }; @@ -74,10 +103,7 @@ public: }; -void MatchSelectors( const std::vector& aSelectors, - const BOARD_ITEM* aItem, const NETCLASS* aNetclass, - const BOARD_ITEM* bItem, const NETCLASS* bNetclass, - std::vector* aSelected ); +DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint ); diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp index 28a8b27d02..9921ae3db6 100644 --- a/pcbnew/drc/drc_rule_parser.cpp +++ b/pcbnew/drc/drc_rule_parser.cpp @@ -183,14 +183,8 @@ DRC_SELECTOR* DRC_RULES_PARSER::parseDRC_SELECTOR( wxString* aRuleName ) NeedRIGHT(); break; - case T_priority: - NeedNUMBER( "priority" ); - selector->m_Priority = (int)strtol( CurText(), NULL, 10 ); - NeedRIGHT(); - break; - default: - Expecting( "match_netclass, match_type, match_layer, match_area, rule, or priority" ); + Expecting( "match_netclass, match_type, match_layer, match_area, or rule" ); } } @@ -211,61 +205,40 @@ DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE() if( token != T_LEFT ) Expecting( T_LEFT ); - int sign = 1; token = NextTok(); switch( token ) { - case T_allow: - // TODO - break; - - case T_clearance: - if( NextTok() == T_relaxed ) + case T_disallow: + switch( NextTok() ) { - sign = -1; - NextTok(); + case T_track: rule->m_DisallowFlags |= DISALLOW_TRACKS; break; + case T_via: rule->m_DisallowFlags |= DISALLOW_VIAS; break; + case T_micro_via: rule->m_DisallowFlags |= DISALLOW_MICRO_VIAS; break; + case T_blind_via: rule->m_DisallowFlags |= DISALLOW_BB_VIAS; break; + case T_pad: rule->m_DisallowFlags |= DISALLOW_PADS; break; + case T_zone: rule->m_DisallowFlags |= DISALLOW_ZONES; break; + case T_text: rule->m_DisallowFlags |= DISALLOW_TEXTS; break; + case T_graphic: rule->m_DisallowFlags |= DISALLOW_GRAPHICS; break; + case T_hole: rule->m_DisallowFlags |= DISALLOW_HOLES; break; + default: Expecting( "track, via, micro_via, blind_via, pad, zone, text, " + "graphic, or hole" ); } - - rule->m_Clearance = parseValue( T_clearance ) * sign; NeedRIGHT(); break; - case T_track_width: - if( NextTok() == T_relaxed ) - { - sign = -1; - NextTok(); - } - - rule->m_TrackWidth = parseValue( T_track_width ) * sign; - NeedRIGHT(); + case T_constraint: + parseConstraint( rule ); break; - case T_annulus_width: - if( NextTok() == T_relaxed ) - { - sign = -1; - NextTok(); - } - - rule->m_AnnulusWidth = parseValue( T_annulus_width ) * sign; - NeedRIGHT(); - break; - - case T_hole: - if( NextTok() == T_relaxed ) - { - sign = -1; - NextTok(); - } - - rule->m_Hole = parseValue( T_hole ) * sign; + case T_condition: + NeedSYMBOL(); + rule->m_Condition = FromUTF8(); NeedRIGHT(); break; default: - Expecting( "allow, clearance, track_width, annulus_width, or hole" ); + Expecting( "disallow, constraint or condition" ); } } @@ -273,6 +246,82 @@ DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE() } +void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) +{ + T token; + int constraintType; + int value; + + switch( NextTok() ) + { + case T_clearance: constraintType = CLEARANCE_CONSTRAINT; break; + case T_track_width: constraintType = TRACK_CONSTRAINT; break; + case T_annulus_width: constraintType = ANNULUS_CONSTRAINT; break; + case T_hole: constraintType = HOLE_CONSTRAINT; break; + default: Expecting( "clearance, track_width, annulus_width, or hole" ); return; + } + + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) + { + if( token != T_LEFT ) + Expecting( T_LEFT ); + + token = NextTok(); + + switch( token ) + { + case T_min: + NextTok(); + value = parseValue( token ); + + switch( constraintType ) + { + case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Min = value; break; + case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Min = value; break; + case ANNULUS_CONSTRAINT: aRule->m_MinAnnulusWidth = value; break; + case HOLE_CONSTRAINT: aRule->m_MinHole = value; break; + } + + NeedRIGHT(); + break; + + case T_max: + NextTok(); + value = parseValue( token ); + + switch( constraintType ) + { + case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Max = value; break; + case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Max = value; break; + default: Expecting( "min" ); + } + + NeedRIGHT(); + break; + + case T_opt: + NextTok(); + value = parseValue( token ); + + switch( constraintType ) + { + case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Opt = value; break; + case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Opt = value; break; + default: Expecting( "min" ); + } + + NeedRIGHT(); + break; + + default: + Expecting( "allow or constraint" ); + } + } + + aRule->m_ConstraintFlags |= constraintType; +} + + int DRC_RULES_PARSER::parseValue( DRCRULE_T::T aToken ) { char* tmp; @@ -283,20 +332,14 @@ int DRC_RULES_PARSER::parseValue( DRCRULE_T::T aToken ) if( errno ) { - wxString error; - error.Printf( _( "Invalid floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ), - GetChars( CurSource() ), CurLineNumber(), CurOffset() ); - - THROW_IO_ERROR( error ); + THROW_PARSE_ERROR( _( "Invalid floating point number" ), CurSource(), CurLine(), + CurLineNumber(), CurOffset() ); } if( CurText() == tmp ) { - wxString error; - error.Printf( _( "Missing floating point number in\nfile: \"%s\"\nline: %d\noffset: %d" ), - GetChars( CurSource() ), CurLineNumber(), CurOffset() ); - - THROW_IO_ERROR( error ); + THROW_PARSE_ERROR( _( "Missing floating point number" ), CurSource(), CurLine(), + CurLineNumber(), CurOffset() ); } return KiROUND( fval * IU_PER_MM ); diff --git a/pcbnew/drc/drc_rule_parser.h b/pcbnew/drc/drc_rule_parser.h index dc305d1c81..3a154e68a5 100644 --- a/pcbnew/drc/drc_rule_parser.h +++ b/pcbnew/drc/drc_rule_parser.h @@ -49,6 +49,7 @@ private: DRC_RULE* parseDRC_RULE(); + void parseConstraint( DRC_RULE* aRule ); int parseValue( DRCRULE_T::T aToken ); private: