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 double avgLength = 0;
144
145 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
146 avgLength += ent.total;
147
148 avgLength /= (double) aMatchedConnections.size();
149
150 for( const auto& ent : aMatchedConnections )
151 {
152 int skew = KiROUND( ent.total - avgLength );
153 bool fail_min = false;
154 bool fail_max = false;
155
156 if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
157 fail_max = true;
158 else if( aConstraint.GetValue().HasMin() && abs( skew ) < aConstraint.GetValue().Min() )
159 fail_min = true;
160
161 if( fail_min || fail_max )
162 {
163 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
164 wxString msg;
165
166 if( fail_min )
167 {
168 msg.Printf( _( "(%s min skew %s; actual %s; average net length %s; actual %s)" ),
169 aConstraint.GetName(),
170 MessageTextFromValue( aConstraint.GetValue().Min() ),
171 MessageTextFromValue( skew ),
172 MessageTextFromValue( avgLength ),
173 MessageTextFromValue( ent.total ) );
174 }
175 else
176 {
177 msg.Printf( _( "(%s max skew %s; actual %s; average net length %s; actual %s)" ),
178 aConstraint.GetName(),
179 MessageTextFromValue( aConstraint.GetValue().Max() ),
180 MessageTextFromValue( skew ),
181 MessageTextFromValue( avgLength ),
182 MessageTextFromValue( ent.total ) );
183 }
184
185 drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
186
187 for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
188 drcItem->SetItems( offendingTrack );
189
190 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
191
192 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
193 ( *ent.items.begin() )->GetLayer() );
194 }
195 }
196}
197
198
200 const std::vector<CONNECTION>& aMatchedConnections )
201{
202 for( const auto& ent : aMatchedConnections )
203 {
204 std::shared_ptr<DRC_ITEM> drcItem = nullptr;
205
206 if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
207 {
209 wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
210 aConstraint.GetName(),
211 aConstraint.GetValue().Max(),
212 ent.viaCount );
213
214 drcItem->SetErrorMessage( _( "Too many vias on a connection" ) + wxS( " " ) + msg );
215 }
216 else if( aConstraint.GetValue().HasMin() && ent.viaCount < aConstraint.GetValue().Min() )
217 {
219 wxString msg = wxString::Format( _( "(%s min count %d; actual %d)" ),
220 aConstraint.GetName(),
221 aConstraint.GetValue().Min(),
222 ent.viaCount );
223
224 drcItem->SetErrorMessage( _( "Too few vias on a connection" ) + wxS( " " ) + msg );
225 }
226
227 if( drcItem )
228 {
229 for( auto offendingTrack : ent.items )
230 drcItem->SetItems( offendingTrack );
231
232 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
233
234 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
235 ( *ent.items.begin() )->GetLayer() );
236 }
237 }
238}
239
240
242{
243 return runInternal( false );
244}
245
246
248{
250 m_report.Clear();
251
252 if( !aDelayReportMode )
253 {
254 if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
255 return false;
256 }
257
258 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
259
260 std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
261
262 ftCache->Rebuild( m_board );
263
264 const size_t progressDelta = 100;
265 size_t count = 0;
266 size_t ii = 0;
267
269 [&]( BOARD_ITEM *item ) -> bool
270 {
271 count++;
272 return true;
273 } );
274
276 [&]( BOARD_ITEM *item ) -> bool
277 {
278 if( !reportProgress( ii++, count, progressDelta ) )
279 return false;
280
281 const DRC_CONSTRAINT_T constraintsToCheck[] = {
285 };
286
287 for( int i = 0; i < 3; i++ )
288 {
289 auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
290 item->GetLayer() );
291
292 if( constraint.IsNull() )
293 continue;
294
295 auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
296
297 itemSets[ constraint.GetParentRule() ].insert( citem );
298 }
299
300 return true;
301 } );
302
303 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
304
305 for( const std::pair< DRC_RULE* const, std::set<BOARD_CONNECTED_ITEM*> >& it : itemSets )
306 {
307 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
308
309 for( BOARD_CONNECTED_ITEM* citem : it.second )
310 netMap[ citem->GetNetCode() ].insert( citem );
311
312 for( const std::pair< const int, std::set<BOARD_CONNECTED_ITEM*> >& nitem : netMap )
313 {
314 CONNECTION ent;
315 ent.items = nitem.second;
316 ent.netcode = nitem.first;
318
319 ent.viaCount = 0;
320 ent.totalRoute = 0;
321 ent.totalVia = 0;
322 ent.totalPadToDie = 0;
323 ent.fromItem = nullptr;
324 ent.toItem = nullptr;
325
326 for( BOARD_CONNECTED_ITEM* citem : nitem.second )
327 {
328 if( citem->Type() == PCB_VIA_T )
329 {
331 const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
332
333 ent.viaCount++;
334
336 {
337 const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
338 PCB_LAYER_ID topmost;
339 PCB_LAYER_ID bottommost;
340
341 v->GetOutermostConnectedLayers( &topmost, &bottommost );
342
343 if( topmost != UNDEFINED_LAYER && topmost != bottommost )
344 ent.totalVia += stackup.GetLayerDistance( topmost, bottommost );
345 }
346 }
347 else if( citem->Type() == PCB_TRACE_T )
348 {
349 ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
350 }
351 else if ( citem->Type() == PCB_ARC_T )
352 {
353 ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
354 }
355 else if( citem->Type() == PCB_PAD_T )
356 {
357 ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
358 }
359 }
360
361 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
362 ent.matchingRule = it.first;
363
364 // fixme: doesn't seem to work ;-)
365 auto ftPath = ftCache->QueryFromToPath( ent.items );
366
367 if( ftPath )
368 {
369 ent.from = ftPath->fromName;
370 ent.to = ftPath->toName;
371 }
372 else
373 {
374 ent.from = ent.to = _( "<unconstrained>" );
375 }
376
377 m_report.Add( ent );
378 matches[ it.first ].push_back(ent);
379 }
380 }
381
382 if( !aDelayReportMode )
383 {
384 if( !reportPhase( _( "Checking length constraints..." ) ) )
385 return false;
386
387 ii = 0;
388 count = matches.size();
389
390 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
391 {
392 DRC_RULE *rule = it.first;
393 auto& matchedConnections = it.second;
394
395 if( !reportProgress( ii++, count, progressDelta ) )
396 return false;
397
398 std::sort( matchedConnections.begin(), matchedConnections.end(),
399 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
400 {
401 return a.netname < b.netname;
402 } );
403
404 reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
405 it.first->m_Name ) );
406
407 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
408 {
409 reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
410 "%d matching items, "
411 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
412 "vias: %d" ),
413 ent.netname,
414 ent.from,
415 ent.to,
416 (int) ent.items.size(),
417 MessageTextFromValue( ent.total ),
418 MessageTextFromValue( ent.totalRoute ),
419 MessageTextFromValue( ent.totalVia ),
420 MessageTextFromValue( ent.totalPadToDie ),
421 ent.viaCount ) );
422 }
423
424
425 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
426
427 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
428 checkLengths( *lengthConstraint, matchedConnections );
429
430 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
431
432 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
433 checkSkews( *skewConstraint, matchedConnections );
434
435 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
436
437 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
438 checkViaCounts( *viaCountConstraint, matchedConnections );
439 }
440
442 }
443
444 return !m_drcEngine->IsCancelled();
445}
446
447
448namespace detail
449{
451}
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:77
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:282
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:853
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:794
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:460
wxString GetName() const
Definition: drc_rule.h:149
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
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:652
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:338
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:100
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
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
const wxString & GetNetname() const
Definition: netinfo.h:114
NETINFO_ITEM * GetNetItem(int aNetCode) const
Definition: pad.h:53
void GetOutermostConnectedLayers(PCB_LAYER_ID *aTopmost, PCB_LAYER_ID *aBottommost) const
Return the top-most and bottom-most connected layers.
Definition: pcb_track.cpp:1001
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:98
@ DRCE_LENGTH_OUT_OF_RANGE
Definition: drc_item.h:97
@ DRCE_VIA_COUNT_OUT_OF_RANGE
Definition: drc_item.h:99
DRC_CONSTRAINT_T
Definition: drc_rule.h:45
@ LENGTH_CONSTRAINT
Definition: drc_rule.h:64
@ VIA_COUNT_CONSTRAINT
Definition: drc_rule.h:69
@ SKEW_CONSTRAINT
Definition: drc_rule.h:65
#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
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118