Browse Source

ADDED: "bridged_mask" constraint.

The constraint takes no values, but can have a severity
assigned.  This allows solder mask bridges that meet
certain criteria to be ignored.

For a mask aperture, only the aperture must pass the
DRC rule conditional.  For bridges between two copper
items, both items must pass the conditional.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21732
master
Jeff Young 4 weeks ago
parent
commit
175e8a4a94
  1. 1
      common/drc_rules.keywords
  2. 1
      pcbnew/dialogs/panel_setup_rules.cpp
  3. 1
      pcbnew/dialogs/panel_setup_rules_help_2constraints.h
  4. 6
      pcbnew/dialogs/panel_setup_rules_help_9more_examples.h
  5. 3
      pcbnew/drc/drc_rule.h
  6. 8
      pcbnew/drc/drc_rule_parser.cpp
  7. 34
      pcbnew/drc/drc_test_provider_solder_mask.cpp

1
common/drc_rules.keywords

@ -2,6 +2,7 @@ annular_width
assertion
assign_component_class
board_edge
bridged_mask
buried_via
clearance
condition

1
pcbnew/dialogs/panel_setup_rules.cpp

@ -465,6 +465,7 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
{
tokens = wxT( "annular_width|"
"assertion|"
"bridged_mask|"
"clearance|"
"connection_width|"
"courtyard_clearance|"

1
pcbnew/dialogs/panel_setup_rules_help_2constraints.h

@ -5,6 +5,7 @@ _HKI( "### Constraints\n"
"|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n"
"| `annular_width` | min/opt/max | Checks the width of annular rings on vias.<br> |\n"
"| `assertion` | \"&lt;expression>\" | Checks the given expression.<br> |\n"
"| `bridged_mask` | | Checks for solder mask bridges between copper items. This constraint does not take a min/opt/max value. In combination with a severity clause, this constraint can be used to allow or disallow solder mask bridging in various conditions.<br> |\n"
"| `clearance` | min | Specifies the **electrical** clearance between copper objects of different nets. (See `physical_clearance` if you wish to specify clearance between objects regardless of net.)<br><br>To allow copper objects to overlap (collide), create a `clearance` constraint with the `min` value less than zero (for example, `-1`).<br> |\n"
"| `courtyard_clearance` | min | Checks the clearance between footprint courtyards and generates an error if any two courtyards are closer than the `min` distance. If a footprint does not have a courtyard shape, no errors will be generated from this constraint.<br> |\n"
"| `creepage` | min | Specifies the creepage distance between copper objects of different nets.<br> |\n"

6
pcbnew/dialogs/panel_setup_rules_help_9more_examples.h

@ -148,4 +148,10 @@ _HKI( "### More Examples\n"
" (constraint solder_paste_abs_margin (opt -50mm))\n"
" (condition \"A.Do_not_Populate\"))\n"
"\n"
"\n"
" # Allow solder mask bridging under guard ring mask apertures\n"
" (rule guard_ring_bridging\n"
" (constraint bridged_mask))\n"
" (condition \"A.intersectsArea('guard_ring')\")\n"
" (severity ignore))\n"
"" );

3
pcbnew/drc/drc_rule.h

@ -79,7 +79,8 @@ enum DRC_CONSTRAINT_T
ASSERTION_CONSTRAINT,
CONNECTION_WIDTH_CONSTRAINT,
TRACK_ANGLE_CONSTRAINT,
VIA_DANGLING_CONSTRAINT
VIA_DANGLING_CONSTRAINT,
BRIDGED_MASK_CONSTRAINT
};

8
pcbnew/drc/drc_rule_parser.cpp

@ -521,7 +521,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
case T_connection_width: c.m_Type = CONNECTION_WIDTH_CONSTRAINT; break;
case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
case T_via_dangling: c.m_Type = VIA_DANGLING_CONSTRAINT; break;
case T_via_dangling: c.m_Type = VIA_DANGLING_CONSTRAINT; break;
case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
case T_thermal_relief_gap: c.m_Type = THERMAL_RELIEF_GAP_CONSTRAINT; break;
case T_thermal_spoke_width: c.m_Type = THERMAL_SPOKE_WIDTH_CONSTRAINT; break;
@ -537,6 +537,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
case T_diff_pair_uncoupled: c.m_Type = MAX_UNCOUPLED_CONSTRAINT; break;
case T_physical_clearance: c.m_Type = PHYSICAL_CLEARANCE_CONSTRAINT; break;
case T_physical_hole_clearance: c.m_Type = PHYSICAL_HOLE_CLEARANCE_CONSTRAINT; break;
case T_bridged_mask: c.m_Type = BRIDGED_MASK_CONSTRAINT; break;
default:
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
wxT( "assertion, clearance, hole_clearance, edge_clearance, physical_clearance, "
@ -545,7 +546,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
"disallow, zone_connection, thermal_relief_gap, thermal_spoke_width, "
"min_resolved_spokes, solder_mask_expansion, solder_paste_abs_margin, "
"solder_paste_rel_margin, length, skew, via_count, via_dangling, via_diameter, "
"diff_pair_gap or diff_pair_uncoupled" ) );
"diff_pair_gap, diff_pair_uncoupled or bridged_mask" ) );
reportError( msg );
}
@ -558,7 +559,8 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
bool unitless = c.m_Type == VIA_COUNT_CONSTRAINT
|| c.m_Type == MIN_RESOLVED_SPOKES_CONSTRAINT
|| c.m_Type == TRACK_ANGLE_CONSTRAINT
|| c.m_Type == VIA_DANGLING_CONSTRAINT;
|| c.m_Type == VIA_DANGLING_CONSTRAINT
|| c.m_Type == BRIDGED_MASK_CONSTRAINT;
allowsTimeDomain = c.m_Type == LENGTH_CONSTRAINT || c.m_Type == SKEW_CONSTRAINT;

34
pcbnew/drc/drc_test_provider_solder_mask.cpp

@ -78,7 +78,7 @@ private:
bool checkMaskAperture( BOARD_ITEM* aMaskItem, BOARD_ITEM* aTestItem, PCB_LAYER_ID aTestLayer,
int aTestNet, BOARD_ITEM** aCollidingItem );
bool checkItemMask( BOARD_ITEM* aMaskItem, int aTestNet );
bool checkItemMask( BOARD_ITEM* aItem, int aTestNet );
private:
DRC_RULE m_bridgeRule;
@ -320,7 +320,7 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::checkMaskAperture( BOARD_ITEM* aMaskItem, BO
PCB_LAYER_ID aTestLayer, int aTestNet,
BOARD_ITEM** aCollidingItem )
{
if( aTestLayer == F_Mask && !aTestItem->IsOnLayer( PADSTACK::ALL_LAYERS ) )
if( aTestLayer == F_Mask && !aTestItem->IsOnLayer( F_Cu ) )
return false;
if( aTestLayer == B_Mask && !aTestItem->IsOnLayer( B_Cu ) )
@ -388,18 +388,18 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::checkMaskAperture( BOARD_ITEM* aMaskItem, BO
}
bool DRC_TEST_PROVIDER_SOLDER_MASK::checkItemMask( BOARD_ITEM* aMaskItem, int aTestNet )
bool DRC_TEST_PROVIDER_SOLDER_MASK::checkItemMask( BOARD_ITEM* aItem, int aTestNet )
{
if( FOOTPRINT* fp = aMaskItem->GetParentFootprint() )
if( FOOTPRINT* fp = aItem->GetParentFootprint() )
{
// If we're allowing bridges then we're allowing bridges. Nothing to check.
if( fp->AllowSolderMaskBridges() )
return false;
// Items belonging to a net-tie may share the mask aperture of pads in the same group.
if( aMaskItem->Type() == PCB_PAD_T && fp->IsNetTie() )
if( aItem->Type() == PCB_PAD_T && fp->IsNetTie() )
{
PAD* pad = static_cast<PAD*>( aMaskItem );
PAD* pad = static_cast<PAD*>( aItem );
std::map<wxString, int> padNumberToGroupIdxMap = fp->MapPadNumbersToNetTieGroups();
int groupIdx = padNumberToGroupIdxMap[ pad->GetNumber() ];
@ -432,6 +432,9 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testItemAgainstItems( BOARD_ITEM* aItem, con
PCB_SHAPE* shape = aItem->Type() == PCB_SHAPE_T ? static_cast<PCB_SHAPE*>( aItem ) : nullptr;
int itemNet = -1;
DRC_CONSTRAINT constraint;
std::optional<bool> itemConstraintIgnored;
if( aItem->IsConnected() )
itemNet = static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode();
@ -550,11 +553,28 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testItemAgainstItems( BOARD_ITEM* aItem, con
clearance += otherPad->GetSolderMaskExpansion( aTargetLayer );
else if( otherVia && !otherVia->IsTented( aTargetLayer ) )
clearance += otherVia->GetSolderMaskExpansion();
else if( shape )
else if( otherShape )
clearance += otherShape->GetSolderMaskExpansion();
if( itemShape->Collide( otherItemShape.get(), clearance, &actual, &pos ) )
{
if( !itemConstraintIgnored.has_value() )
{
constraint = m_drcEngine->EvalRules( BRIDGED_MASK_CONSTRAINT, aItem, nullptr, aRefLayer );
itemConstraintIgnored = constraint.GetSeverity() == RPT_SEVERITY_IGNORE;
}
constraint = m_drcEngine->EvalRules( BRIDGED_MASK_CONSTRAINT, other, nullptr, aTargetLayer );
bool otherConstraintIgnored = constraint.GetSeverity() == RPT_SEVERITY_IGNORE;
// Mask apertures are ignored on their own; in other cases both participants must be ignored
if( ( isMaskAperture( aItem ) && itemConstraintIgnored )
|| ( isMaskAperture( other ) && otherConstraintIgnored )
|| ( itemConstraintIgnored && otherConstraintIgnored ) )
{
return !m_drcEngine->IsCancelled();
}
wxString msg;
BOARD_ITEM* colliding = nullptr;

Loading…
Cancel
Save