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 (C) 2004-2023 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 <pad.h>
24#include <pcb_track.h>
25
26#include <drc/drc_item.h>
27#include <drc/drc_rule.h>
30
33
34
35/*
36 Single-ended matched length + skew + via count test.
37 Errors generated:
38 - DRCE_LENGTH_OUT_OF_RANGE
39 - DRCE_SKEW_OUT_OF_RANGE
40 - DRCE_TOO_MANY_VIAS
41 Todo: arc support
42*/
43
45{
46public:
48 m_board( nullptr )
49 {
50 }
51
53 {
54 }
55
56 virtual bool Run() override;
57
58 virtual const wxString GetName() const override
59 {
60 return wxT( "length" );
61 };
62
63 virtual const wxString GetDescription() const override
64 {
65 return wxT( "Tests matched track lengths." );
66 }
67
68private:
69
70 bool runInternal( bool aDelayReportMode = false );
71
73
74 void checkLengths( const DRC_CONSTRAINT& aConstraint,
75 const std::vector<CONNECTION>& aMatchedConnections );
76 void checkSkews( const DRC_CONSTRAINT& aConstraint,
77 const std::vector<CONNECTION>& aMatchedConnections );
78 void checkViaCounts( const DRC_CONSTRAINT& aConstraint,
79 const std::vector<CONNECTION>& aMatchedConnections );
80
81private:
84};
85
87 const std::vector<CONNECTION>& aMatchedConnections )
88{
89 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
90 {
91 bool minViolation = false;
92 bool maxViolation = false;
93 int minLen = 0;
94 int maxLen = 0;
95
96 if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
97 {
98 minViolation = true;
99 minLen = aConstraint.GetValue().Min();
100 }
101 else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
102 {
103 maxViolation = true;
104 maxLen = aConstraint.GetValue().Max();
105 }
106
107 if( ( minViolation || maxViolation ) )
108 {
109 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
110 wxString msg;
111
112 if( minViolation )
113 {
114 msg = formatMsg( _( "(%s min length %s; actual %s)" ),
115 aConstraint.GetName(),
116 minLen,
117 ent.total );
118 }
119 else if( maxViolation )
120 {
121 msg = formatMsg( _( "(%s max length %s; actual %s)" ),
122 aConstraint.GetName(),
123 maxLen,
124 ent.total );
125 }
126
127 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
128
129 for( auto offendingTrack : ent.items )
130 drcItem->AddItem( offendingTrack );
131
132 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
133
134 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
135 ( *ent.items.begin() )->GetLayer() );
136 }
137 }
138}
139
141 const std::vector<CONNECTION>& aMatchedConnections )
142{
143 auto checkSkewsImpl = [this, &aConstraint]( const std::vector<CONNECTION>& connections )
144 {
145 double maxLength = 0;
146 wxString maxNetname;
147
148 for( const DRC_LENGTH_REPORT::ENTRY& ent : connections )
149 {
150 if( ent.total > maxLength )
151 {
152 maxLength = ent.total;
153 maxNetname = ent.netname;
154 }
155 }
156
157 for( const auto& ent : connections )
158 {
159 int skew = KiROUND( ent.total - maxLength );
160 bool fail_min = false;
161 bool fail_max = false;
162
163 if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
164 fail_max = true;
165 else if( aConstraint.GetValue().HasMin() && abs( skew ) < aConstraint.GetValue().Min() )
166 fail_min = true;
167
168 if( fail_min || fail_max )
169 {
170 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
171 wxString msg;
172
173 if( fail_min )
174 {
175 msg.Printf( _( "(%s min skew %s; actual %s; target net length %s (from %s); "
176 "actual %s)" ),
177 aConstraint.GetName(),
178 MessageTextFromValue( aConstraint.GetValue().Min() ),
179 MessageTextFromValue( skew ), MessageTextFromValue( maxLength ),
180 maxNetname, MessageTextFromValue( ent.total ) );
181 }
182 else
183 {
184 msg.Printf( _( "(%s max skew %s; actual %s; target net length %s (from %s); "
185 "actual %s)" ),
186 aConstraint.GetName(),
187 MessageTextFromValue( aConstraint.GetValue().Max() ),
188 MessageTextFromValue( skew ), MessageTextFromValue( maxLength ),
189 maxNetname, MessageTextFromValue( ent.total ) );
190 }
191
192 drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
193
194 for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
195 drcItem->SetItems( offendingTrack );
196
197 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
198
199 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
200 ( *ent.items.begin() )->GetLayer() );
201 }
202 }
203 };
204
206 {
207 // Find all pairs of nets in the matched connections
208 std::map<int, CONNECTION> netcodeMap;
209
210 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
211 netcodeMap[ent.netcode] = ent;
212
213 std::vector<std::vector<CONNECTION>> matchedDiffPairs;
214
215 for( auto& [netcode, connection] : netcodeMap )
216 {
217 NETINFO_ITEM* matchedNet = m_board->DpCoupledNet( connection.netinfo );
218
219 if( matchedNet )
220 {
221 int matchedNetcode = matchedNet->GetNetCode();
222
223 if( netcodeMap.count( matchedNetcode ) )
224 {
225 std::vector<CONNECTION> pair{ connection, netcodeMap[matchedNetcode] };
226 matchedDiffPairs.emplace_back( std::move( pair ) );
227 netcodeMap.erase( matchedNetcode );
228 }
229 }
230 }
231
232 // Test all found pairs of nets
233 for( const std::vector<CONNECTION>& matchedDiffPair : matchedDiffPairs )
234 checkSkewsImpl( matchedDiffPair );
235 }
236 else
237 {
238 // Test all matched nets as a group
239 checkSkewsImpl( aMatchedConnections );
240 }
241}
242
243
245 const std::vector<CONNECTION>& aMatchedConnections )
246{
247 for( const auto& ent : aMatchedConnections )
248 {
249 std::shared_ptr<DRC_ITEM> drcItem = nullptr;
250
251 if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
252 {
254 wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
255 aConstraint.GetName(),
256 aConstraint.GetValue().Max(),
257 ent.viaCount );
258
259 drcItem->SetErrorMessage( _( "Too many vias on a connection" ) + wxS( " " ) + msg );
260 }
261 else if( aConstraint.GetValue().HasMin() && ent.viaCount < aConstraint.GetValue().Min() )
262 {
264 wxString msg = wxString::Format( _( "(%s min count %d; actual %d)" ),
265 aConstraint.GetName(),
266 aConstraint.GetValue().Min(),
267 ent.viaCount );
268
269 drcItem->SetErrorMessage( _( "Too few vias on a connection" ) + wxS( " " ) + msg );
270 }
271
272 if( drcItem )
273 {
274 for( auto offendingTrack : ent.items )
275 drcItem->SetItems( offendingTrack );
276
277 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
278
279 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
280 ( *ent.items.begin() )->GetLayer() );
281 }
282 }
283}
284
285
287{
288 return runInternal( false );
289}
290
291
293{
295 m_report.Clear();
296
297 if( !aDelayReportMode )
298 {
299 if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
300 return false;
301 }
302
303 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
304
305 std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
306
307 ftCache->Rebuild( m_board );
308
309 const size_t progressDelta = 100;
310 size_t count = 0;
311 size_t ii = 0;
312
314 [&]( BOARD_ITEM *item ) -> bool
315 {
316 count++;
317 return true;
318 } );
319
321 [&]( BOARD_ITEM *item ) -> bool
322 {
323 if( !reportProgress( ii++, count, progressDelta ) )
324 return false;
325
326 const DRC_CONSTRAINT_T constraintsToCheck[] = {
330 };
331
332 for( int i = 0; i < 3; i++ )
333 {
334 auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
335 item->GetLayer() );
336
337 if( constraint.IsNull() )
338 continue;
339
340 auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
341
342 itemSets[ constraint.GetParentRule() ].insert( citem );
343 }
344
345 return true;
346 } );
347
348 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
349
350 for( const std::pair< DRC_RULE* const, std::set<BOARD_CONNECTED_ITEM*> >& it : itemSets )
351 {
352 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
353
354 for( BOARD_CONNECTED_ITEM* citem : it.second )
355 netMap[ citem->GetNetCode() ].insert( citem );
356
357 for( const std::pair< const int, std::set<BOARD_CONNECTED_ITEM*> >& nitem : netMap )
358 {
359 CONNECTION ent;
360 ent.items = nitem.second;
361 ent.netcode = nitem.first;
364
365 ent.viaCount = 0;
366 ent.totalRoute = 0;
367 ent.totalVia = 0;
368 ent.totalPadToDie = 0;
369 ent.fromItem = nullptr;
370 ent.toItem = nullptr;
371
372 for( BOARD_CONNECTED_ITEM* citem : nitem.second )
373 {
374 if( citem->Type() == PCB_VIA_T )
375 {
377 const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
378
379 ent.viaCount++;
380
382 {
383 const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
384 PCB_LAYER_ID topmost;
385 PCB_LAYER_ID bottommost;
386
387 v->GetOutermostConnectedLayers( &topmost, &bottommost );
388
389 if( topmost != UNDEFINED_LAYER && topmost != bottommost )
390 ent.totalVia += stackup.GetLayerDistance( topmost, bottommost );
391 }
392 }
393 else if( citem->Type() == PCB_TRACE_T )
394 {
395 ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
396 }
397 else if ( citem->Type() == PCB_ARC_T )
398 {
399 ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
400 }
401 else if( citem->Type() == PCB_PAD_T )
402 {
403 ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
404 }
405 }
406
407 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
408 ent.matchingRule = it.first;
409
410 // fixme: doesn't seem to work ;-)
411 auto ftPath = ftCache->QueryFromToPath( ent.items );
412
413 if( ftPath )
414 {
415 ent.from = ftPath->fromName;
416 ent.to = ftPath->toName;
417 }
418 else
419 {
420 ent.from = ent.to = _( "<unconstrained>" );
421 }
422
423 m_report.Add( ent );
424 matches[ it.first ].push_back(ent);
425 }
426 }
427
428 if( !aDelayReportMode )
429 {
430 if( !reportPhase( _( "Checking length constraints..." ) ) )
431 return false;
432
433 ii = 0;
434 count = matches.size();
435
436 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
437 {
438 DRC_RULE *rule = it.first;
439 auto& matchedConnections = it.second;
440
441 if( !reportProgress( ii++, count, progressDelta ) )
442 return false;
443
444 std::sort( matchedConnections.begin(), matchedConnections.end(),
445 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
446 {
447 return a.netname < b.netname;
448 } );
449
450 reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
451 it.first->m_Name ) );
452
453 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
454 {
455 reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
456 "%d matching items, "
457 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
458 "vias: %d" ),
459 ent.netname,
460 ent.from,
461 ent.to,
462 (int) ent.items.size(),
463 MessageTextFromValue( ent.total ),
464 MessageTextFromValue( ent.totalRoute ),
465 MessageTextFromValue( ent.totalVia ),
466 MessageTextFromValue( ent.totalPadToDie ),
467 ent.viaCount ) );
468 }
469
470
471 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
472
473 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
474 checkLengths( *lengthConstraint, matchedConnections );
475
476 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
477
478 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
479 checkSkews( *skewConstraint, matchedConnections );
480
481 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
482
483 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
484 checkViaCounts( *viaCountConstraint, matchedConnections );
485 }
486
488 }
489
490 return !m_drcEngine->IsCancelled();
491}
492
493
494namespace detail
495{
497}
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,...
Container for design settings for a BOARD object.
BOARD_STACKUP & GetStackupDescriptor()
bool m_UseHeightForLengthCalcs
Enable inclusion of stackup height in track length measurements and length tuning.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
Manage layers needed to make a physical board.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:865
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition: board.cpp:1974
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:877
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:475
wxString GetName() const
Definition: drc_rule.h:156
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:148
bool GetOption(OPTIONS option) const
Definition: drc_rule.h:179
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:152
BOARD * GetBoard() const
Definition: drc_engine.h:89
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:675
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:357
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 const wxString GetDescription() const override
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...
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
virtual void reportRuleStatistics()
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
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
Definition: pad.h:54
void GetOutermostConnectedLayers(PCB_LAYER_ID *aTopmost, PCB_LAYER_ID *aBottommost) const
Return the top-most and bottom-most connected layers.
Definition: pcb_track.cpp:1142
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:101
@ DRCE_LENGTH_OUT_OF_RANGE
Definition: drc_item.h:100
@ DRCE_VIA_COUNT_OUT_OF_RANGE
Definition: drc_item.h:102
DRC_CONSTRAINT_T
Definition: drc_rule.h:47
@ LENGTH_CONSTRAINT
Definition: drc_rule.h:66
@ VIA_COUNT_CONSTRAINT
Definition: drc_rule.h:71
@ SKEW_CONSTRAINT
Definition: drc_rule.h:67
#define _(s)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
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
@ 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