KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <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
349
350 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
351
352 for( const auto& [rule, ruleItems] : itemSets )
353 {
354 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
355
356 for( BOARD_CONNECTED_ITEM* item : ruleItems )
357 netMap[item->GetNetCode()].insert( item );
358
359 for( const auto& [netCode, netItems] : netMap )
360 {
361 std::vector<LENGTH_CALCULATION_ITEM> lengthItems;
362 lengthItems.reserve( netItems.size() );
363
364 CONNECTION ent;
365 ent.items = netItems;
366 ent.netcode = netCode;
369
370 ent.viaCount = 0;
371 ent.totalRoute = 0;
372 ent.totalVia = 0;
373 ent.totalPadToDie = 0;
374 ent.fromItem = nullptr;
375 ent.toItem = nullptr;
376
377 for( BOARD_CONNECTED_ITEM* item : netItems )
378 {
379 LENGTH_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( item );
380
381 if( lengthItem.Type() != LENGTH_CALCULATION_ITEM::TYPE::UNKNOWN )
382 lengthItems.emplace_back( lengthItem );
383 }
384
385 constexpr PATH_OPTIMISATIONS opts = {
386 .OptimiseViaLayers = true, .MergeTracks = true, .OptimiseTracesInPads = true, .InferViaInPad = false
387 };
388 LENGTH_DETAILS details = calc->CalculateLengthDetails( lengthItems, opts );
389 ent.viaCount = details.NumVias;
390 ent.totalVia = details.ViaLength;
391 ent.totalRoute = static_cast<double>( details.TrackLength );
392 ent.totalPadToDie = details.PadToDieLength;
393 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
394 ent.matchingRule = rule;
395
396 // fixme: doesn't seem to work ;-)
397 auto ftPath = ftCache->QueryFromToPath( ent.items );
398
399 if( ftPath )
400 {
401 ent.from = ftPath->fromName;
402 ent.to = ftPath->toName;
403 }
404 else
405 {
406 ent.from = ent.to = _( "<unconstrained>" );
407 }
408
409 m_report.Add( ent );
410 matches[rule].push_back( ent );
411 }
412 }
413
414 if( !aDelayReportMode )
415 {
416 if( !reportPhase( _( "Checking length constraints..." ) ) )
417 return false;
418
419 ii = 0;
420 count = matches.size();
421
422 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
423 {
424 DRC_RULE *rule = it.first;
425 auto& matchedConnections = it.second;
426
427 if( !reportProgress( ii++, count, progressDelta ) )
428 return false;
429
430 std::sort( matchedConnections.begin(), matchedConnections.end(),
431 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
432 {
433 return a.netname < b.netname;
434 } );
435
436 reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
437 it.first->m_Name ) );
438
439 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
440 {
441 reportAux( wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
442 "%d matching items, "
443 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
444 "vias: %d" ),
445 ent.netname, ent.from, ent.to, static_cast<int>( ent.items.size() ),
446 MessageTextFromValue( ent.total ), MessageTextFromValue( ent.totalRoute ),
447 MessageTextFromValue( ent.totalVia ),
448 MessageTextFromValue( ent.totalPadToDie ), ent.viaCount ) );
449 }
450
451
452 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
453
454 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
455 checkLengths( *lengthConstraint, matchedConnections );
456
457 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
458
459 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
460 checkSkews( *skewConstraint, matchedConnections );
461
462 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
463
464 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
465 checkViaCounts( *viaCountConstraint, matchedConnections );
466 }
467
469 }
470
471 return !m_drcEngine->IsCancelled();
472}
473
474
475namespace detail
476{
478}
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:78
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:297
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:897
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition: board.cpp:2082
LENGTH_CALCULATION * GetLengthCalculation() const
Returns the track length calculator.
Definition: board.h:1300
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:495
wxString GetName() const
Definition: drc_rule.h:160
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
bool GetOption(OPTIONS option) const
Definition: drc_rule.h:183
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:156
BOARD * GetBoard() const
Definition: drc_engine.h:96
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:693
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 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)
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)
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)
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.
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:572
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:68
@ VIA_COUNT_CONSTRAINT
Definition: drc_rule.h:73
@ SKEW_CONSTRAINT
Definition: drc_rule.h:69
#define _(s)
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