KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_chain_bridging_multipad.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
23
24#include <filesystem>
25#include <fstream>
26#include <string>
27
28#include <board.h>
30#include <drc/drc_engine.h>
31#include <drc/drc_item.h>
32#include <netinfo.h>
33#include <pcb_marker.h>
35
36
37// These tests pin down the bridging-length contribution of multi-pad chain footprints
38// in the matched-length DRC. History:
39//
40// fd87668d29 introduced gather-then-skip behavior: any 3+ pad chain-member footprint
41// silently contributed zero bridging length, which the comment block in the provider
42// claimed to be guarding against. MR review C-2 flagged that as the bug it was trying
43// to prevent.
44//
45// Post C-2 fix the bridging contribution is the maximum pairwise cross-net pad span
46// across the chain-member pads on the footprint. This is the most generous (upper
47// bound) bridge such a device could create, keeps the contribution non-zero, and
48// scales identically to any pad count >= 2.
49
50
51namespace
52{
53
54static const char* DRU_HEADER = "(version 1)\n";
55
56
57std::string makeChainBudgetRule( const wxString& aName, const wxString& aMinMM,
58 const wxString& aMaxMM )
59{
60 return wxString::Format( "%s(rule \"%s\"\n"
61 " (condition \"A.NetClass == 'Default'\")\n"
62 " (constraint net_chain_length (min %smm) (max %smm))\n"
63 ")\n",
64 DRU_HEADER, aName, aMinMM, aMaxMM )
65 .ToStdString();
66}
67
68
69// Load a board from the test data dir, write a temp DRU file, run the matched-length DRC and
70// return the count of DRCE_LENGTH_OUT_OF_RANGE markers raised on the chain "SIG". Both nets
71// named "/NET_*" are auto-tagged into chain "SIG".
72size_t runChainLengthDrc( const std::string& aBoardFile, const std::string& aRuleText,
73 const std::string& aTmpSubdir )
74{
75 namespace fs = std::filesystem;
76
77 fs::path tmpDir = fs::temp_directory_path() / aTmpSubdir;
78 fs::create_directories( tmpDir );
79
80 fs::path druPath = tmpDir / "chain_bridge.kicad_dru";
81
82 {
83 std::ofstream druOut( druPath );
84 druOut << aRuleText;
85 }
86
87 PCB_IO_KICAD_SEXPR plugin;
88 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
89 plugin.LoadBoard( KI_TEST::GetPcbnewTestDataDir() + aBoardFile, board.get() );
90 board->BuildConnectivity();
91
92 for( NETINFO_ITEM* net : board->GetNetInfo() )
93 {
94 if( net && net->GetNetname().StartsWith( wxS( "/NET_" ) ) )
95 net->SetNetChain( wxS( "SIG" ) );
96 }
97
98 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
99
100 auto drcEngine = std::make_shared<DRC_ENGINE>( board.get(), &bds );
101 wxFileName ruleFile( druPath.string() );
102 drcEngine->InitEngine( ruleFile );
103 bds.m_DRCEngine = drcEngine;
104
112
113 size_t lengthViolations = 0;
114
115 drcEngine->SetViolationHandler(
116 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I&, int,
117 const std::function<void( PCB_MARKER* )>& )
118 {
119 if( aItem->GetErrorCode() == DRCE_LENGTH_OUT_OF_RANGE )
120 ++lengthViolations;
121 } );
122
123 drcEngine->RunTests( EDA_UNITS::MM, true, false );
124
125 std::error_code ec;
126 fs::remove( druPath, ec );
127
128 return lengthViolations;
129}
130
131} // namespace
132
133
134BOOST_AUTO_TEST_SUITE( DRCChainBridgingMultipad )
135
136
137// 3-pad footprint, 2 nets (e.g. ferrite bead with centre tap). Pads at x = -2.5, 0, +2.5.
138// Nets A, A, B. Track copper = 20 mm (10 mm on NET_A + 10 mm on NET_B). Cross-net pad
139// pairs span max 5 mm (-2.5 NET_A to +2.5 NET_B). Total bridged chain = 25 mm.
140BOOST_AUTO_TEST_CASE( ThreePadTwoNetFootprintContributesMaxPairwiseSpan )
141{
142 static const char* boardFile = "net_chains/chain_bridging_3pad_2net.kicad_pcb";
143
144 // 26 mm budget passes (chain ~25 mm).
145 size_t pass =
146 runChainLengthDrc( boardFile, makeChainBudgetRule( "Pass", "0", "26" ),
147 "kicad_drc_chain_bridging_3pad_pass" );
148 BOOST_CHECK_MESSAGE( pass == 0, "Expected no length violation under 26 mm budget, got "
149 << pass );
150
151 // 22 mm budget fails: pre-fix would silently drop bridging and report ~20 mm. Post-fix
152 // the 5 mm max pairwise span is included.
153 size_t fail =
154 runChainLengthDrc( boardFile, makeChainBudgetRule( "Fail", "0", "22" ),
155 "kicad_drc_chain_bridging_3pad_fail" );
156 BOOST_CHECK_MESSAGE( fail == 1, "Expected one length violation under 22 mm budget, got "
157 << fail );
158}
159
160
161// 3-pad footprint, 3 nets (transformer-style). Pads at x = -2.5, 0, +2.5 on nets A, B, C.
162// Track copper = 30 mm split evenly. Max cross-net pairwise span = 5 mm. Pre-fix the whole
163// footprint was silently skipped because chainPads.size() > 2 broke the gather loop.
164BOOST_AUTO_TEST_CASE( ThreePadThreeNetFootprintContributesMaxPairwiseSpan )
165{
166 static const char* boardFile = "net_chains/chain_bridging_3pad_3net.kicad_pcb";
167
168 // Single T1 footprint gives 30 mm copper + 5 mm bridging = 35 mm. Budget 36 mm passes.
169 size_t pass =
170 runChainLengthDrc( boardFile, makeChainBudgetRule( "Pass", "0", "36" ),
171 "kicad_drc_chain_bridging_3net_pass" );
172 BOOST_CHECK_MESSAGE( pass == 0, "Expected no length violation under 36 mm budget, got "
173 << pass );
174
175 // 32 mm budget fails: pre-fix the 3-pad-3-net footprint was silently dropped (reporting
176 // 30 mm copper). Post-fix the 5 mm max pairwise span pushes total to 35 mm.
177 size_t fail =
178 runChainLengthDrc( boardFile, makeChainBudgetRule( "Fail", "0", "32" ),
179 "kicad_drc_chain_bridging_3net_fail" );
180 BOOST_CHECK_MESSAGE( fail == 1, "Expected one length violation under 32 mm budget, got "
181 << fail );
182}
183
184
185// 4-pad footprint, 2 nets (e.g. dual-winding chip with split netting). Pads at
186// x = -3, -1, +1, +3 on nets A, A, B, B. Track copper = 20 mm. Cross-net pad pairs:
187// (-3 A, +1 B) = 4, (-3 A, +3 B) = 6, (-1 A, +1 B) = 2, (-1 A, +3 B) = 4. Max = 6 mm.
188BOOST_AUTO_TEST_CASE( FourPadFootprintContributesMaxPairwiseSpan )
189{
190 static const char* boardFile = "net_chains/chain_bridging_4pad.kicad_pcb";
191
192 // Chain total: 20 mm copper + 6 mm bridging = 26 mm. Budget 27 mm passes.
193 size_t pass =
194 runChainLengthDrc( boardFile, makeChainBudgetRule( "Pass", "0", "27" ),
195 "kicad_drc_chain_bridging_4pad_pass" );
196 BOOST_CHECK_MESSAGE( pass == 0, "Expected no length violation under 27 mm budget, got "
197 << pass );
198
199 // 24 mm budget fails: pre-fix dropped bridging, post-fix adds 6 mm max pairwise span.
200 size_t fail =
201 runChainLengthDrc( boardFile, makeChainBudgetRule( "Fail", "0", "24" ),
202 "kicad_drc_chain_bridging_4pad_fail" );
203 BOOST_CHECK_MESSAGE( fail == 1, "Expected one length violation under 24 mm budget, got "
204 << fail );
205}
206
207
208// 3-pad footprint, single net. All pads on NET_A. No cross-net bridging; must contribute 0.
209BOOST_AUTO_TEST_CASE( ThreePadSingleNetFootprintContributesZero )
210{
211 static const char* boardFile = "net_chains/chain_bridging_3pad_1net.kicad_pcb";
212
213 // Pure copper sum is 20 mm with no bridging contribution. Budget 21 mm passes.
214 size_t pass =
215 runChainLengthDrc( boardFile, makeChainBudgetRule( "Pass", "0", "21" ),
216 "kicad_drc_chain_bridging_singlenet_pass" );
217 BOOST_CHECK_MESSAGE( pass == 0, "Single-net 3-pad footprint must not contribute bridging; "
218 "got " << pass << " violations under 21 mm budget" );
219}
220
221
General utilities for PCB file IO for QA programs.
Container for design settings for a BOARD object.
std::map< int, SEVERITY > m_DRCSeverities
std::shared_ptr< DRC_ENGINE > m_DRCEngine
Handle the data for a net.
Definition netinfo.h:46
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:37
@ DRCE_LIB_FOOTPRINT_ISSUES
Definition drc_item.h:80
@ DRCE_INVALID_OUTLINE
Definition drc_item.h:70
@ DRCE_DRILL_OUT_OF_RANGE
Definition drc_item.h:58
@ DRCE_DANGLING_VIA
Definition drc_item.h:48
@ DRCE_LENGTH_OUT_OF_RANGE
Definition drc_item.h:102
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:81
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(ThreePadTwoNetFootprintContributesMaxPairwiseSpan)
BOOST_AUTO_TEST_SUITE_END()
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683