KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_skew_tuning.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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * QA test for signal-aware skew tuning: when both P & N nets belong to the same signal
24 * and additional nets share that signal, the skew basis must include the extra nets' length.
25 */
26
27#include <boost/test/unit_test.hpp>
28
29#include <board.h>
30#include <pcb_track.h>
31#include <netinfo.h>
32#include <router/pns_router.h>
34// Aggregation now exposed via router interface (PNS_KICAD_IFACE)
36
37// NOTE: This test exercises only the aggregation helper to avoid needing a full router world setup.
38
39BOOST_AUTO_TEST_SUITE( SignalSkewTuning )
40
41BOOST_AUTO_TEST_CASE( ExtraSignalLengthExcludedSet )
42{
43 BOARD board;
44
45 // Create three nets in one signal: DP_P, DP_N (diff pair), and AUX (extra)
46 NETINFO_ITEM* nP = new NETINFO_ITEM( &board, wxS("/CLK_P"), 1 ); board.Add( nP );
47 NETINFO_ITEM* nN = new NETINFO_ITEM( &board, wxS("/CLK_N"), 2 ); board.Add( nN );
48 NETINFO_ITEM* nA = new NETINFO_ITEM( &board, wxS("AUX"), 3 ); board.Add( nA );
49
50 for( NETINFO_ITEM* n : { nP, nN, nA } )
51 n->SetNetChain( wxS("SIG_CLK") );
52
53 // Add one track per net of varying lengths
54 auto addTrack = [&]( int netCode, int x1, int x2 )
55 {
56 PCB_TRACK* t = new PCB_TRACK( &board );
57 t->SetNetCode( netCode );
58 t->SetStart( VECTOR2I( x1, 0 ) );
59 t->SetEnd( VECTOR2I( x2, 0 ) );
60 t->SetWidth( 100000 );
61 board.Add( t );
62 };
63
64 addTrack( 1, 0, 1000000 ); // 1 mm
65 addTrack( 2, 0, 1200000 ); // 1.2 mm
66 addTrack( 3, 0, 800000 ); // 0.8 mm (extra net)
67
68 // Use interface method
69 PNS_KICAD_IFACE_BASE ifaceBase; // base exposes GetSignalAggregate for testing without full tool setup
70 ifaceBase.SetBoard( &board );
71 long long extraLen = 0; long long extraDelay = 0;
72 bool agg = ifaceBase.GetSignalAggregate( nP, nN, extraLen, extraDelay );
73 BOOST_CHECK( agg );
74 BOOST_CHECK_EQUAL( extraLen, 800000 );
75}
76
77
78// Regression test for H-7: MEANDER_SKEW_PLACER::Move must narrow the doMove target by the
79// chain-extras aggregate plus any unmeasured stub on the active net. Before the fix, the skew
80// placer dispatched directly to doMove with m_coupledLength + targetSkew, which double-counted
81// the chain extras already baked into m_coupledLength at Start() and caused over-meandering by
82// exactly the chain budget.
83//
84// Codex review caught a subtle pitfall in the offset source: chainNarrowingOffset() relies on
85// initChainExtras(), which calls GetSignalAggregate(CurrentNets()[0], CurrentNets()[1], ...).
86// MEANDER_PLACER::CurrentNets() (the parent class) returns only the active net, so without an
87// override the aggregate would exclude only the active net and therefore include the coupled
88// net's routed length. m_coupledLength baked at Start() uses GetSignalAggregate(P, N, ...) which
89// excludes BOTH legs. The two values must match, otherwise the skew Move would over-subtract by
90// exactly the coupled net's routed length.
91//
92// We can't drive the full router from a unit test, but we can encode the arithmetic contract
93// the placer relies on: given the variables captured at Start(), the meander target the placer
94// passes to doMove must equal coupled_origPath_without_extras + targetSkew - unmeasured. In
95// terms of cached state that simplifies to:
96//
97// target = m_coupledLength + targetSkew - (m_chainExtrasLength + unmeasured)
98//
99// because m_coupledLength = coupled_origPath + extraSignalLen at Start(). If anyone re-introduces
100// the regression by removing the offset, the symbolic identity below stops holding.
101BOOST_AUTO_TEST_CASE( SkewMeanderTargetHonorsChainBudget )
102{
103 // Synthetic snapshot of state captured at MEANDER_SKEW_PLACER::Start() for a diff pair in a
104 // chain that includes one extra net.
105 const long long coupled_origPath = 12'000'000; // 12 mm coupled net PNS path
106 const long long active_origPath = 11'500'000; // 11.5 mm active net PNS path (baseline)
107 const long long chainExtras = 5'000'000; // 5 mm of routed length on sibling nets (excl P & N)
108 const long long unmeasuredStub = 300'000; // 0.3 mm stub outside PNS path on active net
109
110 const long long m_coupledLength = coupled_origPath + chainExtras;
111 const long long m_baselineLength = active_origPath;
112 const long long m_chainExtrasLength = chainExtras; // matches Start()'s exclude-both aggregate
113 const long long activeBoardLen = active_origPath + unmeasuredStub;
114
115 // Reproduce MEANDER_PLACER_BASE::chainNarrowingOffset() arithmetic.
116 const long long unmeasured = std::max( 0LL, activeBoardLen - m_baselineLength );
117 const long long offset = m_chainExtrasLength + unmeasured;
118 BOOST_CHECK_EQUAL( offset, chainExtras + unmeasuredStub );
119
120 // Pick a target skew. The placer should request a meander-only path length such that the
121 // final active total minus the final coupled total equals targetSkew.
122 const long long targetSkew = 200'000; // 0.2 mm
123
124 const long long meanderTarget = m_coupledLength + targetSkew - offset;
125
126 // The meander only adds length on the active net's PNS path. Final active PNS path =
127 // meanderTarget. Final active total signal = meanderTarget + unmeasuredStub + chainExtras.
128 const long long activeTotalAfterTune = meanderTarget + unmeasuredStub + chainExtras;
129 const long long coupledTotal = coupled_origPath + chainExtras; // unchanged during this session
130
131 const long long realizedSkew = activeTotalAfterTune - coupledTotal;
132 BOOST_CHECK_EQUAL( realizedSkew, targetSkew );
133
134 // Sanity-check the previously-broken arithmetic so the regression is documented.
135 const long long brokenMeanderTarget = m_coupledLength + targetSkew; // pre-fix path
136 const long long brokenActiveAfter = brokenMeanderTarget + unmeasuredStub + chainExtras;
137 const long long brokenSkew = brokenActiveAfter - coupledTotal;
138 BOOST_CHECK_EQUAL( brokenSkew, targetSkew + offset );
139 BOOST_CHECK( brokenSkew != targetSkew );
140
141 // Codex's case: if CurrentNets() were not overridden in MEANDER_SKEW_PLACER, the chain-extras
142 // helper would call GetSignalAggregate(active, active, ...) excluding only the active net. The
143 // coupled net's routed length would leak into m_chainExtrasLength, causing the offset to
144 // include coupled_origPath. The Move target would then under-shoot the meander by exactly
145 // that amount.
146 const long long badChainExtras = chainExtras + coupled_origPath; // exclude-active-only
147 const long long badOffset = badChainExtras + unmeasured;
148 const long long badMeanderTarget = m_coupledLength + targetSkew - badOffset;
149 const long long badActiveAfter = badMeanderTarget + unmeasuredStub + chainExtras;
150 const long long badRealizedSkew = badActiveAfter - coupledTotal;
151 BOOST_CHECK_EQUAL( badRealizedSkew, targetSkew - coupled_origPath );
152 BOOST_CHECK( badRealizedSkew != targetSkew );
153}
154
155
156// Companion runtime check on GetSignalAggregate's exclusion semantics. With both P and N passed,
157// only sibling nets in the chain contribute. With the same net passed twice, only that single
158// net is excluded (this is what MEANDER_PLACER::CurrentNets() would have caused for skew before
159// the override fix).
160BOOST_AUTO_TEST_CASE( SignalAggregateExcludesBothDiffPairLegs )
161{
162 BOARD board;
163
164 NETINFO_ITEM* nP = new NETINFO_ITEM( &board, wxS( "/D_P" ), 1 ); board.Add( nP );
165 NETINFO_ITEM* nN = new NETINFO_ITEM( &board, wxS( "/D_N" ), 2 ); board.Add( nN );
166 NETINFO_ITEM* nA = new NETINFO_ITEM( &board, wxS( "/AUX" ), 3 ); board.Add( nA );
167
168 for( NETINFO_ITEM* n : { nP, nN, nA } )
169 n->SetNetChain( wxS( "SIG_D" ) );
170
171 auto addTrack = [&]( int netCode, int x1, int x2 )
172 {
173 PCB_TRACK* t = new PCB_TRACK( &board );
174 t->SetNetCode( netCode );
175 t->SetStart( VECTOR2I( x1, 0 ) );
176 t->SetEnd( VECTOR2I( x2, 0 ) );
177 t->SetWidth( 100000 );
178 board.Add( t );
179 };
180
181 addTrack( nP->GetNetCode(), 0, 5'000'000 ); // P 5 mm
182 addTrack( nN->GetNetCode(), 0, 7'000'000 ); // N 7 mm
183 addTrack( nA->GetNetCode(), 0, 2'000'000 ); // AUX 2 mm
184
186 iface.SetBoard( &board );
187
188 long long extraLen = 0, extraDelay = 0;
189
190 BOOST_REQUIRE( iface.GetSignalAggregate( nP, nN, extraLen, extraDelay ) );
191 BOOST_CHECK_EQUAL( extraLen, 2'000'000 ); // only AUX
192
193 extraLen = 0;
194 BOOST_REQUIRE( iface.GetSignalAggregate( nP, nP, extraLen, extraDelay ) );
195 BOOST_CHECK_EQUAL( extraLen, 7'000'000 + 2'000'000 ); // N + AUX (this is the buggy path)
196}
197
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1247
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:97
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:93
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:96
virtual void SetWidth(int aWidth)
Definition pcb_track.h:90
void SetBoard(BOARD *aBoard)
bool GetSignalAggregate(PNS::NET_HANDLE aNetP, PNS::NET_HANDLE aNetN, long long &aExtraLength, long long &aExtraDelay) const override
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(ExtraSignalLengthExcludedSet)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687