KiCad PCB EDA Suite
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-2022 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#include <pcb_expr_evaluator.h>
35
36/*
37 Single-ended matched length + skew + via count test.
38 Errors generated:
39 - DRCE_LENGTH_OUT_OF_RANGE
40 - DRCE_SKEW_OUT_OF_RANGE
41 - DRCE_TOO_MANY_VIAS
42 Todo: arc support
43*/
44
46{
47public:
49 m_board( nullptr )
50 {
51 }
52
54 {
55 }
56
57 virtual bool Run() override;
58
59 virtual const wxString GetName() const override
60 {
61 return wxT( "length" );
62 };
63
64 virtual const wxString GetDescription() const override
65 {
66 return wxT( "Tests matched track lengths." );
67 }
68
70
71private:
72
73 bool runInternal( bool aDelayReportMode = false );
74
76
77 void checkLengths( const DRC_CONSTRAINT& aConstraint,
78 const std::vector<CONNECTION>& aMatchedConnections );
79 void checkSkews( const DRC_CONSTRAINT& aConstraint,
80 const std::vector<CONNECTION>& aMatchedConnections );
81 void checkViaCounts( const DRC_CONSTRAINT& aConstraint,
82 const std::vector<CONNECTION>& aMatchedConnections );
83
86};
87
89 const std::vector<CONNECTION>& aMatchedConnections )
90{
91 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
92 {
93 bool minViolation = false;
94 bool maxViolation = false;
95 int minLen = 0;
96 int maxLen = 0;
97
98 if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
99 {
100 minViolation = true;
101 minLen = aConstraint.GetValue().Min();
102 }
103 else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
104 {
105 maxViolation = true;
106 maxLen = aConstraint.GetValue().Max();
107 }
108
109 if( ( minViolation || maxViolation ) )
110 {
111 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
112 wxString msg;
113
114 if( minViolation )
115 {
116 msg = formatMsg( _( "(%s min length %s; actual %s)" ),
117 aConstraint.GetName(),
118 minLen,
119 ent.total );
120 }
121 else if( maxViolation )
122 {
123 msg = formatMsg( _( "(%s max length %s; actual %s)" ),
124 aConstraint.GetName(),
125 maxLen,
126 ent.total );
127 }
128
129 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
130
131 for( auto offendingTrack : ent.items )
132 drcItem->AddItem( offendingTrack );
133
134 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
135
136 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
137 ( *ent.items.begin() )->GetLayer() );
138 }
139 }
140}
141
143 const std::vector<CONNECTION>& aMatchedConnections )
144{
145 int avgLength = 0;
146
147 for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
148 avgLength += ent.total;
149
150 avgLength /= aMatchedConnections.size();
151
152 for( const auto& ent : aMatchedConnections )
153 {
154 int skew = ent.total - avgLength;
155 if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
156 {
157 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
158 wxString msg;
159
160 msg.Printf( _( "(%s max skew %s; actual %s; average net length %s; actual %s)" ),
161 aConstraint.GetName(),
162 MessageTextFromValue( aConstraint.GetValue().Max() ),
163 MessageTextFromValue( skew ),
164 MessageTextFromValue( avgLength ),
165 MessageTextFromValue( ent.total ) );
166
167 drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
168
169 for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
170 drcItem->SetItems( offendingTrack );
171
172 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
173
174 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
175 ( *ent.items.begin() )->GetLayer() );
176 }
177 }
178}
179
180
182 const std::vector<CONNECTION>& aMatchedConnections )
183{
184 for( const auto& ent : aMatchedConnections )
185 {
186 if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
187 {
188 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
189 wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
190 aConstraint.GetName(),
191 aConstraint.GetValue().Max(),
192 ent.viaCount );
193
194 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
195
196 for( auto offendingTrack : ent.items )
197 drcItem->SetItems( offendingTrack );
198
199 drcItem->SetViolatingRule( aConstraint.GetParentRule() );
200
201 reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
202 ( *ent.items.begin() )->GetLayer() );
203 }
204 }
205}
206
207
209{
210 return runInternal( false );
211}
212
213
215{
217 m_report.Clear();
218
219 if( !aDelayReportMode )
220 {
221 if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
222 return false;
223 }
224
225 std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
226
227 std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
228
229 ftCache->Rebuild( m_board );
230
231 const size_t progressDelta = 100;
232 size_t count = 0;
233 size_t ii = 0;
234
236 [&]( BOARD_ITEM *item ) -> bool
237 {
238 count++;
239 return true;
240 } );
241
243 [&]( BOARD_ITEM *item ) -> bool
244 {
245 if( !reportProgress( ii++, count, progressDelta ) )
246 return false;
247
248 const DRC_CONSTRAINT_T constraintsToCheck[] = {
252 };
253
254 for( int i = 0; i < 3; i++ )
255 {
256 auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
257 item->GetLayer() );
258
259 if( constraint.IsNull() )
260 continue;
261
262 auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
263
264 itemSets[ constraint.GetParentRule() ].insert( citem );
265 }
266
267 return true;
268 } );
269
270 std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
271
272 for( const std::pair< DRC_RULE* const, std::set<BOARD_CONNECTED_ITEM*> >& it : itemSets )
273 {
274 std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
275
276 for( BOARD_CONNECTED_ITEM* citem : it.second )
277 netMap[ citem->GetNetCode() ].insert( citem );
278
279 for( const std::pair< const int, std::set<BOARD_CONNECTED_ITEM*> >& nitem : netMap )
280 {
281 CONNECTION ent;
282 ent.items = nitem.second;
283 ent.netcode = nitem.first;
285
286 ent.viaCount = 0;
287 ent.totalRoute = 0;
288 ent.totalVia = 0;
289 ent.totalPadToDie = 0;
290 ent.fromItem = nullptr;
291 ent.toItem = nullptr;
292
293 for( BOARD_CONNECTED_ITEM* citem : nitem.second )
294 {
295 if( citem->Type() == PCB_VIA_T )
296 {
298 const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
299
300 ent.viaCount++;
301
303 {
304 const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
305
306 ent.totalVia += stackup.GetLayerDistance( v->TopLayer(), v->BottomLayer() );
307 }
308 }
309 else if( citem->Type() == PCB_TRACE_T )
310 {
311 ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
312 }
313 else if ( citem->Type() == PCB_ARC_T )
314 {
315 ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
316 }
317 else if( citem->Type() == PCB_PAD_T )
318 {
319 ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
320 }
321 }
322
323 ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
324 ent.matchingRule = it.first;
325
326 // fixme: doesn't seem to work ;-)
327 auto ftPath = ftCache->QueryFromToPath( ent.items );
328
329 if( ftPath )
330 {
331 ent.from = ftPath->fromName;
332 ent.to = ftPath->toName;
333 }
334 else
335 {
336 ent.from = ent.to = _( "<unconstrained>" );
337 }
338
339 m_report.Add( ent );
340 matches[ it.first ].push_back(ent);
341 }
342 }
343
344 if( !aDelayReportMode )
345 {
346 if( !reportPhase( _( "Checking length constraints..." ) ) )
347 return false;
348
349 ii = 0;
350 count = matches.size();
351
352 for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
353 {
354 DRC_RULE *rule = it.first;
355 auto& matchedConnections = it.second;
356
357 if( !reportProgress( ii++, count, progressDelta ) )
358 return false;
359
360 std::sort( matchedConnections.begin(), matchedConnections.end(),
361 [] ( const CONNECTION&a, const CONNECTION&b ) -> int
362 {
363 return a.netname < b.netname;
364 } );
365
366 reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
367 it.first->m_Name ) );
368
369 for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
370 {
371 reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
372 "%d matching items, "
373 "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
374 "vias: %d" ),
375 ent.netname,
376 ent.from,
377 ent.to,
378 (int) ent.items.size(),
379 MessageTextFromValue( ent.total ),
380 MessageTextFromValue( ent.totalRoute ),
381 MessageTextFromValue( ent.totalVia ),
382 MessageTextFromValue( ent.totalPadToDie ),
383 ent.viaCount ) );
384 }
385
386
387 std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
388
389 if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
390 checkLengths( *lengthConstraint, matchedConnections );
391
392 std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
393
394 if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
395 checkSkews( *skewConstraint, matchedConnections );
396
397 std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
398
399 if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
400 checkViaCounts( *viaCountConstraint, matchedConnections );
401 }
402
404 }
405
406 return !m_drcEngine->IsCancelled();
407}
408
409
410namespace detail
411{
413}
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:58
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:265
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:765
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:643
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:424
wxString GetName() const
Definition: drc_rule.h:147
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:143
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:671
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:325
void Add(const ENTRY &ent)
std::optional< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition: drc_rule.cpp:65
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).
DRC_LENGTH_REPORT BuildLengthReport() const
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_ITEMs and po...
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
virtual void reportRuleStatistics()
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
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:119
NETINFO_ITEM * GetNetItem(int aNetCode) const
Definition: pad.h:59
PCB_LAYER_ID BottomLayer() const
Definition: pcb_track.cpp:570
PCB_LAYER_ID TopLayer() const
Definition: pcb_track.cpp:564
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
The common library.
@ DRCE_SKEW_OUT_OF_RANGE
Definition: drc_item.h:96
@ DRCE_TOO_MANY_VIAS
Definition: drc_item.h:97
@ DRCE_LENGTH_OUT_OF_RANGE
Definition: drc_item.h:95
DRC_CONSTRAINT_T
Definition: drc_rule.h:44
@ LENGTH_CONSTRAINT
Definition: drc_rule.h:63
@ VIA_COUNT_CONSTRAINT
Definition: drc_rule.h:68
@ SKEW_CONSTRAINT
Definition: drc_rule.h:64
#define _(s)
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ 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:102
@ 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:103
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101