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.

1926 lines
72 KiB

6 years ago
6 years ago
5 years ago
6 years ago
9 months ago
6 years ago
6 years ago
5 years ago
5 years ago
6 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
1 year ago
4 years ago
6 years ago
6 years ago
1 year ago
5 years ago
6 years ago
6 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2014 Dick Hollenbeck, dick@softplc.com
  6. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <atomic>
  26. #include <wx/log.h>
  27. #include <reporter.h>
  28. #include <progress_reporter.h>
  29. #include <string_utils.h>
  30. #include <board_design_settings.h>
  31. #include <drc/drc_engine.h>
  32. #include <drc/drc_rtree.h>
  33. #include <drc/drc_rule_parser.h>
  34. #include <drc/drc_rule.h>
  35. #include <drc/drc_rule_condition.h>
  36. #include <drc/drc_test_provider.h>
  37. #include <drc/drc_item.h>
  38. #include <drc/drc_cache_generator.h>
  39. #include <footprint.h>
  40. #include <pad.h>
  41. #include <pcb_track.h>
  42. #include <core/profile.h>
  43. #include <thread_pool.h>
  44. #include <zone.h>
  45. // wxListBox's performance degrades horrifically with very large datasets. It's not clear
  46. // they're useful to the user anyway.
  47. #define ERROR_LIMIT 199
  48. #define EXTENDED_ERROR_LIMIT 499
  49. /**
  50. * Flag to enable DRC profile timing logging.
  51. *
  52. * Use "KICAD_DRC_PROFILE" to enable.
  53. *
  54. * @ingroup trace_env_vars
  55. */
  56. static const wxChar* traceDrcProfile = wxT( "KICAD_DRC_PROFILE" );
  57. void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
  58. {
  59. wxString valueStr;
  60. if( wxGetEnv( wxT( "DRC_DEBUG" ), &valueStr ) )
  61. {
  62. int setLevel = wxAtoi( valueStr );
  63. if( level <= setLevel )
  64. printf( "%-30s:%d | %s\n", function, line, (const char *) msg.c_str() );
  65. }
  66. }
  67. DRC_ENGINE::DRC_ENGINE( BOARD* aBoard, BOARD_DESIGN_SETTINGS *aSettings ) :
  68. UNITS_PROVIDER( pcbIUScale, EDA_UNITS::MM ),
  69. m_designSettings ( aSettings ),
  70. m_board( aBoard ),
  71. m_drawingSheet( nullptr ),
  72. m_schematicNetlist( nullptr ),
  73. m_rulesValid( false ),
  74. m_reportAllTrackErrors( false ),
  75. m_testFootprints( false ),
  76. m_reporter( nullptr ),
  77. m_progressReporter( nullptr )
  78. {
  79. m_errorLimits.resize( DRCE_LAST + 1 );
  80. for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
  81. m_errorLimits[ii] = ERROR_LIMIT;
  82. }
  83. DRC_ENGINE::~DRC_ENGINE()
  84. {
  85. m_rules.clear();
  86. for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
  87. {
  88. for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
  89. delete constraint;
  90. delete pair.second;
  91. }
  92. }
  93. static bool isKeepoutZone( const BOARD_ITEM* aItem, bool aCheckFlags )
  94. {
  95. if( !aItem || aItem->Type() != PCB_ZONE_T )
  96. return false;
  97. const ZONE* zone = static_cast<const ZONE*>( aItem );
  98. if( !zone->GetIsRuleArea() )
  99. return false;
  100. if( !zone->HasKeepoutParametersSet() )
  101. return false;
  102. if( aCheckFlags )
  103. {
  104. if( !zone->GetDoNotAllowTracks()
  105. && !zone->GetDoNotAllowVias()
  106. && !zone->GetDoNotAllowPads()
  107. && !zone->GetDoNotAllowZoneFills()
  108. && !zone->GetDoNotAllowFootprints() )
  109. {
  110. return false;
  111. }
  112. }
  113. return true;
  114. }
  115. std::shared_ptr<DRC_RULE> DRC_ENGINE::createImplicitRule( const wxString& name )
  116. {
  117. std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
  118. rule->m_Name = name;
  119. rule->m_Implicit = true;
  120. addRule( rule );
  121. return rule;
  122. }
  123. void DRC_ENGINE::loadImplicitRules()
  124. {
  125. ReportAux( wxString::Format( wxT( "Building implicit rules (per-item/class overrides, etc...)" ) ) );
  126. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  127. // 1) global defaults
  128. std::shared_ptr<DRC_RULE> rule = createImplicitRule( _( "board setup constraints" ) );
  129. DRC_CONSTRAINT widthConstraint( TRACK_WIDTH_CONSTRAINT );
  130. widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
  131. rule->AddConstraint( widthConstraint );
  132. DRC_CONSTRAINT connectionConstraint( CONNECTION_WIDTH_CONSTRAINT );
  133. connectionConstraint.Value().SetMin( bds.m_MinConn );
  134. rule->AddConstraint( connectionConstraint );
  135. DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
  136. drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
  137. rule->AddConstraint( drillConstraint );
  138. DRC_CONSTRAINT annulusConstraint( ANNULAR_WIDTH_CONSTRAINT );
  139. annulusConstraint.Value().SetMin( bds.m_ViasMinAnnularWidth );
  140. rule->AddConstraint( annulusConstraint );
  141. DRC_CONSTRAINT diameterConstraint( VIA_DIAMETER_CONSTRAINT );
  142. diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
  143. rule->AddConstraint( diameterConstraint );
  144. DRC_CONSTRAINT holeToHoleConstraint( HOLE_TO_HOLE_CONSTRAINT );
  145. holeToHoleConstraint.Value().SetMin( bds.m_HoleToHoleMin );
  146. rule->AddConstraint( holeToHoleConstraint );
  147. rule = createImplicitRule( _( "board setup constraints zone fill strategy" ) );
  148. DRC_CONSTRAINT thermalSpokeCountConstraint( MIN_RESOLVED_SPOKES_CONSTRAINT );
  149. thermalSpokeCountConstraint.Value().SetMin( bds.m_MinResolvedSpokes );
  150. rule->AddConstraint( thermalSpokeCountConstraint );
  151. rule = createImplicitRule( _( "board setup constraints silk" ) );
  152. rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
  153. DRC_CONSTRAINT silkClearanceConstraint( SILK_CLEARANCE_CONSTRAINT );
  154. silkClearanceConstraint.Value().SetMin( bds.m_SilkClearance );
  155. rule->AddConstraint( silkClearanceConstraint );
  156. rule = createImplicitRule( _( "board setup constraints silk text height" ) );
  157. rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
  158. DRC_CONSTRAINT silkTextHeightConstraint( TEXT_HEIGHT_CONSTRAINT );
  159. silkTextHeightConstraint.Value().SetMin( bds.m_MinSilkTextHeight );
  160. rule->AddConstraint( silkTextHeightConstraint );
  161. rule = createImplicitRule( _( "board setup constraints silk text thickness" ) );
  162. rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
  163. DRC_CONSTRAINT silkTextThicknessConstraint( TEXT_THICKNESS_CONSTRAINT );
  164. silkTextThicknessConstraint.Value().SetMin( bds.m_MinSilkTextThickness );
  165. rule->AddConstraint( silkTextThicknessConstraint );
  166. rule = createImplicitRule( _( "board setup constraints hole" ) );
  167. DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
  168. holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
  169. rule->AddConstraint( holeClearanceConstraint );
  170. rule = createImplicitRule( _( "board setup constraints edge" ) );
  171. DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
  172. edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
  173. rule->AddConstraint( edgeClearanceConstraint );
  174. rule = createImplicitRule( _( "board setup constraints courtyard" ) );
  175. DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
  176. holeToHoleConstraint.Value().SetMin( 0 );
  177. rule->AddConstraint( courtyardClearanceConstraint );
  178. // 2) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
  179. std::shared_ptr<DRC_RULE> uViaRule = createImplicitRule( _( "board setup micro-via constraints" ) );
  180. uViaRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Via_Type == 'Micro'" ) );
  181. DRC_CONSTRAINT uViaDrillConstraint( HOLE_SIZE_CONSTRAINT );
  182. uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
  183. uViaRule->AddConstraint( uViaDrillConstraint );
  184. DRC_CONSTRAINT uViaDiameterConstraint( VIA_DIAMETER_CONSTRAINT );
  185. uViaDiameterConstraint.Value().SetMin( bds.m_MicroViasMinSize );
  186. uViaRule->AddConstraint( uViaDiameterConstraint );
  187. // 3) per-netclass rules
  188. std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
  189. std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
  190. auto makeNetclassRules = [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
  191. {
  192. wxString ncName = nc->GetName();
  193. wxString expr;
  194. ncName.Replace( "'", "\\'" );
  195. if( nc->HasClearance() )
  196. {
  197. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  198. netclassRule->m_Name = wxString::Format(
  199. _( "netclass '%s'" ), nc->GetClearanceParent()->GetHumanReadableName() );
  200. netclassRule->m_Implicit = true;
  201. expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
  202. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  203. netclassClearanceRules.push_back( netclassRule );
  204. DRC_CONSTRAINT constraint( CLEARANCE_CONSTRAINT );
  205. constraint.Value().SetMin( nc->GetClearance() );
  206. netclassRule->AddConstraint( constraint );
  207. }
  208. if( nc->HasTrackWidth() )
  209. {
  210. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  211. netclassRule->m_Name = wxString::Format(
  212. _( "netclass '%s'" ), nc->GetTrackWidthParent()->GetHumanReadableName() );
  213. netclassRule->m_Implicit = true;
  214. expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
  215. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  216. netclassClearanceRules.push_back( netclassRule );
  217. DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
  218. constraint.Value().SetMin( bds.m_TrackMinWidth );
  219. constraint.Value().SetOpt( nc->GetTrackWidth() );
  220. netclassRule->AddConstraint( constraint );
  221. }
  222. if( nc->HasDiffPairWidth() )
  223. {
  224. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  225. netclassRule->m_Name =
  226. wxString::Format( _( "netclass '%s' (diff pair)" ),
  227. nc->GetDiffPairWidthParent()->GetHumanReadableName() );
  228. netclassRule->m_Implicit = true;
  229. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.inDiffPair('*')" ), ncName );
  230. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  231. netclassItemSpecificRules.push_back( netclassRule );
  232. DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
  233. constraint.Value().SetMin( bds.m_TrackMinWidth );
  234. constraint.Value().SetOpt( nc->GetDiffPairWidth() );
  235. netclassRule->AddConstraint( constraint );
  236. }
  237. if( nc->HasDiffPairGap() )
  238. {
  239. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  240. netclassRule->m_Name =
  241. wxString::Format( _( "netclass '%s' (diff pair)" ),
  242. nc->GetDiffPairGapParent()->GetHumanReadableName() );
  243. netclassRule->m_Implicit = true;
  244. expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
  245. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  246. netclassItemSpecificRules.push_back( netclassRule );
  247. DRC_CONSTRAINT constraint( DIFF_PAIR_GAP_CONSTRAINT );
  248. constraint.Value().SetMin( bds.m_MinClearance );
  249. constraint.Value().SetOpt( nc->GetDiffPairGap() );
  250. netclassRule->AddConstraint( constraint );
  251. // A narrower diffpair gap overrides the netclass min clearance
  252. if( nc->GetDiffPairGap() < nc->GetClearance() )
  253. {
  254. netclassRule = std::make_shared<DRC_RULE>();
  255. netclassRule->m_Name =
  256. wxString::Format( _( "netclass '%s' (diff pair)" ),
  257. nc->GetDiffPairGapParent()->GetHumanReadableName() );
  258. netclassRule->m_Implicit = true;
  259. expr = wxString::Format( wxT( "A.NetClass == '%s' && AB.isCoupledDiffPair()" ),
  260. ncName );
  261. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  262. netclassItemSpecificRules.push_back( netclassRule );
  263. DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
  264. min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
  265. netclassRule->AddConstraint( min_clearanceConstraint );
  266. }
  267. }
  268. if( nc->HasViaDiameter() )
  269. {
  270. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  271. netclassRule->m_Name = wxString::Format(
  272. _( "netclass '%s'" ), nc->GetViaDiameterParent()->GetHumanReadableName() );
  273. netclassRule->m_Implicit = true;
  274. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
  275. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  276. netclassItemSpecificRules.push_back( netclassRule );
  277. DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
  278. constraint.Value().SetMin( bds.m_ViasMinSize );
  279. constraint.Value().SetOpt( nc->GetViaDiameter() );
  280. netclassRule->AddConstraint( constraint );
  281. }
  282. if( nc->HasViaDrill() )
  283. {
  284. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  285. netclassRule->m_Name = wxString::Format(
  286. _( "netclass '%s'" ), nc->GetViaDrillParent()->GetHumanReadableName() );
  287. netclassRule->m_Implicit = true;
  288. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
  289. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  290. netclassItemSpecificRules.push_back( netclassRule );
  291. DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
  292. constraint.Value().SetMin( bds.m_MinThroughDrill );
  293. constraint.Value().SetOpt( nc->GetViaDrill() );
  294. netclassRule->AddConstraint( constraint );
  295. }
  296. if( nc->HasuViaDiameter() )
  297. {
  298. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  299. netclassRule->m_Name =
  300. wxString::Format( _( "netclass '%s' (uvia)" ),
  301. nc->GetuViaDiameterParent()->GetHumanReadableName() );
  302. netclassRule->m_Implicit = true;
  303. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
  304. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  305. netclassItemSpecificRules.push_back( netclassRule );
  306. DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
  307. constraint.Value().SetMin( bds.m_MicroViasMinSize );
  308. constraint.Value().SetMin( nc->GetuViaDiameter() );
  309. netclassRule->AddConstraint( constraint );
  310. }
  311. if( nc->HasuViaDrill() )
  312. {
  313. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  314. netclassRule->m_Name = wxString::Format(
  315. _( "netclass '%s' (uvia)" ), nc->GetuViaDrillParent()->GetHumanReadableName() );
  316. netclassRule->m_Implicit = true;
  317. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
  318. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  319. netclassItemSpecificRules.push_back( netclassRule );
  320. DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
  321. constraint.Value().SetMin( bds.m_MicroViasMinDrill );
  322. constraint.Value().SetOpt( nc->GetuViaDrill() );
  323. netclassRule->AddConstraint( constraint );
  324. }
  325. };
  326. m_board->SynchronizeNetsAndNetClasses( false );
  327. makeNetclassRules( bds.m_NetSettings->GetDefaultNetclass(), true );
  328. for( const auto& [name, netclass] : bds.m_NetSettings->GetNetclasses() )
  329. makeNetclassRules( netclass, false );
  330. for( const auto& [name, netclass] : bds.m_NetSettings->GetCompositeNetclasses() )
  331. makeNetclassRules( netclass, false );
  332. // The netclass clearance rules have to be sorted by min clearance so the right one fires
  333. // if 'A' and 'B' belong to two different netclasses.
  334. //
  335. // The item-specific netclass rules are all unary, so there's no 'A' vs 'B' issue.
  336. std::sort( netclassClearanceRules.begin(), netclassClearanceRules.end(),
  337. []( const std::shared_ptr<DRC_RULE>& lhs, const std::shared_ptr<DRC_RULE>& rhs )
  338. {
  339. return lhs->m_Constraints[0].m_Value.Min()
  340. < rhs->m_Constraints[0].m_Value.Min();
  341. } );
  342. for( std::shared_ptr<DRC_RULE>& ncRule : netclassClearanceRules )
  343. addRule( ncRule );
  344. for( std::shared_ptr<DRC_RULE>& ncRule : netclassItemSpecificRules )
  345. addRule( ncRule );
  346. // 3) keepout area rules
  347. std::vector<ZONE*> keepoutZones;
  348. for( ZONE* zone : m_board->Zones() )
  349. {
  350. if( isKeepoutZone( zone, true ) )
  351. keepoutZones.push_back( zone );
  352. }
  353. for( FOOTPRINT* footprint : m_board->Footprints() )
  354. {
  355. for( ZONE* zone : footprint->Zones() )
  356. {
  357. if( isKeepoutZone( zone, true ) )
  358. keepoutZones.push_back( zone );
  359. }
  360. }
  361. for( ZONE* zone : keepoutZones )
  362. {
  363. wxString name = zone->GetZoneName();
  364. if( name.IsEmpty() )
  365. rule = createImplicitRule( _( "keepout area" ) );
  366. else
  367. rule = createImplicitRule( wxString::Format( _( "keepout area '%s'" ), name ) );
  368. rule->m_ImplicitItemId = zone->m_Uuid;
  369. rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( wxT( "A.intersectsArea('%s')" ),
  370. zone->m_Uuid.AsString() ) );
  371. rule->m_LayerCondition = zone->GetLayerSet();
  372. int disallowFlags = 0;
  373. if( zone->GetDoNotAllowTracks() )
  374. disallowFlags |= DRC_DISALLOW_TRACKS;
  375. if( zone->GetDoNotAllowVias() )
  376. disallowFlags |= DRC_DISALLOW_VIAS;
  377. if( zone->GetDoNotAllowPads() )
  378. disallowFlags |= DRC_DISALLOW_PADS;
  379. if( zone->GetDoNotAllowZoneFills() )
  380. disallowFlags |= DRC_DISALLOW_ZONES;
  381. if( zone->GetDoNotAllowFootprints() )
  382. disallowFlags |= DRC_DISALLOW_FOOTPRINTS;
  383. DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
  384. disallowConstraint.m_DisallowFlags = disallowFlags;
  385. rule->AddConstraint( disallowConstraint );
  386. }
  387. ReportAux( wxString::Format( wxT( "Building %d implicit netclass rules" ),
  388. (int) netclassClearanceRules.size() ) );
  389. }
  390. void DRC_ENGINE::loadRules( const wxFileName& aPath )
  391. {
  392. if( aPath.FileExists() )
  393. {
  394. std::vector<std::shared_ptr<DRC_RULE>> rules;
  395. FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) );
  396. if( fp )
  397. {
  398. DRC_RULES_PARSER parser( fp, aPath.GetFullPath() );
  399. parser.Parse( rules, m_reporter );
  400. }
  401. // Copy the rules into the member variable afterwards so that if Parse() throws then
  402. // the possibly malformed rules won't contaminate the current ruleset.
  403. for( std::shared_ptr<DRC_RULE>& rule : rules )
  404. m_rules.push_back( rule );
  405. }
  406. }
  407. void DRC_ENGINE::compileRules()
  408. {
  409. ReportAux( wxString::Format( wxT( "Compiling Rules (%d rules): " ), (int) m_rules.size() ) );
  410. for( std::shared_ptr<DRC_RULE>& rule : m_rules )
  411. {
  412. DRC_RULE_CONDITION* condition = nullptr;
  413. if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
  414. {
  415. condition = rule->m_Condition;
  416. condition->Compile( nullptr );
  417. }
  418. for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
  419. {
  420. if( !m_constraintMap.count( constraint.m_Type ) )
  421. m_constraintMap[ constraint.m_Type ] = new std::vector<DRC_ENGINE_CONSTRAINT*>();
  422. DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
  423. engineConstraint->layerTest = rule->m_LayerCondition;
  424. engineConstraint->condition = condition;
  425. engineConstraint->constraint = constraint;
  426. engineConstraint->parentRule = rule;
  427. m_constraintMap[ constraint.m_Type ]->push_back( engineConstraint );
  428. }
  429. }
  430. }
  431. void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
  432. {
  433. m_testProviders = DRC_TEST_PROVIDER_REGISTRY::Instance().GetTestProviders();
  434. for( DRC_TEST_PROVIDER* provider : m_testProviders )
  435. {
  436. ReportAux( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
  437. provider->SetDRCEngine( this );
  438. }
  439. m_rules.clear();
  440. m_rulesValid = false;
  441. for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
  442. {
  443. for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
  444. delete constraint;
  445. delete pair.second;
  446. }
  447. m_constraintMap.clear();
  448. m_board->IncrementTimeStamp(); // Clear board-level caches
  449. try // attempt to load full set of rules (implicit + user rules)
  450. {
  451. loadImplicitRules();
  452. loadRules( aRulePath );
  453. compileRules();
  454. }
  455. catch( PARSE_ERROR& original_parse_error )
  456. {
  457. try // try again with just our implicit rules
  458. {
  459. loadImplicitRules();
  460. compileRules();
  461. }
  462. catch( PARSE_ERROR& )
  463. {
  464. wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
  465. }
  466. throw original_parse_error;
  467. }
  468. for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
  469. m_errorLimits[ ii ] = ERROR_LIMIT;
  470. m_rulesValid = true;
  471. }
  472. void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
  473. BOARD_COMMIT* aCommit )
  474. {
  475. PROF_TIMER timer;
  476. SetUserUnits( aUnits );
  477. m_reportAllTrackErrors = aReportAllTrackErrors;
  478. m_testFootprints = aTestFootprints;
  479. for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
  480. {
  481. if( m_designSettings->Ignore( ii ) )
  482. m_errorLimits[ ii ] = 0;
  483. else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
  484. m_errorLimits[ ii ] = EXTENDED_ERROR_LIMIT;
  485. else
  486. m_errorLimits[ ii ] = ERROR_LIMIT;
  487. }
  488. DRC_TEST_PROVIDER::Init();
  489. m_board->IncrementTimeStamp(); // Invalidate all caches...
  490. DRC_CACHE_GENERATOR cacheGenerator;
  491. cacheGenerator.SetDRCEngine( this );
  492. if( !cacheGenerator.Run() ) // ... and regenerate them.
  493. return;
  494. // Recompute component classes
  495. m_board->GetComponentClassManager().ForceComponentClassRecalculation();
  496. int timestamp = m_board->GetTimeStamp();
  497. for( DRC_TEST_PROVIDER* provider : m_testProviders )
  498. {
  499. ReportAux( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
  500. if( !provider->RunTests( aUnits ) )
  501. break;
  502. }
  503. timer.Stop();
  504. wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
  505. // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
  506. // caches while DRC is running is problematic.
  507. wxASSERT( timestamp == m_board->GetTimeStamp() );
  508. }
  509. #define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
  510. DRC_CONSTRAINT DRC_ENGINE::EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
  511. PCB_LAYER_ID aLayer, REPORTER* aReporter )
  512. {
  513. DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
  514. REPORT( "" )
  515. REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
  516. EscapeHTML( PrintZoneConnection( constraint.m_ZoneConnection ) ) ) )
  517. if( constraint.m_ZoneConnection == ZONE_CONNECTION::THT_THERMAL )
  518. {
  519. const PAD* pad = nullptr;
  520. if( a->Type() == PCB_PAD_T )
  521. pad = static_cast<const PAD*>( a );
  522. else if( b->Type() == PCB_PAD_T )
  523. pad = static_cast<const PAD*>( b );
  524. if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
  525. {
  526. constraint.m_ZoneConnection = ZONE_CONNECTION::THERMAL;
  527. }
  528. else
  529. {
  530. REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
  531. EscapeHTML( PrintZoneConnection( ZONE_CONNECTION::FULL ) ) ) )
  532. constraint.m_ZoneConnection = ZONE_CONNECTION::FULL;
  533. }
  534. }
  535. return constraint;
  536. }
  537. DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a,
  538. const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
  539. REPORTER* aReporter )
  540. {
  541. /*
  542. * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
  543. * kills performance when running bulk DRC tests (where aReporter is nullptr).
  544. */
  545. const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ?
  546. static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
  547. const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ?
  548. static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
  549. bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
  550. bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
  551. const PAD* pad = nullptr;
  552. const ZONE* zone = nullptr;
  553. const FOOTPRINT* parentFootprint = nullptr;
  554. if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
  555. || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
  556. || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  557. {
  558. if( a && a->Type() == PCB_PAD_T )
  559. pad = static_cast<const PAD*>( a );
  560. else if( a && a->Type() == PCB_ZONE_T )
  561. zone = static_cast<const ZONE*>( a );
  562. if( b && b->Type() == PCB_PAD_T )
  563. pad = static_cast<const PAD*>( b );
  564. else if( b && b->Type() == PCB_ZONE_T )
  565. zone = static_cast<const ZONE*>( b );
  566. if( pad )
  567. parentFootprint = pad->GetParentFootprint();
  568. }
  569. DRC_CONSTRAINT constraint;
  570. constraint.m_Type = aConstraintType;
  571. auto applyConstraint =
  572. [&]( const DRC_ENGINE_CONSTRAINT* c )
  573. {
  574. if( c->constraint.m_Value.HasMin() )
  575. {
  576. if( c->parentRule && c->parentRule->m_Implicit )
  577. constraint.m_ImplicitMin = true;
  578. constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
  579. }
  580. if( c->constraint.m_Value.HasOpt() )
  581. constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
  582. if( c->constraint.m_Value.HasMax() )
  583. constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
  584. switch( c->constraint.m_Type )
  585. {
  586. case CLEARANCE_CONSTRAINT:
  587. case COURTYARD_CLEARANCE_CONSTRAINT:
  588. case SILK_CLEARANCE_CONSTRAINT:
  589. case HOLE_CLEARANCE_CONSTRAINT:
  590. case EDGE_CLEARANCE_CONSTRAINT:
  591. case PHYSICAL_CLEARANCE_CONSTRAINT:
  592. case PHYSICAL_HOLE_CLEARANCE_CONSTRAINT:
  593. if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
  594. constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
  595. break;
  596. default:
  597. break;
  598. }
  599. // While the expectation would be to OR the disallow flags, we've already
  600. // masked them down to aItem's type -- so we're really only looking for a
  601. // boolean here.
  602. constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
  603. constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
  604. constraint.SetParentRule( c->constraint.GetParentRule() );
  605. constraint.SetOptionsFromOther( c->constraint );
  606. };
  607. const FOOTPRINT* footprints[2] = {a ? a->GetParentFootprint() : nullptr,
  608. b ? b->GetParentFootprint() : nullptr};
  609. // Handle Footprint net ties, which will zero out the clearance for footprint objects
  610. if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
  611. && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
  612. // and not both connected. Connected items of different nets still need to be checked
  613. // for their standard clearance value
  614. || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
  615. && footprints[0] ) ) // And that footprint exists
  616. && !a_is_non_copper // Also, both elements need to be on copper layers
  617. && !b_is_non_copper )
  618. {
  619. const BOARD_ITEM* child_items[2] = {a, b};
  620. // These are the items being compared against, so the order is reversed
  621. const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
  622. for( int ii = 0; ii < 2; ++ii )
  623. {
  624. // We need both a footprint item and a connected item to check for a net tie
  625. if( !footprints[ii] || !alt_items[ii] )
  626. continue;
  627. const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
  628. auto it = netcodes.find( alt_items[ii]->GetNetCode() );
  629. if( it != netcodes.end() )
  630. {
  631. REPORT( "" )
  632. REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
  633. EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
  634. constraint.SetName( _( "net tie" ) );
  635. constraint.m_Value.SetMin( 0 );
  636. return constraint;
  637. }
  638. }
  639. }
  640. // Local overrides take precedence over everything *except* board min clearance
  641. if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
  642. {
  643. int override_val = 0;
  644. std::optional<int> overrideA;
  645. std::optional<int> overrideB;
  646. if( ac && !b_is_non_copper )
  647. overrideA = ac->GetClearanceOverrides( nullptr );
  648. if( bc && !a_is_non_copper )
  649. overrideB = bc->GetClearanceOverrides( nullptr );
  650. if( overrideA.has_value() || overrideB.has_value() )
  651. {
  652. wxString msg;
  653. if( overrideA.has_value() )
  654. {
  655. REPORT( "" )
  656. REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
  657. EscapeHTML( a->GetItemDescription( this, true ) ),
  658. MessageTextFromValue( overrideA.value() ) ) )
  659. override_val = ac->GetClearanceOverrides( &msg ).value();
  660. }
  661. if( overrideB.has_value() )
  662. {
  663. REPORT( "" )
  664. REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
  665. EscapeHTML( b->GetItemDescription( this, true ) ),
  666. EscapeHTML( MessageTextFromValue( overrideB.value() ) ) ) )
  667. if( overrideB > override_val )
  668. override_val = bc->GetClearanceOverrides( &msg ).value();
  669. }
  670. if( override_val )
  671. {
  672. if( aConstraintType == CLEARANCE_CONSTRAINT )
  673. {
  674. if( override_val < m_designSettings->m_MinClearance )
  675. {
  676. override_val = m_designSettings->m_MinClearance;
  677. msg = _( "board minimum" );
  678. REPORT( "" )
  679. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  680. MessageTextFromValue( override_val ) ) )
  681. }
  682. }
  683. else
  684. {
  685. if( override_val < m_designSettings->m_HoleClearance )
  686. {
  687. override_val = m_designSettings->m_HoleClearance;
  688. msg = _( "board minimum hole" );
  689. REPORT( "" )
  690. REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
  691. MessageTextFromValue( override_val ) ) )
  692. }
  693. }
  694. constraint.SetName( msg );
  695. constraint.m_Value.SetMin( override_val );
  696. return constraint;
  697. }
  698. }
  699. }
  700. else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
  701. {
  702. if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
  703. {
  704. wxString msg;
  705. ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
  706. REPORT( "" )
  707. REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
  708. EscapeHTML( pad->GetItemDescription( this, true ) ),
  709. EscapeHTML( PrintZoneConnection( override ) ) ) )
  710. constraint.SetName( msg );
  711. constraint.m_ZoneConnection = override;
  712. return constraint;
  713. }
  714. }
  715. else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
  716. {
  717. if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
  718. {
  719. wxString msg;
  720. int gap_override = pad->GetLocalThermalGapOverride( &msg );
  721. REPORT( "" )
  722. REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
  723. EscapeHTML( pad->GetItemDescription( this, true ) ),
  724. EscapeHTML( MessageTextFromValue( gap_override ) ) ) )
  725. constraint.SetName( msg );
  726. constraint.m_Value.SetMin( gap_override );
  727. return constraint;
  728. }
  729. }
  730. else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  731. {
  732. if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
  733. {
  734. wxString msg;
  735. int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
  736. REPORT( "" )
  737. REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
  738. EscapeHTML( pad->GetItemDescription( this, true ) ),
  739. EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
  740. if( zone && zone->GetMinThickness() > spoke_override )
  741. {
  742. spoke_override = zone->GetMinThickness();
  743. REPORT( "" )
  744. REPORT( wxString::Format( _( "%s min thickness: %s." ),
  745. EscapeHTML( zone->GetItemDescription( this, true ) ),
  746. EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
  747. }
  748. constraint.SetName( msg );
  749. constraint.m_Value.SetMin( spoke_override );
  750. return constraint;
  751. }
  752. }
  753. auto testAssertion =
  754. [&]( const DRC_ENGINE_CONSTRAINT* c )
  755. {
  756. REPORT( wxString::Format( _( "Checking assertion \"%s\"." ),
  757. EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
  758. if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer,
  759. aReporter ) )
  760. {
  761. REPORT( _( "Assertion passed." ) )
  762. }
  763. else
  764. {
  765. REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
  766. }
  767. };
  768. auto processConstraint =
  769. [&]( const DRC_ENGINE_CONSTRAINT* c )
  770. {
  771. bool implicit = c->parentRule && c->parentRule->m_Implicit;
  772. REPORT( "" )
  773. switch( c->constraint.m_Type )
  774. {
  775. case CLEARANCE_CONSTRAINT:
  776. case COURTYARD_CLEARANCE_CONSTRAINT:
  777. case SILK_CLEARANCE_CONSTRAINT:
  778. case HOLE_CLEARANCE_CONSTRAINT:
  779. case EDGE_CLEARANCE_CONSTRAINT:
  780. case PHYSICAL_CLEARANCE_CONSTRAINT:
  781. case PHYSICAL_HOLE_CLEARANCE_CONSTRAINT:
  782. REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
  783. EscapeHTML( c->constraint.GetName() ),
  784. MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
  785. break;
  786. case CREEPAGE_CONSTRAINT:
  787. REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
  788. EscapeHTML( c->constraint.GetName() ),
  789. MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
  790. break;
  791. case MAX_UNCOUPLED_CONSTRAINT:
  792. REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
  793. EscapeHTML( c->constraint.GetName() ),
  794. MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
  795. break;
  796. case SKEW_CONSTRAINT:
  797. REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
  798. EscapeHTML( c->constraint.GetName() ),
  799. MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
  800. break;
  801. case THERMAL_RELIEF_GAP_CONSTRAINT:
  802. REPORT( wxString::Format( _( "Checking %s gap: %s." ),
  803. EscapeHTML( c->constraint.GetName() ),
  804. MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
  805. break;
  806. case THERMAL_SPOKE_WIDTH_CONSTRAINT:
  807. REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
  808. EscapeHTML( c->constraint.GetName() ),
  809. MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
  810. break;
  811. case MIN_RESOLVED_SPOKES_CONSTRAINT:
  812. REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
  813. EscapeHTML( c->constraint.GetName() ),
  814. EDA_UNIT_UTILS::UI::MessageTextFromValue( unityScale, EDA_UNITS::UNSCALED,
  815. c->constraint.m_Value.Min() ) ) )
  816. break;
  817. case ZONE_CONNECTION_CONSTRAINT:
  818. REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
  819. EscapeHTML( c->constraint.GetName() ),
  820. EscapeHTML( PrintZoneConnection( c->constraint.m_ZoneConnection ) ) ) )
  821. break;
  822. case TRACK_WIDTH_CONSTRAINT:
  823. case ANNULAR_WIDTH_CONSTRAINT:
  824. case VIA_DIAMETER_CONSTRAINT:
  825. case HOLE_SIZE_CONSTRAINT:
  826. case TEXT_HEIGHT_CONSTRAINT:
  827. case TEXT_THICKNESS_CONSTRAINT:
  828. case DIFF_PAIR_GAP_CONSTRAINT:
  829. case LENGTH_CONSTRAINT:
  830. case CONNECTION_WIDTH_CONSTRAINT:
  831. case HOLE_TO_HOLE_CONSTRAINT:
  832. {
  833. if( aReporter )
  834. {
  835. wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  836. wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  837. wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  838. if( implicit )
  839. {
  840. min = MessageTextFromValue( c->constraint.m_Value.Min() );
  841. opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
  842. switch( c->constraint.m_Type )
  843. {
  844. case TRACK_WIDTH_CONSTRAINT:
  845. if( c->constraint.m_Value.HasOpt() )
  846. {
  847. REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
  848. EscapeHTML( c->constraint.GetName() ),
  849. opt ) )
  850. }
  851. else if( c->constraint.m_Value.HasMin() )
  852. {
  853. REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
  854. EscapeHTML( c->constraint.GetName() ),
  855. min ) )
  856. }
  857. break;
  858. case ANNULAR_WIDTH_CONSTRAINT:
  859. REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
  860. EscapeHTML( c->constraint.GetName() ),
  861. opt ) )
  862. break;
  863. case VIA_DIAMETER_CONSTRAINT:
  864. if( c->constraint.m_Value.HasOpt() )
  865. {
  866. REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
  867. EscapeHTML( c->constraint.GetName() ),
  868. opt ) )
  869. }
  870. else if( c->constraint.m_Value.HasMin() )
  871. {
  872. REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
  873. EscapeHTML( c->constraint.GetName() ),
  874. min ) )
  875. }
  876. break;
  877. case HOLE_SIZE_CONSTRAINT:
  878. if( c->constraint.m_Value.HasOpt() )
  879. {
  880. REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
  881. EscapeHTML( c->constraint.GetName() ),
  882. opt ) )
  883. }
  884. else if( c->constraint.m_Value.HasMin() )
  885. {
  886. REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
  887. EscapeHTML( c->constraint.GetName() ),
  888. min ) )
  889. }
  890. break;
  891. case TEXT_HEIGHT_CONSTRAINT:
  892. case TEXT_THICKNESS_CONSTRAINT:
  893. case CONNECTION_WIDTH_CONSTRAINT:
  894. REPORT( wxString::Format( _( "Checking %s: min %s." ),
  895. EscapeHTML( c->constraint.GetName() ),
  896. min ) )
  897. break;
  898. case DIFF_PAIR_GAP_CONSTRAINT:
  899. if( c->constraint.m_Value.HasOpt() )
  900. {
  901. REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
  902. EscapeHTML( c->constraint.GetName() ),
  903. opt ) )
  904. }
  905. else if( c->constraint.m_Value.HasMin() )
  906. {
  907. REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
  908. EscapeHTML( c->constraint.GetName() ),
  909. min ) )
  910. }
  911. break;
  912. case HOLE_TO_HOLE_CONSTRAINT:
  913. REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
  914. EscapeHTML( c->constraint.GetName() ),
  915. min ) )
  916. break;
  917. default:
  918. REPORT( wxString::Format( _( "Checking %s." ),
  919. EscapeHTML( c->constraint.GetName() ) ) )
  920. }
  921. }
  922. else
  923. {
  924. if( c->constraint.m_Value.HasMin() )
  925. min = MessageTextFromValue( c->constraint.m_Value.Min() );
  926. if( c->constraint.m_Value.HasOpt() )
  927. opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
  928. if( c->constraint.m_Value.HasMax() )
  929. max = MessageTextFromValue( c->constraint.m_Value.Max() );
  930. REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
  931. EscapeHTML( c->constraint.GetName() ),
  932. min,
  933. opt,
  934. max ) )
  935. }
  936. }
  937. break;
  938. }
  939. default:
  940. REPORT( wxString::Format( _( "Checking %s." ),
  941. EscapeHTML( c->constraint.GetName() ) ) )
  942. }
  943. if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
  944. {
  945. if( a_is_non_copper || b_is_non_copper )
  946. {
  947. if( implicit )
  948. {
  949. REPORT( _( "Netclass clearances apply only between copper items." ) )
  950. }
  951. else if( a_is_non_copper )
  952. {
  953. REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
  954. EscapeHTML( a->GetItemDescription( this, true ) ) ) )
  955. }
  956. else if( b_is_non_copper )
  957. {
  958. REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
  959. EscapeHTML( b->GetItemDescription( this, true ) ) ) )
  960. }
  961. return;
  962. }
  963. }
  964. else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
  965. {
  966. int mask;
  967. if( a->GetFlags() & HOLE_PROXY )
  968. {
  969. mask = DRC_DISALLOW_HOLES;
  970. }
  971. else if( a->Type() == PCB_VIA_T )
  972. {
  973. mask = DRC_DISALLOW_VIAS;
  974. switch( static_cast<const PCB_VIA*>( a )->GetViaType() )
  975. {
  976. case VIATYPE::BLIND_BURIED: mask |= DRC_DISALLOW_BB_VIAS; break;
  977. case VIATYPE::MICROVIA: mask |= DRC_DISALLOW_MICRO_VIAS; break;
  978. default: break;
  979. }
  980. }
  981. else
  982. {
  983. switch( a->Type() )
  984. {
  985. case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
  986. case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
  987. case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
  988. case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
  989. case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
  990. case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
  991. case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
  992. case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
  993. case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
  994. case PCB_ZONE_T:
  995. // Treat teardrop areas as tracks for DRC purposes
  996. if( static_cast<const ZONE*>( a )->IsTeardropArea() )
  997. mask = DRC_DISALLOW_TRACKS;
  998. else
  999. mask = DRC_DISALLOW_ZONES;
  1000. break;
  1001. case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
  1002. default: mask = 0; break;
  1003. }
  1004. }
  1005. if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
  1006. {
  1007. if( implicit )
  1008. REPORT( _( "Keepout constraint not met." ) )
  1009. else
  1010. REPORT( _( "Disallow constraint not met." ) )
  1011. return;
  1012. }
  1013. LSET itemLayers = a->GetLayerSet();
  1014. if( a->Type() == PCB_FOOTPRINT_T )
  1015. {
  1016. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
  1017. if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
  1018. itemLayers |= LSET::FrontMask();
  1019. if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
  1020. itemLayers |= LSET::BackMask();
  1021. }
  1022. if( !( c->layerTest & itemLayers ).any() )
  1023. {
  1024. if( implicit )
  1025. {
  1026. REPORT( _( "Keepout layer(s) not matched." ) )
  1027. }
  1028. else if( c->parentRule )
  1029. {
  1030. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  1031. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  1032. }
  1033. else
  1034. {
  1035. REPORT( _( "Rule layer not matched; rule ignored." ) )
  1036. }
  1037. return;
  1038. }
  1039. }
  1040. if( ( aLayer != UNDEFINED_LAYER && !c->layerTest.test( aLayer ) )
  1041. || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
  1042. {
  1043. if( implicit )
  1044. {
  1045. REPORT( _( "Constraint layer not matched." ) )
  1046. }
  1047. else if( c->parentRule )
  1048. {
  1049. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  1050. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  1051. }
  1052. else
  1053. {
  1054. REPORT( _( "Rule layer not matched; rule ignored." ) )
  1055. }
  1056. }
  1057. else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
  1058. && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
  1059. {
  1060. // Report non-drilled-holes as an implicit condition
  1061. REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
  1062. a->GetItemDescription( this, true ) ) )
  1063. }
  1064. else if( !c->condition || c->condition->GetExpression().IsEmpty() )
  1065. {
  1066. if( aReporter )
  1067. {
  1068. if( implicit )
  1069. {
  1070. REPORT( _( "Unconditional constraint applied." ) )
  1071. }
  1072. else if( constraint.m_Type == ASSERTION_CONSTRAINT )
  1073. {
  1074. REPORT( _( "Unconditional rule applied." ) )
  1075. testAssertion( c );
  1076. }
  1077. else
  1078. {
  1079. REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
  1080. }
  1081. }
  1082. applyConstraint( c );
  1083. }
  1084. else
  1085. {
  1086. if( implicit )
  1087. {
  1088. // Don't report on implicit rule conditions; they're synthetic.
  1089. }
  1090. else
  1091. {
  1092. REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
  1093. EscapeHTML( c->condition->GetExpression() ) ) )
  1094. }
  1095. if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
  1096. {
  1097. if( aReporter )
  1098. {
  1099. if( implicit )
  1100. {
  1101. REPORT( _( "Constraint applied." ) )
  1102. }
  1103. else if( constraint.m_Type == ASSERTION_CONSTRAINT )
  1104. {
  1105. REPORT( _( "Rule applied." ) )
  1106. testAssertion( c );
  1107. }
  1108. else
  1109. {
  1110. REPORT( _( "Rule applied; overrides previous constraints." ) )
  1111. }
  1112. }
  1113. applyConstraint( c );
  1114. }
  1115. else
  1116. {
  1117. REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
  1118. : _( "Condition not satisfied; rule ignored." ) )
  1119. }
  1120. }
  1121. };
  1122. if( m_constraintMap.count( aConstraintType ) )
  1123. {
  1124. std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
  1125. for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
  1126. processConstraint( rule );
  1127. }
  1128. if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
  1129. return constraint;
  1130. // Special case for pad zone connections which can iherit from their parent footprints.
  1131. // We've already checked for local overrides, and there were no rules targetting the pad
  1132. // itself, so we know we're inheriting and need to see if there are any rules targetting
  1133. // the parent footprint.
  1134. if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
  1135. || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
  1136. || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT ) )
  1137. {
  1138. REPORT( "" )
  1139. REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
  1140. EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
  1141. if( a == pad )
  1142. a = parentFootprint;
  1143. else
  1144. b = parentFootprint;
  1145. if( m_constraintMap.count( aConstraintType ) )
  1146. {
  1147. std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
  1148. for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
  1149. processConstraint( rule );
  1150. if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
  1151. return constraint;
  1152. }
  1153. }
  1154. // Unfortunately implicit rules don't work for local clearances (such as zones) because
  1155. // they have to be max'ed with netclass values (which are already implicit rules), and our
  1156. // rule selection paradigm is "winner takes all".
  1157. if( aConstraintType == CLEARANCE_CONSTRAINT )
  1158. {
  1159. int global = constraint.m_Value.Min();
  1160. int clearance = global;
  1161. bool needBlankLine = true;
  1162. if( ac && ac->GetLocalClearance().has_value() )
  1163. {
  1164. int localA = ac->GetLocalClearance().value();
  1165. if( needBlankLine )
  1166. {
  1167. REPORT( "" )
  1168. needBlankLine = false;
  1169. }
  1170. REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
  1171. EscapeHTML( a->GetItemDescription( this, true ) ),
  1172. MessageTextFromValue( localA ) ) )
  1173. if( localA > clearance )
  1174. {
  1175. wxString msg;
  1176. clearance = ac->GetLocalClearance( &msg ).value();
  1177. constraint.SetParentRule( nullptr );
  1178. constraint.SetName( msg );
  1179. constraint.m_Value.SetMin( clearance );
  1180. }
  1181. }
  1182. if( bc && bc->GetLocalClearance().has_value() )
  1183. {
  1184. int localB = bc->GetLocalClearance().value();
  1185. if( needBlankLine )
  1186. {
  1187. REPORT( "" )
  1188. needBlankLine = false;
  1189. }
  1190. REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
  1191. EscapeHTML( b->GetItemDescription( this, true ) ),
  1192. MessageTextFromValue( localB ) ) )
  1193. if( localB > clearance )
  1194. {
  1195. wxString msg;
  1196. clearance = bc->GetLocalClearance( &msg ).value();
  1197. constraint.SetParentRule( nullptr );
  1198. constraint.SetName( msg );
  1199. constraint.m_Value.SetMin( clearance );
  1200. }
  1201. }
  1202. if( !a_is_non_copper && !b_is_non_copper )
  1203. {
  1204. if( needBlankLine )
  1205. {
  1206. REPORT( "" )
  1207. needBlankLine = false;
  1208. }
  1209. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  1210. MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
  1211. if( clearance < m_designSettings->m_MinClearance )
  1212. {
  1213. constraint.SetParentRule( nullptr );
  1214. constraint.SetName( _( "board minimum" ) );
  1215. constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
  1216. }
  1217. }
  1218. return constraint;
  1219. }
  1220. else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
  1221. {
  1222. REPORT( "" )
  1223. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  1224. MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
  1225. if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
  1226. {
  1227. constraint.SetParentRule( nullptr );
  1228. constraint.SetName( _( "board minimum" ) );
  1229. constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
  1230. }
  1231. return constraint;
  1232. }
  1233. else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
  1234. {
  1235. if( pad && parentFootprint )
  1236. {
  1237. ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
  1238. if( local != ZONE_CONNECTION::INHERITED )
  1239. {
  1240. REPORT( "" )
  1241. REPORT( wxString::Format( _( "%s zone connection: %s." ),
  1242. EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
  1243. EscapeHTML( PrintZoneConnection( local ) ) ) )
  1244. constraint.SetParentRule( nullptr );
  1245. constraint.SetName( _( "footprint" ) );
  1246. constraint.m_ZoneConnection = local;
  1247. return constraint;
  1248. }
  1249. }
  1250. if( zone )
  1251. {
  1252. ZONE_CONNECTION local = zone->GetPadConnection();
  1253. REPORT( "" )
  1254. REPORT( wxString::Format( _( "%s pad connection: %s." ),
  1255. EscapeHTML( zone->GetItemDescription( this, true ) ),
  1256. EscapeHTML( PrintZoneConnection( local ) ) ) )
  1257. constraint.SetParentRule( nullptr );
  1258. constraint.SetName( _( "zone" ) );
  1259. constraint.m_ZoneConnection = local;
  1260. return constraint;
  1261. }
  1262. }
  1263. else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
  1264. {
  1265. if( zone )
  1266. {
  1267. int local = zone->GetThermalReliefGap();
  1268. REPORT( "" )
  1269. REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
  1270. EscapeHTML( zone->GetItemDescription( this, true ) ),
  1271. EscapeHTML( MessageTextFromValue( local ) ) ) )
  1272. constraint.SetParentRule( nullptr );
  1273. constraint.SetName( _( "zone" ) );
  1274. constraint.m_Value.SetMin( local );
  1275. return constraint;
  1276. }
  1277. }
  1278. else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  1279. {
  1280. if( zone )
  1281. {
  1282. int local = zone->GetThermalReliefSpokeWidth();
  1283. REPORT( "" )
  1284. REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
  1285. EscapeHTML( zone->GetItemDescription( this, true ) ),
  1286. EscapeHTML( MessageTextFromValue( local ) ) ) )
  1287. constraint.SetParentRule( nullptr );
  1288. constraint.SetName( _( "zone" ) );
  1289. constraint.m_Value.SetMin( local );
  1290. return constraint;
  1291. }
  1292. }
  1293. if( !constraint.GetParentRule() )
  1294. {
  1295. constraint.m_Type = NULL_CONSTRAINT;
  1296. constraint.m_DisallowFlags = 0;
  1297. }
  1298. return constraint;
  1299. }
  1300. void DRC_ENGINE::ProcessAssertions( const BOARD_ITEM* a,
  1301. std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
  1302. REPORTER* aReporter )
  1303. {
  1304. /*
  1305. * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
  1306. * kills performance when running bulk DRC tests (where aReporter is nullptr).
  1307. */
  1308. auto testAssertion =
  1309. [&]( const DRC_ENGINE_CONSTRAINT* c )
  1310. {
  1311. REPORT( wxString::Format( _( "Checking rule assertion \"%s\"." ),
  1312. EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
  1313. if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
  1314. a->GetLayer(), aReporter ) )
  1315. {
  1316. REPORT( _( "Assertion passed." ) )
  1317. }
  1318. else
  1319. {
  1320. REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
  1321. aFailureHandler( &c->constraint );
  1322. }
  1323. };
  1324. auto processConstraint =
  1325. [&]( const DRC_ENGINE_CONSTRAINT* c )
  1326. {
  1327. REPORT( "" )
  1328. REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
  1329. if( !( a->GetLayerSet() & c->layerTest ).any() )
  1330. {
  1331. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  1332. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  1333. }
  1334. if( !c->condition || c->condition->GetExpression().IsEmpty() )
  1335. {
  1336. REPORT( _( "Unconditional rule applied." ) )
  1337. testAssertion( c );
  1338. }
  1339. else
  1340. {
  1341. REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
  1342. EscapeHTML( c->condition->GetExpression() ) ) )
  1343. if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
  1344. a->GetLayer(), aReporter ) )
  1345. {
  1346. REPORT( _( "Rule applied." ) )
  1347. testAssertion( c );
  1348. }
  1349. else
  1350. {
  1351. REPORT( _( "Condition not satisfied; rule ignored." ) )
  1352. }
  1353. }
  1354. };
  1355. if( m_constraintMap.count( ASSERTION_CONSTRAINT ) )
  1356. {
  1357. std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
  1358. for( int ii = 0; ii < (int) ruleset->size(); ++ii )
  1359. processConstraint( ruleset->at( ii ) );
  1360. }
  1361. }
  1362. #undef REPORT
  1363. bool DRC_ENGINE::IsErrorLimitExceeded( int error_code )
  1364. {
  1365. assert( error_code >= 0 && error_code <= DRCE_LAST );
  1366. return m_errorLimits[ error_code ] <= 0;
  1367. }
  1368. void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
  1369. int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
  1370. {
  1371. static std::mutex globalLock;
  1372. m_errorLimits[ aItem->GetErrorCode() ] -= 1;
  1373. if( m_violationHandler )
  1374. {
  1375. std::lock_guard<std::mutex> guard( globalLock );
  1376. m_violationHandler( aItem, aPos, aMarkerLayer, aCustomHandler );
  1377. }
  1378. if( m_reporter )
  1379. {
  1380. wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
  1381. aItem->GetViolatingTest()->GetName(),
  1382. aItem->GetErrorMessage(),
  1383. aItem->GetErrorCode() );
  1384. DRC_RULE* rule = aItem->GetViolatingRule();
  1385. if( rule )
  1386. msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
  1387. m_reporter->Report( msg );
  1388. wxString violatingItemsStr = wxT( "Violating items: " );
  1389. m_reporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
  1390. aPos.x,
  1391. aPos.y ) );
  1392. }
  1393. }
  1394. void DRC_ENGINE::ReportAux ( const wxString& aStr )
  1395. {
  1396. if( !m_reporter )
  1397. return;
  1398. m_reporter->Report( aStr, RPT_SEVERITY_INFO );
  1399. }
  1400. bool DRC_ENGINE::KeepRefreshing( bool aWait )
  1401. {
  1402. if( !m_progressReporter )
  1403. return true;
  1404. return m_progressReporter->KeepRefreshing( aWait );
  1405. }
  1406. void DRC_ENGINE::AdvanceProgress()
  1407. {
  1408. if( m_progressReporter )
  1409. m_progressReporter->AdvanceProgress();
  1410. }
  1411. void DRC_ENGINE::SetMaxProgress( int aSize )
  1412. {
  1413. if( m_progressReporter )
  1414. m_progressReporter->SetMaxProgress( aSize );
  1415. }
  1416. bool DRC_ENGINE::ReportProgress( double aProgress )
  1417. {
  1418. if( !m_progressReporter )
  1419. return true;
  1420. m_progressReporter->SetCurrentProgress( aProgress );
  1421. return m_progressReporter->KeepRefreshing( false );
  1422. }
  1423. bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
  1424. {
  1425. if( !m_progressReporter )
  1426. return true;
  1427. m_progressReporter->AdvancePhase( aMessage );
  1428. bool retval = m_progressReporter->KeepRefreshing( false );
  1429. wxSafeYield( nullptr, true ); // Force an update for the message
  1430. return retval;
  1431. }
  1432. bool DRC_ENGINE::IsCancelled() const
  1433. {
  1434. return m_progressReporter && m_progressReporter->IsCancelled();
  1435. }
  1436. bool DRC_ENGINE::HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID )
  1437. {
  1438. //drc_dbg(10,"hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size( ) );
  1439. if( m_constraintMap.count( constraintID ) )
  1440. return m_constraintMap[ constraintID ]->size() > 0;
  1441. return false;
  1442. }
  1443. bool DRC_ENGINE::QueryWorstConstraint( DRC_CONSTRAINT_T aConstraintId, DRC_CONSTRAINT& aConstraint )
  1444. {
  1445. int worst = 0;
  1446. if( m_constraintMap.count( aConstraintId ) )
  1447. {
  1448. for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
  1449. {
  1450. int current = c->constraint.GetValue().Min();
  1451. if( current > worst )
  1452. {
  1453. worst = current;
  1454. aConstraint = c->constraint;
  1455. }
  1456. }
  1457. }
  1458. return worst > 0;
  1459. }
  1460. std::set<int> DRC_ENGINE::QueryDistinctConstraints( DRC_CONSTRAINT_T aConstraintId )
  1461. {
  1462. std::set<int> distinctMinimums;
  1463. if( m_constraintMap.count( aConstraintId ) )
  1464. {
  1465. for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
  1466. distinctMinimums.emplace( c->constraint.GetValue().Min() );
  1467. }
  1468. return distinctMinimums;
  1469. }
  1470. // fixme: move two functions below to pcbcommon?
  1471. int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
  1472. wxString& aBaseDpName )
  1473. {
  1474. int rv = 0;
  1475. int count = 0;
  1476. for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
  1477. {
  1478. int ch = *it;
  1479. if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
  1480. {
  1481. continue;
  1482. }
  1483. else if( ch == '+' )
  1484. {
  1485. aComplementNet = wxT( "-" );
  1486. rv = 1;
  1487. }
  1488. else if( ch == '-' )
  1489. {
  1490. aComplementNet = wxT( "+" );
  1491. rv = -1;
  1492. }
  1493. else if( ch == 'N' )
  1494. {
  1495. aComplementNet = wxT( "P" );
  1496. rv = -1;
  1497. }
  1498. else if ( ch == 'P' )
  1499. {
  1500. aComplementNet = wxT( "N" );
  1501. rv = 1;
  1502. }
  1503. else
  1504. {
  1505. break;
  1506. }
  1507. }
  1508. if( rv != 0 && count >= 1 )
  1509. {
  1510. aBaseDpName = aNetName.Left( aNetName.Length() - count );
  1511. aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
  1512. }
  1513. return rv;
  1514. }
  1515. bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
  1516. {
  1517. wxString refName = aNet->GetNetname();
  1518. wxString dummy, coupledNetName;
  1519. if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
  1520. {
  1521. NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
  1522. if( !net )
  1523. return false;
  1524. if( polarity > 0 )
  1525. {
  1526. aNetP = aNet->GetNetCode();
  1527. aNetN = net->GetNetCode();
  1528. }
  1529. else
  1530. {
  1531. aNetP = net->GetNetCode();
  1532. aNetN = aNet->GetNetCode();
  1533. }
  1534. return true;
  1535. }
  1536. return false;
  1537. }
  1538. /**
  1539. * Check if the given collision between a track and another item occurs during the track's entry
  1540. * into a net-tie pad.
  1541. */
  1542. bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
  1543. const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
  1544. {
  1545. FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
  1546. if( parentFootprint && parentFootprint->IsNetTie() )
  1547. {
  1548. int epsilon = GetDesignSettings()->GetDRCEpsilon();
  1549. std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
  1550. for( PAD* pad : parentFootprint->Pads() )
  1551. {
  1552. if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
  1553. {
  1554. if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
  1555. return true;
  1556. }
  1557. }
  1558. }
  1559. return false;
  1560. }
  1561. DRC_TEST_PROVIDER* DRC_ENGINE::GetTestProvider( const wxString& name ) const
  1562. {
  1563. for( DRC_TEST_PROVIDER* prov : m_testProviders )
  1564. {
  1565. if( name == prov->GetName() )
  1566. return prov;
  1567. }
  1568. return nullptr;
  1569. }