7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-19 00:21:36 +00:00

Adjust DRC clearance for net ties

Items that are part of net ties (pads and copper shapes that connect to
them) should allow connections from any net in the tie.  This prevents
clearance errors from being generated for matched nets

Fixes https://gitlab.com/kicad/code/kicad/-/issues/14008
This commit is contained in:
Seth Hillbrand 2024-12-10 15:52:22 -08:00
parent ad985aff9d
commit 41d4a8caab
7 changed files with 1977 additions and 0 deletions

View File

@ -160,7 +160,10 @@ bool DRC_CACHE_GENERATOR::Run()
// before we start.
for( FOOTPRINT* footprint : m_board->Footprints() )
{
footprint->BuildCourtyardCaches();
footprint->BuildNetTieCache();
}
std::vector<std::future<size_t>> returns;

View File

@ -745,6 +745,39 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
constraint.SetParentRule( c->constraint.GetParentRule() );
};
// Handle Footprint net ties, which will zero out the clearance for footprint objects
if( aConstraintType == CLEARANCE_CONSTRAINT && ( ac || bc ) && !a_is_non_copper && !b_is_non_copper )
{
const FOOTPRINT* footprints[2] = {a ? a->GetParentFootprint() : nullptr,
b ? b->GetParentFootprint() : nullptr};
const BOARD_ITEM* child_items[2] = {a, b};
// These are the items being compared against, so the order is reversed
const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
for( int ii = 0; ii < 2; ++ii )
{
// We need both a footprint item and a connected item to check for a net tie
if( !footprints[ii] || !alt_items[ii] )
continue;
const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
auto it = netcodes.find( alt_items[ii]->GetNetCode() );
if( it != netcodes.end() )
{
REPORT( "" )
REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
constraint.SetName( _( "net tie" ) );
constraint.m_Value.SetMin( 0 );
return constraint;
}
}
}
// Local overrides take precedence over everything *except* board min clearance
if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
{

View File

@ -3013,6 +3013,73 @@ void FOOTPRINT::BuildCourtyardCaches( OUTLINE_ERROR_HANDLER* aErrorHandler )
}
void FOOTPRINT::BuildNetTieCache()
{
m_netTieCache.clear();
std::map<wxString, int> map = MapPadNumbersToNetTieGroups();
std::map<PCB_LAYER_ID, std::vector<PCB_SHAPE*>> layer_shapes;
std::for_each( m_drawings.begin(), m_drawings.end(),
[&]( BOARD_ITEM* item )
{
if( item->Type() != PCB_SHAPE_T )
return;
for( PCB_LAYER_ID layer : item->GetLayerSet() )
layer_shapes[layer].push_back( static_cast<PCB_SHAPE*>( item ) );
} );
for( size_t ii = 0; ii < m_pads.size(); ++ii )
{
PAD* pad = m_pads[ ii ];
bool has_nettie = false;
auto it = map.find( pad->GetNumber() );
if( it == map.end() || it->second < 0 )
continue;
for( size_t jj = ii + 1; jj < m_pads.size(); ++jj )
{
PAD* other = m_pads[ jj ];
auto it2 = map.find( other->GetNumber() );
if( it2 == map.end() || it2->second < 0 )
continue;
if( it2->second == it->second )
{
m_netTieCache[pad].insert( pad->GetNetCode() );
m_netTieCache[pad].insert( other->GetNetCode() );
m_netTieCache[other].insert( other->GetNetCode() );
m_netTieCache[other].insert( pad->GetNetCode() );
has_nettie = true;
}
}
if( !has_nettie )
continue;
for( auto& [ layer, shapes ] : layer_shapes )
{
auto pad_shape = pad->GetEffectiveShape( layer );
for( auto other_shape : shapes )
{
auto shape = other_shape->GetEffectiveShape( layer );
if( pad_shape->Collide( shape.get() ) )
{
std::set<int>& nettie = m_netTieCache[pad];
m_netTieCache[other_shape].insert( nettie.begin(), nettie.end() );
}
}
}
}
}
std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
{
std::map<wxString, int> padNumberToGroupIdxMap;

View File

@ -499,6 +499,26 @@ public:
*/
void CheckNetTiePadGroups( const std::function<void( const wxString& )>& aErrorHandler );
/**
* Cache the pads that are allowed to connect to each other in the footprint.
*/
void BuildNetTieCache();
/**
* Get the set of net codes that are allowed to connect to a footprint item
*/
const std::set<int>& GetNetTieCache( const BOARD_ITEM* aItem ) const
{
static const std::set<int> emptySet;
auto it = m_netTieCache.find( aItem );
if( it == m_netTieCache.end() )
return emptySet;
return it->second;
}
/**
* Generate pads shapes on layer \a aLayer as polygons and adds these polygons to
* \a aBuffer.
@ -1065,6 +1085,9 @@ private:
// A pad group is a comma-separated list of pad numbers.
std::vector<wxString> m_netTiePadGroups;
// A list of 1:N footprint item to allowed net numbers
std::map<const BOARD_ITEM*, std::set<int>> m_netTieCache;
// Optional overrides
ZONE_CONNECTION m_zoneConnection;
std::optional<int> m_clearance;

View File

LOADING design file

View File

@ -0,0 +1,586 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.15,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.15,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [
"via_dangling|139050000|55475000|63bbfb0d-b742-4265-a65c-2888c8e24076|00000000-0000-0000-0000-000000000000"
],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "ignore",
"lib_footprint_mismatch": "ignore",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_clearance": 0.0,
"solder_mask_min_width": 0.0,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "error",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "issue14008.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": {
"GND": "rgb(27, 155, 78)",
"GNDD": "rgb(142, 255, 83)"
},
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [],
"text_variables": {}
}

View File

@ -63,6 +63,7 @@ BOOST_FIXTURE_TEST_CASE( DRCFalsePositiveRegressions, DRC_REGRESSION_TEST_FIXTUR
"issue12609", // Arc collison edge case
"issue14412", // Solder mask bridge between pads in a net-tie pad group
"issue15280", // Very wide spokes mis-counted as being single spoke
"issue14008", // Net-tie clearance error
"unconnected-netnames/unconnected-netnames", // Raised false schematic partity error
};