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