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 <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 "length";
63  };
64 
65  virtual const wxString GetDescription() const override
66  {
67  return "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 
118  if( minViolation )
119  {
120  m_msg.Printf( _( "(%s min length: %s; actual: %s)" ),
121  aConstraint.GetName(),
122  MessageTextFromValue( userUnits(), minLen ),
123  MessageTextFromValue( userUnits(), ent.total ) );
124  }
125  else if( maxViolation )
126  {
127  m_msg.Printf( _( "(%s max length: %s; actual: %s)" ),
128  aConstraint.GetName(),
129  MessageTextFromValue( userUnits(), maxLen ),
130  MessageTextFromValue( userUnits(), ent.total ) );
131  }
132 
133  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
134 
135  for( auto offendingTrack : ent.items )
136  drcItem->AddItem( offendingTrack );
137 
138  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
139 
140  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
141  }
142  }
143 }
144 
146  std::vector<CONNECTION>& aMatchedConnections )
147 {
148  int avgLength = 0;
149 
150  for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
151  avgLength += ent.total;
152 
153  avgLength /= aMatchedConnections.size();
154 
155  for( const auto& ent : aMatchedConnections )
156  {
157  int skew = ent.total - avgLength;
158  if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
159  {
160  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
161 
162  m_msg.Printf( _( "(%s max skew: %s; actual: %s; average net length: %s; actual: %s)" ),
163  aConstraint.GetName(),
164  MessageTextFromValue( userUnits(), aConstraint.GetValue().Max() ),
165  MessageTextFromValue( userUnits(), skew ),
166  MessageTextFromValue( userUnits(), avgLength ),
167  MessageTextFromValue( userUnits(), ent.total ) );
168 
169  drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + m_msg );
170 
171  for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
172  drcItem->SetItems( offendingTrack );
173 
174  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
175 
176  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
177  }
178  }
179 }
180 
181 
183  std::vector<CONNECTION>& aMatchedConnections )
184 {
185  for( const auto& ent : aMatchedConnections )
186  {
187  if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
188  {
189  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
190 
191  m_msg.Printf( _( "(%s max count: %d; actual: %d)" ),
192  aConstraint.GetName(),
193  aConstraint.GetValue().Max(),
194  ent.viaCount );
195 
196  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
197 
198  for( auto offendingTrack : ent.items )
199  drcItem->SetItems( offendingTrack );
200 
201  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
202 
203  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
204  }
205  }
206 }
207 
208 
210 {
211  return runInternal( false );
212 }
213 
214 
216 {
218  m_report.Clear();
219 
220  if( !aDelayReportMode )
221  {
222  if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
223  return false;
224  }
225 
226  std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
227 
228  auto evaluateLengthConstraints =
229  [&]( BOARD_ITEM *item ) -> bool
230  {
231  const DRC_CONSTRAINT_T constraintsToCheck[] = {
235  };
236 
237  for( int i = 0; i < 3; i++ )
238  {
239  auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
240  item->GetLayer() );
241 
242  if( constraint.IsNull() )
243  continue;
244 
245  auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
246 
247  itemSets[ constraint.GetParentRule() ].insert( citem );
248  }
249 
250  return true;
251  };
252 
253  auto ftCache = m_board->GetConnectivity()->GetFromToCache();
254 
255  ftCache->Rebuild( m_board );
256 
258  evaluateLengthConstraints );
259 
260  std::map<DRC_RULE*, std::vector<CONNECTION> > matches;
261 
262  for( auto it : itemSets )
263  {
264  std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
265 
266  for( auto citem : it.second )
267  netMap[ citem->GetNetCode() ].insert( citem );
268 
269 
270  for( auto nitem : netMap )
271  {
272  CONNECTION ent;
273  ent.items = nitem.second;
274  ent.netcode = nitem.first;
276 
277  ent.viaCount = 0;
278  ent.totalRoute = 0;
279  ent.totalVia = 0;
280  ent.totalPadToDie = 0;
281  ent.fromItem = nullptr;
282  ent.toItem = nullptr;
283 
284  for( BOARD_CONNECTED_ITEM* citem : nitem.second )
285  {
286  if( citem->Type() == PCB_VIA_T )
287  {
289 
290  ent.viaCount++;
291 
293  {
294  const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
295 
297  v->TopLayer(), v->BottomLayer() );
298  }
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 CONNECTION&a, const CONNECTION&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  checkLengths( *lengthConstraint, matchedConnections );
373 
374  OPT<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
375 
376  if( skewConstraint )
377  checkSkews( *skewConstraint, matchedConnections );
378 
379  OPT<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
380 
381  if( viaCountConstraint )
382  checkViaCounts( *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: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:760
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.