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