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-2020 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 <pad.h>
23 #include <track.h>
24 
25 #include <drc/drc_item.h>
26 #include <drc/drc_rule.h>
27 #include <drc/drc_test_provider.h>
28 #include <drc/drc_length_report.h>
29 
32 
33 #include <pcb_expr_evaluator.h>
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 {
46 public:
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 "length";
61  };
62 
63  virtual const wxString GetDescription() const override
64  {
65  return "Tests matched track lengths.";
66  }
67 
68  virtual int GetNumPhases() const override
69  {
70  return 1;
71  }
72 
73  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
74 
76 
77 private:
78 
79  bool runInternal( bool aDelayReportMode = false );
80 
82  typedef std::set<BOARD_CONNECTED_ITEM*> CITEMS;
83  typedef std::vector<LENGTH_ENTRY> LENGTH_ENTRIES;
84 
85  void checkLengthViolations( DRC_CONSTRAINT& aConstraint, LENGTH_ENTRIES& aMatchedConnections );
86  void checkSkewViolations( DRC_CONSTRAINT& aConstraint, LENGTH_ENTRIES& aMatchedConnections );
87  void checkViaCountViolations( DRC_CONSTRAINT& aConstraint, LENGTH_ENTRIES& aMatchedConnections );
88 
91 };
92 
93 
94 static int computeViaThruLength( VIA *aVia, const std::set<BOARD_CONNECTED_ITEM*> &conns )
95 {
96  return 0; // fixme: not yet there...
97 }
98 
99 
101  LENGTH_ENTRIES& matchedConnections )
102 {
103  for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
104  {
105  bool minViolation = false;
106  bool maxViolation = false;
107  int minLen = 0;
108  int maxLen = 0;
109 
110  if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
111  {
112  minViolation = true;
113  minLen = aConstraint.GetValue().Min();
114  }
115  else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
116  {
117  maxViolation = true;
118  maxLen = aConstraint.GetValue().Max();
119  }
120 
121  if( ( minViolation || maxViolation ) )
122  {
123  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
124 
125  if( minViolation )
126  {
127  m_msg.Printf( _( "(%s min length: %s; actual: %s)" ),
128  aConstraint.GetName(),
129  MessageTextFromValue( userUnits(), minLen ),
130  MessageTextFromValue( userUnits(), ent.total ) );
131  }
132  else if( maxViolation )
133  {
134  m_msg.Printf( _( "(%s max length: %s; actual: %s)" ),
135  aConstraint.GetName(),
136  MessageTextFromValue( userUnits(), maxLen ),
137  MessageTextFromValue( userUnits(), ent.total ) );
138  }
139 
140  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
141 
142  for( auto offendingTrack : ent.items )
143  drcItem->AddItem( offendingTrack );
144 
145  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
146 
147  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
148  }
149  }
150 }
151 
153  LENGTH_ENTRIES& matchedConnections )
154 {
155  int avgLength = 0;
156 
157  for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
158  avgLength += ent.total;
159 
160  avgLength /= matchedConnections.size();
161 
162  for( const auto& ent : matchedConnections )
163  {
164  int skew = ent.total - avgLength;
165  if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
166  {
167  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
168 
169  m_msg.Printf( _( "(%s max skew: %s; actual: %s; average net length: %s; actual: %s)" ),
170  aConstraint.GetName(),
171  MessageTextFromValue( userUnits(), aConstraint.GetValue().Max() ),
172  MessageTextFromValue( userUnits(), skew ),
173  MessageTextFromValue( userUnits(), avgLength ),
174  MessageTextFromValue( userUnits(), ent.total ) );
175 
176  drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + m_msg );
177 
178  for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
179  drcItem->SetItems( offendingTrack );
180 
181  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
182 
183  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
184  }
185  }
186 }
187 
188 
190  LENGTH_ENTRIES& matchedConnections )
191 {
192  for( const auto& ent : matchedConnections )
193  {
194  if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
195  {
196  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
197 
198  m_msg.Printf( _( "(%s max count: %d; actual: %d)" ),
199  aConstraint.GetName(),
200  aConstraint.GetValue().Max(),
201  ent.viaCount );
202 
203  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
204 
205  for( auto offendingTrack : ent.items )
206  drcItem->SetItems( offendingTrack );
207 
208  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
209 
210  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
211  }
212  }
213 }
214 
215 
217 {
218  return runInternal( false );
219 }
220 
221 
223 {
225  m_report.Clear();
226 
227  if( !aDelayReportMode )
228  {
229  if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
230  return false;
231  }
232 
233  std::map<DRC_RULE*, CITEMS> itemSets;
234 
235  auto evaluateLengthConstraints =
236  [&]( BOARD_ITEM *item ) -> bool
237  {
238  const DRC_CONSTRAINT_T constraintsToCheck[] = {
242  };
243 
244  for( int i = 0; i < 3; i++ )
245  {
246  auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
247  item->GetLayer() );
248 
249  if( constraint.IsNull() )
250  continue;
251 
252  auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
253 
254  itemSets[ constraint.GetParentRule() ].insert( citem );
255  }
256 
257  return true;
258  };
259 
260  auto ftCache = m_board->GetConnectivity()->GetFromToCache();
261 
262  ftCache->Rebuild( m_board );
263 
265  evaluateLengthConstraints );
266 
267  std::map<DRC_RULE*, LENGTH_ENTRIES> matches;
268 
269  for( auto it : itemSets )
270  {
271  std::map<int, CITEMS> netMap;
272 
273  for( auto citem : it.second )
274  netMap[ citem->GetNetCode() ].insert( citem );
275 
276 
277  for( auto nitem : netMap )
278  {
279  LENGTH_ENTRY ent;
280  ent.items = nitem.second;
281  ent.netcode = nitem.first;
283 
284  ent.viaCount = 0;
285  ent.totalRoute = 0;
286  ent.totalVia = 0;
287  ent.totalPadToDie = 0;
288  ent.fromItem = nullptr;
289  ent.toItem = nullptr;
290 
291  for( BOARD_CONNECTED_ITEM* citem : nitem.second )
292  {
293  if( citem->Type() == PCB_VIA_T )
294  {
295  ent.viaCount++;
296  ent.totalVia += computeViaThruLength( static_cast<VIA*>( citem ), nitem.second );
297  }
298  else if( citem->Type() == PCB_TRACE_T )
299  {
300  ent.totalRoute += static_cast<TRACK*>( citem )->GetLength();
301  }
302  else if ( citem->Type() == PCB_ARC_T )
303  {
304  ent.totalRoute += static_cast<ARC*>( citem )->GetLength();
305  }
306  else if( citem->Type() == PCB_PAD_T )
307  {
308  ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
309  }
310  }
311 
312  ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
313  ent.matchingRule = it.first;
314 
315  // fixme: doesn't seem to work ;-)
316  auto ftPath = ftCache->QueryFromToPath( ent.items );
317 
318  if( ftPath )
319  {
320  ent.from = ftPath->fromName;
321  ent.to = ftPath->toName;
322  }
323  else
324  {
325  ent.from = ent.to = _("<unconstrained>");
326  }
327 
328  m_report.Add( ent );
329  matches[ it.first ].push_back(ent);
330  }
331  }
332 
333  if( !aDelayReportMode )
334  {
335  for( auto it : matches )
336  {
337  DRC_RULE *rule = it.first;
338  auto& matchedConnections = it.second;
339 
340  std::sort( matchedConnections.begin(), matchedConnections.end(),
341  [] ( const LENGTH_ENTRY&a, const LENGTH_ENTRY&b ) -> int
342  {
343  return a.netname < b.netname;
344  } );
345 
346  reportAux( wxString::Format( "Length-constrained traces for rule '%s':",
347  it.first->m_Name ) );
348 
349  for( auto& ent : matchedConnections )
350  {
351  reportAux(wxString::Format( " - net: %s, from: %s, to: %s, "
352  "%d matching items, "
353  "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
354  "vias: %d",
355  ent.netname,
356  ent.from,
357  ent.to,
358  (int) ent.items.size(),
359  MessageTextFromValue( userUnits(), ent.total ),
360  MessageTextFromValue( userUnits(), ent.totalRoute ),
361  MessageTextFromValue( userUnits(), ent.totalVia ),
362  MessageTextFromValue( userUnits(), ent.totalPadToDie ),
363  ent.viaCount ) );
364  }
365 
366 
367  OPT<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
368 
369  if( lengthConstraint )
370  checkLengthViolations( *lengthConstraint, matchedConnections );
371 
372  OPT<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
373 
374  if( skewConstraint )
375  checkSkewViolations( *skewConstraint, matchedConnections );
376 
377  OPT<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
378 
379  if( viaCountConstraint )
380  checkViaCountViolations( *viaCountConstraint, matchedConnections );
381  }
382 
384  }
385 
386  return true;
387 }
388 
389 
391 {
393 }
394 
395 
396 namespace detail
397 {
399 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
OPT< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition: drc_rule.cpp:52
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:125
Definition: track.h:343
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:245
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:82
void checkSkewViolations(DRC_CONSTRAINT &aConstraint, LENGTH_ENTRIES &aMatchedConnections)
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULUS > dummy
void checkViaCountViolations(DRC_CONSTRAINT &aConstraint, LENGTH_ENTRIES &aMatchedConnections)
class ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
static int computeViaThruLength(VIA *aVia, const std::set< BOARD_CONNECTED_ITEM * > &conns)
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:752
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)
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual void reportRuleStatistics()
T Max() const
Definition: minoptmax.h:34
virtual bool Run() override
Runs this provider against the given PCB with configured options (if any).
wxString GetName() const
Definition: drc_rule.h:127
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
DRC_CONSTRAINT_T
Definition: drc_rule.h:41
void Add(const ENTRY &ent)
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:125
std::set< BOARD_CONNECTED_ITEM * > CITEMS
BOARD * GetBoard() const
Definition: drc_engine.h:87
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:414
const wxString & GetNetname() const
Definition: netinfo.h:119
EDA_UNITS userUnits() const
void checkLengthViolations(DRC_CONSTRAINT &aConstraint, LENGTH_ENTRIES &aMatchedConnections)
virtual const wxString GetDescription() const override
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintId, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:736
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
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, wxPoint aMarkerPos)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
DRC_TEST_PROVIDER is a base class that represents a DRC "provider" which runs some DRC functions over...
bool HasMax() const
Definition: minoptmax.h:38
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:121
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
BOARD_CONNECTED_ITEM * toItem
#define _(s)
Definition: 3d_actions.cpp:33
DRC_ENGINE * m_drcEngine
The common library.
boost::optional< T > OPT
Definition: optional.h:7
class 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:162