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
24
25#include <queue>
26#include <unordered_map>
27
28#include <base_units.h>
29#include <board.h>
30#include <footprint.h>
31#include <math/vector2d.h>
32#include <netinfo.h>
33#include <pad.h>
34#include <pcb_track.h>
35
36
37double FootprintChainBridgingLength( const FOOTPRINT* aFootprint, const wxString& aNetChain )
38{
39 if( !aFootprint || aNetChain.IsEmpty() )
40 return 0.0;
41
42 std::vector<const PAD*> chainPads;
43
44 for( const PAD* pad : aFootprint->Pads() )
45 {
46 const NETINFO_ITEM* pn = pad->GetNet();
47
48 if( !pn || pn->GetNetChain() != aNetChain )
49 continue;
50
51 chainPads.push_back( pad );
52 }
53
54 if( chainPads.size() < 2 )
55 return 0.0;
56
57 int firstNet = chainPads.front()->GetNetCode();
58
59 auto crossesNet = [firstNet]( const PAD* p ) { return p->GetNetCode() != firstNet; };
60
61 if( !std::any_of( chainPads.begin() + 1, chainPads.end(), crossesNet ) )
62 return 0.0;
63
64 double maxSpan = 0.0;
65
66 for( size_t i = 0; i < chainPads.size(); ++i )
67 {
68 for( size_t j = i + 1; j < chainPads.size(); ++j )
69 {
70 if( chainPads[i]->GetNetCode() == chainPads[j]->GetNetCode() )
71 continue;
72
73 VECTOR2D delta = VECTOR2D( chainPads[i]->GetCenter() )
74 - VECTOR2D( chainPads[j]->GetCenter() );
75 maxSpan = std::max( maxSpan, delta.EuclideanNorm() );
76 }
77 }
78
79 return maxSpan;
80}
81
82
83double BoardChainBridgingLength( const BOARD* aBoard, const wxString& aNetChain )
84{
85 if( !aBoard || aNetChain.IsEmpty() )
86 return 0.0;
87
88 double total = 0.0;
89
90 for( const FOOTPRINT* fp : aBoard->Footprints() )
91 total += FootprintChainBridgingLength( fp, aNetChain );
92
93 return total;
94}
95
96
97double ChainBridgingDelayPerMm( const BOARD* aBoard, const wxString& aNetChain, const double aDefaultBridgeUnitDelay )
98{
99 if( !aBoard || aNetChain.IsEmpty() )
100 return aDefaultBridgeUnitDelay;
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 if( !ninfo->GetNetClass()->HasTuningProfile() )
110 continue;
111
112 const wxString tuningProfileName = ninfo->GetNetClass()->GetTuningProfile();
113 const std::shared_ptr<TUNING_PROFILES> tuningParams =
115 const TUNING_PROFILE& tuningProfile = tuningParams->GetTuningProfile( tuningProfileName );
116
117 return tuningProfile.m_NetChainBridgePropagationDelay;
118 }
119
120 return aDefaultBridgeUnitDelay;
121}
122
123
124std::tuple<double, double> BoardChainBridging( const BOARD* aBoard, const wxString& aNetChain,
125 const double aDefaultBridgeUnitDelay )
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 =
136 ChainBridgingDelayPerMm( aBoard, aNetChain, aDefaultBridgeUnitDelay ) * lengthIU / pcbIUScale.IU_PER_MM;
137
138 return { lengthIU, delayIU };
139}
140
141
142std::vector<CHAIN_BRIDGE> EnumerateChainBridges( const BOARD* aBoard, const wxString& aNetChain,
143 const double aDefaultBridgeUnitDelay )
144{
145 std::vector<CHAIN_BRIDGE> bridges;
146
147 if( !aBoard || aNetChain.IsEmpty() )
148 return bridges;
149
150 const double delayIUPerMm = ChainBridgingDelayPerMm( aBoard, aNetChain, aDefaultBridgeUnitDelay );
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: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:373
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2777
const FOOTPRINTS & Footprints() const
Definition board.h:421
const TRACKS & Tracks() const
Definition board.h:419
PROJECT * GetProject() const
Definition board.h:662
bool IsEmpty() const
Definition board.cpp:668
bool IsEmpty() const
std::deque< PAD * > & Pads()
Definition footprint.h:375
wxString GetTuningProfile() const
Definition netclass.h:252
bool HasTuningProfile() const
Definition netclass.h:250
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
NETCLASS * GetNetClass()
Definition netinfo.h:91
Definition pad.h:61
std::shared_ptr< TUNING_PROFILES > & TuningProfileParameters()
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
std::tuple< double, double > BoardChainBridging(const BOARD *aBoard, const wxString &aNetChain, const double aDefaultBridgeUnitDelay)
Compute both the chain bridging length and its associated propagation delay (in internal delay IU,...
double ChainBridgingDelayPerMm(const BOARD *aBoard, const wxString &aNetChain, const double aDefaultBridgeUnitDelay)
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::vector< CHAIN_BRIDGE > EnumerateChainBridges(const BOARD *aBoard, const wxString &aNetChain, const double aDefaultBridgeUnitDelay)
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.
One series-component bridge edge inside a chain graph.
Result of PartitionNetChainAroundNet().
Represents a single line in the tuning profile configuration grid.
int m_NetChainBridgePropagationDelay
wxString result
Test unit parsing edge cases and error handling.
int delta
VECTOR2< double > VECTOR2D
Definition vector2d.h:682