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