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