Jeff Young 2020-05-16 00:25:33 +01:00
parent 1d914133c9
commit d3f017d825
31 changed files with 1084 additions and 141 deletions

2
.gitignore vendored
View File

@ -3,6 +3,8 @@ boost_root
Build*
build*
common/fp_lib_table_keywords.cpp
common/drc_rules_keywords.cpp
common/drc_rules_lexer.h
common/netlist_keywords.*
common/netlist_lexer.h
common/pcb_plot_params_lexer.h

View File

@ -463,6 +463,7 @@ set( PCB_COMMON_SRCS
${CMAKE_SOURCE_DIR}/pcbnew/connectivity/connectivity_data.cpp
${CMAKE_SOURCE_DIR}/pcbnew/convert_drawsegment_list_to_polygon.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_item.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_rule.cpp
${CMAKE_SOURCE_DIR}/pcbnew/eagle_plugin.cpp
${CMAKE_SOURCE_DIR}/pcbnew/footprint_editor_settings.cpp
${CMAKE_SOURCE_DIR}/pcbnew/gpcb_plugin.cpp
@ -525,6 +526,15 @@ make_lexer(
PCBPLOTPARAMS_T
)
# auto-generate drc_rules_lexer.h and drc_rules_keywords.cpp
make_lexer(
common
drc_rules.keywords
drc_rules_lexer.h
drc_rules_keywords.cpp
DRCRULE_T
)
# auto-generate pcbnew_sexpr.h and pcbnew_sexpr.cpp
make_lexer(
pcbcommon

25
common/drc_rules.keywords Normal file
View File

@ -0,0 +1,25 @@
allow
annulus_width
blind_via
board_edge
clearance
track_width
graphic
hole
match_area
match_layer
match_netclass
match_type
micro_via
npth
pad
priority
pth
relaxed
rule
selector
text
track
version
via
zone

View File

@ -29,6 +29,8 @@
#include <netclass.h>
#include <config_params.h>
#include <board_stackup_manager/class_board_stackup.h>
#include <drc/drc_rule.h>
#define DEFAULT_SILK_LINE_WIDTH 0.12
#define DEFAULT_COPPER_LINE_WIDTH 0.20
@ -194,6 +196,7 @@ struct TEXT_ITEM_INFO
// forward declaration from class_track.h
enum class VIATYPE : int;
/**
* BOARD_DESIGN_SETTINGS
* contains design settings for a BOARD object.
@ -207,7 +210,9 @@ public:
std::vector<DIFF_PAIR_DIMENSION> m_DiffPairDimensionsList;
// List of netclasses. There is always the default netclass.
NETCLASSES m_NetClasses;
NETCLASSES m_NetClasses;
std::vector<DRC_SELECTOR*> m_DRCRuleSelectors;
std::vector<DRC_RULE*> m_DRCRules;
bool m_MicroViasAllowed; ///< true to allow micro vias
bool m_BlindBuriedViaAllowed; ///< true to allow blind/buried vias
@ -389,6 +394,10 @@ 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,
@ -863,10 +872,6 @@ public:
bool GetTextUpright( PCB_LAYER_ID aLayer ) const;
int GetLayerClass( PCB_LAYER_ID aLayer ) const;
private:
void formatNetClass( NETCLASS* aNetClass, OUTPUTFORMATTER* aFormatter, int aNestLevel,
int aControlBits ) const;
};
#endif // BOARD_DESIGN_SETTINGS_H_

View File

@ -103,6 +103,16 @@ enum KICAD_T
PCB_ITEM_LIST_T, ///< class BOARD_ITEM_LIST, a list of board items
PCB_NETINFO_T, ///< class NETINFO_ITEM, a description of a net
PCB_LOCATE_STDVIA_T,
PCB_LOCATE_UVIA_T,
PCB_LOCATE_BBVIA_T,
PCB_LOCATE_TEXT_T,
PCB_LOCATE_GRAPHIC_T,
PCB_LOCATE_HOLE_T,
PCB_LOCATE_PTH_T,
PCB_LOCATE_NPTH_T,
PCB_LOCATE_BOARD_EDGE_T,
// Schematic draw Items. The order of these items effects the sort order.
// It is currently ordered to mimic the old Eeschema locate behavior where
// the smallest item is the selected item.
@ -235,6 +245,11 @@ constexpr KICAD_T BaseType( const KICAD_T aType )
case SCH_COMPONENT_LOCATE_POWER_T:
return SCH_COMPONENT_T;
case PCB_LOCATE_HOLE_T:
case PCB_LOCATE_PTH_T:
case PCB_LOCATE_NPTH_T:
return PCB_LOCATE_HOLE_T;
default:
return aType;
}

View File

@ -233,6 +233,7 @@ set( PCBNEW_DRC_SRCS
drc/drc_courtyard_tester.cpp
drc/drc.cpp
drc/drc_clearance_test_functions.cpp
drc/drc_rule_parser.cpp
)
set( PCBNEW_NETLIST_SRCS

View File

@ -79,36 +79,63 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert )
}
int BOARD_CONNECTED_ITEM::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const
int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const
{
NETCLASSPTR netclass;
NETCLASS* myNetclass = nullptr;
NETCLASS* itemNetclass = nullptr;
BOARD_DESIGN_SETTINGS* bds = nullptr;
// NB: we must check the net first, as when it is 0 GetNetClass() will return the
// orphaned net netclass, not the default netclass.
if( GetBoard() )
{
if( m_netinfo->GetNet() == 0 )
netclass = GetBoard()->GetDesignSettings().GetDefault();
myNetclass = GetBoard()->GetDesignSettings().GetDefault().get();
else
netclass = GetNetClass();
myNetclass = GetNetClass().get();
bds = &GetBoard()->GetDesignSettings();
}
// No clearance if "this" is not (yet) linked to a board therefore no available netclass
int myClearance = netclass ? netclass->GetClearance() : 0;
if( aItem && aItem->GetBoard() && dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem ) )
{
if( dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNet()->GetNet() == 0 )
itemNetclass = GetBoard()->GetDesignSettings().GetDefault().get();
else
itemNetclass = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetClass().get();
if( aItem && aItem->GetClearance() > myClearance )
return aItem->GetClearance( nullptr, aSource );
bds = &aItem->GetBoard()->GetDesignSettings();
}
int myClearance = myNetclass ? myNetclass->GetClearance() : 0;
int itemClearance = itemNetclass ? itemNetclass->GetClearance() : 0;
wxString ruleSource;
int ruleClearance = bds ? bds->GetRuleClearance( this, myNetclass,
aItem, itemNetclass, &ruleSource ) : 0;
int clearance = std::max( std::max( myClearance, itemClearance ), ruleClearance );
if( aSource )
{
if( netclass )
*aSource = wxString::Format( _( "%s netclass clearance" ),
netclass->GetName() );
if( clearance == myClearance && myNetclass )
{
*aSource = wxString::Format( _( "'%s' netclass clearance" ), myNetclass->GetName() );
}
else if( clearance == itemClearance && itemNetclass )
{
*aSource = wxString::Format( _( "'%s' netclass clearance" ), itemNetclass->GetName() );
}
else if( clearance == ruleClearance && !ruleSource.IsEmpty() )
{
*aSource = wxString::Format( _( "'%s' rule clearance" ), ruleSource );
}
else
{
*aSource = _( "No netclass" );
}
}
return myClearance;
return clearance;
}

View File

@ -157,12 +157,11 @@ public:
* returned clearance is the greater of this object's NETCLASS clearance and
* aItem's NETCLASS clearance. If \a aItem is NULL, then this objects clearance
* is returned.
* @param aItem is an optional BOARD_CONNECTED_ITEM
* @param aItem is an optional BOARD_ITEM
* @param aSource [out] optionally reports the source as a user-readable string
* @return int - the clearance in internal units.
*/
virtual int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr,
wxString* aSource = nullptr ) const;
virtual int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const;
/**
* Function GetNetClass

View File

@ -36,6 +36,7 @@
#include <board_design_settings.h>
#include <drc/drc.h>
#include <widgets/ui_common.h>
#include <drc/drc_rule.h>
#define CopperLayerCountKey wxT( "CopperLayerCount" )
#define BoardThicknessKey wxT( "BoardThickness" )
@ -962,12 +963,11 @@ int BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue()
{
int clearance = m_NetClasses.GetDefault()->GetClearance();
//Read list of Net Classes
for( NETCLASSES::const_iterator nc = m_NetClasses.begin(); nc != m_NetClasses.end(); ++nc )
{
NETCLASSPTR netclass = nc->second;
clearance = std::max( clearance, netclass->GetClearance() );
}
for( const std::pair<wxString, NETCLASSPTR>& netclass : m_NetClasses.NetClasses() )
clearance = std::max( clearance, netclass.second->GetClearance() );
for( const DRC_RULE* rule : m_DRCRules )
clearance = std::max( clearance, rule->m_Clearance );
return clearance;
}
@ -977,11 +977,48 @@ int BOARD_DESIGN_SETTINGS::GetSmallestClearanceValue()
{
int clearance = m_NetClasses.GetDefault()->GetClearance();
//Read list of Net Classes
for( NETCLASSES::const_iterator nc = m_NetClasses.begin(); nc != m_NetClasses.end(); ++nc )
for( const std::pair<wxString, NETCLASSPTR>& netclass : m_NetClasses.NetClasses() )
clearance = std::min( clearance, netclass.second->GetClearance() );
return clearance;
}
int BOARD_DESIGN_SETTINGS::GetRuleClearance( const BOARD_ITEM* aItem, const NETCLASS* aNetclass,
const BOARD_ITEM* bItem, const NETCLASS* bNetclass,
wxString* aSource )
{
std::vector<DRC_SELECTOR*> matched;
MatchSelectors( m_DRCRuleSelectors, aItem, aNetclass, bItem, bNetclass, &matched );
std::sort( matched.begin(), matched.end(),
[]( DRC_SELECTOR* a, DRC_SELECTOR* b ) -> bool
{
return a->m_Priority < b->m_Priority;
});
int clearance = 0;
for( DRC_SELECTOR* selector : matched )
{
NETCLASSPTR netclass = nc->second;
clearance = std::min( clearance, netclass->GetClearance() );
// 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 );
*aSource = selector->m_Rule->m_Name;
}
else if( selector->m_Rule->m_Clearance < 0 )
{
clearance = std::min( clearance, abs( selector->m_Rule->m_Clearance ) );
*aSource = selector->m_Rule->m_Name;
}
}
return clearance;

View File

@ -88,6 +88,20 @@ public:
return aItem && PCB_DIMENSION_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_GRAPHIC_T )
return true;
}
return false;
}
void SetValue( int aValue ) { m_Value = aValue; }
int GetValue() const { return m_Value; }

View File

@ -77,6 +77,22 @@ public:
return aItem && PCB_LINE_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_GRAPHIC_T )
return true;
else if( *p == PCB_LOCATE_BOARD_EDGE_T )
return m_Layer == Edge_Cuts;
}
return false;
}
/** Polygonal shape is not always filled.
* For now it is filled on all layers but Edge_Cut layer
*/

View File

@ -56,6 +56,22 @@ public:
return aItem && PCB_MODULE_EDGE_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_GRAPHIC_T )
return true;
else if( *p == PCB_LOCATE_BOARD_EDGE_T )
return m_Layer == Edge_Cuts;
}
return false;
}
/**
* Move an edge of the footprint.
* This is a footprint shape modification.

View File

@ -623,14 +623,14 @@ wxPoint D_PAD::ShapePos() const
}
int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const
int D_PAD::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const
{
int myClearance;
int clearance;
// A pad can have specific clearance that overrides its NETCLASS clearance value
if( m_LocalClearance )
{
myClearance = m_LocalClearance;
clearance = m_LocalClearance;
if( aSource )
*aSource = wxString::Format( _( "pad %s clearance" ), GetName() );
@ -639,22 +639,19 @@ int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const
// A footprint can have a specific clearance value
else if( GetParent() && GetParent()->GetLocalClearance() )
{
myClearance = GetParent()->GetLocalClearance();
clearance = GetParent()->GetLocalClearance();
if( aSource )
*aSource = wxString::Format( _( "%s footprint clearance" ), GetParent()->GetReference() );
}
// Otherwise use the baseclass method to fetch the netclass setting
// Otherwise use the baseclass method to fetch the netclass and/or rule setting
else
{
myClearance = BOARD_CONNECTED_ITEM::GetClearance( nullptr, aSource );
clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, aSource );
}
if( aItem && aItem->GetClearance() > myClearance )
return aItem->GetClearance( nullptr, aSource );
return myClearance;
return clearance;
}

View File

@ -140,6 +140,27 @@ public:
return aItem && PCB_PAD_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_CONNECTED_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( m_Drill.x > 0 && m_Drill.y > 0 )
{
if( *p == PCB_LOCATE_HOLE_T )
return true;
else if( *p == PCB_LOCATE_PTH_T && m_Attribute != PAD_ATTRIB_HOLE_NOT_PLATED )
return true;
else if( *p == PCB_LOCATE_NPTH_T && m_Attribute == PAD_ATTRIB_HOLE_NOT_PLATED )
return true;
}
}
return false;
}
MODULE* GetParent() const { return (MODULE*) m_Parent; }
/**
@ -459,12 +480,11 @@ public:
* returned clearance is the greater of this object's clearance and
* aItem's clearance. If \a aItem is NULL, then this objects clearance
* is returned.
* @param aItem is an optional BOARD_CONNECTED_ITEM
* @param aItem is an optional BOARD_ITEM
* @param aSource [out] optionally reports the source as a user-readable string
* @return int - the clearance in internal units.
*/
int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr,
wxString* aSource = nullptr ) const override;
int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override;
// Mask margins handling:

View File

@ -53,6 +53,20 @@ public:
return aItem && PCB_TEXT_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_TEXT_T )
return true;
}
return false;
}
wxString GetShownText( int aDepth = 0 ) const override;
bool Matches( wxFindReplaceData& aSearchData, void* aAuxData ) override

View File

@ -74,6 +74,20 @@ public:
return aItem && PCB_MODULE_TEXT_T == aItem->Type();
}
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_TEXT_T )
return true;
}
return false;
}
bool Matches( wxFindReplaceData& aSearchData, void* aAuxData ) override
{
return BOARD_ITEM::Matches( GetShownText(), aSearchData );

View File

@ -117,7 +117,7 @@ BITMAP_DEF VIA::GetMenuImage() const
}
int TRACK::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const
int TRACK::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const
{
// Currently tracks have no specific clearance parameter on a per track or per
// segment basis. The NETCLASS clearance is used.

View File

@ -200,12 +200,11 @@ public:
* returned clearance is the greater of this object's clearance and
* aItem's clearance. If \a aItem is NULL, then this objects clearance
* is returned.
* @param aItem is an optional BOARD_CONNECTED_ITEM
* @param aItem is an optional BOARD_ITEM
* @param aSource [out] optionally reports the source as a user-readable string
* @return int - the clearance in internal units.
*/
int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr,
wxString* aSource = nullptr ) const override;
int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override;
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
@ -350,6 +349,24 @@ public:
// Do not create a copy constructor. The one generated by the compiler is adequate.
bool IsType( const KICAD_T aScanTypes[] ) const override
{
if( BOARD_CONNECTED_ITEM::IsType( aScanTypes ) )
return true;
for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
{
if( *p == PCB_LOCATE_STDVIA_T && m_ViaType == VIATYPE::THROUGH )
return true;
else if( *p == PCB_LOCATE_UVIA_T && m_ViaType == VIATYPE::MICROVIA )
return true;
else if( *p == PCB_LOCATE_BBVIA_T && m_ViaType == VIATYPE::BLIND_BURIED )
return true;
}
return false;
}
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
virtual LSET GetLayerSet() const override;

View File

@ -458,33 +458,31 @@ bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccur
}
int ZONE_CONTAINER::GetClearance( BOARD_CONNECTED_ITEM* aItem, wxString* aSource ) const
int ZONE_CONTAINER::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const
{
if( m_isKeepout )
return 0;
// The actual zone clearance is the biggest of the zone netclass clearance
// and the zone clearance setting in the zone properties dialog.
int myClearance = m_ZoneClearance;
int zoneClearance = m_ZoneClearance;
wxString source;
int clearance = BOARD_CONNECTED_ITEM::GetClearance( aItem, &source );
if( aSource )
*aSource = _( "zone clearance" );
if( !m_isKeepout ) // Net class has no meaning for a keepout area.
if( clearance > zoneClearance )
{
NETCLASSPTR myClass = GetNetClass();
if( aSource )
*aSource = source;
if( myClass->GetClearance() > myClearance )
{
myClearance = myClass->GetClearance();
if( aSource )
*aSource = wxString::Format( _( "'%s' netclass clearance" ), myClass->GetName() );
}
return clearance;
}
else
{
if( aSource )
*aSource = _( "zone clearance" );
// Get the final clearance between me and aItem
if( aItem && aItem->GetClearance() > myClearance )
return aItem->GetClearance( nullptr, aSource );
return myClearance;
return zoneClearance;
}
}

View File

@ -110,8 +110,7 @@ public:
*/
const EDA_RECT GetBoundingBox() const override;
int GetClearance( BOARD_CONNECTED_ITEM* aItem = nullptr,
wxString* aSource = nullptr ) const override;
int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const override;
/**
* Function IsOnCopperLayer

View File

@ -60,7 +60,6 @@ DIALOG_DRC::DIALOG_DRC( DRC* aTester, PCB_EDIT_FRAME* aEditorFrame, wxWindow* aP
m_tester = aTester;
m_brdEditor = aEditorFrame;
m_currentBoard = m_brdEditor->GetBoard();
m_BrdSettings = m_brdEditor->GetBoard()->GetDesignSettings();
m_markerTreeModel = new RC_TREE_MODEL( m_brdEditor, m_markerDataView );
m_markerDataView->AssociateModel( m_markerTreeModel );
@ -117,7 +116,6 @@ void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
// updating data which can be modified outside the dialog (DRC parameters, units ...)
// because the dialog is not modal
m_BrdSettings = m_brdEditor->GetBoard()->GetDesignSettings();
displayDRCValues();
m_markerTreeModel->SetProvider( m_markersProvider );
@ -129,9 +127,9 @@ void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
void DIALOG_DRC::displayDRCValues()
{
m_trackMinWidth.SetValue( m_BrdSettings.m_TrackMinWidth );
m_viaMinSize.SetValue( m_BrdSettings.m_ViasMinSize );
m_uviaMinSize.SetValue( m_BrdSettings.m_MicroViasMinSize );
m_trackMinWidth.SetValue( bds().m_TrackMinWidth );
m_viaMinSize.SetValue( bds().m_ViasMinSize );
m_uviaMinSize.SetValue( bds().m_MicroViasMinSize );
}
@ -162,11 +160,9 @@ void DIALOG_DRC::initValues()
void DIALOG_DRC::setDRCParameters()
{
m_BrdSettings.m_TrackMinWidth = (int) m_trackMinWidth.GetValue();
m_BrdSettings.m_ViasMinSize = (int) m_viaMinSize.GetValue();
m_BrdSettings.m_MicroViasMinSize = (int) m_uviaMinSize.GetValue();
m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings );
bds().m_TrackMinWidth = (int) m_trackMinWidth.GetValue();
bds().m_ViasMinSize = (int) m_viaMinSize.GetValue();
bds().m_MicroViasMinSize = (int) m_uviaMinSize.GetValue();
}
@ -287,7 +283,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
wxMenu menu;
wxString msg;
switch( m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] )
switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] )
{
case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
@ -307,7 +303,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
menu.AppendSeparator();
if( m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
{
msg.Printf( _( "Change severity to Error for all '%s' violations" ),
rcItem->GetErrorText( rcItem->GetErrorCode() ),
@ -356,8 +352,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
break;
case 3:
m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings );
bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
// Rebuild model and view
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->SetProvider( m_markersProvider );
@ -365,8 +360,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
break;
case 4:
m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings );
bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
// Rebuild model and view
static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->SetProvider( m_markersProvider );
@ -375,8 +369,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
case 5:
{
m_BrdSettings.m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
m_brdEditor->GetBoard()->SetDesignSettings( m_BrdSettings );
bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
std::vector<MARKER_PCB*>& markers = m_brdEditor->GetBoard()->Markers();

View File

@ -47,8 +47,6 @@ class
DIALOG_DRC: public DIALOG_DRC_BASE
{
public:
BOARD_DESIGN_SETTINGS m_BrdSettings;
/// Constructors
DIALOG_DRC( DRC* aTester, PCB_EDIT_FRAME* aEditorFrame, wxWindow* aParent );
~DIALOG_DRC();
@ -94,6 +92,8 @@ private:
void deleteAllMarkers();
void refreshBoardEditor();
BOARD_DESIGN_SETTINGS& bds() { return m_currentBoard->GetDesignSettings(); }
BOARD* m_currentBoard; // the board currently on test
DRC* m_tester;
PCB_EDIT_FRAME* m_brdEditor;

View File

@ -54,6 +54,8 @@
#include <drc/drc_item.h>
#include <drc/drc_courtyard_tester.h>
#include <tools/zone_filler_tool.h>
#include <confirm.h>
#include "drc_rule_parser.h"
DRC::DRC() :
PCB_TOOL_BASE( "pcbnew.DRCTool" ),
@ -95,6 +97,8 @@ void DRC::Reset( RESET_REASON aReason )
DestroyDRCDialog( wxID_OK );
m_pcb = m_pcbEditorFrame->GetBoard();
readRules();
}
}
@ -350,8 +354,36 @@ int DRC::TestZoneToZoneOutlines()
}
void DRC::readRules()
{
wxString rulesFilepath = m_pcbEditorFrame->Prj().AbsolutePath( "drc-rules" );
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
bds.m_DRCRuleSelectors.clear();
bds.m_DRCRules.clear();
FILE* fp = wxFopen( rulesFilepath, wxT( "rt" ) );
if( fp )
{
try
{
DRC_RULES_PARSER parser( m_pcb, fp, rulesFilepath );
parser.Parse( bds.m_DRCRuleSelectors, bds.m_DRCRules );
}
catch( PARSE_ERROR& pe )
{
DisplayError( m_drcDialog, pe.What() );
}
}
}
void DRC::RunTests( wxTextCtrl* aMessages )
{
// TODO: timestamp file and read only if newer
readRules();
// be sure m_pcb is the current board, not a old one
// ( the board can be reloaded )
m_pcb = m_pcbEditorFrame->GetBoard();
@ -727,7 +759,7 @@ void DRC::testPad2Pad()
void DRC::testDrilledHoles()
{
BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
// Test drilled holes to minimize drill bit breakage.
//
@ -738,8 +770,8 @@ void DRC::testDrilledHoles()
struct DRILLED_HOLE
{
wxPoint m_location;
int m_drillRadius;
BOARD_ITEM* m_owner;
int m_drillRadius = 0;
BOARD_ITEM* m_owner = nullptr;
};
std::vector<DRILLED_HOLE> holes;
@ -750,19 +782,37 @@ void DRC::testDrilledHoles()
{
for( D_PAD* pad : mod->Pads( ) )
{
int minDimension = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
int holeSize = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
if( minDimension == 0 )
if( holeSize == 0 )
continue;
if( !dsnSettings.Ignore( DRCE_TOO_SMALL_PAD_DRILL )
&& minDimension < dsnSettings.m_MinThroughDrill )
NETCLASS* netclass = pad->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
: pad->GetNetClass().get();
int minHole = bds.m_MinThroughDrill;
wxString minHoleSource = _( "board" );
std::vector<DRC_SELECTOR*> matched;
MatchSelectors( bds.m_DRCRuleSelectors, pad, netclass, nullptr, nullptr, &matched );
for( DRC_SELECTOR* selector : matched )
{
if( selector->m_Rule->m_Hole > minHole )
{
minHole = selector->m_Rule->m_Hole;
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
}
}
if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) && holeSize < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_PAD_DRILL );
msg.Printf( drcItem->GetErrorText() + _( " (board min through hole %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_MinThroughDrill, true ),
MessageTextFromValue( userUnits(), minDimension, true ) );
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( userUnits(), minHole, true ),
MessageTextFromValue( userUnits(), holeSize, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( pad );
@ -788,15 +838,39 @@ void DRC::testDrilledHoles()
if( !via )
continue;
NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
: via->GetNetClass().get();
int minHole = 0;
wxString minHoleSource;
std::vector<DRC_SELECTOR*> matched;
MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched );
for( DRC_SELECTOR* selector : matched )
{
if( selector->m_Rule->m_Hole > minHole )
{
minHole = selector->m_Rule->m_Hole;
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
}
}
if( via->GetViaType() == VIATYPE::MICROVIA )
{
if( !dsnSettings.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL )
&& via->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
if( bds.m_MicroViasMinDrill > minHole )
{
minHole = bds.m_MicroViasMinDrill;
minHoleSource = _( "board" );
}
if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) && via->GetDrillValue() < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinDrill, true ),
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( userUnits(), minHole, true ),
MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) );
drcItem->SetErrorMessage( msg );
@ -808,13 +882,19 @@ void DRC::testDrilledHoles()
}
else
{
if( !dsnSettings.Ignore( DRCE_TOO_SMALL_VIA_DRILL )
&& via->GetDrillValue() < dsnSettings.m_MinThroughDrill )
if( bds.m_MinThroughDrill > minHole )
{
minHole = bds.m_MinThroughDrill;
minHoleSource = _( "board" );
}
if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) && via->GetDrillValue() < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL );
msg.Printf( drcItem->GetErrorText() + _( " (board min through hole %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_MinThroughDrill, true ),
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( userUnits(), minHole, true ),
MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) );
drcItem->SetErrorMessage( msg );
@ -831,7 +911,7 @@ void DRC::testDrilledHoles()
}
}
if( dsnSettings.m_HoleToHoleMin == 0 || dsnSettings.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
if( bds.m_HoleToHoleMin == 0 || bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
return;
for( size_t ii = 0; ii < holes.size(); ++ii )
@ -849,12 +929,12 @@ void DRC::testDrilledHoles()
int actual = KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) );
actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius );
if( actual < dsnSettings.m_HoleToHoleMin )
if( actual < bds.m_HoleToHoleMin )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_DRILLED_HOLES_TOO_CLOSE );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_HoleToHoleMin, true ),
MessageTextFromValue( userUnits(), bds.m_HoleToHoleMin, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );

View File

@ -162,7 +162,7 @@ private:
PCB_EDIT_FRAME* m_pcbEditorFrame; // The pcb frame editor which owns the board
BOARD* m_pcb;
SHAPE_POLY_SET m_board_outlines; // The board outline including cutouts
DIALOG_DRC* m_drcDialog;
DIALOG_DRC* m_drcDialog;
std::vector<DRC_ITEM*> m_unconnected; // list of unconnected pads
std::vector<DRC_ITEM*> m_footprints; // list of footprint warnings
@ -177,6 +177,8 @@ private:
*/
void updatePointers();
void readRules();
EDA_UNITS userUnits() const { return m_pcbEditorFrame->GetUserUnits(); }
/**

View File

@ -139,13 +139,18 @@ bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint
void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt,
bool aTestZones )
{
BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
wxString msg;
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
std::vector<DRC_SELECTOR*> matched;
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
LSET layerMask = aRefSeg->GetLayerSet();
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
wxString msg;
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
LSET layerMask = aRefSeg->GetLayerSet();
NETCLASS* netclass = aRefSeg->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
: aRefSeg->GetNetClass().get();
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
MatchSelectors( bds.m_DRCRuleSelectors, aRefSeg, netclass, nullptr, nullptr, &matched );
/******************************************/
@ -154,17 +159,45 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
if( aRefSeg->Type() == PCB_VIA_T )
{
VIA *refvia = static_cast<VIA*>( aRefSeg );
VIA *refvia = static_cast<VIA*>( aRefSeg );
int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
int minAnnulus;
wxString minAnnulusSource;
for( DRC_SELECTOR* selector : matched )
{
if( selector->m_Rule->m_AnnulusWidth > minAnnulus )
{
minAnnulus = selector->m_Rule->m_AnnulusWidth;
minAnnulusSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
}
}
// test if the via size is smaller than minimum
if( refvia->GetViaType() == VIATYPE::MICROVIA )
{
if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
if( viaAnnulus < minAnnulus )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS );
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
minAnnulusSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( marker );
}
if( refvia->GetWidth() < bds.m_MicroViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinSize, true ),
MessageTextFromValue( userUnits(), bds.m_MicroViasMinSize, true ),
MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
drcItem->SetErrorMessage( msg );
@ -176,14 +209,19 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
}
else
{
int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
if( bds.m_ViasMinAnnulus > minAnnulus )
{
minAnnulus = bds.m_ViasMinAnnulus;
minAnnulusSource = _( "board" );
}
if( viaAnnulus < dsnSettings.m_ViasMinAnnulus )
if( viaAnnulus < minAnnulus )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinSize, true ),
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
minAnnulusSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
drcItem->SetErrorMessage( msg );
@ -193,12 +231,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
addMarkerToPcb( marker );
}
if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
if( refvia->GetWidth() < bds.m_ViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinSize, true ),
MessageTextFromValue( userUnits(), bds.m_ViasMinSize, true ),
MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
drcItem->SetErrorMessage( msg );
@ -228,7 +266,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
}
// test if the type of via is allowed due to design rules
if( refvia->GetViaType() == VIATYPE::MICROVIA && !dsnSettings.m_MicroViasAllowed )
if( refvia->GetViaType() == VIATYPE::MICROVIA && !bds.m_MicroViasAllowed )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MICROVIA_NOT_ALLOWED );
@ -241,7 +279,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
}
// test if the type of via is allowed due to design rules
if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed )
if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !bds.m_BlindBuriedViaAllowed )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_BURIED_VIA_NOT_ALLOWED );
@ -266,7 +304,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
if( layer1 > layer2 )
std::swap( layer1, layer2 );
if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 )
if( layer2 == B_Cu && layer1 == bds.GetCopperLayerCount() - 2 )
err = false;
else if( layer1 == F_Cu && layer2 == In1_Cu )
err = false;
@ -290,14 +328,27 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
}
else // This is a track segment
{
if( refSegWidth < dsnSettings.m_TrackMinWidth )
int minWidth = bds.m_TrackMinWidth;
wxString minWidthSource = _( "board" );
for( DRC_SELECTOR* selector : matched )
{
if( selector->m_Rule->m_TrackWidth > minWidth )
{
minWidth = selector->m_Rule->m_AnnulusWidth;
minWidthSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
}
}
if( refSegWidth < minWidth )
{
wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_TRACK_WIDTH );
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), dsnSettings.m_TrackMinWidth, true ),
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
minWidthSource,
MessageTextFromValue( userUnits(), bds.m_TrackMinWidth, true ),
MessageTextFromValue( userUnits(), refSegWidth, true ) );
drcItem->SetErrorMessage( msg );
@ -538,15 +589,17 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
/* Phase 4: test DRC with to board edge */
/***********************************************/
{
SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
static DRAWSEGMENT dummyEdge;
dummyEdge.SetLayer( Edge_Cuts );
SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
wxString clearanceSource;
int minClearance = aRefSeg->GetClearance( nullptr, &clearanceSource );
int minClearance = aRefSeg->GetClearance( &dummyEdge, &clearanceSource );
if( dsnSettings.m_CopperEdgeClearance > minClearance )
if( bds.m_CopperEdgeClearance > minClearance )
{
minClearance = dsnSettings.m_CopperEdgeClearance;
clearanceSource = _( "board edge clearance" );
minClearance = bds.m_CopperEdgeClearance;
clearanceSource = _( "board edge" );
}
int halfWidth = refSegWidth / 2;
@ -585,7 +638,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_EDGE );
msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );

View File

@ -94,7 +94,6 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const
case DRCE_TRACK_NEAR_EDGE: msg = _HKI( "Track too close to board edge" ); break;
case DRCE_INVALID_OUTLINE: msg = _HKI( "Board has malformed outline" ); break;
// use &lt; since this is text ultimately embedded in HTML
case DRCE_NETCLASS_TRACKWIDTH: msg = _HKI( "NetClass Track Width too small" ); break;
case DRCE_NETCLASS_CLEARANCE: msg = _HKI( "NetClass Clearance too small" ); break;
case DRCE_NETCLASS_VIAANNULUS: msg = _HKI( "NetClass via annulus too small" ); break;

172
pcbnew/drc/drc_rule.cpp Normal file
View File

@ -0,0 +1,172 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <fctsys.h>
#include <drc/drc_rule.h>
#include <board_design_settings.h>
#include <class_board.h>
#include <class_board_item.h>
/*
* Match tokens:
* match_netclass
* match_type
* match_layer
* match_all
* match_area
*
* (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:
* track
* via
* micro_via
* blind_via
* pad
* zone
* text
* graphic
* board_edge
* hole
* npth
* pth
*
* Rule tokens:
* allow
* 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 "signal" (clearance 20)) // implied priority of 1
* (rule "neckdown" (clearance relaxed 15) (priority 2))
*
* (rule "allowMicrovias" (allow microvia))
*/
void MatchSelectors( const std::vector<DRC_SELECTOR*>& aSelectors,
const BOARD_ITEM* aItem, const NETCLASS* aNetclass,
const BOARD_ITEM* bItem, const NETCLASS* bNetclass,
std::vector<DRC_SELECTOR*>* aSelected )
{
for( DRC_SELECTOR* candidate : aSelectors )
{
if( candidate->m_MatchNetclasses.size() == 2 )
{
if( !bItem )
continue;
NETCLASS* firstNetclass = candidate->m_MatchNetclasses[0];
NETCLASS* secondNetclass = candidate->m_MatchNetclasses[1];
if( !( aNetclass == firstNetclass && bNetclass == secondNetclass )
&& !( aNetclass == secondNetclass && bNetclass == firstNetclass ) )
{
continue;
}
}
else if( candidate->m_MatchNetclasses.size() == 1 )
{
NETCLASS* matchNetclass = candidate->m_MatchNetclasses[0];
if( matchNetclass != aNetclass && !( bItem && matchNetclass == bNetclass ) )
continue;
}
if( candidate->m_MatchTypes.size() == 2 )
{
if( !bItem )
continue;
KICAD_T firstType[2] = { candidate->m_MatchTypes[0], EOT };
KICAD_T secondType[2] = { candidate->m_MatchTypes[1], EOT };
if( !( aItem->IsType( firstType ) && bItem->IsType( secondType ) )
&& !( aItem->IsType( secondType ) && bItem->IsType( firstType ) ) )
{
continue;
}
}
else if( candidate->m_MatchTypes.size() == 1 )
{
KICAD_T matchType[2] = { candidate->m_MatchTypes[0], EOT };
if( !aItem->IsType( matchType ) && !( bItem && bItem->IsType( matchType ) ) )
continue;
}
if( candidate->m_MatchLayers.size() )
{
PCB_LAYER_ID matchLayer = candidate->m_MatchLayers[0];
if( !aItem->GetLayerSet().test( matchLayer )
&& !( bItem && bItem->GetLayerSet().test( matchLayer ) ) )
{
continue;
}
}
if( candidate->m_MatchAreas.size() )
{
if( candidate->m_MatchAreas[0] == "$board" )
{
// matches everything
}
else
{
// TODO: area/room matches...
}
}
// All tests done; if we're still here then it matches
aSelected->push_back( candidate );
}
}

72
pcbnew/drc/drc_rule.h Normal file
View File

@ -0,0 +1,72 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DRC_RULE_H
#define DRC_RULE_H
#include <core/typeinfo.h>
#include <netclass.h>
#include <layers_id_colors_and_visibility.h>
class BOARD_ITEM;
class DRC_RULE
{
public:
wxString m_Name;
// 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;
};
class DRC_SELECTOR
{
public:
std::vector<NETCLASS*> m_MatchNetclasses;
std::vector<KICAD_T> m_MatchTypes;
std::vector<PCB_LAYER_ID> m_MatchLayers;
std::vector<wxString> m_MatchAreas;
int m_Priority; // 0 indicates automatic priority generation
DRC_RULE* m_Rule;
public:
};
void MatchSelectors( const std::vector<DRC_SELECTOR*>& aSelectors,
const BOARD_ITEM* aItem, const NETCLASS* aNetclass,
const BOARD_ITEM* bItem, const NETCLASS* bNetclass,
std::vector<DRC_SELECTOR*>* aSelected );
#endif // DRC_RULE_H

View File

@ -0,0 +1,278 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <fctsys.h>
#include <drc/drc_rule_parser.h>
#include <drc_rules_lexer.h>
#include <class_board.h>
#include <class_board_item.h>
using namespace DRCRULE_T;
DRC_RULES_PARSER::DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename ) :
DRC_RULES_LEXER( aFile, aFilename ),
m_board( aBoard ),
m_requiredVersion( 0 ),
m_tooRecent( false )
{
for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
{
std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
m_layerMap[ untranslated ] = PCB_LAYER_ID( layer );
}
}
void DRC_RULES_PARSER::Parse( std::vector<DRC_SELECTOR*>& aSelectors,
std::vector<DRC_RULE*>& aRules )
{
std::vector< std::pair<DRC_SELECTOR*, wxString> > selectorRules;
T token;
NeedLEFT();
if( NextTok() != T_version )
Expecting( "version" );
NeedNUMBER( "version" );
m_requiredVersion = (int)strtol( CurText(), NULL, 10 );
m_tooRecent = ( m_requiredVersion > DRC_RULE_FILE_VERSION );
NeedRIGHT();
for( token = NextTok(); token != T_EOF; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_selector:
{
wxString ruleName;
aSelectors.push_back( parseDRC_SELECTOR( &ruleName ) );
selectorRules.emplace_back( aSelectors.back(), ruleName );
}
break;
case T_rule:
aRules.push_back( parseDRC_RULE() );
break;
default:
Expecting( "selector or rule" );
}
}
// Hook up the selectors to their rules
std::map<wxString, DRC_RULE*> ruleMap;
for( DRC_RULE* rule : aRules )
ruleMap[ rule->m_Name ] = rule;
for( const std::pair<DRC_SELECTOR*, wxString>& entry : selectorRules )
entry.first->m_Rule = ruleMap[ entry.second ];
}
DRC_SELECTOR* DRC_RULES_PARSER::parseDRC_SELECTOR( wxString* aRuleName )
{
NETCLASSES& netclasses = m_board->GetDesignSettings().m_NetClasses;
DRC_SELECTOR* selector = new DRC_SELECTOR();
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_match_netclass:
NeedSYMBOL();
selector->m_MatchNetclasses.push_back( netclasses.Find( FromUTF8() ).get() );
NeedRIGHT();
break;
case T_match_type:
switch( NextTok() )
{
case T_track: selector->m_MatchTypes.push_back( PCB_TRACE_T ); break;
case T_via: selector->m_MatchTypes.push_back( PCB_LOCATE_STDVIA_T ); break;
case T_micro_via: selector->m_MatchTypes.push_back( PCB_LOCATE_UVIA_T ); break;
case T_blind_via: selector->m_MatchTypes.push_back( PCB_LOCATE_BBVIA_T ); break;
case T_pad: selector->m_MatchTypes.push_back( PCB_PAD_T ); break;
case T_zone: selector->m_MatchTypes.push_back( PCB_ZONE_AREA_T ); break;
case T_text: selector->m_MatchTypes.push_back( PCB_LOCATE_TEXT_T ); break;
case T_graphic: selector->m_MatchTypes.push_back( PCB_LOCATE_GRAPHIC_T ); break;
case T_hole: selector->m_MatchTypes.push_back( PCB_LOCATE_HOLE_T ); break;
case T_npth: selector->m_MatchTypes.push_back( PCB_LOCATE_NPTH_T ); break;
case T_pth: selector->m_MatchTypes.push_back( PCB_LOCATE_PTH_T ); break;
case T_board_edge: selector->m_MatchTypes.push_back( PCB_LOCATE_BOARD_EDGE_T ); break;
default: Expecting( "track, via, micro_via, blind_via, pad, zone, text, "
"graphic, hole, npth, pth, or board_edge" );
}
NeedRIGHT();
break;
case T_match_layer:
NeedSYMBOL();
selector->m_MatchLayers.push_back( m_layerMap[ curText ] );
NeedRIGHT();
break;
case T_match_area:
// TODO
break;
case T_rule:
NeedSYMBOL();
*aRuleName = FromUTF8();
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" );
}
}
return selector;
}
DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE()
{
DRC_RULE* rule = new DRC_RULE();
T token;
NeedSYMBOL();
rule->m_Name = FromUTF8();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
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 )
{
sign = -1;
NextTok();
}
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();
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;
NeedRIGHT();
break;
default:
Expecting( "allow, clearance, track_width, annulus_width, or hole" );
}
}
return rule;
}
int DRC_RULES_PARSER::parseValue( DRCRULE_T::T aToken )
{
char* tmp;
errno = 0;
double fval = strtod( CurText(), &tmp );
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 );
}
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 );
}
return KiROUND( fval * IU_PER_MM );
}

View File

@ -0,0 +1,62 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DRC_RULE_PARSER_H
#define DRC_RULE_PARSER_H
#include <core/typeinfo.h>
#include <netclass.h>
#include <layers_id_colors_and_visibility.h>
#include <drc/drc_rule.h>
#include <drc_rules_lexer.h>
class BOARD_ITEM;
#define DRC_RULE_FILE_VERSION 20200515
class DRC_RULES_PARSER : public DRC_RULES_LEXER
{
public:
DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename );
void Parse( std::vector<DRC_SELECTOR*>& aSelectors, std::vector<DRC_RULE*>& aRules );
private:
DRC_SELECTOR* parseDRC_SELECTOR( wxString* aRuleName );
DRC_RULE* parseDRC_RULE();
int parseValue( DRCRULE_T::T aToken );
private:
BOARD* m_board;
int m_requiredVersion;
bool m_tooRecent;
std::unordered_map<std::string, PCB_LAYER_ID> m_layerMap;
};
#endif // DRC_RULE_PARSER_H

View File

@ -516,20 +516,26 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POL
*/
void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aHoles )
{
static DRAWSEGMENT dummyEdge;
dummyEdge.SetLayer( Edge_Cuts );
// a small extra clearance to be sure actual track clearance is not smaller
// than requested clearance due to many approximations in calculations,
// like arc to segment approx, rounding issues...
// 2 microns are a good value
int extra_margin = Millimeter2iu( 0.002 );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int zone_clearance = aZone->GetClearance();
int edgeClearance = m_board->GetDesignSettings().m_CopperEdgeClearance;
int zone_to_edgecut_clearance = std::max( aZone->GetZoneClearance(), edgeClearance );
int edge_clearance = aZone->GetClearance( &dummyEdge );
if( bds.m_CopperEdgeClearance > edge_clearance )
edge_clearance = bds.m_CopperEdgeClearance;
// items outside the zone bounding box are skipped
// the bounding box is the zone bounding box + the biggest clearance found in Netclass list
EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
int biggest_clearance = bds.GetBiggestClearanceValue();
biggest_clearance = std::max( biggest_clearance, zone_clearance ) + extra_margin;
zone_boundingbox.Inflate( biggest_clearance );
@ -615,7 +621,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
if( aItem->IsOnLayer( Edge_Cuts ) )
{
gap = zone_to_edgecut_clearance;
gap = edge_clearance;
// edge cuts by definition don't have a width
ignoreLineWidth = true;