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 
84  void checkLengths( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
85  void checkSkews( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
86  void checkViaCounts( DRC_CONSTRAINT& aConstraint, std::vector<CONNECTION>& aMatchedConnections );
87 
90 };
91 
92 
93 static int computeViaThruLength( PCB_VIA *aVia, const std::set<BOARD_CONNECTED_ITEM*> &conns )
94 {
95  return 0; // fixme: not yet there...
96 }
97 
98 
100  std::vector<CONNECTION>& aMatchedConnections )
101 {
102  for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
103  {
104  bool minViolation = false;
105  bool maxViolation = false;
106  int minLen = 0;
107  int maxLen = 0;
108 
109  if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
110  {
111  minViolation = true;
112  minLen = aConstraint.GetValue().Min();
113  }
114  else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
115  {
116  maxViolation = true;
117  maxLen = aConstraint.GetValue().Max();
118  }
119 
120  if( ( minViolation || maxViolation ) )
121  {
122  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
123 
124  if( minViolation )
125  {
126  m_msg.Printf( _( "(%s min length: %s; actual: %s)" ),
127  aConstraint.GetName(),
128  MessageTextFromValue( userUnits(), minLen ),
129  MessageTextFromValue( userUnits(), ent.total ) );
130  }
131  else if( maxViolation )
132  {
133  m_msg.Printf( _( "(%s max length: %s; actual: %s)" ),
134  aConstraint.GetName(),
135  MessageTextFromValue( userUnits(), maxLen ),
136  MessageTextFromValue( userUnits(), ent.total ) );
137  }
138 
139  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
140 
141  for( auto offendingTrack : ent.items )
142  drcItem->AddItem( offendingTrack );
143 
144  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
145 
146  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
147  }
148  }
149 }
150 
152  std::vector<CONNECTION>& aMatchedConnections )
153 {
154  int avgLength = 0;
155 
156  for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
157  avgLength += ent.total;
158 
159  avgLength /= aMatchedConnections.size();
160 
161  for( const auto& ent : aMatchedConnections )
162  {
163  int skew = ent.total - avgLength;
164  if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
165  {
166  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
167 
168  m_msg.Printf( _( "(%s max skew: %s; actual: %s; average net length: %s; actual: %s)" ),
169  aConstraint.GetName(),
170  MessageTextFromValue( userUnits(), aConstraint.GetValue().Max() ),
171  MessageTextFromValue( userUnits(), skew ),
172  MessageTextFromValue( userUnits(), avgLength ),
173  MessageTextFromValue( userUnits(), ent.total ) );
174 
175  drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + m_msg );
176 
177  for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
178  drcItem->SetItems( offendingTrack );
179 
180  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
181 
182  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
183  }
184  }
185 }
186 
187 
189  std::vector<CONNECTION>& aMatchedConnections )
190 {
191  for( const auto& ent : aMatchedConnections )
192  {
193  if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
194  {
195  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
196 
197  m_msg.Printf( _( "(%s max count: %d; actual: %d)" ),
198  aConstraint.GetName(),
199  aConstraint.GetValue().Max(),
200  ent.viaCount );
201 
202  drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
203 
204  for( auto offendingTrack : ent.items )
205  drcItem->SetItems( offendingTrack );
206 
207  drcItem->SetViolatingRule( aConstraint.GetParentRule() );
208 
209  reportViolation( drcItem, (*ent.items.begin() )->GetPosition() );
210  }
211  }
212 }
213 
214 
216 {
217  return runInternal( false );
218 }
219 
220 
222 {
224  m_report.Clear();
225 
226  if( !aDelayReportMode )
227  {
228  if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
229  return false;
230  }
231 
232  std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
233 
234  auto evaluateLengthConstraints =
235  [&]( BOARD_ITEM *item ) -> bool
236  {
237  const DRC_CONSTRAINT_T constraintsToCheck[] = {
241  };
242 
243  for( int i = 0; i < 3; i++ )
244  {
245  auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
246  item->GetLayer() );
247 
248  if( constraint.IsNull() )
249  continue;
250 
251  auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
252 
253  itemSets[ constraint.GetParentRule() ].insert( citem );
254  }
255 
256  return true;
257  };
258 
259  auto ftCache = m_board->GetConnectivity()->GetFromToCache();
260 
261  ftCache->Rebuild( m_board );
262 
264  evaluateLengthConstraints );
265 
266  std::map<DRC_RULE*, std::vector<CONNECTION> > matches;
267 
268  for( auto it : itemSets )
269  {
270  std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
271 
272  for( auto citem : it.second )
273  netMap[ citem->GetNetCode() ].insert( citem );
274 
275 
276  for( auto nitem : netMap )
277  {
278  CONNECTION ent;
279  ent.items = nitem.second;
280  ent.netcode = nitem.first;
282 
283  ent.viaCount = 0;
284  ent.totalRoute = 0;
285  ent.totalVia = 0;
286  ent.totalPadToDie = 0;
287  ent.fromItem = nullptr;
288  ent.toItem = nullptr;
289 
290  for( BOARD_CONNECTED_ITEM* citem : nitem.second )
291  {
292  if( citem->Type() == PCB_VIA_T )
293  {
294  ent.viaCount++;
295  ent.totalVia += computeViaThruLength( static_cast<PCB_VIA*>( citem ),
296  nitem.second );
297  }
298  else if( citem->Type() == PCB_TRACE_T )
299  {
300  ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
301  }
302  else if ( citem->Type() == PCB_ARC_T )
303  {
304  ent.totalRoute += static_cast<PCB_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 CONNECTION&a, const CONNECTION&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  checkLengths( *lengthConstraint, matchedConnections );
371 
372  OPT<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
373 
374  if( skewConstraint )
375  checkSkews( *skewConstraint, matchedConnections );
376 
377  OPT<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
378 
379  if( viaCountConstraint )
380  checkViaCounts( *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: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:254
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
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const wxPoint &aMarkerPos)
void checkSkews(DRC_CONSTRAINT &aConstraint, std::vector< CONNECTION > &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
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 * 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:344
const wxString & GetNetname() const
Definition: netinfo.h:119
#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: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
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 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
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,...)
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