You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
14 KiB

5 years ago
  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. #include <common.h>
  20. #include <board.h>
  21. #include <board_design_settings.h>
  22. #include <pad.h>
  23. #include <pcb_track.h>
  24. #include <drc/drc_item.h>
  25. #include <drc/drc_rule.h>
  26. #include <drc/drc_test_provider.h>
  27. #include <drc/drc_length_report.h>
  28. #include <connectivity/connectivity_data.h>
  29. #include <connectivity/from_to_cache.h>
  30. #include <pcbexpr_evaluator.h>
  31. /*
  32. Single-ended matched length + skew + via count test.
  33. Errors generated:
  34. - DRCE_LENGTH_OUT_OF_RANGE
  35. - DRCE_SKEW_OUT_OF_RANGE
  36. - DRCE_TOO_MANY_VIAS
  37. Todo: arc support
  38. */
  39. class DRC_TEST_PROVIDER_MATCHED_LENGTH : public DRC_TEST_PROVIDER
  40. {
  41. public:
  42. DRC_TEST_PROVIDER_MATCHED_LENGTH () :
  43. m_board( nullptr )
  44. {
  45. }
  46. virtual ~DRC_TEST_PROVIDER_MATCHED_LENGTH()
  47. {
  48. }
  49. virtual bool Run() override;
  50. virtual const wxString GetName() const override
  51. {
  52. return wxT( "length" );
  53. };
  54. virtual const wxString GetDescription() const override
  55. {
  56. return wxT( "Tests matched track lengths." );
  57. }
  58. DRC_LENGTH_REPORT BuildLengthReport() const;
  59. private:
  60. bool runInternal( bool aDelayReportMode = false );
  61. using CONNECTION = DRC_LENGTH_REPORT::ENTRY;
  62. void checkLengths( const DRC_CONSTRAINT& aConstraint,
  63. const std::vector<CONNECTION>& aMatchedConnections );
  64. void checkSkews( const DRC_CONSTRAINT& aConstraint,
  65. const std::vector<CONNECTION>& aMatchedConnections );
  66. void checkViaCounts( const DRC_CONSTRAINT& aConstraint,
  67. const std::vector<CONNECTION>& aMatchedConnections );
  68. BOARD* m_board;
  69. DRC_LENGTH_REPORT m_report;
  70. };
  71. void DRC_TEST_PROVIDER_MATCHED_LENGTH::checkLengths( const DRC_CONSTRAINT& aConstraint,
  72. const std::vector<CONNECTION>& aMatchedConnections )
  73. {
  74. for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
  75. {
  76. bool minViolation = false;
  77. bool maxViolation = false;
  78. int minLen = 0;
  79. int maxLen = 0;
  80. if( aConstraint.GetValue().HasMin() && ent.total < aConstraint.GetValue().Min() )
  81. {
  82. minViolation = true;
  83. minLen = aConstraint.GetValue().Min();
  84. }
  85. else if( aConstraint.GetValue().HasMax() && ent.total > aConstraint.GetValue().Max() )
  86. {
  87. maxViolation = true;
  88. maxLen = aConstraint.GetValue().Max();
  89. }
  90. if( ( minViolation || maxViolation ) )
  91. {
  92. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_LENGTH_OUT_OF_RANGE );
  93. wxString msg;
  94. if( minViolation )
  95. {
  96. msg = formatMsg( _( "(%s min length %s; actual %s)" ),
  97. aConstraint.GetName(),
  98. minLen,
  99. ent.total );
  100. }
  101. else if( maxViolation )
  102. {
  103. msg = formatMsg( _( "(%s max length %s; actual %s)" ),
  104. aConstraint.GetName(),
  105. maxLen,
  106. ent.total );
  107. }
  108. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  109. for( auto offendingTrack : ent.items )
  110. drcItem->AddItem( offendingTrack );
  111. drcItem->SetViolatingRule( aConstraint.GetParentRule() );
  112. reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
  113. ( *ent.items.begin() )->GetLayer() );
  114. }
  115. }
  116. }
  117. void DRC_TEST_PROVIDER_MATCHED_LENGTH::checkSkews( const DRC_CONSTRAINT& aConstraint,
  118. const std::vector<CONNECTION>& aMatchedConnections )
  119. {
  120. int avgLength = 0;
  121. for( const DRC_LENGTH_REPORT::ENTRY& ent : aMatchedConnections )
  122. avgLength += ent.total;
  123. avgLength /= aMatchedConnections.size();
  124. for( const auto& ent : aMatchedConnections )
  125. {
  126. int skew = ent.total - avgLength;
  127. if( aConstraint.GetValue().HasMax() && abs( skew ) > aConstraint.GetValue().Max() )
  128. {
  129. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SKEW_OUT_OF_RANGE );
  130. wxString msg;
  131. msg.Printf( _( "(%s max skew %s; actual %s; average net length %s; actual %s)" ),
  132. aConstraint.GetName(),
  133. MessageTextFromValue( aConstraint.GetValue().Max() ),
  134. MessageTextFromValue( skew ),
  135. MessageTextFromValue( avgLength ),
  136. MessageTextFromValue( ent.total ) );
  137. drcItem->SetErrorMessage( drcItem->GetErrorText() + " " + msg );
  138. for( BOARD_CONNECTED_ITEM* offendingTrack : ent.items )
  139. drcItem->SetItems( offendingTrack );
  140. drcItem->SetViolatingRule( aConstraint.GetParentRule() );
  141. reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
  142. ( *ent.items.begin() )->GetLayer() );
  143. }
  144. }
  145. }
  146. void DRC_TEST_PROVIDER_MATCHED_LENGTH::checkViaCounts( const DRC_CONSTRAINT& aConstraint,
  147. const std::vector<CONNECTION>& aMatchedConnections )
  148. {
  149. for( const auto& ent : aMatchedConnections )
  150. {
  151. if( aConstraint.GetValue().HasMax() && ent.viaCount > aConstraint.GetValue().Max() )
  152. {
  153. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TOO_MANY_VIAS );
  154. wxString msg = wxString::Format( _( "(%s max count %d; actual %d)" ),
  155. aConstraint.GetName(),
  156. aConstraint.GetValue().Max(),
  157. ent.viaCount );
  158. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  159. for( auto offendingTrack : ent.items )
  160. drcItem->SetItems( offendingTrack );
  161. drcItem->SetViolatingRule( aConstraint.GetParentRule() );
  162. reportViolation( drcItem, ( *ent.items.begin() )->GetPosition(),
  163. ( *ent.items.begin() )->GetLayer() );
  164. }
  165. }
  166. }
  167. bool DRC_TEST_PROVIDER_MATCHED_LENGTH::Run()
  168. {
  169. return runInternal( false );
  170. }
  171. bool DRC_TEST_PROVIDER_MATCHED_LENGTH::runInternal( bool aDelayReportMode )
  172. {
  173. m_board = m_drcEngine->GetBoard();
  174. m_report.Clear();
  175. if( !aDelayReportMode )
  176. {
  177. if( !reportPhase( _( "Gathering length-constrained connections..." ) ) )
  178. return false;
  179. }
  180. std::map<DRC_RULE*, std::set<BOARD_CONNECTED_ITEM*> > itemSets;
  181. std::shared_ptr<FROM_TO_CACHE> ftCache = m_board->GetConnectivity()->GetFromToCache();
  182. ftCache->Rebuild( m_board );
  183. const size_t progressDelta = 100;
  184. size_t count = 0;
  185. size_t ii = 0;
  186. forEachGeometryItem( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T }, LSET::AllCuMask(),
  187. [&]( BOARD_ITEM *item ) -> bool
  188. {
  189. count++;
  190. return true;
  191. } );
  192. forEachGeometryItem( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T }, LSET::AllCuMask(),
  193. [&]( BOARD_ITEM *item ) -> bool
  194. {
  195. if( !reportProgress( ii++, count, progressDelta ) )
  196. return false;
  197. const DRC_CONSTRAINT_T constraintsToCheck[] = {
  198. LENGTH_CONSTRAINT,
  199. SKEW_CONSTRAINT,
  200. VIA_COUNT_CONSTRAINT,
  201. };
  202. for( int i = 0; i < 3; i++ )
  203. {
  204. auto constraint = m_drcEngine->EvalRules( constraintsToCheck[i], item, nullptr,
  205. item->GetLayer() );
  206. if( constraint.IsNull() )
  207. continue;
  208. auto citem = static_cast<BOARD_CONNECTED_ITEM*>( item );
  209. itemSets[ constraint.GetParentRule() ].insert( citem );
  210. }
  211. return true;
  212. } );
  213. std::map< DRC_RULE*, std::vector<CONNECTION> > matches;
  214. for( const std::pair< DRC_RULE* const, std::set<BOARD_CONNECTED_ITEM*> >& it : itemSets )
  215. {
  216. std::map<int, std::set<BOARD_CONNECTED_ITEM*> > netMap;
  217. for( BOARD_CONNECTED_ITEM* citem : it.second )
  218. netMap[ citem->GetNetCode() ].insert( citem );
  219. for( const std::pair< const int, std::set<BOARD_CONNECTED_ITEM*> >& nitem : netMap )
  220. {
  221. CONNECTION ent;
  222. ent.items = nitem.second;
  223. ent.netcode = nitem.first;
  224. ent.netname = m_board->GetNetInfo().GetNetItem( ent.netcode )->GetNetname();
  225. ent.viaCount = 0;
  226. ent.totalRoute = 0;
  227. ent.totalVia = 0;
  228. ent.totalPadToDie = 0;
  229. ent.fromItem = nullptr;
  230. ent.toItem = nullptr;
  231. for( BOARD_CONNECTED_ITEM* citem : nitem.second )
  232. {
  233. if( citem->Type() == PCB_VIA_T )
  234. {
  235. const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  236. const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
  237. ent.viaCount++;
  238. if( bds.m_UseHeightForLengthCalcs )
  239. {
  240. const PCB_VIA* v = static_cast<PCB_VIA*>( citem );
  241. ent.totalVia += stackup.GetLayerDistance( v->TopLayer(), v->BottomLayer() );
  242. }
  243. }
  244. else if( citem->Type() == PCB_TRACE_T )
  245. {
  246. ent.totalRoute += static_cast<PCB_TRACK*>( citem )->GetLength();
  247. }
  248. else if ( citem->Type() == PCB_ARC_T )
  249. {
  250. ent.totalRoute += static_cast<PCB_ARC*>( citem )->GetLength();
  251. }
  252. else if( citem->Type() == PCB_PAD_T )
  253. {
  254. ent.totalPadToDie += static_cast<PAD*>( citem )->GetPadToDieLength();
  255. }
  256. }
  257. ent.total = ent.totalRoute + ent.totalVia + ent.totalPadToDie;
  258. ent.matchingRule = it.first;
  259. // fixme: doesn't seem to work ;-)
  260. auto ftPath = ftCache->QueryFromToPath( ent.items );
  261. if( ftPath )
  262. {
  263. ent.from = ftPath->fromName;
  264. ent.to = ftPath->toName;
  265. }
  266. else
  267. {
  268. ent.from = ent.to = _( "<unconstrained>" );
  269. }
  270. m_report.Add( ent );
  271. matches[ it.first ].push_back(ent);
  272. }
  273. }
  274. if( !aDelayReportMode )
  275. {
  276. if( !reportPhase( _( "Checking length constraints..." ) ) )
  277. return false;
  278. ii = 0;
  279. count = matches.size();
  280. for( std::pair< DRC_RULE* const, std::vector<CONNECTION> > it : matches )
  281. {
  282. DRC_RULE *rule = it.first;
  283. auto& matchedConnections = it.second;
  284. if( !reportProgress( ii++, count, progressDelta ) )
  285. return false;
  286. std::sort( matchedConnections.begin(), matchedConnections.end(),
  287. [] ( const CONNECTION&a, const CONNECTION&b ) -> int
  288. {
  289. return a.netname < b.netname;
  290. } );
  291. reportAux( wxString::Format( wxT( "Length-constrained traces for rule '%s':" ),
  292. it.first->m_Name ) );
  293. for( const DRC_LENGTH_REPORT::ENTRY& ent : matchedConnections )
  294. {
  295. reportAux(wxString::Format( wxT( " - net: %s, from: %s, to: %s, "
  296. "%d matching items, "
  297. "total: %s (tracks: %s, vias: %s, pad-to-die: %s), "
  298. "vias: %d" ),
  299. ent.netname,
  300. ent.from,
  301. ent.to,
  302. (int) ent.items.size(),
  303. MessageTextFromValue( ent.total ),
  304. MessageTextFromValue( ent.totalRoute ),
  305. MessageTextFromValue( ent.totalVia ),
  306. MessageTextFromValue( ent.totalPadToDie ),
  307. ent.viaCount ) );
  308. }
  309. std::optional<DRC_CONSTRAINT> lengthConstraint = rule->FindConstraint( LENGTH_CONSTRAINT );
  310. if( lengthConstraint && lengthConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
  311. checkLengths( *lengthConstraint, matchedConnections );
  312. std::optional<DRC_CONSTRAINT> skewConstraint = rule->FindConstraint( SKEW_CONSTRAINT );
  313. if( skewConstraint && skewConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
  314. checkSkews( *skewConstraint, matchedConnections );
  315. std::optional<DRC_CONSTRAINT> viaCountConstraint = rule->FindConstraint( VIA_COUNT_CONSTRAINT );
  316. if( viaCountConstraint && viaCountConstraint->GetSeverity() != RPT_SEVERITY_IGNORE )
  317. checkViaCounts( *viaCountConstraint, matchedConnections );
  318. }
  319. reportRuleStatistics();
  320. }
  321. return !m_drcEngine->IsCancelled();
  322. }
  323. namespace detail
  324. {
  325. static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_MATCHED_LENGTH> dummy;
  326. }