Rework silk-to-pad checker to handle all solder mask clipping of silk.

Fixes https://gitlab.com/kicad/code/kicad/issues/5851
This commit is contained in:
Jeff Young 2020-10-04 12:44:22 +01:00
parent 36ceb8075e
commit 85c6cebd77
13 changed files with 127 additions and 83 deletions

View File

@ -29,7 +29,7 @@ zone
edge_clearance
hole_clearance
courtyard_clearance
silk_to_pad
silk_to_mask
silk_to_silk
skew
diff_pair_gap

View File

@ -250,7 +250,7 @@ set( PCBNEW_DRC_SRCS
drc/drc_test_provider_misc.cpp
drc/drc_test_provider_track_width.cpp
drc/drc_test_provider_via_diameter.cpp
drc/drc_test_provider_silk_to_pad.cpp
drc/drc_test_provider_silk_to_mask.cpp
drc/drc_test_provider_silk_to_silk.cpp
drc/drc_test_provider_matched_length.cpp
drc/drc_test_provider_diff_pair_coupling.cpp

View File

@ -200,11 +200,22 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
if( context == SEXPR_OPEN )
{
if( sexprs.empty() )
tokens = "rule version";
{
tokens = "rule "
"version";
}
else if( sexprs.top() == "rule" )
tokens = "condition constraint layer";
{
tokens = "condition "
"constraint "
"layer";
}
else if( sexprs.top() == "constraint" )
tokens = "max min opt";
{
tokens = "max "
"min "
"opt";
}
}
else if( context == SEXPR_TOKEN )
{
@ -214,7 +225,20 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
}
else if( sexprs.top() == "constraint" )
{
tokens = "annulus_width clearance disallow hole track_width";
tokens = "annulus_width "
"clearance "
"courtyard_clearance "
"diff_pair_gap "
"diff_pair_uncoupled "
"disallow "
"edge_clearance "
"length "
"hole "
"hole_clearance "
"silk_to_mask "
"silk_to_silk skew "
"track_width "
"via_count ";
}
else if( sexprs.top() == "disallow"
|| sexprs.top() == "buried_via"
@ -227,11 +251,21 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
|| sexprs.top() == "via"
|| sexprs.top() == "zone" )
{
tokens = "buried_via graphic hole micro_via pad text track via zone";
tokens = "buried_via "
"graphic "
"hole "
"micro_via "
"pad "
"text "
"track "
"via "
"zone";
}
else if( sexprs.top() == "layer" )
{
tokens = "inner outer \"x\"";
tokens = "inner "
"outer "
"\"x\"";
}
}
else if( context == STRING && !sexprs.empty() && sexprs.top() == "condition" )

View File

@ -126,7 +126,7 @@ void DRC_ENGINE::loadImplicitRules()
holeClearanceConstraint.Value().SetMin( 0 );
rule->AddConstraint( courtyardClearanceConstraint );
DRC_CONSTRAINT silkToPadClearanceConstraint( DRC_CONSTRAINT_TYPE_SILK_TO_PAD );
DRC_CONSTRAINT silkToPadClearanceConstraint( DRC_CONSTRAINT_TYPE_SILK_TO_MASK );
silkToPadClearanceConstraint.Value().SetMin( 0 );
rule->AddConstraint( silkToPadClearanceConstraint );
@ -243,7 +243,7 @@ static wxString formatConstraint( const DRC_CONSTRAINT& constraint )
{ DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE, "edge_clearance", formatMinMax },
{ DRC_CONSTRAINT_TYPE_HOLE_SIZE, "hole_size", formatMinMax },
{ DRC_CONSTRAINT_TYPE_COURTYARD_CLEARANCE, "courtyard_clearance", formatMinMax },
{ DRC_CONSTRAINT_TYPE_SILK_TO_PAD, "silk_to_pad", formatMinMax },
{ DRC_CONSTRAINT_TYPE_SILK_TO_MASK, "silk_to_mask", formatMinMax },
{ DRC_CONSTRAINT_TYPE_SILK_TO_SILK, "silk_to_silk", formatMinMax },
{ DRC_CONSTRAINT_TYPE_TRACK_WIDTH, "track_width", formatMinMax },
{ DRC_CONSTRAINT_TYPE_ANNULAR_WIDTH, "annular_width", formatMinMax },
@ -414,10 +414,8 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aTestTracksAgainstZones,
for( DRC_TEST_PROVIDER* provider : m_testProviders )
{
if( provider->IsEnabled() )
{
phases += provider->GetNumPhases();
}
}
m_progressReporter->AddPhases( phases );
}

View File

@ -164,13 +164,13 @@ DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE,
_( "Unresolved text variable" ),
wxT( "unresolved_variable" ) );
DRC_ITEM DRC_ITEM::silkOverPad( DRCE_SILK_OVER_PAD,
_( "Silkscreen overlapping pad" ),
wxT( "silk_over_pad" ) );
DRC_ITEM DRC_ITEM::silkMaskClearance( DRCE_SILK_MASK_CLEARANCE,
_( "Silkscreen clipped by solder mask" ),
wxT( "silk_over_copper" ) );
DRC_ITEM DRC_ITEM::silkClearance( DRCE_SILK_CLEARANCE,
_( "Silkscreen clearance" ),
wxT( "silk_clearance" ) );
DRC_ITEM DRC_ITEM::silkSilkClearance( DRCE_SILK_SILK_CLEARANCE,
_( "Silkscreen overlap" ),
wxT( "silk_overlap" ) );
DRC_ITEM DRC_ITEM::lengthOutOfRange( DRCE_LENGTH_OUT_OF_RANGE,
_( "Trace length out of range" ),
@ -224,8 +224,8 @@ std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
DRC_ITEM::extraFootprint,
DRC_ITEM::netConflict,
DRC_ITEM::unresolvedVariable,
DRC_ITEM::silkClearance,
DRC_ITEM::silkOverPad,
DRC_ITEM::silkSilkClearance,
DRC_ITEM::silkMaskClearance,
DRC_ITEM::lengthOutOfRange,
DRC_ITEM::skewOutOfRange,
DRC_ITEM::tooManyVias,
@ -270,8 +270,8 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
case DRCE_NET_CONFLICT: return std::make_shared<DRC_ITEM>( netConflict );
case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint );
case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable );
case DRCE_SILK_OVER_PAD: return std::make_shared<DRC_ITEM>( silkOverPad );
case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance );
case DRCE_SILK_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkSilkClearance );
case DRCE_SILK_MASK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkMaskClearance );
case DRCE_LENGTH_OUT_OF_RANGE: return std::make_shared<DRC_ITEM>( lengthOutOfRange );
case DRCE_SKEW_OUT_OF_RANGE: return std::make_shared<DRC_ITEM>( skewOutOfRange );
case DRCE_TOO_MANY_VIAS: return std::make_shared<DRC_ITEM>( tooManyVias );

View File

@ -68,8 +68,9 @@ enum PCB_DRC_CODE {
DRCE_NET_CONFLICT, // pad net doesn't match netlist
DRCE_UNRESOLVED_VARIABLE,
DRCE_SILK_OVER_PAD, // silkscreen over component pad(s)
DRCE_SILK_CLEARANCE, // silk to silk clearance error
DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it
// over pads, exposed copper, etc.)
DRCE_SILK_SILK_CLEARANCE, // silk to silk clearance error
DRCE_LENGTH_OUT_OF_RANGE,
DRCE_SKEW_OUT_OF_RANGE,
DRCE_TOO_MANY_VIAS,
@ -152,8 +153,8 @@ private:
static DRC_ITEM extraFootprint;
static DRC_ITEM netConflict;
static DRC_ITEM unresolvedVariable;
static DRC_ITEM silkOverPad;
static DRC_ITEM silkClearance;
static DRC_ITEM silkMaskClearance;
static DRC_ITEM silkSilkClearance;
static DRC_ITEM lengthOutOfRange;
static DRC_ITEM skewOutOfRange;
static DRC_ITEM tooManyVias;

View File

@ -100,9 +100,11 @@ public:
for( auto subshape : subshapes )
{
const BOX2I& bbox = subshape->BBox();
BOX2I bbox = subshape->BBox();
bbox.Inflate( Millimeter2iu( 20 ) );
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
m_tree[layer]->Insert( mmin, mmax, new ITEM_WITH_SHAPE( aItem, subshape, itemShape ) );
m_count++;
}

View File

@ -45,7 +45,7 @@ enum DRC_CONSTRAINT_TYPE_T
DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE,
DRC_CONSTRAINT_TYPE_HOLE_SIZE,
DRC_CONSTRAINT_TYPE_COURTYARD_CLEARANCE,
DRC_CONSTRAINT_TYPE_SILK_TO_PAD,
DRC_CONSTRAINT_TYPE_SILK_TO_MASK,
DRC_CONSTRAINT_TYPE_SILK_TO_SILK,
DRC_CONSTRAINT_TYPE_TRACK_WIDTH,
DRC_CONSTRAINT_TYPE_ANNULAR_WIDTH,

View File

@ -276,7 +276,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
case T_edge_clearance: constraint.m_Type = DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE; break;
case T_hole: constraint.m_Type = DRC_CONSTRAINT_TYPE_HOLE_SIZE; break;
case T_courtyard_clearance: constraint.m_Type = DRC_CONSTRAINT_TYPE_COURTYARD_CLEARANCE; break;
case T_silk_to_pad: constraint.m_Type = DRC_CONSTRAINT_TYPE_SILK_TO_PAD; break;
case T_silk_to_mask: constraint.m_Type = DRC_CONSTRAINT_TYPE_SILK_TO_MASK; break;
case T_silk_to_silk: constraint.m_Type = DRC_CONSTRAINT_TYPE_SILK_TO_SILK; break;
case T_track_width: constraint.m_Type = DRC_CONSTRAINT_TYPE_TRACK_WIDTH; break;
case T_annular_width: constraint.m_Type = DRC_CONSTRAINT_TYPE_ANNULAR_WIDTH; break;

View File

@ -127,7 +127,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testOverlappingComponentCourtyards()
{
const int delta = 100; // This is the number of tests between 2 calls to the progress bar
if( !reportPhase( _( "Checking footprint courtyard overlap..." ) ) )
if( !reportPhase( _( "Checking footprints for overlapping courtyards..." ) ) )
return;
int ii = 0;

View File

@ -24,13 +24,9 @@
#include <common.h>
#include <class_board.h>
#include <class_drawsegment.h>
#include <class_pad.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/polygon_test_point_inside.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
#include <drc/drc_engine.h>
@ -43,21 +39,21 @@
/*
Silk to pads clearance test. Check all pads against silkscreen (mask opening in the pad vs silkscreen)
Errors generated:
- DRCE_SILK_ON_PADS
- DRCE_SILK_MASK_CLEARANCE
*/
namespace test {
class DRC_TEST_PROVIDER_SILK_TO_PAD : public ::DRC_TEST_PROVIDER
class DRC_TEST_PROVIDER_SILK_TO_MASK : public ::DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER_SILK_TO_PAD ():
DRC_TEST_PROVIDER_SILK_TO_MASK ():
m_board( nullptr ),
m_largestClearance( 0 )
{
}
virtual ~DRC_TEST_PROVIDER_SILK_TO_PAD()
virtual ~DRC_TEST_PROVIDER_SILK_TO_MASK()
{
}
@ -65,12 +61,12 @@ public:
virtual const wxString GetName() const override
{
return "silk_to_pad";
return "silk_to_mask";
};
virtual const wxString GetDescription() const override
{
return "Tests for silkscreen covering components pads";
return "Tests for silkscreen being clipped by solder mask";
}
virtual int GetNumPhases() const override
@ -89,28 +85,28 @@ private:
};
bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
bool test::DRC_TEST_PROVIDER_SILK_TO_MASK::Run()
{
m_board = m_drcEngine->GetBoard();
DRC_CONSTRAINT worstClearanceConstraint;
m_largestClearance = 0;
if( m_drcEngine->QueryWorstConstraint( DRC_CONSTRAINT_TYPE_SILK_TO_PAD,
if( m_drcEngine->QueryWorstConstraint( DRC_CONSTRAINT_TYPE_SILK_TO_MASK,
worstClearanceConstraint, DRCCQ_LARGEST_MINIMUM ) )
{
m_largestClearance = worstClearanceConstraint.m_Value.Min();
}
reportAux( "Worst clearance : %d nm", m_largestClearance );
reportPhase(( "Pad to silkscreen clearances..." ));
reportPhase( _( "Checking silkscreen for potential soldermask clipping..." ) );
DRC_RTREE padTree, silkTree;
DRC_RTREE maskTree, silkTree;
auto addPadToTree =
[&padTree]( BOARD_ITEM *item ) -> bool
auto addMaskToTree =
[&maskTree]( BOARD_ITEM *item ) -> bool
{
padTree.insert( item );
maskTree.insert( item );
return true;
};
@ -125,10 +121,10 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
[&]( const DRC_RTREE::LAYER_PAIR& aLayers, DRC_RTREE::ITEM_WITH_SHAPE* aRefItem,
DRC_RTREE::ITEM_WITH_SHAPE* aTestItem, bool* aCollisionDetected ) -> bool
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_OVER_PAD ) )
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
return false;
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_TO_PAD,
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_TO_MASK,
aRefItem->parent,
aTestItem->parent );
@ -147,7 +143,7 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
if( !aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) )
return true;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_OVER_PAD );
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE );
wxString msg;
drcItem->SetItems( aRefItem->parent, aTestItem->parent );
@ -159,21 +155,28 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
return true;
};
int numPads = forEachGeometryItem( { PCB_PAD_T }, LSET::AllTechMask() | LSET::AllCuMask(),
addPadToTree );
int numPads = forEachGeometryItem( { PCB_PAD_T,
PCB_LINE_T,
PCB_MODULE_EDGE_T,
PCB_TEXT_T,
PCB_MODULE_TEXT_T },
LSET( 2, F_Mask, B_Mask ), addMaskToTree );
int numSilk = forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T },
int numSilk = forEachGeometryItem( { PCB_LINE_T,
PCB_MODULE_EDGE_T,
PCB_TEXT_T,
PCB_MODULE_TEXT_T },
LSET( 2, F_SilkS, B_SilkS ), addSilkToTree );
reportAux( _("Testing %d pads against %d silkscreen features."), numPads, numSilk );
reportAux( _("Testing %d exposed copper against %d silkscreen features."), numPads, numSilk );
const std::vector<DRC_RTREE::LAYER_PAIR> layerPairs =
{
DRC_RTREE::LAYER_PAIR( F_SilkS, F_Cu ),
DRC_RTREE::LAYER_PAIR( B_SilkS, B_Cu )
DRC_RTREE::LAYER_PAIR( F_SilkS, F_Mask ),
DRC_RTREE::LAYER_PAIR( B_SilkS, B_Mask )
};
padTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance );
maskTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance );
reportRuleStatistics();
@ -181,13 +184,13 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
}
std::set<DRC_CONSTRAINT_TYPE_T> test::DRC_TEST_PROVIDER_SILK_TO_PAD::GetConstraintTypes() const
std::set<DRC_CONSTRAINT_TYPE_T> test::DRC_TEST_PROVIDER_SILK_TO_MASK::GetConstraintTypes() const
{
return { DRC_CONSTRAINT_TYPE_SILK_TO_PAD };
return { DRC_CONSTRAINT_TYPE_SILK_TO_MASK };
}
namespace detail
{
static DRC_REGISTER_TEST_PROVIDER<test::DRC_TEST_PROVIDER_SILK_TO_PAD> dummy;
static DRC_REGISTER_TEST_PROVIDER<test::DRC_TEST_PROVIDER_SILK_TO_MASK> dummy;
}

View File

@ -27,7 +27,6 @@
#include <geometry/polygon_test_point_inside.h>
#include <geometry/seg.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
#include <drc/drc_engine.h>
@ -40,7 +39,7 @@
/*
Silk to silk clearance test. Check all silkscreen features against each other.
Errors generated:
- DRCE_SILK_CLEARANCE
- DRCE_SILK_SILK_CLEARANCE
*/
@ -95,7 +94,7 @@ bool DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
}
reportAux( "Worst clearance : %d nm", m_largestClearance );
reportPhase(( "Silkscreen clearances..." ));
reportPhase( _( "Checking silkscreen for overlapping items..." ) );
DRC_RTREE silkTree;
@ -110,7 +109,7 @@ bool DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
[&]( const DRC_RTREE::LAYER_PAIR& aLayers, DRC_RTREE::ITEM_WITH_SHAPE* aRefItem,
DRC_RTREE::ITEM_WITH_SHAPE* aTestItem, bool* aCollisionDetected ) -> bool
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ) )
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_SILK_CLEARANCE ) )
return false;
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_TO_SILK,
@ -165,15 +164,20 @@ bool DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
if( !aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) )
return true;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_SILK_CLEARANCE );
wxString msg;
/* For now we're just reporting silkscreen collisions without any dimensional
* data. I suspect it's usually noise, and they can always use the clearance
* resolution report if they want.
*
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
constraint.GetParentRule()->m_Name,
MessageTextFromValue( userUnits(), minClearance ),
MessageTextFromValue( userUnits(), actual ) );
drcItem->SetErrorMessage( msg );
*/
drcItem->SetItems( aRefItem->parent, aTestItem->parent );
drcItem->SetViolatingRule( constraint.GetParentRule() );

View File

@ -51,6 +51,8 @@ add_executable( drc_proto
../../pcbnew/drc/drc_test_provider_via_diameter.cpp
../../pcbnew/drc/drc_test_provider_lvs.cpp
../../pcbnew/drc/drc_test_provider_misc.cpp
../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp
../../pcbnew/drc/drc_test_provider_silk_to_silk.cpp
../../pcbnew/drc/drc_engine.cpp
../../pcbnew/drc/drc_item.cpp
../qa_utils/mocks.cpp