KiCad PCB EDA Suite
Loading...
Searching...
No Matches
net_chain_bridging.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 <net_chain_bridging.h>
22
23#include <queue>
24#include <unordered_map>
25
26#include <base_units.h>
27#include <board.h>
28#include <footprint.h>
29#include <math/vector2d.h>
30#include <netinfo.h>
31#include <pad.h>
32#include <pcb_track.h>
33
34
35double FootprintChainBridgingLength( const FOOTPRINT* aFootprint, const wxString& aNetChain )
36{
37 if( !aFootprint || aNetChain.IsEmpty() )
38 return 0.0;
39
40 std::vector<const PAD*> chainPads;
41
42 for( const PAD* pad : aFootprint->Pads() )
43 {
44 const NETINFO_ITEM* pn = pad->GetNet();
45
46 if( !pn || pn->GetNetChain() != aNetChain )
47 continue;
48
49 chainPads.push_back( pad );
50 }
51
52 if( chainPads.size() < 2 )
53 return 0.0;
54
55 int firstNet = chainPads.front()->GetNetCode();
56
57 auto crossesNet = [firstNet]( const PAD* p ) { return p->GetNetCode() != firstNet; };
58
59 if( !std::any_of( chainPads.begin() + 1, chainPads.end(), crossesNet ) )
60 return 0.0;
61
62 double maxSpan = 0.0;
63
64 for( size_t i = 0; i < chainPads.size(); ++i )
65 {
66 for( size_t j = i + 1; j < chainPads.size(); ++j )
67 {
68 if( chainPads[i]->GetNetCode() == chainPads[j]->GetNetCode() )
69 continue;
70
71 VECTOR2D delta = VECTOR2D( chainPads[i]->GetCenter() )
72 - VECTOR2D( chainPads[j]->GetCenter() );
73 maxSpan = std::max( maxSpan, delta.EuclideanNorm() );
74 }
75 }
76
77 return maxSpan;
78}
79
80
81double BoardChainBridgingLength( const BOARD* aBoard, const wxString& aNetChain )
82{
83 if( !aBoard || aNetChain.IsEmpty() )
84 return 0.0;
85
86 double total = 0.0;
87
88 for( const FOOTPRINT* fp : aBoard->Footprints() )
89 total += FootprintChainBridgingLength( fp, aNetChain );
90
91 return total;
92}
93
94
95double ChainBridgingDelayPerMm( const BOARD* aBoard, const wxString& aNetChain )
96{
97 double delayIUPerMm = DEFAULT_PROPAGATION_DELAY_PS_PER_MM * pcbIUScale.IU_PER_PS;
98
99 if( !aBoard || aNetChain.IsEmpty() )
100 return delayIUPerMm;
101
102 for( const PCB_TRACK* track : aBoard->Tracks() )
103 {
104 const NETINFO_ITEM* ninfo = track->GetNet();
105
106 if( !ninfo || ninfo->GetNetChain() != aNetChain )
107 continue;
108
109 double tLen = 0.0;
110 double tDelay = 0.0;
111
112 std::tie( std::ignore, tLen, std::ignore, tDelay, std::ignore ) =
113 aBoard->GetTrackLength( *track );
114
115 if( tLen > 0.0 && tDelay > 0.0 )
116 {
117 delayIUPerMm = tDelay / ( tLen / pcbIUScale.IU_PER_MM );
118 break;
119 }
120 }
121
122 return delayIUPerMm;
123}
124
125
126std::tuple<double, double> BoardChainBridging( const BOARD* aBoard, const wxString& aNetChain )
127{
128 if( !aBoard || aNetChain.IsEmpty() )
129 return { 0.0, 0.0 };
130
131 double lengthIU = BoardChainBridgingLength( aBoard, aNetChain );
132
133 if( lengthIU <= 0.0 )
134 return { 0.0, 0.0 };
135
136 double delayIU = ChainBridgingDelayPerMm( aBoard, aNetChain ) * lengthIU
137 / pcbIUScale.IU_PER_MM;
138
139 return { lengthIU, delayIU };
140}
141
142
143std::vector<CHAIN_BRIDGE> EnumerateChainBridges( const BOARD* aBoard, const wxString& aNetChain )
144{
145 std::vector<CHAIN_BRIDGE> bridges;
146
147 if( !aBoard || aNetChain.IsEmpty() )
148 return bridges;
149
150 const double delayIUPerMm = ChainBridgingDelayPerMm( aBoard, aNetChain );
151
152 for( FOOTPRINT* fp : aBoard->Footprints() )
153 {
154 if( !fp )
155 continue;
156
157 std::vector<PAD*> chainPads;
158
159 for( PAD* pad : fp->Pads() )
160 {
161 const NETINFO_ITEM* pn = pad->GetNet();
162
163 if( pn && pn->GetNetChain() == aNetChain )
164 chainPads.push_back( pad );
165 }
166
167 if( chainPads.size() < 2 )
168 continue;
169
170 for( size_t i = 0; i < chainPads.size(); ++i )
171 {
172 for( size_t j = i + 1; j < chainPads.size(); ++j )
173 {
174 if( chainPads[i]->GetNetCode() == chainPads[j]->GetNetCode() )
175 continue;
176
177 VECTOR2D delta = VECTOR2D( chainPads[i]->GetCenter() )
178 - VECTOR2D( chainPads[j]->GetCenter() );
179 double length = delta.EuclideanNorm();
180 double delay = delayIUPerMm * length / pcbIUScale.IU_PER_MM;
181
182 bridges.push_back( CHAIN_BRIDGE{ chainPads[i], chainPads[j], length, delay } );
183 }
184 }
185 }
186
187 return bridges;
188}
189
190
192 const PAD* aStartPad, const PAD* aEndPad )
193{
195
196 if( !aBoard || !aStartPad || !aEndPad || aStartPad == aEndPad )
197 {
199 return result;
200 }
201
202 // Pads must live on this board. Cross-board references would silently produce
203 // incorrect partitions, since EnumerateChainBridges() walks aBoard's footprints.
204 if( aStartPad->GetBoard() != aBoard || aEndPad->GetBoard() != aBoard )
205 {
207 return result;
208 }
209
210 NETINFO_ITEM* queryNetInfo = aBoard->FindNet( aQueryNet );
211
212 if( !queryNetInfo || queryNetInfo->GetNetChain().IsEmpty() )
213 {
215 return result;
216 }
217
218 // Compare by NETINFO pointer rather than netcode so a pad with a stale netcode (e.g.
219 // copied from another board with the same code) is rejected.
220 if( aStartPad->GetNet() != queryNetInfo )
221 {
223 return result;
224 }
225
226 if( aEndPad->GetNet() != queryNetInfo )
227 {
229 return result;
230 }
231
232 const wxString& chainName = queryNetInfo->GetNetChain();
233 std::vector<CHAIN_BRIDGE> bridges = EnumerateChainBridges( aBoard, chainName );
234
235 // Cut-graph adjacency: every chain bridge becomes an undirected edge between its
236 // two pads' netcodes, except edges incident on aQueryNet. Removing aQueryNet
237 // effectively partitions the chain at the inspected net.
238 std::unordered_map<int, std::set<int>> adjacency;
239
240 // Multi-pad parts can contribute several cross-net neighbors for one endpoint pad;
241 // seed every such neighbor so BFS reaches all of them.
242 std::set<int> startSeeds;
243 std::set<int> endSeeds;
244 bool queryNetHasBridge = false;
245
246 auto seedIfEndpoint = [&]( const PAD* aBridgePad, int aNeighborNet,
247 const PAD* aQueryPad, std::set<int>& aSeeds )
248 {
249 if( aBridgePad == aQueryPad && aNeighborNet != aQueryNet )
250 aSeeds.insert( aNeighborNet );
251 };
252
253 for( const CHAIN_BRIDGE& br : bridges )
254 {
255 if( !br.padA || !br.padB )
256 continue;
257
258 int netA = br.padA->GetNetCode();
259 int netB = br.padB->GetNetCode();
260
261 if( netA == aQueryNet || netB == aQueryNet )
262 {
263 queryNetHasBridge = true;
264 seedIfEndpoint( br.padA, netB, aStartPad, startSeeds );
265 seedIfEndpoint( br.padB, netA, aStartPad, startSeeds );
266 seedIfEndpoint( br.padA, netB, aEndPad, endSeeds );
267 seedIfEndpoint( br.padB, netA, aEndPad, endSeeds );
268 continue;
269 }
270
271 adjacency[netA].insert( netB );
272 adjacency[netB].insert( netA );
273 }
274
275 // Distinguish "chain has nothing to partition" from "query net is unbridged in this
276 // chain". Both produce empty answers but mean different things to the caller.
277 if( !queryNetHasBridge )
278 {
280 return result;
281 }
282
283 auto bfsFill = [&]( const std::set<int>& aSeeds, std::set<int>& aOut )
284 {
285 std::queue<int> frontier;
286
287 for( int seed : aSeeds )
288 {
289 if( aOut.insert( seed ).second )
290 frontier.push( seed );
291 }
292
293 while( !frontier.empty() )
294 {
295 int cur = frontier.front();
296 frontier.pop();
297
298 auto it = adjacency.find( cur );
299
300 if( it == adjacency.end() )
301 continue;
302
303 for( int nbr : it->second )
304 {
305 if( aOut.insert( nbr ).second )
306 frontier.push( nbr );
307 }
308 }
309 };
310
311 bfsFill( startSeeds, result.beforeStart );
312 bfsFill( endSeeds, result.afterEnd );
313
314 for( int net : result.beforeStart )
315 {
316 if( result.afterEnd.count( net ) )
317 {
319 return result;
320 }
321 }
322
324 return result;
325}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
std::tuple< int, double, double, double, double > GetTrackLength(const PCB_TRACK &aTrack) const
Return data on the length and number of track segments connected to a given track.
Definition board.cpp:3190
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2609
const FOOTPRINTS & Footprints() const
Definition board.h:364
const TRACKS & Tracks() const
Definition board.h:362
bool IsEmpty() const
Definition board.cpp:614
bool IsEmpty() const
std::deque< PAD * > & Pads()
Definition footprint.h:377
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:116
Definition pad.h:65
double ChainBridgingDelayPerMm(const BOARD *aBoard, const wxString &aNetChain)
Pick a single per-IU-per-mm delay for a given chain.
NET_CHAIN_PARTITION PartitionNetChainAroundNet(const BOARD *aBoard, int aQueryNet, const PAD *aStartPad, const PAD *aEndPad)
Partition the chain containing aQueryNet around it, cut at the bridges incident on aStartPad and aEnd...
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,...
std::vector< CHAIN_BRIDGE > EnumerateChainBridges(const BOARD *aBoard, const wxString &aNetChain)
Enumerate every per-pad-pair bridge edge contributed by every footprint on the board to the named cha...
double BoardChainBridgingLength(const BOARD *aBoard, const wxString &aNetChain)
Sum chain bridging length across every footprint on the board.
double FootprintChainBridgingLength(const FOOTPRINT *aFootprint, const wxString &aNetChain)
Compute the bridging length contributed by a single footprint to a net chain.
constexpr double DEFAULT_PROPAGATION_DELAY_PS_PER_MM
One series-component bridge edge inside a chain graph.
Result of PartitionNetChainAroundNet().
const uint32_t seed
wxString result
Test unit parsing edge cases and error handling.
int delta
VECTOR2< double > VECTOR2D
Definition vector2d.h:686