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{
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
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;
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,
434 LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
435 LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL );
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
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:934
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition: board.cpp:2168
LENGTH_DELAY_CALCULATION * GetLengthCalculation() const
Returns the track length calculator.
Definition: board.h:1340
int GetCopperLayerCount() const
Definition: board.cpp:859
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:522
wxString GetName() const
Definition: drc_rule.h:168
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:160
bool GetOption(OPTIONS option) const
Definition: drc_rule.h:193
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:164
bool IsNull() const
Definition: drc_rule.h:155
BOARD * GetBoard() const
Definition: drc_engine.h:100
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:706
bool IsCancelled() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:393
void Add(const ENTRY &ent)
std::optional< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition: drc_rule.cpp:67
bool runInternal(bool aDelayReportMode=false)
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)
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
REPORTER * getLogReporter() const
DRC_ENGINE * m_drcEngine
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:56
const wxString & GetNetname() const
Definition: netinfo.h:114
int GetNetCode() const
Definition: netinfo.h:108
NETINFO_ITEM * GetNetItem(int aNetCode) const
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.
bool OptimiseViaLayers
Optimise via layers for height calculations, ensuring only the distance between routed segments is co...
@ 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