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
116 if( minViolation )
117 {
118 drcItem->SetErrorDetail( formatMsg( _( "(%s min length %s; actual %s)" ),
119 aConstraint.GetName(),
120 minLen,
122 ? ent.totalDelay
123 : ent.total,
124 dataType ) );
125 }
126 else if( maxViolation )
127 {
128 drcItem->SetErrorDetail( formatMsg( _( "(%s max length %s; actual %s)" ),
129 aConstraint.GetName(),
130 maxLen,
132 ? ent.totalDelay
133 : ent.total,
134 dataType ) );
135 }
136
137 for( auto offendingTrack : ent.items )
138 drcItem->AddItem( offendingTrack );
139
140 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
141
142 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
143 }
144 }
145}
146
148 const std::vector<CONNECTION>& aMatchedConnections )
149{
150 auto checkSkewsImpl = [this, &aConstraint]( const std::vector<CONNECTION>& connections )
151 {
152 const bool isTimeDomain = aConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
153 const EDA_DATA_TYPE dataType = isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
154
155 double maxLength = 0;
156 wxString maxNetname;
157
158 if( !isTimeDomain )
159 {
160 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
161 {
162 if( ent.total > maxLength )
163 {
164 maxLength = ent.total;
165 maxNetname = ent.netname;
166 }
167 }
168 }
169 else
170 {
171 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
172 {
173 if( ent.totalDelay > maxLength )
174 {
175 maxLength = ent.totalDelay;
176 maxNetname = ent.netname;
177 }
178 }
179 }
180
181 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
182 {
183 int skew = isTimeDomain ? KiROUND( ent.totalDelay - maxLength )
184 : KiROUND( ent.total - maxLength );
185
186 bool fail_min = false;
187 bool fail_max = false;
188
189 if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
190 fail_max = true;
191 else if( aConstraint.GetValue().HasMin() && abs( skew ) < aConstraint.GetValue().Min() )
192 fail_min = true;
193
194 if( fail_min || fail_max )
195 {
196 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
197 wxString msg;
198
199 double reportTotal = isTimeDomain ? ent.totalDelay : ent.total;
200
201 if( fail_min )
202 {
203 msg.Printf( _( "(%s min skew %s; actual %s; target net length %s (from %s); actual %s)" ),
204 aConstraint.GetName(),
205 MessageTextFromValue( aConstraint.GetValue().Min(), true, dataType ),
206 MessageTextFromValue( skew, true, dataType ),
207 MessageTextFromValue( maxLength, true, dataType ),
208 maxNetname,
209 MessageTextFromValue( reportTotal, true, dataType ) );
210 }
211 else
212 {
213 msg.Printf( _( "(%s max skew %s; actual %s; target net length %s (from %s); actual %s)" ),
214 aConstraint.GetName(),
215 MessageTextFromValue( aConstraint.GetValue().Max(), true, dataType ),
216 MessageTextFromValue( skew, true, dataType ),
217 MessageTextFromValue( maxLength, true, dataType ),
218 maxNetname,
219 MessageTextFromValue( reportTotal, true, dataType ) );
220 }
221
222 drcItem->SetErrorDetail( msg );
223
224 for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
225 drcItem->SetItems( offendingTrack );
226
227 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
228
229 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
230 }
231 }
232 };
233
235 {
236 // Find all pairs of nets in the matched connections
237 std::map<int, CONNECTION> netcodeMap;
238
239 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
240 netcodeMap[ent.netcode] = ent;
241
242 std::vector<std::vector<CONNECTION>> matchedDiffPairs;
243
244 for( auto& [netcode, connection] : netcodeMap )
245 {
246 NETINFO_ITEM* matchedNet = m_board->DpCoupledNet( connection.netinfo );
247
248 if( matchedNet )
249 {
250 int matchedNetcode = matchedNet->GetNetCode();
251
252 if( netcodeMap.count( matchedNetcode ) )
253 {
254 std::vector<CONNECTION> pair{ connection, netcodeMap[matchedNetcode] };
255 matchedDiffPairs.emplace_back( std::move( pair ) );
256 netcodeMap.erase( matchedNetcode );
257 }
258 }
259 }
260
261 // Test all found pairs of nets
262 for( const std::vector<CONNECTION>& matchedDiffPair : matchedDiffPairs )
263 checkSkewsImpl( matchedDiffPair );
264 }
265 else
266 {
267 // Test all matched nets as a group
268 checkSkewsImpl( aMatchedConnections );
269 }
270}
271
272
274 const std::vector<CONNECTION>& aMatchedConnections )
275{
276 for( const auto& ent : aMatchedConnections )
277 {
278 std::shared_ptr<DRC_ITEM> drcItem = nullptr;
279
280 if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
281 {
283 wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
284 aConstraint.GetName(),
285 aConstraint.GetValue().Max(),
286 ent.viaCount );
287
288 drcItem->SetErrorMessage( _( "Too many vias on a connection" ) + wxS( " " ) + msg );
289 }
290 else if( aConstraint.GetValue().HasMin() && ent.viaCount < aConstraint.GetValue().Min() )
291 {
293 wxString msg = wxString::Format( _( "(%s min count %d; actual %d)" ),
294 aConstraint.GetName(),
295 aConstraint.GetValue().Min(),
296 ent.viaCount );
297
298 drcItem->SetErrorMessage( _( "Too few vias on a connection" ) + wxS( " " ) + msg );
299 }
300
301 if( drcItem )
302 {
303 for( const BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
304 drcItem->SetItems( offendingTrack );
305
306 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
307
308 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(), ( *ent.items.begin() )->GetLayer() );
309 }
310 }
311}
312
313
315{
316 return runInternal( false );
317}
318
319
321{
322 m_board = m_drcEngine->GetBoard();
323 m_report.Clear();
324
325 if( !aDelayReportMode )
326 {
327 if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
328 return false;
329 }
330
331 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
332 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
333
334 std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
335
336 ftCache->Rebuild( m_board );
337
338 const size_t progressDelta = 100;
339 size_t count = 0;
340 size_t ii = 0;
341
343 [&]( BOARD_ITEM *item ) -> bool
344 {
345 count++;
346 return true;
347 } );
348
350 [&]( BOARD_ITEM *item ) -> bool
351 {
352 if( !reportProgress( ii++, count, progressDelta ) )
353 return false;
354
356 {
357 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( jj, item, nullptr, item->GetLayer() );
358
359 if( constraint.IsNull() )
360 continue;
361
362 BOARD_CONNECTED_ITEM* citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
363
364 itemSets[ constraint.GetParentRule() ].insert( citem );
365 }
366
367 return true;
368 } );
369
370 LENGTH_DELAY_CALCULATION* calc = m_board->GetLengthCalculation();
371
372 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
373
374 for( const auto& [rule, ruleItems] : itemSets )
375 {
376 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
377
378 for( BOARD_CONNECTED_ITEM* item : ruleItems )
379 netMap[item->GetNetCode()].insert( item );
380
381 for( const auto& [netCode, netItems] : netMap )
382 {
383 std::vector<LENGTH_DELAY_CALCULATION_ITEM> lengthItems;
384 lengthItems.reserve( netItems.size() );
385
386 CONNECTION ent;
387 ent.items = netItems;
388 ent.netcode = netCode;
389 ent.netname = m_board->GetNetInfo().GetNetItem( ent.netcode )->GetNetname();
390 ent.netinfo = m_board->GetNetInfo().GetNetItem( ent.netcode );
391
392 ent.viaCount = 0;
393 ent.totalRoute = 0;
394 ent.totalVia = 0;
395 ent.totalPadToDie = 0;
396 ent.fromItem = nullptr;
397 ent.toItem = nullptr;
398
399 for( BOARD_CONNECTED_ITEM* item : netItems )
400 {
401 LENGTH_DELAY_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( item );
402
403 if( lengthItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
404 lengthItems.emplace_back( lengthItem );
405 }
406
407 constexpr PATH_OPTIMISATIONS opts = {
408 .OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
409 };
410 LENGTH_DELAY_STATS details = calc->CalculateLengthDetails( lengthItems, opts, nullptr, nullptr,
413 ent.viaCount = details.NumVias;
414 ent.totalVia = details.ViaLength;
415 ent.totalViaDelay = details.ViaDelay;
416 ent.totalRoute = static_cast<double>( details.TrackLength );
417 ent.totalRouteDelay = static_cast<double>( details.TrackDelay );
418 ent.totalPadToDie = details.PadToDieLength;
419 ent.totalPadToDieDelay = details.PadToDieDelay;
420 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
421 ent.totalDelay = ent.totalRouteDelay + static_cast<double>( ent.totalViaDelay )
422 + static_cast<double>( ent.totalPadToDieDelay );
423 ent.matchingRule = rule;
424
425 if( FROM_TO_CACHE::FT_PATH* ftPath = ftCache->QueryFromToPath( ent.items ) )
426 {
427 ent.from = ftPath->fromName;
428 ent.to = ftPath->toName;
429 }
430 else
431 {
432 ent.from = ent.to = _( "<unconstrained>" );
433 }
434
435 m_report.Add( ent );
436 matches[rule].push_back( ent );
437 }
438 }
439
440 if( !aDelayReportMode )
441 {
442 if( !reportPhase( _( "Checking length constraints..." ) ) )
443 return false;
444
445 ii = 0;
446 count = matches.size();
447
448 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
449 {
450 DRC_RULE *rule = it.first;
451 auto& matchedConnections = it.second;
452
453 if( !reportProgress( ii++, count, progressDelta ) )
454 return false;
455
456 std::sort( matchedConnections.begin(), matchedConnections.end(),
457 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
458 {
459 return a.netname < b.netname;
460 } );
461
462 if( getLogReporter() )
463 {
464 REPORT_AUX( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
465 it.first->m_Name ) );
466
467 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
468 {
469 REPORT_AUX( wxString::Format( wxT( " - net: %s, from: %s, to: %s, %d matching items, "
470 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
471 "vias: %d" ),
472 ent.netname,
473 ent.from, ent.to,
474 static_cast<int>( ent.items.size() ),
475 MessageTextFromValue( ent.total ),
476 MessageTextFromValue( ent.totalRoute ),
477 MessageTextFromValue( ent.totalVia ),
478 MessageTextFromValue( ent.totalPadToDie ),
479 ent.viaCount ) );
480 }
481 }
482
483 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
484
485 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
486 checkLengths( *lengthConstraint, matchedConnections );
487
488 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
489
490 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
491 checkSkews( *skewConstraint, matchedConnections );
492
493 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
494
495 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
496 checkViaCounts( *viaCountConstraint, matchedConnections );
497 }
498 }
499
500 return !m_drcEngine->IsCancelled();
501}
502
503
504namespace detail
505{
507}
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:83
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
wxString GetName() const
Definition drc_rule.h:194
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:186
bool GetOption(OPTIONS option) const
Definition drc_rule.h:219
DRC_RULE * GetParentRule() const
Definition drc_rule.h:190
bool IsNull() const
Definition drc_rule.h:181
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:400
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)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
REPORTER * getLogReporter() const
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:105
@ DRCE_LENGTH_OUT_OF_RANGE
Definition drc_item.h:104
@ DRCE_VIA_COUNT_OUT_OF_RANGE
Definition drc_item.h:106
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