KiCad PCB EDA Suite
Loading...
Searching...
No Matches
length_delay_calculation.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
25
26#include <board.h>
29#include <wx/log.h>
30
31
33 const std::shared_ptr<SHAPE_POLY_SET>& aPadShape,
34 const VECTOR2I& aInsidePoint, VECTOR2I& aIntersection )
35{
36 bool found = false;
37 int64_t bestDistSq = std::numeric_limits<int64_t>::max();
38
39 for( int i = 0; i < aPadShape->OutlineCount(); i++ )
40 {
41 const SHAPE_LINE_CHAIN& outline = aPadShape->Outline( i );
42
43 for( int j = 0; j < outline.SegmentCount(); j++ )
44 {
45 const SEG& seg = outline.CSegment( j );
46 std::vector<VECTOR2I> intersections;
47
48 if( !aArc.IntersectLine( seg, &intersections ) )
49 continue;
50
51 for( const VECTOR2I& pt : intersections )
52 {
53 if( !seg.Contains( pt ) )
54 continue;
55
56 int64_t distSq = ( pt - aInsidePoint ).SquaredEuclideanNorm();
57
58 if( distSq < bestDistSq )
59 {
60 bestDistSq = distSq;
61 aIntersection = pt;
62 found = true;
63 }
64 }
65 }
66 }
67
68 return found;
69}
70
71
73 bool aForward )
74{
75 wxASSERT( aLine.PointCount() >= 2 );
76
77 const int start = aForward ? 0 : aLine.PointCount() - 1;
78 const int delta = aForward ? 1 : -1;
79
80 const auto& shape = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
81
82 // Find the first point OUTSIDE the pad
83 int firstOutside = -1;
84 VECTOR2I intersectionPt;
85 bool hasIntersection = false;
86
87 for( int vertex = start + delta; aForward ? vertex < aLine.PointCount() : vertex >= 0; vertex += delta )
88 {
89 if( !shape->Contains( aLine.GetPoint( vertex ) ) )
90 {
91 firstOutside = vertex;
92 int prevVertex = vertex - delta;
93
94 // Check if the crossing segment is part of an arc
95 ssize_t arcIdx = aLine.ArcIndex( prevVertex );
96
97 if( arcIdx >= 0 && aLine.ArcIndex( vertex ) == arcIdx )
98 {
99 hasIntersection = findArcPadIntersection( aLine.Arc( arcIdx ), shape, aLine.GetPoint( prevVertex ),
100 intersectionPt );
101 }
102
103 // Fallback to segment intersection if arc intersection didn't work
104 if( !hasIntersection )
105 {
106 SEG seg( aLine.GetPoint( vertex ), aLine.GetPoint( prevVertex ) );
107 VECTOR2I loc;
108
109 if( shape->Collide( seg, 0, nullptr, &loc ) )
110 {
111 intersectionPt = loc;
112 hasIntersection = true;
113 }
114 }
115
116 break;
117 }
118 }
119
120 if( firstOutside < 0 )
121 return; // All points inside pad, nothing to clip
122
123 // Build new chain using Slice (preserves arcs correctly without index corruption)
124 SHAPE_LINE_CHAIN newChain;
125
126 if( aForward )
127 {
128 // Chain: padCenter -> intersection -> [firstOutside to end]
129 newChain.Append( aPad->GetPosition() );
130 if( hasIntersection )
131 newChain.Append( intersectionPt );
132 newChain.Append( aLine.Slice( firstOutside, -1 ) );
133 }
134 else
135 {
136 // Chain: [0 to firstOutside] -> intersection -> padCenter
137 newChain.Append( aLine.Slice( 0, firstOutside ) );
138 if( hasIntersection )
139 newChain.Append( intersectionPt );
140 newChain.Append( aPad->GetPosition() );
141 }
142
143 aLine = newChain;
144}
145
146
147LENGTH_DELAY_STATS LENGTH_DELAY_CALCULATION::CalculateLengthDetails( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
148 const PATH_OPTIMISATIONS aOptimisations,
149 const PAD* aStartPad, const PAD* aEndPad,
150 const LENGTH_DELAY_LAYER_OPT aLayerOpt,
151 const LENGTH_DELAY_DOMAIN_OPT aDomain ) const
152{
153 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
154 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "========== CalculateLengthDetails START ==========" ) );
155 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: input has %zu items" ), aItems.size() );
156 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: optimisations - OptimiseViaLayers=%d, MergeTracks=%d, OptimiseTracesInPads=%d, InferViaInPad=%d" ),
157 aOptimisations.OptimiseViaLayers, aOptimisations.MergeTracks,
158 aOptimisations.OptimiseTracesInPads, aOptimisations.InferViaInPad );
159
160 // Count initial items by type
161 int initialPads = 0, initialVias = 0, initialLines = 0, initialUnknown = 0;
162 for( const LENGTH_DELAY_CALCULATION_ITEM& item : aItems )
163 {
164 switch( item.Type() )
165 {
166 case LENGTH_DELAY_CALCULATION_ITEM::TYPE::PAD: initialPads++; break;
167 case LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA: initialVias++; break;
168 case LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE: initialLines++; break;
169 default: initialUnknown++; break;
170 }
171 }
172 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: initial items - PADs=%d, VIAs=%d, LINEs=%d, UNKNOWN=%d" ),
173 initialPads, initialVias, initialLines, initialUnknown );
174
175 // If this set of items has not been optimised, optimise for shortest electrical path
176 if( aOptimisations.OptimiseViaLayers || aOptimisations.MergeTracks || aOptimisations.MergeTracks )
177 {
178 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: performing optimisations..." ) );
179
180 std::vector<LENGTH_DELAY_CALCULATION_ITEM*> pads;
181 std::vector<LENGTH_DELAY_CALCULATION_ITEM*> lines;
182 std::vector<LENGTH_DELAY_CALCULATION_ITEM*> vias;
183
184 // Map of line endpoints to line objects
185 std::map<VECTOR2I, std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>> linesPositionMap;
186
187 // Map of pad positions to pad objects
188 std::map<VECTOR2I, std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>> padsPositionMap;
189
190 for( LENGTH_DELAY_CALCULATION_ITEM& item : aItems )
191 {
192 if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::PAD )
193 {
194 pads.emplace_back( &item );
195 padsPositionMap[item.GetPad()->GetPosition()].insert( &item );
196 }
197 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
198 {
199 vias.emplace_back( &item );
200 }
201 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
202 {
203 lines.emplace_back( &item );
204 linesPositionMap[item.GetLine().CPoint( 0 )].insert( &item );
205 linesPositionMap[item.GetLine().CLastPoint()].insert( &item );
206 }
207 }
208
209 if( aOptimisations.OptimiseViaLayers )
210 {
211 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: optimising via layers (%zu vias)" ), vias.size() );
212 optimiseViaLayers( vias, lines, linesPositionMap, padsPositionMap );
213 }
214
215 if( aOptimisations.MergeTracks )
216 {
217 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: merging tracks (%zu lines)" ), lines.size() );
218 mergeLines( lines, linesPositionMap );
219 }
220
221 if( aOptimisations.OptimiseTracesInPads )
222 {
223 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: optimising traces in pads" ) );
224 optimiseTracesInPads( pads, lines );
225 }
226 }
227
228 LENGTH_DELAY_STATS details;
229
230 // Create the layer detail maps if required
232 {
233 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: creating layer detail maps" ) );
234 details.LayerLengths = std::make_unique<std::map<PCB_LAYER_ID, int64_t>>();
235
237 details.LayerDelays = std::make_unique<std::map<PCB_LAYER_ID, int64_t>>();
238 }
239
240 const bool useHeight = m_board->GetDesignSettings().m_UseHeightForLengthCalcs;
241 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: useHeight=%d" ), useHeight );
242
243 // If this is a contiguous set of items, check if we have an inferred fanout via at either end. Note that this
244 // condition only arises as a result of how PNS assembles tuning paths - for DRC / net inspector calculations these
245 // fanout vias will be present in the object set and therefore do not need to be inferred
246 if( aOptimisations.InferViaInPad && useHeight )
247 {
248 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: inferring vias in pads" ) );
249 inferViaInPad( aStartPad, aItems.front(), details );
250 inferViaInPad( aEndPad, aItems.back(), details );
251 }
252
253 // Add stats for each item
254 int processedPads = 0, processedVias = 0, processedLines = 0;
255 int mergedRetired = 0, unknownType = 0;
256
257 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: processing %zu items..." ), aItems.size() );
258
259 for( const LENGTH_DELAY_CALCULATION_ITEM& item : aItems )
260 {
261 // Don't include merged items
263 || item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
264 {
266 mergedRetired++;
267 else
268 unknownType++;
269 continue;
270 }
271
272 // Calculate the space domain statistics
273 if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
274 {
275 const int64_t length = item.GetLine().Length();
276
277 details.TrackLength += length;
278 processedLines++;
279
280 if( details.LayerLengths )
281 ( *details.LayerLengths )[item.GetStartLayer()] += length;
282 }
283 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && useHeight )
284 {
285 const auto [layerStart, layerEnd] = item.GetLayers();
286 int64_t viaHeight = StackupHeight( layerStart, layerEnd );
287 details.ViaLength += viaHeight;
288 details.NumVias += 1;
289 processedVias++;
290
291 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: via from layer %d to %d, height=%lld" ),
292 layerStart, layerEnd, viaHeight );
293 }
294 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::PAD )
295 {
296 int64_t padToDie = item.GetPad()->GetPadToDieLength();
297 details.PadToDieLength += padToDie;
298 details.NumPads += 1;
299 processedPads++;
300
301 if( padToDie > 0 )
302 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: pad with pad-to-die length=%lld" ), padToDie );
303 }
304 }
305
306 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: processed items - PADs=%d, VIAs=%d, LINEs=%d" ),
307 processedPads, processedVias, processedLines );
308 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: skipped items - merged/retired=%d, unknown=%d" ),
309 mergedRetired, unknownType );
310
311 // Calculate the time domain statistics if required
312 if( aDomain == LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL && !aItems.empty() )
313 {
314 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: calculating time domain statistics" ) );
315
316 // TODO(JJ): Populate this
318 ctx.NetClass = aItems.front().GetEffectiveNetClass(); // We don't care if this is merged for net class lookup
319
320 const std::vector<int64_t> itemDelays = m_tuningProfileParameters->GetPropagationDelays( aItems, ctx );
321
322 wxASSERT( itemDelays.size() == aItems.size() );
323
324 for( size_t i = 0; i < aItems.size(); ++i )
325 {
326 const LENGTH_DELAY_CALCULATION_ITEM& item = aItems[i];
327
328 if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
329 {
330 details.TrackDelay += itemDelays[i];
331
332 if( details.LayerDelays )
333 ( *details.LayerDelays )[item.GetStartLayer()] += itemDelays[i];
334 }
335 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && useHeight )
336 {
337 details.ViaDelay += itemDelays[i];
338 }
339 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::PAD )
340 {
341 details.PadToDieDelay += itemDelays[i];
342 }
343 }
344
345 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: total delays - Track=%lld, Via=%lld, PadToDie=%lld" ),
346 details.TrackDelay, details.ViaDelay, details.PadToDieDelay );
347 }
348
349 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "CalculateLengthDetails: RESULTS:" ) );
350 wxLogTrace( wxT( "PNS_TUNE" ), wxT( " Track length: %lld" ), details.TrackLength );
351 wxLogTrace( wxT( "PNS_TUNE" ), wxT( " Via length: %d (from %d vias)" ), details.ViaLength, details.NumVias );
352 wxLogTrace( wxT( "PNS_TUNE" ), wxT( " Pad-to-die length: %d (from %d pads)" ), details.PadToDieLength, details.NumPads );
353 wxLogTrace( wxT( "PNS_TUNE" ), wxT( " TOTAL LENGTH: %lld" ), details.TotalLength() );
354 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "========== CalculateLengthDetails END ==========" ) );
355 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
356
357 return details;
358}
359
360
362 LENGTH_DELAY_STATS& aDetails ) const
363{
364 if( aPad && aItem.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
365 {
366 const PCB_LAYER_ID startBottomLayer = aItem.GetStartLayer();
367 const LSET padLayers = aPad->Padstack().LayerSet();
368
369 if( !padLayers.Contains( startBottomLayer ) )
370 {
371 // This must be either F_Cu or B_Cu
372 const PCB_LAYER_ID padLayer = padLayers.Contains( F_Cu ) ? F_Cu : B_Cu;
373
374 aDetails.NumVias += 1;
375 aDetails.ViaLength += StackupHeight( startBottomLayer, padLayer );
376 }
377 }
378}
379
380
381int64_t LENGTH_DELAY_CALCULATION::CalculateLength( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
382 const PATH_OPTIMISATIONS aOptimisations, const PAD* aStartPad,
383 const PAD* aEndPad ) const
384{
385 return CalculateLengthDetails( aItems, aOptimisations, aStartPad, aEndPad ).TotalLength();
386}
387
388
389int64_t LENGTH_DELAY_CALCULATION::CalculateDelay( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
390 const PATH_OPTIMISATIONS aOptimisations, const PAD* aStartPad,
391 const PAD* aEndPad ) const
392{
393 return CalculateLengthDetails( aItems, aOptimisations, aStartPad, aEndPad, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
395 .TotalDelay();
396}
397
398
399int LENGTH_DELAY_CALCULATION::StackupHeight( const PCB_LAYER_ID aFirstLayer, const PCB_LAYER_ID aSecondLayer ) const
400{
401 if( !m_board || !m_board->GetDesignSettings().m_UseHeightForLengthCalcs )
402 return 0;
403
404 if( m_board->GetDesignSettings().m_HasStackup )
405 {
406 const BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
407 return stackup.GetLayerDistance( aFirstLayer, aSecondLayer );
408 }
409 else
410 {
411 BOARD_STACKUP stackup;
412 stackup.BuildDefaultStackupList( &m_board->GetDesignSettings(), m_board->GetCopperLayerCount() );
413 return stackup.GetLayerDistance( aFirstLayer, aSecondLayer );
414 }
415}
416
417
419 std::vector<LENGTH_DELAY_CALCULATION_ITEM*>& aLines,
420 std::map<VECTOR2I, std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>>& aLinesPositionMap )
421{
422 // Vector of pads, and an associated flag to indicate whether they have been visited by the clustering algorithm
423 std::vector<LENGTH_DELAY_CALCULATION_ITEM*> pads;
424
425 auto removeFromPositionMap = [&aLinesPositionMap]( LENGTH_DELAY_CALCULATION_ITEM* line )
426 {
427 aLinesPositionMap[line->GetLine().CPoint( 0 )].erase( line );
428 aLinesPositionMap[line->GetLine().CLastPoint()].erase( line );
429 };
430
431 // Attempts to merge unmerged lines in to aPrimaryLine
432 auto tryMerge = [&removeFromPositionMap, &aLinesPositionMap]( const MERGE_POINT aMergePoint,
433 const VECTOR2I& aMergePos,
434 const LENGTH_DELAY_CALCULATION_ITEM* aPrimaryItem,
435 SHAPE_LINE_CHAIN& aPrimaryLine, bool* aDidMerge )
436 {
437 const auto startItr = aLinesPositionMap.find( aMergePos );
438
439 if( startItr == aLinesPositionMap.end() )
440 return;
441
442 std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>& startItems = startItr->second;
443
444 if( startItems.size() != 1 )
445 return;
446
447 LENGTH_DELAY_CALCULATION_ITEM* lineToMerge = *startItems.begin();
448
449 // Don't merge if line is an arc
450 if( !lineToMerge->GetLine().CArcs().empty() )
451 return;
452
453 // Don't merge if lines are on different layers
454 if( aPrimaryItem->GetStartLayer() != lineToMerge->GetStartLayer() )
455 return;
456
457 // Merge the lines
459 mergeShapeLineChains( aPrimaryLine, lineToMerge->GetLine(), aMergePoint );
460 removeFromPositionMap( lineToMerge );
461 *aDidMerge = true;
462 };
463
464 // Cluster all lines in to contiguous entities
465 for( LENGTH_DELAY_CALCULATION_ITEM* primaryItem : aLines )
466 {
467 // Don't start with an already merged line
468 if( primaryItem->GetMergeStatus() != LENGTH_DELAY_CALCULATION_ITEM::MERGE_STATUS::UNMERGED )
469 continue;
470
471 // Remove starting line from the position map
472 removeFromPositionMap( primaryItem );
473
474 SHAPE_LINE_CHAIN& primaryLine = primaryItem->GetLine();
475
476 // Merge all endpoints
478 bool mergeComplete = false;
479
480 while( !mergeComplete )
481 {
482 bool startMerged = false;
483 bool endMerged = false;
484
485 VECTOR2I startPos = primaryLine.CPoint( 0 );
486 VECTOR2I endPos = primaryLine.CLastPoint();
487
488 tryMerge( MERGE_POINT::START, startPos, primaryItem, primaryLine, &startMerged );
489 tryMerge( MERGE_POINT::END, endPos, primaryItem, primaryLine, &endMerged );
490
491 mergeComplete = !startMerged && !endMerged;
492 }
493 }
494}
495
496
498 const MERGE_POINT aMergePoint )
499{
500 if( aMergePoint == MERGE_POINT::START )
501 {
502 if( aSecondary.GetPoint( 0 ) == aPrimary.GetPoint( 0 ) )
503 {
504 for( auto itr = aSecondary.CPoints().begin() + 1; itr != aSecondary.CPoints().end(); ++itr )
505 aPrimary.Insert( 0, *itr );
506 }
507 else
508 {
509 wxASSERT( aSecondary.CLastPoint() == aPrimary.GetPoint( 0 ) );
510
511 for( auto itr = aSecondary.CPoints().rbegin() + 1; itr != aSecondary.CPoints().rend(); ++itr )
512 aPrimary.Insert( 0, *itr );
513 }
514 }
515 else
516 {
517 if( aSecondary.GetPoint( 0 ) == aPrimary.CLastPoint() )
518 {
519 for( auto itr = aSecondary.CPoints().begin() + 1; itr != aSecondary.CPoints().end(); ++itr )
520 aPrimary.Append( *itr );
521 }
522 else
523 {
524 wxASSERT( aSecondary.CLastPoint() == aPrimary.CLastPoint() );
525
526 for( auto itr = aSecondary.CPoints().rbegin() + 1; itr != aSecondary.CPoints().rend(); ++itr )
527 aPrimary.Append( *itr );
528 }
529 }
530}
531
532
533void LENGTH_DELAY_CALCULATION::optimiseTracesInPads( const std::vector<LENGTH_DELAY_CALCULATION_ITEM*>& aPads,
534 const std::vector<LENGTH_DELAY_CALCULATION_ITEM*>& aLines )
535{
536 for( LENGTH_DELAY_CALCULATION_ITEM* padItem : aPads )
537 {
538 const PAD* pad = padItem->GetPad();
539
540 for( LENGTH_DELAY_CALCULATION_ITEM* lineItem : aLines )
541 {
542 // Ignore merged lines
543 if( lineItem->GetMergeStatus() != LENGTH_DELAY_CALCULATION_ITEM::MERGE_STATUS::MERGED_IN_USE )
544 continue;
545
546 const PCB_LAYER_ID pcbLayer = lineItem->GetStartLayer();
547 SHAPE_LINE_CHAIN& line = lineItem->GetLine();
548
549 OptimiseTraceInPad( line, pad, pcbLayer );
550 }
551 }
552}
553
554
556 const std::vector<LENGTH_DELAY_CALCULATION_ITEM*>& aVias, std::vector<LENGTH_DELAY_CALCULATION_ITEM*>& aLines,
557 std::map<VECTOR2I, std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>>& aLinesPositionMap,
558 const std::map<VECTOR2I, std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>>& aPadsPositionMap )
559{
560 for( LENGTH_DELAY_CALCULATION_ITEM* via : aVias )
561 {
562 auto lineItr = aLinesPositionMap.find( via->GetVia()->GetPosition() );
563
564 if( lineItr == aLinesPositionMap.end() )
565 continue;
566
567 std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>& connectedLines = lineItr->second;
568
569 if( connectedLines.empty() )
570 {
571 // No connected lines - this via is floating. Set both layers to the same
572 via->SetLayers( via->GetVia()->GetLayer(), via->GetVia()->GetLayer() );
573 }
574 else if( connectedLines.size() == 1 )
575 {
576 // This is either a via stub, or a via-in-pad
577 bool isViaInPad = false;
578 const PCB_LAYER_ID lineLayer = ( *connectedLines.begin() )->GetStartLayer();
579
580 auto padItr = aPadsPositionMap.find( via->GetVia()->GetPosition() );
581
582 if( padItr != aPadsPositionMap.end() )
583 {
584 // This could be a via-in-pad - check for overlapping pads which are not on the line layer
585 const std::unordered_set<LENGTH_DELAY_CALCULATION_ITEM*>& pads = padItr->second;
586
587 if( pads.size() == 1 )
588 {
589 const LENGTH_DELAY_CALCULATION_ITEM* padItem = *pads.begin();
590
591 if( !padItem->GetPad()->Padstack().LayerSet().Contains( lineLayer ) )
592 {
593 // This is probably a via-in-pad
594 isViaInPad = true;
595 via->SetLayers( lineLayer, padItem->GetStartLayer() );
596 }
597 }
598 }
599
600 if( !isViaInPad )
601 {
602 // This is a via stub - make its electrical length 0
603 via->SetLayers( lineLayer, lineLayer );
604 }
605 }
606 else
607 {
608 // This via has more than one track ending at it. Calculate the connected layer span (which may be shorter
609 // than the overall via span)
610 LSET layers;
611
612 for( const LENGTH_DELAY_CALCULATION_ITEM* lineItem : connectedLines )
613 layers.set( lineItem->GetStartLayer() );
614
615 LSEQ cuStack = layers.CuStack();
616
617 PCB_LAYER_ID firstLayer = UNDEFINED_LAYER;
618 PCB_LAYER_ID lastLayer = UNDEFINED_LAYER;
619
620 for( PCB_LAYER_ID layer : cuStack )
621 {
622 if( firstLayer == UNDEFINED_LAYER )
623 firstLayer = layer;
624 else
625 lastLayer = layer;
626 }
627
628 if( lastLayer == UNDEFINED_LAYER )
629 via->SetLayers( firstLayer, firstLayer );
630 else
631 via->SetLayers( firstLayer, lastLayer );
632 }
633 }
634}
635
636
638 const PCB_LAYER_ID aPcbLayer )
639{
640 // Only consider lines which terminate in the pad
641 if( aLine.CPoint( 0 ) != aPad->GetPosition() && aLine.CLastPoint() != aPad->GetPosition() )
642 return;
643
644 if( !aPad->FlashLayer( aPcbLayer ) )
645 return;
646
647 const auto& shape = aPad->GetEffectivePolygon( aPcbLayer, ERROR_INSIDE );
648
649 if( shape->Contains( aLine.CPoint( 0 ) ) )
650 clipLineToPad( aLine, aPad, aPcbLayer, true );
651 else if( shape->Contains( aLine.CLastPoint() ) )
652 clipLineToPad( aLine, aPad, aPcbLayer, false );
653}
654
655
658{
659 if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( aBoardItem ) )
660 {
661 if( track->Type() == PCB_VIA_T )
662 {
663 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
664
666 item.SetVia( via );
668 item.SetEffectiveNetClass( via->GetEffectiveNetClass() );
669
670 return item;
671 }
672
673 if( track->Type() == PCB_ARC_T )
674 {
675 const PCB_ARC* arcParent = static_cast<const PCB_ARC*>( track );
676 SHAPE_ARC shapeArc( arcParent->GetStart(), arcParent->GetMid(), arcParent->GetEnd(),
677 arcParent->GetWidth() );
678 SHAPE_LINE_CHAIN chainArc( shapeArc );
679
681 item.SetLine( chainArc );
682 item.SetLayers( track->GetLayer() );
683 item.SetEffectiveNetClass( arcParent->GetEffectiveNetClass() );
684
685 return item;
686 }
687
688 if( track->Type() == PCB_TRACE_T )
689 {
690 std::vector<VECTOR2I> points{ track->GetStart(), track->GetEnd() };
691 SHAPE_LINE_CHAIN shape( points );
692
694 item.SetLine( shape );
695 item.SetLayers( track->GetLayer() );
696 item.SetEffectiveNetClass( track->GetEffectiveNetClass() );
697
698 return item;
699 }
700 }
701 else if( const PAD* pad = dynamic_cast<const PAD*>( aBoardItem ) )
702 {
704 item.SetPad( pad );
705
706 const LSET& layers = pad->Padstack().LayerSet();
707 PCB_LAYER_ID firstLayer = UNDEFINED_LAYER;
708 PCB_LAYER_ID secondLayer = UNDEFINED_LAYER;
709
710 for( auto itr = layers.copper_layers_begin(); itr != layers.copper_layers_end(); ++itr )
711 {
712 if( firstLayer == UNDEFINED_LAYER )
713 firstLayer = *itr;
714 else
715 secondLayer = *itr;
716 }
717
718 item.SetLayers( firstLayer, secondLayer );
719 item.SetEffectiveNetClass( pad->GetEffectiveNetClass() );
720
721 return item;
722 }
723
724 return {};
725}
726
727
729 std::unique_ptr<TUNING_PROFILE_PARAMETERS_IFACE>&& aProvider )
730{
731 m_tuningProfileParameters = std::move( aProvider );
732}
733
734
739
740
741int64_t LENGTH_DELAY_CALCULATION::CalculateLengthForDelay( const int64_t aDesiredDelay,
742 const TUNING_PROFILE_GEOMETRY_CONTEXT& aCtx ) const
743{
744 return m_tuningProfileParameters->GetTrackLengthForPropagationDelay( aDesiredDelay, aCtx );
745}
746
747
749 const SHAPE_LINE_CHAIN& aShape, const TUNING_PROFILE_GEOMETRY_CONTEXT& aCtx ) const
750{
751 return m_tuningProfileParameters->CalculatePropagationDelayForShapeLineChain( aShape, aCtx );
752}
@ ERROR_INSIDE
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
Manage layers needed to make a physical board.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Lightweight class which holds a pad, via, or a routed trace outline.
void SetLine(const SHAPE_LINE_CHAIN &aLine)
Sets the source SHAPE_LINE_CHAIN of this item.
void SetVia(const PCB_VIA *aVia)
Sets the VIA associated with this item.
TYPE Type() const
Gets the routing item type.
void SetPad(const PAD *aPad)
Sets the parent PAD associated with this item.
void SetMergeStatus(const MERGE_STATUS aStatus)
Sets the MERGE_STATUS of this item.
SHAPE_LINE_CHAIN & GetLine() const
Gets the SHAPE_LINE_CHAIN associated with this item.
const PAD * GetPad() const
Gets the parent PAD associated with this item.
PCB_LAYER_ID GetStartLayer() const
Gets the start board layer for the proxied item.
void SetEffectiveNetClass(const NETCLASS *aNetClass)
Sets the effective net class for the item.
void CalculateViaLayers(const BOARD *aBoard)
Calculates active via payers for a proxied VIA object.
void SetLayers(const PCB_LAYER_ID aStart, const PCB_LAYER_ID aEnd=PCB_LAYER_ID::UNDEFINED_LAYER)
Sets the first and last layers associated with this item.
std::unique_ptr< TUNING_PROFILE_PARAMETERS_IFACE > m_tuningProfileParameters
The active provider of tuning profile parameters.
int64_t CalculateLengthForDelay(int64_t aDesiredDelay, const TUNING_PROFILE_GEOMETRY_CONTEXT &aCtx) const
Calculates the length of track required for the given delay in a specific geometry context.
int64_t CalculatePropagationDelayForShapeLineChain(const SHAPE_LINE_CHAIN &aShape, const TUNING_PROFILE_GEOMETRY_CONTEXT &aCtx) const
Gets the propagation delay for the given shape line chain.
static void clipLineToPad(SHAPE_LINE_CHAIN &aLine, const PAD *aPad, PCB_LAYER_ID aLayer, bool aForward=true)
Clips the given line to the minimal direct electrical length within the pad.
MERGE_POINT
Enum to describe whether track merging is attempted from the start or end of a track segment.
void inferViaInPad(const PAD *aPad, const LENGTH_DELAY_CALCULATION_ITEM &aItem, LENGTH_DELAY_STATS &aDetails) const
Infers if there is a via in the given pad.
static void optimiseTracesInPads(const std::vector< LENGTH_DELAY_CALCULATION_ITEM * > &aPads, const std::vector< LENGTH_DELAY_CALCULATION_ITEM * > &aLines)
Optimises the given set of items to minimise the electrical path length.
static void mergeLines(std::vector< LENGTH_DELAY_CALCULATION_ITEM * > &aLines, std::map< VECTOR2I, std::unordered_set< LENGTH_DELAY_CALCULATION_ITEM * > > &aLinesPositionMap)
Merges any lines (traces) that are contiguous, on one layer, and with no junctions.
int64_t CalculateDelay(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, PATH_OPTIMISATIONS aOptimisations, const PAD *aStartPad=nullptr, const PAD *aEndPad=nullptr) const
Calculates the electrical propagation delay of the given items.
int64_t CalculateLength(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, PATH_OPTIMISATIONS aOptimisations, const PAD *aStartPad=nullptr, const PAD *aEndPad=nullptr) const
Calculates the electrical length of the given items.
int StackupHeight(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Returns the stackup distance between the two given layers.
void SynchronizeTuningProfileProperties() const
Ensure time domain properties provider is synced with board / project settings if required.
BOARD * m_board
The parent board for all items.
LENGTH_DELAY_CALCULATION_ITEM GetLengthCalculationItem(const BOARD_CONNECTED_ITEM *aBoardItem) const
Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM.
static void optimiseViaLayers(const std::vector< LENGTH_DELAY_CALCULATION_ITEM * > &aVias, std::vector< LENGTH_DELAY_CALCULATION_ITEM * > &aLines, std::map< VECTOR2I, std::unordered_set< LENGTH_DELAY_CALCULATION_ITEM * > > &aLinesPositionMap, const std::map< VECTOR2I, std::unordered_set< LENGTH_DELAY_CALCULATION_ITEM * > > &aPadsPositionMap)
Optimises the via layers.
void SetTuningProfileParametersProvider(std::unique_ptr< TUNING_PROFILE_PARAMETERS_IFACE > &&aProvider)
Sets the provider for tuning profile parameter resolution.
static void mergeShapeLineChains(SHAPE_LINE_CHAIN &aPrimary, const SHAPE_LINE_CHAIN &aSecondary, MERGE_POINT aMergePoint)
Merges two SHAPE_LINE_CHAINs where there is a shared endpoing.
static bool findArcPadIntersection(const SHAPE_ARC &aArc, const std::shared_ptr< SHAPE_POLY_SET > &aPadShape, const VECTOR2I &aInsidePoint, VECTOR2I &aIntersection)
Finds the intersection point between an arc and a pad shape.
LENGTH_DELAY_STATS CalculateLengthDetails(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, PATH_OPTIMISATIONS aOptimisations, const PAD *aStartPad=nullptr, const PAD *aEndPad=nullptr, LENGTH_DELAY_LAYER_OPT aLayerOpt=LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL, LENGTH_DELAY_DOMAIN_OPT aDomain=LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL) const
Calculates the electrical length of the given items.
static void OptimiseTraceInPad(SHAPE_LINE_CHAIN &aLine, const PAD *aPad, PCB_LAYER_ID aPcbLayer)
Optimises the given trace / line to minimise the electrical path length within the given pad.
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
copper_layers_iterator copper_layers_end() const
Definition lset.cpp:924
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:263
copper_layers_iterator copper_layers_begin() const
Definition lset.cpp:918
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
const LSET & LayerSet() const
Definition padstack.h:322
Definition pad.h:55
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:408
VECTOR2I GetPosition() const override
Definition pad.h:209
const PADSTACK & Padstack() const
Definition pad.h:333
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:917
const VECTOR2I & GetMid() const
Definition pcb_track.h:347
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
virtual int GetWidth() const
Definition pcb_track.h:148
Definition seg.h:42
bool Contains(const SEG &aSeg) const
Definition seg.h:324
int IntersectLine(const SEG &aSeg, std::vector< VECTOR2I > *aIpsBuffer) const
Find intersection points between this arc and aSeg, treating aSeg as an infinite line.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
virtual const VECTOR2I GetPoint(int aIndex) const override
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
const std::vector< SHAPE_ARC > & CArcs() const
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
void Insert(size_t aVertex, const VECTOR2I &aP)
const std::vector< VECTOR2I > & CPoints() const
a few functions useful in geometry calculations.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
LENGTH_DELAY_DOMAIN_OPT
Enum which controls the calculation domain of the length / delay calculation methods.
LENGTH_DELAY_LAYER_OPT
Enum which controls the level of detail returned by the length / delay calculation methods.
Holds length measurement result details and statistics.
std::unique_ptr< std::map< PCB_LAYER_ID, int64_t > > LayerDelays
int64_t TotalLength() const
Calculates the total electrical length for this set of statistics.
std::unique_ptr< std::map< PCB_LAYER_ID, int64_t > > LayerLengths
int64_t TotalDelay() const
Calculates the total electrical propagation delay for this set of statistics.
Struct to control which optimisations the length calculation code runs on the given path objects.
bool InferViaInPad
Determines if there is a via-in-pad present on the board but not in the item set.
bool OptimiseViaLayers
Optimise via layers for height calculations, ensuring only the distance between routed segments is co...
bool MergeTracks
Merges all contiguous (end-to-end, same layer) tracks.
bool OptimiseTracesInPads
Optimises the electrical length of tracks within pads.
A data structure to contain basic geometry data which can affect signal propagation calculations.
const NETCLASS * NetClass
The net class this track belongs to.
int delta
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695