KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_trunk_delay.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 */
20
21#include <boost/test/unit_test.hpp>
22
23#include <filesystem>
24#include <fstream>
25
26#include <base_units.h>
27#include <board.h>
29#include <footprint.h>
30#include <net_chain_bridging.h>
31#include <netinfo.h>
32#include <pad.h>
33#include <pcb_track.h>
35
36
37// Two-net daisy chain through a single bridge. Trunk = 20 mm (net A track) +
38// 5 mm (bridge pad-to-pad span) + 25 mm (net B track) = 50 mm. The board has
39// no stackup so the per-track length-delay calculator returns a known-type
40// item with zero delay (trackDelay() does not fall back when Type != UNKNOWN);
41// only the bridge edge carries delay, derived from the chain-wide fallback
42// 5.9 ps/mm. This pins the trunk-delay equal-to-bridge contract.
43static const char* DAISY_PCB = R"(
44(kicad_pcb
45 (version 20250904)
46 (generator "pcbnew")
47 (generator_version "9.99")
48 (layers
49 (0 "F.Cu" signal)
50 (2 "B.Cu" signal)
51 (44 "Edge.Cuts" user)
52 )
53 (net 0 "")
54 (net 1 "/NET_A")
55 (net 2 "/NET_B")
56 (gr_line (start -5 -5) (end 60 -5) (layer "Edge.Cuts") (width 0.05))
57 (gr_line (start 60 -5) (end 60 5) (layer "Edge.Cuts") (width 0.05))
58 (gr_line (start 60 5) (end -5 5) (layer "Edge.Cuts") (width 0.05))
59 (gr_line (start -5 5) (end -5 -5) (layer "Edge.Cuts") (width 0.05))
60 (footprint "Term1" (layer "F.Cu") (uuid "00000000-0000-0000-0000-000000000a01")
61 (at 0 0)
62 (pad "1" smd rect (at 0 0) (size 0.8 0.8) (layers "F.Cu") (net 1 "/NET_A") (uuid "00000000-0000-0000-0000-000000000a02"))
63 )
64 (footprint "Bridge" (layer "F.Cu") (uuid "00000000-0000-0000-0000-000000000b01")
65 (at 22.5 0)
66 (pad "1" smd rect (at -2.5 0) (size 0.8 0.8) (layers "F.Cu") (net 1 "/NET_A") (uuid "00000000-0000-0000-0000-000000000b02"))
67 (pad "2" smd rect (at 2.5 0) (size 0.8 0.8) (layers "F.Cu") (net 2 "/NET_B") (uuid "00000000-0000-0000-0000-000000000b03"))
68 )
69 (footprint "Term2" (layer "F.Cu") (uuid "00000000-0000-0000-0000-000000000c01")
70 (at 50 0)
71 (pad "1" smd rect (at 0 0) (size 0.8 0.8) (layers "F.Cu") (net 2 "/NET_B") (uuid "00000000-0000-0000-0000-000000000c02"))
72 )
73 (segment (start 0 0) (end 20 0) (width 0.2) (layer "F.Cu") (net 1))
74 (segment (start 25 0) (end 50 0) (width 0.2) (layer "F.Cu") (net 2))
75)
76)";
77
78
79namespace
80{
81std::unique_ptr<BOARD> loadBoard( const char* aText, const std::string& aSubdir )
82{
83 namespace fs = std::filesystem;
84 fs::path tmpDir = fs::temp_directory_path() / aSubdir;
85 fs::create_directories( tmpDir );
86 fs::path pcbPath = tmpDir / "trunk_delay.kicad_pcb";
87
88 {
89 std::ofstream out( pcbPath );
90 out << aText;
91 }
92
93 PCB_IO_KICAD_SEXPR plugin;
94 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
95 plugin.LoadBoard( pcbPath.string(), board.get() );
96 board->BuildConnectivity();
97 fs::remove( pcbPath );
98 return board;
99}
100
101
102void tagChainNets( BOARD* aBoard, const wxString& aChain )
103{
104 for( NETINFO_ITEM* n : aBoard->GetNetInfo() )
105 {
106 if( n && n->GetNetname().StartsWith( wxS( "/NET_" ) ) )
107 n->SetNetChain( aChain );
108 }
109}
110
111
112void setTerminals( BOARD* aBoard, const wxString& aChain, double aTermAxMm, double aTermBxMm )
113{
114 PAD* termA = nullptr;
115 PAD* termB = nullptr;
116 constexpr int EPS = 100;
117 const VECTOR2I targetA( static_cast<int>( aTermAxMm * 1000000 ), 0 );
118 const VECTOR2I targetB( static_cast<int>( aTermBxMm * 1000000 ), 0 );
119
120 for( FOOTPRINT* fp : aBoard->Footprints() )
121 {
122 if( fp->Pads().empty() )
123 continue;
124
125 VECTOR2I pos = fp->GetPosition();
126
127 if( std::abs( pos.x - targetA.x ) <= EPS && std::abs( pos.y - targetA.y ) <= EPS )
128 termA = fp->Pads().front();
129
130 if( std::abs( pos.x - targetB.x ) <= EPS && std::abs( pos.y - targetB.y ) <= EPS )
131 termB = fp->Pads().front();
132 }
133
134 for( NETINFO_ITEM* n : aBoard->GetNetInfo() )
135 {
136 if( n && n->GetNetChain() == aChain )
137 {
138 if( termA )
139 n->SetTerminalPad( 0, termA );
140 if( termB )
141 n->SetTerminalPad( 1, termB );
142 }
143 }
144}
145
146
147std::set<BOARD_CONNECTED_ITEM*> collectChainItems( BOARD* aBoard, const wxString& aChain )
148{
149 std::set<BOARD_CONNECTED_ITEM*> items;
150
151 for( PCB_TRACK* t : aBoard->Tracks() )
152 {
153 if( t->GetNet() && t->GetNet()->GetNetChain() == aChain )
154 items.insert( t );
155 }
156
157 for( FOOTPRINT* fp : aBoard->Footprints() )
158 {
159 for( PAD* p : fp->Pads() )
160 {
161 if( p->GetNet() && p->GetNet()->GetNetChain() == aChain )
162 items.insert( p );
163 }
164 }
165
166 return items;
167}
168
169} // namespace
170
171
172BOOST_AUTO_TEST_SUITE( NetChainTrunkDelay )
173
174
175// 50 mm trunk, no stackup => tracks contribute 0 delay, bridge contributes
176// 5 mm at the fallback 5.9 ps/mm. Verifies that the bridge edge's delay is
177// derived consistently with BoardChainBridging() and folded into TrunkDelay().
178BOOST_AUTO_TEST_CASE( DaisyTrunkDelayEqualsBridgeWithoutStackup )
179{
180 auto board = loadBoard( DAISY_PCB, "kicad_chain_trunk_delay" );
181 tagChainNets( board.get(), wxS( "DSY" ) );
182 setTerminals( board.get(), wxS( "DSY" ), 0.0, 50.0 );
183
184 CHAIN_TOPOLOGY topo( board.get(), wxS( "DSY" ),
185 collectChainItems( board.get(), wxS( "DSY" ) ) );
186
187 BOOST_REQUIRE( topo.IsValid() );
188 BOOST_CHECK_CLOSE( topo.TrunkLength(), 50.0e6, 5.0 );
189
190 double expectedDelayIU = DEFAULT_PROPAGATION_DELAY_PS_PER_MM * pcbIUScale.IU_PER_PS * 5.0;
191 BOOST_CHECK_CLOSE( topo.TrunkDelay(), expectedDelayIU, 5.0 );
192 BOOST_CHECK_GT( topo.TrunkDelay(), 0.0 );
193
194 // The bridging-only helper should agree with what the trunk picked up.
195 auto [bridgingLen, bridgingDelay] = BoardChainBridging( board.get(), wxS( "DSY" ) );
196 BOOST_CHECK_CLOSE( bridgingDelay, topo.TrunkDelay(), 0.001 );
197 BOOST_CHECK_CLOSE( bridgingLen, 5.0e6, 0.001 );
198}
199
200
201// Without terminal pads the topology cannot reduce to a trunk; callers must
202// fall back to BoardChainBridging. Verify both: the topology is not valid,
203// and the bridging-only delay covers the 5 mm cross-net pad span at the
204// fallback per-mm rate.
205BOOST_AUTO_TEST_CASE( NoTerminalsFallbackUsesBridgingDelay )
206{
207 auto board = loadBoard( DAISY_PCB, "kicad_chain_trunk_delay_noterms" );
208 tagChainNets( board.get(), wxS( "DSY" ) );
209 // Intentionally do NOT call setTerminals.
210
211 CHAIN_TOPOLOGY topo( board.get(), wxS( "DSY" ),
212 collectChainItems( board.get(), wxS( "DSY" ) ) );
213
214 BOOST_CHECK( !topo.IsValid() );
215 BOOST_CHECK_EQUAL( static_cast<int>( topo.GetStatus() ),
216 static_cast<int>( CHAIN_TOPOLOGY::STATUS::NO_TERMINAL_PADS ) );
217
218 auto [bridgingLen, bridgingDelay] = BoardChainBridging( board.get(), wxS( "DSY" ) );
219
220 BOOST_CHECK_CLOSE( bridgingLen, 5.0e6, 0.001 );
221
222 double expectedDelayIU = DEFAULT_PROPAGATION_DELAY_PS_PER_MM * pcbIUScale.IU_PER_PS * 5.0;
223 BOOST_CHECK_CLOSE( bridgingDelay, expectedDelayIU, 0.001 );
224}
225
226
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1004
const FOOTPRINTS & Footprints() const
Definition board.h:364
const TRACKS & Tracks() const
Definition board.h:362
Build a topological view of a single named net chain's routed copper.
STATUS GetStatus() const
double TrunkLength() const
bool IsValid() const
double TrunkDelay() const
Handle the data for a net.
Definition netinfo.h:50
Definition pad.h:65
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 ...
constexpr double EPS
Floating point comparison epsilon.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
std::tuple< double, double > BoardChainBridging(const BOARD *aBoard, const wxString &aNetChain)
Compute both the chain bridging length and its associated propagation delay (in internal delay IU,...
constexpr double DEFAULT_PROPAGATION_DELAY_PS_PER_MM
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
static const char * DAISY_PCB
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(DaisyTrunkDelayEqualsBridgeWithoutStackup)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687