KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_matched_length.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.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <common.h>
21#include <board.h>
23#include <drc/drc_item.h>
27
30
31
32/*
33 Single-ended matched length + skew + via count test.
34 Errors generated:
35 - DRCE_LENGTH_OUT_OF_RANGE
36 - DRCE_SKEW_OUT_OF_RANGE
37 - DRCE_TOO_MANY_VIAS
38 Todo: arc support
39*/
40
42{
43public:
45 m_board( nullptr )
46 {}
47
49
50 virtual bool Run() override;
51
52 virtual const wxString GetName() const override { return wxT( "length" ); };
53
54private:
55
56 bool runInternal( bool aDelayReportMode = false );
57
59
60 void checkLengths( const DRC_CONSTRAINT& aConstraint,
61 const std::vector<CONNECTION>& aMatchedConnections );
62 void checkSkews( const DRC_CONSTRAINT& aConstraint,
63 const std::vector<CONNECTION>& aMatchedConnections );
64 void checkViaCounts( const DRC_CONSTRAINT& aConstraint,
65 const std::vector<CONNECTION>& aMatchedConnections );
66
67private:
70};
71
73 const std::vector<CONNECTION>& aMatchedConnections )
74{
75 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
76 {
77 bool minViolation = false;
78 bool maxViolation = false;
79 int minLen = 0;
80 int maxLen = 0;
81
82 const bool isTimeDomain = aConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
83 const EDA_DATA_TYPE dataType = isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
84
85 if( !isTimeDomain )
86 {
87 if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
88 {
89 minViolation = true;
90 minLen = aConstraint.GetValue().Min();
91 }
92 else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
93 {
94 maxViolation = true;
95 maxLen = aConstraint.GetValue().Max();
96 }
97 }
98 else
99 {
100 if( aConstraint.GetValue().HasMin() && ent.totalDelay < aConstraint.GetValue().Min() )
101 {
102 minViolation = true;
103 minLen = aConstraint.GetValue().Min();
104 }
105 else if( aConstraint.GetValue().HasMax() && ent.totalDelay > aConstraint.GetValue().Max() )
106 {
107 maxViolation = true;
108 maxLen = aConstraint.GetValue().Max();
109 }
110 }
111
112 if( ( minViolation || maxViolation ) )
113 {
114 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
115 wxString msg;
116
117 if( minViolation )
118 {
120 {
121 msg = formatMsg( _( "(%s min length %s; actual %s)" ),
122 aConstraint.GetName(),
123 minLen,
124 ent.totalDelay,
125 dataType );
126 }
127 else
128 {
129 msg = formatMsg( _( "(%s min length %s; actual %s)" ),
130 aConstraint.GetName(),
131 minLen,
132 ent.total,
133 dataType );
134 }
135 }
136 else if( maxViolation )
137 {
139 {
140 msg = formatMsg( _( "(%s max length %s; actual %s)" ),
141 aConstraint.GetName(),
142 maxLen,
143 ent.totalDelay,
144 dataType );
145 }
146 else
147 {
148 msg = formatMsg( _( "(%s max length %s; actual %s)" ),
149 aConstraint.GetName(),
150 maxLen,
151 ent.total,
152 dataType );
153 }
154 }
155
156 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
157
158 for( auto offendingTrack : ent.items )
159 drcItem->AddItem( offendingTrack );
160
161 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
162
163 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
164 }
165 }
166}
167
169 const std::vector<CONNECTION>& aMatchedConnections )
170{
171 auto checkSkewsImpl = [this, &aConstraint]( const std::vector<CONNECTION>& connections )
172 {
173 const bool isTimeDomain = aConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
174 const EDA_DATA_TYPE dataType = isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
175
176 double maxLength = 0;
177 wxString maxNetname;
178
179 if( !isTimeDomain )
180 {
181 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
182 {
183 if( ent.total > maxLength )
184 {
185 maxLength = ent.total;
186 maxNetname = ent.netname;
187 }
188 }
189 }
190 else
191 {
192 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
193 {
194 if( ent.totalDelay > maxLength )
195 {
196 maxLength = ent.totalDelay;
197 maxNetname = ent.netname;
198 }
199 }
200 }
201
202 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
203 {
204 int skew = isTimeDomain ? KiROUND( ent.totalDelay - maxLength )
205 : KiROUND( ent.total - maxLength );
206
207 bool fail_min = false;
208 bool fail_max = false;
209
210 if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
211 fail_max = true;
212 else if( aConstraint.GetValue().HasMin() && abs( skew ) < aConstraint.GetValue().Min() )
213 fail_min = true;
214
215 if( fail_min || fail_max )
216 {
217 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
218 wxString msg;
219
220 double reportTotal = isTimeDomain ? ent.totalDelay : ent.total;
221
222 if( fail_min )
223 {
224 msg.Printf( _( "(%s min skew %s; actual %s; target net length %s (from %s); "
225 "actual %s)" ),
226 aConstraint.GetName(),
227 MessageTextFromValue( aConstraint.GetValue().Min(), true, dataType ),
228 MessageTextFromValue( skew, true, dataType ),
229 MessageTextFromValue( maxLength, true, dataType ),
230 maxNetname,
231 MessageTextFromValue( reportTotal, true, dataType ) );
232 }
233 else
234 {
235 msg.Printf( _( "(%s max skew %s; actual %s; target net length %s (from %s); "
236 "actual %s)" ),
237 aConstraint.GetName(),
238 MessageTextFromValue( aConstraint.GetValue().Max(), true, dataType ),
239 MessageTextFromValue( skew, true, dataType ),
240 MessageTextFromValue( maxLength, true, dataType ),
241 maxNetname,
242 MessageTextFromValue( reportTotal, true, dataType ) );
243 }
244
245 drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
246
247 for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
248 drcItem->SetItems( offendingTrack );
249
250 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
251
252 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
253 }
254 }
255 };
256
258 {
259 // Find all pairs of nets in the matched connections
260 std::map<int, CONNECTION> netcodeMap;
261
262 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
263 netcodeMap[ent.netcode] = ent;
264
265 std::vector<std::vector<CONNECTION>> matchedDiffPairs;
266
267 for( auto& [netcode, connection] : netcodeMap )
268 {
269 NETINFO_ITEM* matchedNet = m_board->DpCoupledNet( connection.netinfo );
270
271 if( matchedNet )
272 {
273 int matchedNetcode = matchedNet->GetNetCode();
274
275 if( netcodeMap.count( matchedNetcode ) )
276 {
277 std::vector<CONNECTION> pair{ connection, netcodeMap[matchedNetcode] };
278 matchedDiffPairs.emplace_back( std::move( pair ) );
279 netcodeMap.erase( matchedNetcode );
280 }
281 }
282 }
283
284 // Test all found pairs of nets
285 for( const std::vector<CONNECTION>& matchedDiffPair : matchedDiffPairs )
286 checkSkewsImpl( matchedDiffPair );
287 }
288 else
289 {
290 // Test all matched nets as a group
291 checkSkewsImpl( aMatchedConnections );
292 }
293}
294
295
297 const std::vector<CONNECTION>& aMatchedConnections )
298{
299 for( const auto& ent : aMatchedConnections )
300 {
301 std::shared_ptr<DRC_ITEM> drcItem = nullptr;
302
303 if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
304 {
306 wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
307 aConstraint.GetName(),
308 aConstraint.GetValue().Max(),
309 ent.viaCount );
310
311 drcItem->SetErrorMessage( _( "Too many vias on a connection" ) + wxS( " " ) + msg );
312 }
313 else if( aConstraint.GetValue().HasMin() && ent.viaCount < aConstraint.GetValue().Min() )
314 {
316 wxString msg = wxString::Format( _( "(%s min count %d; actual %d)" ),
317 aConstraint.GetName(),
318 aConstraint.GetValue().Min(),
319 ent.viaCount );
320
321 drcItem->SetErrorMessage( _( "Too few vias on a connection" ) + wxS( " " ) + msg );
322 }
323
324 if( drcItem )
325 {
326 for( const BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
327 drcItem->SetItems( offendingTrack );
328
329 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
330
331 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
332 }
333 }
334}
335
336
338{
339 return runInternal( false );
340}
341
342
344{
345 m_board = m_drcEngine->GetBoard();
346 m_report.Clear();
347
348 if( !aDelayReportMode )
349 {
350 if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
351 return false;
352 }
353
354 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
355 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
356
357 std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
358
359 ftCache->Rebuild( m_board );
360
361 const size_t progressDelta = 100;
362 size_t count = 0;
363 size_t ii = 0;
364
366 [&]( BOARD_ITEM *item ) -> bool
367 {
368 count++;
369 return true;
370 } );
371
373 [&]( BOARD_ITEM *item ) -> bool
374 {
375 if( !reportProgress( ii++, count, progressDelta ) )
376 return false;
377
379 {
380 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( jj, item, nullptr, item->GetLayer() );
381
382 if( constraint.IsNull() )
383 continue;
384
385 BOARD_CONNECTED_ITEM* citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
386
387 itemSets[ constraint.GetParentRule() ].insert( citem );
388 }
389
390 return true;
391 } );
392
393 LENGTH_DELAY_CALCULATION* calc = m_board->GetLengthCalculation();
394
395 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
396
397 for( const auto& [rule, ruleItems] : itemSets )
398 {
399 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
400
401 for( BOARD_CONNECTED_ITEM* item : ruleItems )
402 netMap[item->GetNetCode()].insert( item );
403
404 for( const auto& [netCode, netItems] : netMap )
405 {
406 std::vector<LENGTH_DELAY_CALCULATION_ITEM> lengthItems;
407 lengthItems.reserve( netItems.size() );
408
409 CONNECTION ent;
410 ent.items = netItems;
411 ent.netcode = netCode;
412 ent.netname = m_board->GetNetInfo().GetNetItem( ent.netcode )->GetNetname();
413 ent.netinfo = m_board->GetNetInfo().GetNetItem( ent.netcode );
414
415 ent.viaCount = 0;
416 ent.totalRoute = 0;
417 ent.totalVia = 0;
418 ent.totalPadToDie = 0;
419 ent.fromItem = nullptr;
420 ent.toItem = nullptr;
421
422 for( BOARD_CONNECTED_ITEM* item : netItems )
423 {
424 LENGTH_DELAY_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( item );
425
426 if( lengthItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
427 lengthItems.emplace_back( lengthItem );
428 }
429
430 constexpr PATH_OPTIMISATIONS opts = {
431 .OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
432 };
433 LENGTH_DELAY_STATS details = calc->CalculateLengthDetails( lengthItems, opts, nullptr, nullptr,
436 ent.viaCount = details.NumVias;
437 ent.totalVia = details.ViaLength;
438 ent.totalViaDelay = details.ViaDelay;
439 ent.totalRoute = static_cast<double>( details.TrackLength );
440 ent.totalRouteDelay = static_cast<double>( details.TrackDelay );
441 ent.totalPadToDie = details.PadToDieLength;
442 ent.totalPadToDieDelay = details.PadToDieDelay;
443 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
444 ent.totalDelay = ent.totalRouteDelay + static_cast<double>( ent.totalViaDelay )
445 + static_cast<double>( ent.totalPadToDieDelay );
446 ent.matchingRule = rule;
447
448 if( FROM_TO_CACHE::FT_PATH* ftPath = ftCache->QueryFromToPath( ent.items ) )
449 {
450 ent.from = ftPath->fromName;
451 ent.to = ftPath->toName;
452 }
453 else
454 {
455 ent.from = ent.to = _( "<unconstrained>" );
456 }
457
458 m_report.Add( ent );
459 matches[rule].push_back( ent );
460 }
461 }
462
463 if( !aDelayReportMode )
464 {
465 if( !reportPhase( _( "Checking length constraints..." ) ) )
466 return false;
467
468 ii = 0;
469 count = matches.size();
470
471 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
472 {
473 DRC_RULE *rule = it.first;
474 auto& matchedConnections = it.second;
475
476 if( !reportProgress( ii++, count, progressDelta ) )
477 return false;
478
479 std::sort( matchedConnections.begin(), matchedConnections.end(),
480 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
481 {
482 return a.netname < b.netname;
483 } );
484
485 if( getLogReporter() )
486 {
487 REPORT_AUX( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
488 it.first->m_Name ) );
489
490 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
491 {
492 REPORT_AUX( wxString::Format( wxT( " - net: %s, from: %s, to: %s, %d matching items, "
493 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
494 "vias: %d" ),
495 ent.netname,
496 ent.from, ent.to,
497 static_cast<int>( ent.items.size() ),
498 MessageTextFromValue( ent.total ),
499 MessageTextFromValue( ent.totalRoute ),
500 MessageTextFromValue( ent.totalVia ),
501 MessageTextFromValue( ent.totalPadToDie ),
502 ent.viaCount ) );
503 }
504 }
505
506 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
507
508 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
509 checkLengths( *lengthConstraint, matchedConnections );
510
511 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
512
513 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
514 checkSkews( *skewConstraint, matchedConnections );
515
516 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
517
518 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
519 checkViaCounts( *viaCountConstraint, matchedConnections );
520 }
521 }
522
523 return !m_drcEngine->IsCancelled();
524}
525
526
527namespace detail
528{
530}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:317
wxString GetName() const
Definition drc_rule.h:170
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:162
bool GetOption(OPTIONS option) const
Definition drc_rule.h:195
DRC_RULE * GetParentRule() const
Definition drc_rule.h:166
bool IsNull() const
Definition drc_rule.h:157
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:381
std::optional< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition drc_rule.cpp:67
virtual const wxString GetName() const override
void checkSkews(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
virtual ~DRC_TEST_PROVIDER_MATCHED_LENGTH()=default
void checkViaCounts(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void checkLengths(const DRC_CONSTRAINT &aConstraint, const std::vector< CONNECTION > &aMatchedConnections)
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
REPORTER * getLogReporter() const
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::vector< PCB_SHAPE > &aShapes={})
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
Lightweight class which holds a pad, via, or a routed trace outline.
TYPE Type() const
Gets the routing item type.
Class which calculates lengths (and associated routing statistics) in a BOARD context.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:591
T Min() const
Definition minoptmax.h:33
bool HasMax() const
Definition minoptmax.h:38
bool HasMin() const
Definition minoptmax.h:37
T Max() const
Definition minoptmax.h:34
Handle the data for a net.
Definition netinfo.h:54
int GetNetCode() const
Definition netinfo.h:106
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
The common library.
@ DRCE_SKEW_OUT_OF_RANGE
Definition drc_item.h:104
@ DRCE_LENGTH_OUT_OF_RANGE
Definition drc_item.h:103
@ DRCE_VIA_COUNT_OUT_OF_RANGE
Definition drc_item.h:105
DRC_CONSTRAINT_T
Definition drc_rule.h:47
@ LENGTH_CONSTRAINT
Definition drc_rule.h:71
@ VIA_COUNT_CONSTRAINT
Definition drc_rule.h:76
@ SKEW_CONSTRAINT
Definition drc_rule.h:72
#define REPORT_AUX(s)
#define _(s)
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:38
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
BOARD_CONNECTED_ITEM * fromItem
BOARD_CONNECTED_ITEM * toItem
std::set< BOARD_CONNECTED_ITEM * > items
Holds length measurement result details and statistics.
Struct to control which optimisations the length calculation code runs on the given path objects.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ 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