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>
22 #include <board_design_settings.h>
23 #include <pad.h>
24 #include <pcb_track.h>
25 
26 #include <drc/drc_engine.h>
27 #include <drc/drc_item.h>
28 #include <drc/drc_rule.h>
29 #include <drc/drc_test_provider.h>
30 #include <drc/drc_length_report.h>
31 
34 
35 #include <pcb_expr_evaluator.h>
36 
37 /*
38  Single-ended matched length + skew + via count test.
39  Errors generated:
40  - DRCE_LENGTH_OUT_OF_RANGE
41  - DRCE_SKEW_OUT_OF_RANGE
42  - DRCE_TOO_MANY_VIAS
43  Todo: arc support
44 */
45 
47 {
48 public:
50  m_board( nullptr )
51  {
52  }
53 
55  {
56  }
57 
58  virtual bool Run() override;
59 
60  virtual const wxString GetName() const override
61  {
62  return wxT( "length" );
63  };
64 
65  virtual const wxString GetDescription() const override
66  {
67  return wxT( "Tests matched track lengths." );
68  }
69 
70  virtual int GetNumPhases() const override
71  {
72  return 1;
73  }
74 
75  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
76 
78 
79 private:
80 
81  bool runInternal( bool aDelayReportMode = false );
82 
84 
85  void checkLengths( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
86  void checkSkews( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
87  void checkViaCounts( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
88 
91 };
92 
94  std::vector<CONNECTION>& aMatchedConnections )
95 {
96  for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
97  {
98  bool minViolation = false;
99  bool maxViolation = false;
100  int minLen = 0;
101  int maxLen = 0;
102 
103  if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
104  {
105  minViolation = true;
106  minLen = aConstraint.GetValue().Min();
107  }
108  else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
109  {
110  maxViolation = true;
111  maxLen = aConstraint.GetValue().Max();
112  }
113 
114  if( ( minViolation || maxViolation ) )
115  {
116  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
117  wxString msg;
118 
119  if( minViolation )
120  {
121  msg.Printf( _( "(%s min length: %s; actual: %s)" ),
122  aConstraint.GetName(),
123  MessageTextFromValue( userUnits(), minLen ),
124  MessageTextFromValue( userUnits(), ent.total ) );
125  }
126  else if( maxViolation )
127  {
128  msg.Printf( _( "(%s max length: %s; actual: %s)" ),
129  aConstraint.GetName(),
130  MessageTextFromValue( userUnits(), maxLen ),
131  MessageTextFromValue( userUnits(), ent.total ) );
132  }
133 
134  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
135 
136  for( auto offendingTrack : ent.items )
137  drcItem->AddItem( offendingTrack );
138 
139  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
140 
141  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
142  }
143  }
144 }
145 
147  std::vector<CONNECTION>& aMatchedConnections )
148 {
149  int avgLength = 0;
150 
151  for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
152  avgLength += ent.total;
153 
154  avgLength /= aMatchedConnections.size();
155 
156  for( const auto& ent : aMatchedConnections )
157  {
158  int skew = ent.total - avgLength;
159  if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
160  {
161  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
162  wxString msg;
163 
164  msg.Printf( _( "(%s max skew: %s; actual: %s; average net length: %s; actual: %s)" ),
165  aConstraint.GetName(),
166  MessageTextFromValue( userUnits(), aConstraint.GetValue().Max() ),
167  MessageTextFromValue( userUnits(), skew ),
168  MessageTextFromValue( userUnits(), avgLength ),
169  MessageTextFromValue( userUnits(), ent.total ) );
170 
171  drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
172 
173  for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
174  drcItem->SetItems( offendingTrack );
175 
176  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
177 
178  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
179  }
180  }
181 }
182 
183 
185  std::vector<CONNECTION>& aMatchedConnections )
186 {
187  for( const auto& ent : aMatchedConnections )
188  {
189  if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
190  {
191  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
192  wxString msg;
193 
194  msg.Printf( _( "(%s max count: %d; actual: %d)" ),
195  aConstraint.GetName(),
196  aConstraint.GetValue().Max(),
197  ent.viaCount );
198 
199  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
200 
201  for( auto offendingTrack : ent.items )
202  drcItem->SetItems( offendingTrack );
203 
204  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
205 
206  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
207  }
208  }
209 }
210 
211 
213 {
214  return runInternal( false );
215 }
216 
217 
219 {
221  m_report.Clear();
222 
223  if( !aDelayReportMode )
224  {
225  if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
226  return false;
227  }
228 
229  std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
230 
231  auto evaluateLengthConstraints =
232  [&]( BOARD_ITEM *item ) -> bool
233  {
234  const DRC_CONSTRAINT_T constraintsToCheck[] = {
238  };
239 
240  for( int i = 0; i < 3; i++ )
241  {
242  auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
243  item->GetLayer() );
244 
245  if( constraint.IsNull() )
246  continue;
247 
248  auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
249 
250  itemSets[ constraint.GetParentRule() ].insert( citem );
251  }
252 
253  return true;
254  };
255 
256  auto ftCache = m_board->GetConnectivity()->GetFromToCache();
257 
258  ftCache->Rebuild( m_board );
259 
261  evaluateLengthConstraints );
262 
263  std::map<DRC_RULE*, std::vector<CONNECTION> > matches;
264 
265  for( auto it : itemSets )
266  {
267  std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
268 
269  for( auto citem : it.second )
270  netMap[ citem->GetNetCode() ].insert( citem );
271 
272 
273  for( auto nitem : netMap )
274  {
275  CONNECTION ent;
276  ent.items = nitem.second;
277  ent.netcode = nitem.first;
279 
280  ent.viaCount = 0;
281  ent.totalRoute = 0;
282  ent.totalVia = 0;
283  ent.totalPadToDie = 0;
284  ent.fromItem = nullptr;
285  ent.toItem = nullptr;
286 
287  for( BOARD_CONNECTED_ITEM* citem : nitem.second )
288  {
289  if( citem->Type() == PCB_VIA_T )
290  {
292 
293  ent.viaCount++;
294 
296  {
297  const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
298 
300  v->TopLayer(), v->BottomLayer() );
301  }
302  }
303  else if( citem->Type() == PCB_TRACE_T )
304  {
305  ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
306  }
307  else if ( citem->Type() == PCB_ARC_T )
308  {
309  ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
310  }
311  else if( citem->Type() == PCB_PAD_T )
312  {
313  ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
314  }
315  }
316 
317  ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
318  ent.matchingRule = it.first;
319 
320  // fixme: doesn't seem to work ;-)
321  auto ftPath = ftCache->QueryFromToPath( ent.items );
322 
323  if( ftPath )
324  {
325  ent.from = ftPath->fromName;
326  ent.to = ftPath->toName;
327  }
328  else
329  {
330  ent.from = ent.to = _( "<unconstrained>" );
331  }
332 
333  m_report.Add( ent );
334  matches[ it.first ].push_back(ent);
335  }
336  }
337 
338  if( !aDelayReportMode )
339  {
340  for( auto it : matches )
341  {
342  DRC_RULE *rule = it.first;
343  auto& matchedConnections = it.second;
344 
345  std::sort( matchedConnections.begin(), matchedConnections.end(),
346  [] ( const CONNECTION&a, const CONNECTION&b ) -> int
347  {
348  return a.netname < b.netname;
349  } );
350 
351  reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
352  it.first->m_Name ) );
353 
354  for( auto& ent : matchedConnections )
355  {
356  reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
357  "%d matching items, "
358  "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
359  "vias: %d" ),
360  ent.netname,
361  ent.from,
362  ent.to,
363  (int) ent.items.size(),
364  MessageTextFromValue( userUnits(), ent.total ),
365  MessageTextFromValue( userUnits(), ent.totalRoute ),
366  MessageTextFromValue( userUnits(), ent.totalVia ),
367  MessageTextFromValue( userUnits(), ent.totalPadToDie ),
368  ent.viaCount ) );
369  }
370 
371 
372  OPT<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
373 
374  if( lengthConstraint )
375  checkLengths( *lengthConstraint, matchedConnections );
376 
377  OPT<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
378 
379  if( skewConstraint )
380  checkSkews( *skewConstraint, matchedConnections );
381 
382  OPT<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
383 
384  if( viaCountConstraint )
385  checkViaCounts( *viaCountConstraint, matchedConnections );
386  }
387 
389  }
390 
391  return true;
392 }
393 
394 
396 {
398 }
399 
400 
401 namespace detail
402 {
404 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
OPT< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition: drc_rule.cpp:53
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:266
bool HasMin() const
Definition: minoptmax.h:37
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const wxPoint &aMarkerPos)
void checkSkews(DRC_CONSTRAINT &aConstraint, std::vector< CONNECTION > &aMatchedConnections)
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:680
class PAD, a pad in a footprint
Definition: typeinfo.h:89
T Min() const
Definition: minoptmax.h:33
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
bool runInternal(bool aDelayReportMode=false)
PCB_LAYER_ID BottomLayer() const
Definition: pcb_track.cpp:462
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
virtual void reportRuleStatistics()
T Max() const
Definition: minoptmax.h:34
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
wxString GetName() const
Definition: drc_rule.h:130
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
void checkLengths(DRC_CONSTRAINT &aConstraint, std::vector< CONNECTION > &aMatchedConnections)
DRC_CONSTRAINT_T
Definition: drc_rule.h:41
void Add(const ENTRY &ent)
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:126
BOARD_STACKUP & GetStackupDescriptor()
PCB_LAYER_ID TopLayer() const
Definition: pcb_track.cpp:456
BOARD * GetBoard() const
Definition: drc_engine.h:88
BOARD_CONNECTED_ITEM * fromItem
virtual const wxString GetName() const override
virtual bool reportPhase(const wxString &aStageName)
DRC_LENGTH_REPORT BuildLengthReport() const
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
const wxString & GetNetname() const
Definition: netinfo.h:126
#define _(s)
void checkViaCounts(DRC_CONSTRAINT &aConstraint, std::vector< CONNECTION > &aMatchedConnections)
EDA_UNITS userUnits() const
virtual const wxString GetDescription() const override
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:759
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
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out #DRC_ITEMs and po...
bool m_UseHeightForLengthCalcs
Enable inclusion of stackup height in track length measurements and length tuning.
bool HasMax() const
Definition: minoptmax.h:38
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:122
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
BOARD_CONNECTED_ITEM * toItem
std::set< BOARD_CONNECTED_ITEM * > items
DRC_ENGINE * m_drcEngine
The common library.
boost::optional< T > OPT
Definition: optional.h:7
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
NETINFO_ITEM * GetNetItem(int aNetCode) const
virtual void reportAux(wxString fmt,...)
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
Container for design settings for a BOARD object.