KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_terminal_pad_assignment.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 3
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#include <board.h>
22#include <footprint.h>
23#include <pad.h>
24#include <netinfo.h>
26
27namespace
28{
29
30// Mirrors the assignment block in BOARD_NETLIST_UPDATER. Refactored here so a unit test can
31// exercise the storage rule independently of the rest of the updater. Any future divergence
32// between this and the production implementation is itself a bug worth catching.
33void AssignChainTerminalPads( BOARD* aBoard, const wxString& aChain, PAD* aPads[2] )
34{
35 for( int i = 0; i < 2; ++i )
36 {
37 if( !aPads[i] )
38 continue;
39
40 NETINFO_ITEM* termNet = aPads[i]->GetNet();
41
42 if( !termNet || termNet->GetNetChain() != aChain )
43 continue;
44
45 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
46 {
47 if( net != termNet && net->GetNetChain() == aChain
48 && net->GetTerminalPad( i ) )
49 {
50 net->SetTerminalPad( i, nullptr );
51 }
52 }
53
54 termNet->SetTerminalPad( i, aPads[i] );
55 }
56}
57
58PAD* MakePad( FOOTPRINT* aFp, NETINFO_ITEM* aNet, const wxString& aNumber, const VECTOR2I& aPos )
59{
60 PAD* pad = new PAD( aFp );
61 pad->SetFrontShape( PAD_SHAPE::CIRCLE );
62 pad->SetSize( F_Cu, VECTOR2I( 1000000, 1000000 ) );
63 pad->SetPosition( aPos );
64 pad->SetNumber( aNumber );
65 pad->SetNet( aNet );
66 aFp->Add( pad );
67 return pad;
68}
69
70} // namespace
71
72
73BOOST_AUTO_TEST_SUITE( NetChainTerminalPadAssignment )
74
75// Three-net chain Net1 -- Comp1 -- Net2 -- Comp2 -- Net3 with terminals on Net1 (slot 0) and
76// Net3 (slot 1). The middle interior net (Net2) must keep both slots null so the matched-length
77// stub predicate continues to treat it as "not on trunk".
78BOOST_AUTO_TEST_CASE( TerminalsOnlyAttachToOwningNet )
79{
80 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
81
82 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net1" ), 1 );
83 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net2" ), 2 );
84 NETINFO_ITEM* n3 = new NETINFO_ITEM( board.get(), wxS( "Net3" ), 3 );
85 board->Add( n1 );
86 board->Add( n2 );
87 board->Add( n3 );
88
89 n1->SetNetChain( wxS( "BUS_A" ) );
90 n2->SetNetChain( wxS( "BUS_A" ) );
91 n3->SetNetChain( wxS( "BUS_A" ) );
92
93 FOOTPRINT* src = new FOOTPRINT( board.get() );
94 src->SetReference( wxS( "U1" ) );
95 board->Add( src );
96
97 FOOTPRINT* mid = new FOOTPRINT( board.get() );
98 mid->SetReference( wxS( "R1" ) );
99 board->Add( mid );
100
101 FOOTPRINT* sink = new FOOTPRINT( board.get() );
102 sink->SetReference( wxS( "U2" ) );
103 board->Add( sink );
104
105 PAD* pSrc = MakePad( src, n1, wxS( "1" ), VECTOR2I( 0, 0 ) );
106 PAD* pMidA = MakePad( mid, n1, wxS( "1" ), VECTOR2I( 5000000, 0 ) );
107 PAD* pMidB = MakePad( mid, n2, wxS( "2" ), VECTOR2I( 6000000, 0 ) );
108 PAD* pSink = MakePad( sink, n3, wxS( "1" ), VECTOR2I( 15000000, 0 ) );
109
110 (void) pMidA;
111 (void) pMidB;
112
113 PAD* pads[2] = { pSrc, pSink };
114 AssignChainTerminalPads( board.get(), wxS( "BUS_A" ), pads );
115
116 BOOST_CHECK_EQUAL( n1->GetTerminalPad( 0 ), pSrc );
117 BOOST_CHECK( n1->GetTerminalPad( 1 ) == nullptr );
118
119 BOOST_CHECK( n2->GetTerminalPad( 0 ) == nullptr );
120 BOOST_CHECK( n2->GetTerminalPad( 1 ) == nullptr );
121
122 BOOST_CHECK( n3->GetTerminalPad( 0 ) == nullptr );
123 BOOST_CHECK_EQUAL( n3->GetTerminalPad( 1 ), pSink );
124}
125
126
127// A null pad in slot 1 must not clobber an existing assignment from a previous resolver pass
128// (the H-3 null-overwrite case). Only the slot we are providing is touched.
129BOOST_AUTO_TEST_CASE( NullPadDoesNotClobberExisting )
130{
131 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
132
133 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net1" ), 1 );
134 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net2" ), 2 );
135 board->Add( n1 );
136 board->Add( n2 );
137
138 n1->SetNetChain( wxS( "BUS_B" ) );
139 n2->SetNetChain( wxS( "BUS_B" ) );
140
141 FOOTPRINT* fp1 = new FOOTPRINT( board.get() );
142 fp1->SetReference( wxS( "U1" ) );
143 board->Add( fp1 );
144
145 FOOTPRINT* fp2 = new FOOTPRINT( board.get() );
146 fp2->SetReference( wxS( "U2" ) );
147 board->Add( fp2 );
148
149 PAD* pA = MakePad( fp1, n1, wxS( "1" ), VECTOR2I( 0, 0 ) );
150 PAD* pB = MakePad( fp2, n2, wxS( "1" ), VECTOR2I( 10000000, 0 ) );
151
152 n2->SetTerminalPad( 1, pB );
153
154 PAD* pads[2] = { pA, nullptr };
155 AssignChainTerminalPads( board.get(), wxS( "BUS_B" ), pads );
156
157 BOOST_CHECK_EQUAL( n1->GetTerminalPad( 0 ), pA );
158 BOOST_CHECK_EQUAL( n2->GetTerminalPad( 1 ), pB );
159 BOOST_CHECK( n1->GetTerminalPad( 1 ) == nullptr );
160 BOOST_CHECK( n2->GetTerminalPad( 0 ) == nullptr );
161}
162
163
164// Boards saved with the legacy broadcast assignment have the same pad pointer attached to every
165// member net. Re-running the resolver must clear the foreign claims so the middle net stops
166// reporting itself as on-trunk.
167BOOST_AUTO_TEST_CASE( StaleBroadcastSamePadIsCleared )
168{
169 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
170
171 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net1" ), 1 );
172 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net2" ), 2 );
173 NETINFO_ITEM* n3 = new NETINFO_ITEM( board.get(), wxS( "Net3" ), 3 );
174 board->Add( n1 );
175 board->Add( n2 );
176 board->Add( n3 );
177
178 n1->SetNetChain( wxS( "BUS_C" ) );
179 n2->SetNetChain( wxS( "BUS_C" ) );
180 n3->SetNetChain( wxS( "BUS_C" ) );
181
182 FOOTPRINT* src = new FOOTPRINT( board.get() );
183 src->SetReference( wxS( "U1" ) );
184 board->Add( src );
185
186 FOOTPRINT* sink = new FOOTPRINT( board.get() );
187 sink->SetReference( wxS( "U2" ) );
188 board->Add( sink );
189
190 PAD* pSrc = MakePad( src, n1, wxS( "1" ), VECTOR2I( 0, 0 ) );
191 PAD* pSnk = MakePad( sink, n3, wxS( "1" ), VECTOR2I( 10000000, 0 ) );
192
193 n1->SetTerminalPad( 0, pSrc );
194 n2->SetTerminalPad( 0, pSrc );
195 n3->SetTerminalPad( 0, pSrc );
196 n1->SetTerminalPad( 1, pSnk );
197 n2->SetTerminalPad( 1, pSnk );
198 n3->SetTerminalPad( 1, pSnk );
199
200 PAD* pads[2] = { pSrc, pSnk };
201 AssignChainTerminalPads( board.get(), wxS( "BUS_C" ), pads );
202
203 BOOST_CHECK_EQUAL( n1->GetTerminalPad( 0 ), pSrc );
204 BOOST_CHECK( n1->GetTerminalPad( 1 ) == nullptr );
205
206 BOOST_CHECK( n2->GetTerminalPad( 0 ) == nullptr );
207 BOOST_CHECK( n2->GetTerminalPad( 1 ) == nullptr );
208
209 BOOST_CHECK( n3->GetTerminalPad( 0 ) == nullptr );
210 BOOST_CHECK_EQUAL( n3->GetTerminalPad( 1 ), pSnk );
211}
212
213
214// Mirrors the matched-length predicate. With pads owned by Net1/Net3, Net2 must report
215// onTrunk=false even though all three nets share a chain.
216BOOST_AUTO_TEST_CASE( OnTrunkPredicateRequiresNetcodeMatch )
217{
218 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
219
220 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net1" ), 1 );
221 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net2" ), 2 );
222 NETINFO_ITEM* n3 = new NETINFO_ITEM( board.get(), wxS( "Net3" ), 3 );
223 board->Add( n1 );
224 board->Add( n2 );
225 board->Add( n3 );
226
227 FOOTPRINT* fp = new FOOTPRINT( board.get() );
228 fp->SetReference( wxS( "U1" ) );
229 board->Add( fp );
230
231 PAD* pA = MakePad( fp, n1, wxS( "1" ), VECTOR2I( 0, 0 ) );
232 PAD* pB = MakePad( fp, n3, wxS( "2" ), VECTOR2I( 1000000, 0 ) );
233
234 n1->SetTerminalPad( 0, pA );
235 n3->SetTerminalPad( 1, pB );
236
237 auto isOnTrunk = []( NETINFO_ITEM* aNet, int aNetCode )
238 {
239 return ( aNet->GetTerminalPad( 0 )
240 && aNet->GetTerminalPad( 0 )->GetNetCode() == aNetCode )
241 || ( aNet->GetTerminalPad( 1 )
242 && aNet->GetTerminalPad( 1 )->GetNetCode() == aNetCode );
243 };
244
245 BOOST_CHECK( isOnTrunk( n1, 1 ) );
246 BOOST_CHECK( isOnTrunk( n3, 3 ) );
247 BOOST_CHECK( !isOnTrunk( n2, 2 ) );
248
249 // Defense-in-depth: even if a stale broadcast slipped a foreign-net pad onto n2, the
250 // predicate refuses to be fooled because the netcodes mismatch.
251 n2->SetTerminalPad( 0, pA );
252 BOOST_CHECK( !isOnTrunk( n2, 2 ) );
253}
254
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1086
void SetReference(const wxString &aReference)
Definition footprint.h:847
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:115
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:113
void SetTerminalPad(int aIndex, PAD *aPad)
Definition netinfo.h:116
Definition pad.h:61
@ F_Cu
Definition layer_ids.h:60
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(TerminalsOnlyAttachToOwningNet)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683