ADDED assertion constraints for user-defined DRC checks.
This commit is contained in:
parent
f7721dd274
commit
b7e196b710
|
@ -1,4 +1,5 @@
|
||||||
annular_width
|
annular_width
|
||||||
|
assertion
|
||||||
board_edge
|
board_edge
|
||||||
buried_via
|
buried_via
|
||||||
clearance
|
clearance
|
||||||
|
|
|
@ -292,6 +292,7 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
|
||||||
else if( sexprs.top() == "constraint" )
|
else if( sexprs.top() == "constraint" )
|
||||||
{
|
{
|
||||||
tokens = "annular_width|"
|
tokens = "annular_width|"
|
||||||
|
"assertion|"
|
||||||
"clearance|"
|
"clearance|"
|
||||||
"courtyard_clearance|"
|
"courtyard_clearance|"
|
||||||
"diff_pair_gap|"
|
"diff_pair_gap|"
|
||||||
|
@ -330,13 +331,11 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
|
||||||
}
|
}
|
||||||
else if( sexprs.top() == "zone_connection" )
|
else if( sexprs.top() == "zone_connection" )
|
||||||
{
|
{
|
||||||
tokens = "solid "
|
tokens = "none|solid|thermal_relief";
|
||||||
"thermal_relief "
|
|
||||||
"none";
|
|
||||||
}
|
}
|
||||||
else if( sexprs.top() == "min_resolved_spokes" )
|
else if( sexprs.top() == "min_resolved_spokes" )
|
||||||
{
|
{
|
||||||
tokens = "0 1 2 3 4";
|
tokens = "0|1|2|3|4";
|
||||||
}
|
}
|
||||||
else if( sexprs.top() == "layer" )
|
else if( sexprs.top() == "layer" )
|
||||||
{
|
{
|
||||||
|
@ -346,15 +345,14 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
|
||||||
{
|
{
|
||||||
tokens = "warning|error|ignore|exclusion";
|
tokens = "warning|error|ignore|exclusion";
|
||||||
}
|
}
|
||||||
else if( sexprs.top() == "severity" )
|
}
|
||||||
|
else if( context == SEXPR_STRING && !sexprs.empty()
|
||||||
|
&& ( sexprs.top() == "condition" || sexprs.top() == "assertion" ) )
|
||||||
{
|
{
|
||||||
tokens = "error "
|
m_textEditor->AddText( "\"" );
|
||||||
"exclusion "
|
|
||||||
"ignore "
|
|
||||||
"warning";
|
|
||||||
}
|
}
|
||||||
}
|
else if( context == STRING && !sexprs.empty()
|
||||||
else if( context == STRING && !sexprs.empty() && sexprs.top() == "condition" )
|
&& ( sexprs.top() == "condition" || sexprs.top() == "assertion" ) )
|
||||||
{
|
{
|
||||||
if( expr_context == STRUCT_REF )
|
if( expr_context == STRUCT_REF )
|
||||||
{
|
{
|
||||||
|
|
|
@ -194,25 +194,25 @@ For the latter use a `(layer "layer_name")` clause in the rule.
|
||||||
|
|
||||||
(rule "Distance between Vias of Different Nets"
|
(rule "Distance between Vias of Different Nets"
|
||||||
(constraint hole_to_hole (min 0.254mm))
|
(constraint hole_to_hole (min 0.254mm))
|
||||||
(condition "A.Type =='Via' && B.Type =='Via' && A.Net != B.Net"))
|
(condition "A.Type == 'Via' && B.Type == 'Via' && A.Net != B.Net"))
|
||||||
|
|
||||||
(rule "Clearance between Pads of Different Nets"
|
(rule "Clearance between Pads of Different Nets"
|
||||||
(constraint clearance (min 3.0mm))
|
(constraint clearance (min 3.0mm))
|
||||||
(condition "A.Type =='Pad' && B.Type =='Pad' && A.Net != B.Net"))
|
(condition "A.Type == 'Pad' && B.Type == 'Pad' && A.Net != B.Net"))
|
||||||
|
|
||||||
|
|
||||||
(rule "Via Hole to Track Clearance"
|
(rule "Via Hole to Track Clearance"
|
||||||
(constraint hole_clearance (min 0.254mm))
|
(constraint hole_clearance (min 0.254mm))
|
||||||
(condition "A.Type =='Via' && B.Type =='Track'"))
|
(condition "A.Type == 'Via' && B.Type == 'Track'"))
|
||||||
|
|
||||||
(rule "Pad to Track Clearance"
|
(rule "Pad to Track Clearance"
|
||||||
(constraint clearance (min 0.2mm))
|
(constraint clearance (min 0.2mm))
|
||||||
(condition "A.Type =='Pad' && B.Type =='Track'"))
|
(condition "A.Type == 'Pad' && B.Type == 'Track'"))
|
||||||
|
|
||||||
|
|
||||||
(rule "clearance-to-1mm-cutout"
|
(rule "clearance-to-1mm-cutout"
|
||||||
(constraint clearance (min 0.8mm))
|
(constraint clearance (min 0.8mm))
|
||||||
(condition "A.Layer=='Edge.Cuts' && A.Thickness == 1.0mm"))
|
(condition "A.Layer == 'Edge.Cuts' && A.Thickness == 1.0mm"))
|
||||||
|
|
||||||
|
|
||||||
(rule "Max Drill Hole Size Mechanical"
|
(rule "Max Drill Hole Size Mechanical"
|
||||||
|
@ -248,3 +248,9 @@ For the latter use a `(layer "layer_name")` clause in the rule.
|
||||||
(rule holes_in_pads
|
(rule holes_in_pads
|
||||||
(constraint mechanical_hole_clearance (min 0.2mm))
|
(constraint mechanical_hole_clearance (min 0.2mm))
|
||||||
(condition "B.Pad_Type == 'SMD'"))
|
(condition "B.Pad_Type == 'SMD'"))
|
||||||
|
|
||||||
|
|
||||||
|
# Disallow solder mask margin overrides
|
||||||
|
(rule "disallow solder mask margin overrides"
|
||||||
|
(constraint assertion "A.Soldermask_Margin_Override == 0mm")
|
||||||
|
(condition "A.Type == 'Pad'"))
|
|
@ -871,6 +871,18 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto testAssertion =
|
||||||
|
[&]( const DRC_ENGINE_CONSTRAINT* c )
|
||||||
|
{
|
||||||
|
REPORT( wxString::Format( _( "Checking assertion \"%s\"." ),
|
||||||
|
EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
|
||||||
|
|
||||||
|
if( c->constraint.m_Test->EvaluateFor( a, b, aLayer, aReporter ) )
|
||||||
|
REPORT( _( "Assertion passed." ) )
|
||||||
|
else
|
||||||
|
REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
|
||||||
|
};
|
||||||
|
|
||||||
auto processConstraint =
|
auto processConstraint =
|
||||||
[&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
|
[&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
|
||||||
{
|
{
|
||||||
|
@ -1027,8 +1039,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
}
|
}
|
||||||
else if( c->parentRule )
|
else if( c->parentRule )
|
||||||
{
|
{
|
||||||
REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule "
|
REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
|
||||||
"ignored." ),
|
|
||||||
EscapeHTML( c->parentRule->m_LayerSource ) ) )
|
EscapeHTML( c->parentRule->m_LayerSource ) ) )
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1061,8 +1072,22 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
|
|
||||||
if( !c->condition || c->condition->GetExpression().IsEmpty() )
|
if( !c->condition || c->condition->GetExpression().IsEmpty() )
|
||||||
{
|
{
|
||||||
REPORT( implicit ? _( "Unconditional constraint applied." )
|
if( aReporter )
|
||||||
: _( "Unconditional rule applied." ) );
|
{
|
||||||
|
if( implicit )
|
||||||
|
{
|
||||||
|
REPORT( _( "Unconditional constraint applied." ) )
|
||||||
|
}
|
||||||
|
else if( constraint.m_Type == ASSERTION_CONSTRAINT )
|
||||||
|
{
|
||||||
|
REPORT( _( "Unconditional rule applied." ) )
|
||||||
|
testAssertion( c );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constraint = c->constraint;
|
constraint = c->constraint;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1081,8 +1106,22 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
|
|
||||||
if( c->condition->EvaluateFor( a, b, aLayer, aReporter ) )
|
if( c->condition->EvaluateFor( a, b, aLayer, aReporter ) )
|
||||||
{
|
{
|
||||||
REPORT( implicit ? _( "Constraint applied." )
|
if( aReporter )
|
||||||
: _( "Rule applied; overrides previous constraints." ) )
|
{
|
||||||
|
if( implicit )
|
||||||
|
{
|
||||||
|
REPORT( _( "Constraint applied." ) )
|
||||||
|
}
|
||||||
|
else if( constraint.m_Type == ASSERTION_CONSTRAINT )
|
||||||
|
{
|
||||||
|
REPORT( _( "Rule applied." ) )
|
||||||
|
testAssertion( c );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REPORT( _( "Rule applied; overrides previous constraints." ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( c->constraint.m_Value.HasMin() )
|
if( c->constraint.m_Value.HasMin() )
|
||||||
constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
|
constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
|
||||||
|
@ -1239,6 +1278,78 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
|
|
||||||
return constraint;
|
return constraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DRC_ENGINE::ProcessAssertions( const BOARD_ITEM* a,
|
||||||
|
std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
|
||||||
|
REPORTER* aReporter )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
|
||||||
|
* kills performance when running bulk DRC tests (where aReporter is nullptr).
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto testAssertion =
|
||||||
|
[&]( const DRC_ENGINE_CONSTRAINT* c )
|
||||||
|
{
|
||||||
|
REPORT( wxString::Format( _( "Checking rule assertion \"%s\"." ),
|
||||||
|
EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
|
||||||
|
|
||||||
|
if( c->constraint.m_Test->EvaluateFor( a, nullptr, a->GetLayer(), aReporter ) )
|
||||||
|
{
|
||||||
|
REPORT( _( "Assertion passed." ) )
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
|
||||||
|
aFailureHandler( &c->constraint );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto processConstraint =
|
||||||
|
[&]( const DRC_ENGINE_CONSTRAINT* c )
|
||||||
|
{
|
||||||
|
REPORT( "" )
|
||||||
|
REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
|
||||||
|
|
||||||
|
if( !( a->GetLayerSet() & c->layerTest ).any() )
|
||||||
|
{
|
||||||
|
REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
|
||||||
|
EscapeHTML( c->parentRule->m_LayerSource ) ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !c->condition || c->condition->GetExpression().IsEmpty() )
|
||||||
|
{
|
||||||
|
REPORT( _( "Unconditional rule applied." ) )
|
||||||
|
testAssertion( c );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
|
||||||
|
EscapeHTML( c->condition->GetExpression() ) ) )
|
||||||
|
|
||||||
|
if( c->condition->EvaluateFor( a, nullptr, a->GetLayer(), aReporter ) )
|
||||||
|
{
|
||||||
|
REPORT( _( "Rule applied." ) )
|
||||||
|
testAssertion( c );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REPORT( _( "Condition not satisfied; rule ignored." ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if( m_constraintMap.count( ASSERTION_CONSTRAINT ) )
|
||||||
|
{
|
||||||
|
std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
|
||||||
|
|
||||||
|
for( int ii = 0; ii < (int) ruleset->size(); ++ii )
|
||||||
|
processConstraint( ruleset->at( ii ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#undef REPORT
|
#undef REPORT
|
||||||
#undef UNITS
|
#undef UNITS
|
||||||
#undef REPORT_VALUE
|
#undef REPORT_VALUE
|
||||||
|
|
|
@ -152,6 +152,10 @@ public:
|
||||||
DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
|
DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
|
||||||
PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr );
|
PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr );
|
||||||
|
|
||||||
|
void ProcessAssertions( const BOARD_ITEM* a,
|
||||||
|
std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
|
||||||
|
REPORTER* aReporter = nullptr );
|
||||||
|
|
||||||
bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID );
|
bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID );
|
||||||
|
|
||||||
EDA_UNITS UserUnits() const { return m_userUnits; }
|
EDA_UNITS UserUnits() const { return m_userUnits; }
|
||||||
|
|
|
@ -183,6 +183,10 @@ DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE,
|
||||||
_( "Unresolved text variable" ),
|
_( "Unresolved text variable" ),
|
||||||
wxT( "unresolved_variable" ) );
|
wxT( "unresolved_variable" ) );
|
||||||
|
|
||||||
|
DRC_ITEM DRC_ITEM::assertionFailure( DRCE_ASSERTION_FAILURE,
|
||||||
|
_( "Assertion failure" ),
|
||||||
|
wxT( "assertion_failure" ) );
|
||||||
|
|
||||||
DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER,
|
DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER,
|
||||||
_( "Copper sliver" ),
|
_( "Copper sliver" ),
|
||||||
wxT( "copper_sliver" ) );
|
wxT( "copper_sliver" ) );
|
||||||
|
@ -334,6 +338,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
|
||||||
case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint );
|
case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint );
|
||||||
case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared<DRC_ITEM>( libFootprintIssues );
|
case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared<DRC_ITEM>( libFootprintIssues );
|
||||||
case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable );
|
case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable );
|
||||||
|
case DRCE_ASSERTION_FAILURE: return std::make_shared<DRC_ITEM>( assertionFailure );
|
||||||
case DRCE_COPPER_SLIVER: return std::make_shared<DRC_ITEM>( copperSliver );
|
case DRCE_COPPER_SLIVER: return std::make_shared<DRC_ITEM>( copperSliver );
|
||||||
case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps );
|
case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps );
|
||||||
case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance );
|
case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance );
|
||||||
|
|
|
@ -73,6 +73,7 @@ enum PCB_DRC_CODE {
|
||||||
DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole
|
DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole
|
||||||
|
|
||||||
DRCE_UNRESOLVED_VARIABLE,
|
DRCE_UNRESOLVED_VARIABLE,
|
||||||
|
DRCE_ASSERTION_FAILURE, // user-defined (custom rule) assertion
|
||||||
|
|
||||||
DRCE_COPPER_SLIVER,
|
DRCE_COPPER_SLIVER,
|
||||||
DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness
|
DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness
|
||||||
|
@ -177,6 +178,7 @@ private:
|
||||||
static DRC_ITEM netConflict;
|
static DRC_ITEM netConflict;
|
||||||
static DRC_ITEM libFootprintIssues;
|
static DRC_ITEM libFootprintIssues;
|
||||||
static DRC_ITEM unresolvedVariable;
|
static DRC_ITEM unresolvedVariable;
|
||||||
|
static DRC_ITEM assertionFailure;
|
||||||
static DRC_ITEM copperSliver;
|
static DRC_ITEM copperSliver;
|
||||||
static DRC_ITEM silkClearance;
|
static DRC_ITEM silkClearance;
|
||||||
static DRC_ITEM solderMaskBridge;
|
static DRC_ITEM solderMaskBridge;
|
||||||
|
|
|
@ -67,7 +67,8 @@ enum DRC_CONSTRAINT_T
|
||||||
DIFF_PAIR_INTRA_SKEW_CONSTRAINT,
|
DIFF_PAIR_INTRA_SKEW_CONSTRAINT,
|
||||||
VIA_COUNT_CONSTRAINT,
|
VIA_COUNT_CONSTRAINT,
|
||||||
MECHANICAL_CLEARANCE_CONSTRAINT,
|
MECHANICAL_CLEARANCE_CONSTRAINT,
|
||||||
MECHANICAL_HOLE_CLEARANCE_CONSTRAINT
|
MECHANICAL_HOLE_CLEARANCE_CONSTRAINT,
|
||||||
|
ASSERTION_CONSTRAINT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,6 +124,7 @@ class DRC_CONSTRAINT
|
||||||
m_Value(),
|
m_Value(),
|
||||||
m_DisallowFlags( 0 ),
|
m_DisallowFlags( 0 ),
|
||||||
m_ZoneConnection( ZONE_CONNECTION::INHERITED ),
|
m_ZoneConnection( ZONE_CONNECTION::INHERITED ),
|
||||||
|
m_Test( nullptr ),
|
||||||
m_name( aName ),
|
m_name( aName ),
|
||||||
m_parentRule( nullptr )
|
m_parentRule( nullptr )
|
||||||
{
|
{
|
||||||
|
@ -167,6 +169,7 @@ public:
|
||||||
MINOPTMAX<int> m_Value;
|
MINOPTMAX<int> m_Value;
|
||||||
int m_DisallowFlags;
|
int m_DisallowFlags;
|
||||||
ZONE_CONNECTION m_ZoneConnection;
|
ZONE_CONNECTION m_ZoneConnection;
|
||||||
|
DRC_RULE_CONDITION* m_Test;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxString m_name; // For just-in-time constraints
|
wxString m_name; // For just-in-time constraints
|
||||||
|
|
|
@ -260,7 +260,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
|
||||||
if( (int) token == DSN_RIGHT || token == T_EOF )
|
if( (int) token == DSN_RIGHT || token == T_EOF )
|
||||||
{
|
{
|
||||||
msg.Printf( _( "Missing constraint type.| Expected %s." ),
|
msg.Printf( _( "Missing constraint type.| Expected %s." ),
|
||||||
"clearance, hole_clearance, edge_clearance, mechanical_clearance, "
|
"assertion, clearance, hole_clearance, edge_clearance, mechanical_clearance, "
|
||||||
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
|
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
|
||||||
"hole_to_hole, track_width, annular_width, via_diameter, disallow, "
|
"hole_to_hole, track_width, annular_width, via_diameter, disallow, "
|
||||||
"zone_connection, thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, "
|
"zone_connection, thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, "
|
||||||
|
@ -271,6 +271,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
|
||||||
|
|
||||||
switch( token )
|
switch( token )
|
||||||
{
|
{
|
||||||
|
case T_assertion: c.m_Type = ASSERTION_CONSTRAINT; break;
|
||||||
case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break;
|
case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break;
|
||||||
case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break;
|
case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break;
|
||||||
case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break;
|
case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break;
|
||||||
|
@ -298,7 +299,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
|
||||||
case T_mechanical_hole_clearance: c.m_Type = MECHANICAL_HOLE_CLEARANCE_CONSTRAINT; break;
|
case T_mechanical_hole_clearance: c.m_Type = MECHANICAL_HOLE_CLEARANCE_CONSTRAINT; break;
|
||||||
default:
|
default:
|
||||||
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
|
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
|
||||||
"clearance, hole_clearance, edge_clearance, mechanical_clearance, "
|
"assertion, clearance, hole_clearance, edge_clearance, mechanical_clearance, "
|
||||||
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
|
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
|
||||||
"hole_to_hole, track_width, annular_width, disallow, zone_connection, "
|
"hole_to_hole, track_width, annular_width, disallow, zone_connection, "
|
||||||
"thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, length, skew, "
|
"thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, length, skew, "
|
||||||
|
@ -412,6 +413,33 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
|
||||||
aRule->AddConstraint( c );
|
aRule->AddConstraint( c );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if( c.m_Type == ASSERTION_CONSTRAINT )
|
||||||
|
{
|
||||||
|
token = NextTok();
|
||||||
|
|
||||||
|
if( (int) token == DSN_RIGHT )
|
||||||
|
reportError( _( "Missing assertion expression." ) );
|
||||||
|
|
||||||
|
if( IsSymbol( token ) )
|
||||||
|
{
|
||||||
|
c.m_Test = new DRC_RULE_CONDITION( FromUTF8() );
|
||||||
|
c.m_Test->Compile( m_reporter, CurLineNumber(), CurOffset() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.Printf( _( "Unrecognized item '%s'.| Expected quoted expression." ), FromUTF8() );
|
||||||
|
reportError( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (int) NextTok() != DSN_RIGHT )
|
||||||
|
{
|
||||||
|
reportError( wxString::Format( _( "Unrecognized item '%s'." ), FromUTF8() ) );
|
||||||
|
parseUnknown();
|
||||||
|
}
|
||||||
|
|
||||||
|
aRule->AddConstraint( c );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
|
for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2004-2020 KiCad Developers.
|
* Copyright (C) 2004-2021 KiCad Developers.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include <drc/drc_engine.h>
|
#include <drc/drc_engine.h>
|
||||||
#include <drc/drc_item.h>
|
#include <drc/drc_item.h>
|
||||||
#include <drc/drc_rule.h>
|
#include <drc/drc_rule.h>
|
||||||
|
#include <drc/drc_rule_condition.h>
|
||||||
#include <drc/drc_test_provider.h>
|
#include <drc/drc_test_provider.h>
|
||||||
#include <pad.h>
|
#include <pad.h>
|
||||||
#include <pcb_track.h>
|
#include <pcb_track.h>
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
- DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
|
- DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
|
||||||
- DRCE_INVALID_OUTLINE, ///< invalid board outline
|
- DRCE_INVALID_OUTLINE, ///< invalid board outline
|
||||||
- DRCE_UNRESOLVED_VARIABLE,
|
- DRCE_UNRESOLVED_VARIABLE,
|
||||||
|
- DRCE_ASSERTION_FAILURE ///< user-defined assertions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DRC_TEST_PROVIDER_MISC : public DRC_TEST_PROVIDER
|
class DRC_TEST_PROVIDER_MISC : public DRC_TEST_PROVIDER
|
||||||
|
@ -69,6 +71,7 @@ private:
|
||||||
void testOutline();
|
void testOutline();
|
||||||
void testDisabledLayers();
|
void testDisabledLayers();
|
||||||
void testTextVars();
|
void testTextVars();
|
||||||
|
void testAssertions();
|
||||||
|
|
||||||
BOARD* m_board;
|
BOARD* m_board;
|
||||||
};
|
};
|
||||||
|
@ -119,6 +122,19 @@ void DRC_TEST_PROVIDER_MISC::testOutline()
|
||||||
|
|
||||||
void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
|
void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
|
||||||
{
|
{
|
||||||
|
// This is the number of tests between 2 calls to the progress bar
|
||||||
|
const int delta = 2000;
|
||||||
|
|
||||||
|
int ii = 0;
|
||||||
|
int items = 0;
|
||||||
|
|
||||||
|
auto countItems =
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
++items;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
LSET disabledLayers = m_board->GetEnabledLayers().flip();
|
LSET disabledLayers = m_board->GetEnabledLayers().flip();
|
||||||
|
|
||||||
// Perform the test only for copper layers
|
// Perform the test only for copper layers
|
||||||
|
@ -127,6 +143,12 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
|
||||||
auto checkDisabledLayers =
|
auto checkDisabledLayers =
|
||||||
[&]( BOARD_ITEM* item ) -> bool
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
{
|
{
|
||||||
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_DISABLED_LAYER_ITEM ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !reportProgress( ii++, items, delta ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
PCB_LAYER_ID badLayer = UNDEFINED_LAYER;
|
PCB_LAYER_ID badLayer = UNDEFINED_LAYER;
|
||||||
|
|
||||||
if( item->Type() == PCB_PAD_T )
|
if( item->Type() == PCB_PAD_T )
|
||||||
|
@ -172,7 +194,7 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
|
||||||
|
|
||||||
if( badLayer != UNDEFINED_LAYER )
|
if( badLayer != UNDEFINED_LAYER )
|
||||||
{
|
{
|
||||||
std::shared_ptr<DRC_ITEM>drcItem = DRC_ITEM::Create( DRCE_DISABLED_LAYER_ITEM );
|
auto drcItem = DRC_ITEM::Create( DRCE_DISABLED_LAYER_ITEM );
|
||||||
|
|
||||||
m_msg.Printf( _( "(layer %s)" ), LayerName( badLayer ) );
|
m_msg.Printf( _( "(layer %s)" ), LayerName( badLayer ) );
|
||||||
|
|
||||||
|
@ -185,18 +207,78 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), countItems );
|
||||||
forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), checkDisabledLayers );
|
forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), checkDisabledLayers );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DRC_TEST_PROVIDER_MISC::testAssertions()
|
||||||
|
{
|
||||||
|
// This is the number of tests between 2 calls to the progress bar
|
||||||
|
const int delta = 2000;
|
||||||
|
|
||||||
|
int ii = 0;
|
||||||
|
int items = 0;
|
||||||
|
|
||||||
|
auto countItems =
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
++items;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkAssertions =
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !reportProgress( ii++, items, delta ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_drcEngine->ProcessAssertions( item,
|
||||||
|
[&]( const DRC_CONSTRAINT* c )
|
||||||
|
{
|
||||||
|
auto drcItem = DRC_ITEM::Create( DRCE_ASSERTION_FAILURE );
|
||||||
|
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " (" )
|
||||||
|
+ c->GetName() + wxS( ")" ) );
|
||||||
|
drcItem->SetItems( item );
|
||||||
|
|
||||||
|
reportViolation( drcItem, item->GetPosition() );
|
||||||
|
} );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
forEachGeometryItem( {}, LSET::AllLayersMask(), countItems );
|
||||||
|
forEachGeometryItem( {}, LSET::AllLayersMask(), checkAssertions );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DRC_TEST_PROVIDER_MISC::testTextVars()
|
void DRC_TEST_PROVIDER_MISC::testTextVars()
|
||||||
{
|
{
|
||||||
auto checkUnresolvedTextVar =
|
// This is the number of tests between 2 calls to the progress bar
|
||||||
|
const int delta = 2000;
|
||||||
|
|
||||||
|
int ii = 0;
|
||||||
|
int items = 0;
|
||||||
|
|
||||||
|
auto countItems =
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
++items;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkTextVars =
|
||||||
[&]( EDA_ITEM* item ) -> bool
|
[&]( EDA_ITEM* item ) -> bool
|
||||||
{
|
{
|
||||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if( !reportProgress( ii++, items, delta ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
|
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
|
||||||
|
|
||||||
if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) )
|
if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) )
|
||||||
|
@ -209,8 +291,8 @@ void DRC_TEST_PROVIDER_MISC::testTextVars()
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(),
|
forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(), countItems );
|
||||||
checkUnresolvedTextVar );
|
forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(), checkTextVars );
|
||||||
|
|
||||||
DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet();
|
DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet();
|
||||||
DS_DRAW_ITEM_LIST drawItems;
|
DS_DRAW_ITEM_LIST drawItems;
|
||||||
|
@ -273,6 +355,14 @@ bool DRC_TEST_PROVIDER_MISC::Run()
|
||||||
testTextVars();
|
testTextVars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
|
||||||
|
{
|
||||||
|
if( !reportPhase( _( "Checking assertions..." ) ) )
|
||||||
|
return false; // DRC cancelled
|
||||||
|
|
||||||
|
testAssertions();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -840,6 +840,22 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
|
||||||
|
|
||||||
r->Flush();
|
r->Flush();
|
||||||
|
|
||||||
|
r = m_inspectConstraintsDialog->AddPage( _( "Assertions" ) );
|
||||||
|
reportHeader( _( "Assertions for:" ), item, r );
|
||||||
|
|
||||||
|
if( compileError )
|
||||||
|
reportCompileError( r );
|
||||||
|
|
||||||
|
if( courtyardError )
|
||||||
|
{
|
||||||
|
r->Report( "" );
|
||||||
|
r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
|
||||||
|
+ " <a href='drc'>" + _( "Run DRC for a full analysis." ) + "</a>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
drcEngine.ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
|
||||||
|
r->Flush();
|
||||||
|
|
||||||
m_inspectConstraintsDialog->FinishInitialization();
|
m_inspectConstraintsDialog->FinishInitialization();
|
||||||
m_inspectConstraintsDialog->Raise();
|
m_inspectConstraintsDialog->Raise();
|
||||||
m_inspectConstraintsDialog->Show( true );
|
m_inspectConstraintsDialog->Show( true );
|
||||||
|
|
Loading…
Reference in New Issue