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.

1731 lines
65 KiB

6 years ago
6 years ago
5 years ago
6 years 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
4 years ago
6 years ago
6 years ago
5 years 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 (C) 2017-2022 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 <thread>
  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 <footprint.h>
  39. #include <pad.h>
  40. #include <pcb_track.h>
  41. #include <zone.h>
  42. #include <geometry/shape.h>
  43. #include <geometry/shape_segment.h>
  44. #include <geometry/shape_null.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. void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
  50. {
  51. wxString valueStr;
  52. if( wxGetEnv( wxT( "DRC_DEBUG" ), &valueStr ) )
  53. {
  54. int setLevel = wxAtoi( valueStr );
  55. if( level <= setLevel )
  56. printf( "%-30s:%d | %s\n", function, line, (const char *) msg.c_str() );
  57. }
  58. }
  59. DRC_ENGINE::DRC_ENGINE( BOARD* aBoard, BOARD_DESIGN_SETTINGS *aSettings ) :
  60. m_designSettings ( aSettings ),
  61. m_board( aBoard ),
  62. m_drawingSheet( nullptr ),
  63. m_schematicNetlist( nullptr ),
  64. m_rulesValid( false ),
  65. m_userUnits( EDA_UNITS::MILLIMETRES ),
  66. m_reportAllTrackErrors( false ),
  67. m_testFootprints( false ),
  68. m_reporter( nullptr ),
  69. m_progressReporter( nullptr )
  70. {
  71. m_errorLimits.resize( DRCE_LAST + 1 );
  72. for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
  73. m_errorLimits[ ii ] = ERROR_LIMIT;
  74. }
  75. DRC_ENGINE::~DRC_ENGINE()
  76. {
  77. m_rules.clear();
  78. for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
  79. {
  80. for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
  81. delete constraint;
  82. delete pair.second;
  83. }
  84. }
  85. static bool isKeepoutZone( const BOARD_ITEM* aItem, bool aCheckFlags )
  86. {
  87. if( !aItem )
  88. return false;
  89. if( aItem->Type() != PCB_ZONE_T && aItem->Type() != PCB_FP_ZONE_T )
  90. return false;
  91. const ZONE* zone = static_cast<const ZONE*>( aItem );
  92. if( !zone->GetIsRuleArea() )
  93. return false;
  94. if( aCheckFlags )
  95. {
  96. if( !zone->GetDoNotAllowTracks()
  97. && !zone->GetDoNotAllowVias()
  98. && !zone->GetDoNotAllowPads()
  99. && !zone->GetDoNotAllowCopperPour()
  100. && !zone->GetDoNotAllowFootprints() )
  101. {
  102. return false;
  103. }
  104. }
  105. return true;
  106. }
  107. std::shared_ptr<DRC_RULE> DRC_ENGINE::createImplicitRule( const wxString& name )
  108. {
  109. std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
  110. rule->m_Name = name;
  111. rule->m_Implicit = true;
  112. addRule( rule );
  113. return rule;
  114. }
  115. void DRC_ENGINE::loadImplicitRules()
  116. {
  117. ReportAux( wxString::Format( wxT( "Building implicit rules (per-item/class overrides, etc...)" ) ) );
  118. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  119. // 1) global defaults
  120. std::shared_ptr<DRC_RULE> rule = createImplicitRule( _( "board setup constraints" ) );
  121. DRC_CONSTRAINT widthConstraint( TRACK_WIDTH_CONSTRAINT );
  122. widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
  123. rule->AddConstraint( widthConstraint );
  124. DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
  125. drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
  126. rule->AddConstraint( drillConstraint );
  127. DRC_CONSTRAINT annulusConstraint( ANNULAR_WIDTH_CONSTRAINT );
  128. annulusConstraint.Value().SetMin( bds.m_ViasMinAnnularWidth );
  129. rule->AddConstraint( annulusConstraint );
  130. DRC_CONSTRAINT diameterConstraint( VIA_DIAMETER_CONSTRAINT );
  131. diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
  132. rule->AddConstraint( diameterConstraint );
  133. DRC_CONSTRAINT holeToHoleConstraint( HOLE_TO_HOLE_CONSTRAINT );
  134. holeToHoleConstraint.Value().SetMin( bds.m_HoleToHoleMin );
  135. rule->AddConstraint( holeToHoleConstraint );
  136. rule = createImplicitRule( _( "board setup constraints zone fill strategy" ) );
  137. DRC_CONSTRAINT thermalSpokeCountConstraint( MIN_RESOLVED_SPOKES_CONSTRAINT );
  138. thermalSpokeCountConstraint.Value().SetMin( bds.m_MinResolvedSpokes );
  139. rule->AddConstraint( thermalSpokeCountConstraint );
  140. rule = createImplicitRule( _( "board setup constraints silk" ) );
  141. rule->m_LayerCondition = LSET( 2, F_SilkS, B_SilkS );
  142. DRC_CONSTRAINT silkClearanceConstraint( SILK_CLEARANCE_CONSTRAINT );
  143. silkClearanceConstraint.Value().SetMin( bds.m_SilkClearance );
  144. rule->AddConstraint( silkClearanceConstraint );
  145. rule = createImplicitRule( _( "board setup constraints silk text height" ) );
  146. rule->m_LayerCondition = LSET( 2, F_SilkS, B_SilkS );
  147. DRC_CONSTRAINT silkTextHeightConstraint( TEXT_HEIGHT_CONSTRAINT );
  148. silkTextHeightConstraint.Value().SetMin( bds.m_MinSilkTextHeight );
  149. rule->AddConstraint( silkTextHeightConstraint );
  150. rule = createImplicitRule( _( "board setup constraints silk text thickness" ) );
  151. rule->m_LayerCondition = LSET( 2, F_SilkS, B_SilkS );
  152. DRC_CONSTRAINT silkTextThicknessConstraint( TEXT_THICKNESS_CONSTRAINT );
  153. silkTextThicknessConstraint.Value().SetMin( bds.m_MinSilkTextThickness );
  154. rule->AddConstraint( silkTextThicknessConstraint );
  155. rule = createImplicitRule( _( "board setup constraints hole" ) );
  156. DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
  157. holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
  158. rule->AddConstraint( holeClearanceConstraint );
  159. rule = createImplicitRule( _( "board setup constraints edge" ) );
  160. DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
  161. edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
  162. rule->AddConstraint( edgeClearanceConstraint );
  163. rule = createImplicitRule( _( "board setup constraints courtyard" ) );
  164. DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
  165. holeToHoleConstraint.Value().SetMin( 0 );
  166. rule->AddConstraint( courtyardClearanceConstraint );
  167. // 2) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
  168. std::shared_ptr<DRC_RULE> uViaRule = createImplicitRule( _( "board setup micro-via constraints" ) );
  169. uViaRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Via_Type == 'Micro'" ) );
  170. DRC_CONSTRAINT uViaDrillConstraint( HOLE_SIZE_CONSTRAINT );
  171. uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
  172. uViaRule->AddConstraint( uViaDrillConstraint );
  173. DRC_CONSTRAINT uViaDiameterConstraint( VIA_DIAMETER_CONSTRAINT );
  174. uViaDiameterConstraint.Value().SetMin( bds.m_MicroViasMinSize );
  175. uViaRule->AddConstraint( uViaDiameterConstraint );
  176. if( !bds.m_MicroViasAllowed )
  177. {
  178. DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
  179. disallowConstraint.m_DisallowFlags = DRC_DISALLOW_MICRO_VIAS;
  180. uViaRule->AddConstraint( disallowConstraint );
  181. }
  182. if( !bds.m_BlindBuriedViaAllowed )
  183. {
  184. std::shared_ptr<DRC_RULE> bbViaRule = createImplicitRule( _( "board setup constraints" ) );
  185. bbViaRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Via_Type == 'Blind/buried'" ) );
  186. DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
  187. disallowConstraint.m_DisallowFlags = DRC_DISALLOW_BB_VIAS;
  188. bbViaRule->AddConstraint( disallowConstraint );
  189. }
  190. // 3) per-netclass rules
  191. std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
  192. std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
  193. auto makeNetclassRules =
  194. [&]( const NETCLASSPTR& nc, bool isDefault )
  195. {
  196. wxString ncName = nc->GetName();
  197. wxString expr;
  198. if( nc->GetClearance() || nc->GetTrackWidth() )
  199. {
  200. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  201. netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ), ncName );
  202. netclassRule->m_Implicit = true;
  203. expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
  204. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  205. netclassClearanceRules.push_back( netclassRule );
  206. if( nc->GetClearance() )
  207. {
  208. DRC_CONSTRAINT constraint( CLEARANCE_CONSTRAINT );
  209. constraint.Value().SetMin( nc->GetClearance() );
  210. netclassRule->AddConstraint( constraint );
  211. }
  212. if( nc->GetTrackWidth() )
  213. {
  214. DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
  215. constraint.Value().SetMin( bds.m_TrackMinWidth );
  216. constraint.Value().SetOpt( nc->GetTrackWidth() );
  217. netclassRule->AddConstraint( constraint );
  218. }
  219. }
  220. if( nc->GetDiffPairWidth() )
  221. {
  222. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  223. netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
  224. ncName );
  225. netclassRule->m_Implicit = true;
  226. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.inDiffPair('*')" ),
  227. ncName );
  228. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  229. netclassItemSpecificRules.push_back( netclassRule );
  230. DRC_CONSTRAINT constraint( TRACK_WIDTH_CONSTRAINT );
  231. constraint.Value().SetMin( bds.m_TrackMinWidth );
  232. constraint.Value().SetOpt( nc->GetDiffPairWidth() );
  233. netclassRule->AddConstraint( constraint );
  234. }
  235. if( nc->GetDiffPairGap() )
  236. {
  237. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  238. netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
  239. ncName );
  240. netclassRule->m_Implicit = true;
  241. expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
  242. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  243. netclassItemSpecificRules.push_back( netclassRule );
  244. DRC_CONSTRAINT constraint( DIFF_PAIR_GAP_CONSTRAINT );
  245. constraint.Value().SetMin( bds.m_MinClearance );
  246. constraint.Value().SetOpt( nc->GetDiffPairGap() );
  247. netclassRule->AddConstraint( constraint );
  248. // A narrower diffpair gap overrides the netclass min clearance
  249. if( nc->GetDiffPairGap() < nc->GetClearance() )
  250. {
  251. netclassRule = std::make_shared<DRC_RULE>();
  252. netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
  253. ncName );
  254. netclassRule->m_Implicit = true;
  255. expr = wxString::Format( wxT( "A.NetClass == '%s' && AB.isCoupledDiffPair()" ),
  256. ncName );
  257. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  258. netclassItemSpecificRules.push_back( netclassRule );
  259. DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
  260. min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
  261. netclassRule->AddConstraint( min_clearanceConstraint );
  262. }
  263. }
  264. if( nc->GetViaDiameter() || nc->GetViaDrill() )
  265. {
  266. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  267. netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ), ncName );
  268. netclassRule->m_Implicit = true;
  269. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ),
  270. ncName );
  271. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  272. netclassItemSpecificRules.push_back( netclassRule );
  273. if( nc->GetViaDiameter() )
  274. {
  275. DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
  276. constraint.Value().SetMin( bds.m_ViasMinSize );
  277. constraint.Value().SetOpt( nc->GetViaDiameter() );
  278. netclassRule->AddConstraint( constraint );
  279. }
  280. if( nc->GetViaDrill() )
  281. {
  282. DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
  283. constraint.Value().SetMin( bds.m_MinThroughDrill );
  284. constraint.Value().SetOpt( nc->GetViaDrill() );
  285. netclassRule->AddConstraint( constraint );
  286. }
  287. }
  288. if( nc->GetuViaDiameter() || nc->GetuViaDrill() )
  289. {
  290. std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
  291. netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ), ncName );
  292. netclassRule->m_Implicit = true;
  293. expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ),
  294. ncName );
  295. netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
  296. netclassItemSpecificRules.push_back( netclassRule );
  297. if( nc->GetuViaDiameter() )
  298. {
  299. DRC_CONSTRAINT constraint( VIA_DIAMETER_CONSTRAINT );
  300. constraint.Value().SetMin( bds.m_MicroViasMinSize );
  301. constraint.Value().SetMin( nc->GetuViaDiameter() );
  302. netclassRule->AddConstraint( constraint );
  303. }
  304. if( nc->GetuViaDrill() )
  305. {
  306. DRC_CONSTRAINT constraint( HOLE_SIZE_CONSTRAINT );
  307. constraint.Value().SetMin( bds.m_MicroViasMinDrill );
  308. constraint.Value().SetOpt( nc->GetuViaDrill() );
  309. netclassRule->AddConstraint( constraint );
  310. }
  311. }
  312. };
  313. m_board->SynchronizeNetsAndNetClasses();
  314. makeNetclassRules( bds.GetNetClasses().GetDefault(), true );
  315. for( const std::pair<const wxString, NETCLASSPTR>& netclass : bds.GetNetClasses() )
  316. makeNetclassRules( netclass.second, false );
  317. // The netclass clearance rules have to be sorted by min clearance so the right one fires
  318. // if 'A' and 'B' belong to two different netclasses.
  319. //
  320. // The item-specific netclass rules are all unary, so there's no 'A' vs 'B' issue.
  321. std::sort( netclassClearanceRules.begin(), netclassClearanceRules.end(),
  322. []( const std::shared_ptr<DRC_RULE>& lhs, const std::shared_ptr<DRC_RULE>& rhs )
  323. {
  324. return lhs->m_Constraints[0].m_Value.Min()
  325. < rhs->m_Constraints[0].m_Value.Min();
  326. } );
  327. for( std::shared_ptr<DRC_RULE>& ncRule : netclassClearanceRules )
  328. addRule( ncRule );
  329. for( std::shared_ptr<DRC_RULE>& ncRule : netclassItemSpecificRules )
  330. addRule( ncRule );
  331. // 3) keepout area rules
  332. std::vector<ZONE*> keepoutZones;
  333. for( ZONE* zone : m_board->Zones() )
  334. {
  335. if( isKeepoutZone( zone, true ) )
  336. keepoutZones.push_back( zone );
  337. }
  338. for( FOOTPRINT* footprint : m_board->Footprints() )
  339. {
  340. for( ZONE* zone : footprint->Zones() )
  341. {
  342. if( isKeepoutZone( zone, true ) )
  343. keepoutZones.push_back( zone );
  344. }
  345. }
  346. for( ZONE* zone : keepoutZones )
  347. {
  348. wxString name = zone->GetZoneName();
  349. if( name.IsEmpty() )
  350. rule = createImplicitRule( _( "keepout area" ) );
  351. else
  352. rule = createImplicitRule( wxString::Format( _( "keepout area '%s'" ), name ) );
  353. rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( wxT( "A.insideArea('%s')" ),
  354. zone->m_Uuid.AsString() ) );
  355. rule->m_LayerCondition = zone->GetLayerSet();
  356. int disallowFlags = 0;
  357. if( zone->GetDoNotAllowTracks() )
  358. disallowFlags |= DRC_DISALLOW_TRACKS;
  359. if( zone->GetDoNotAllowVias() )
  360. disallowFlags |= DRC_DISALLOW_VIAS;
  361. if( zone->GetDoNotAllowPads() )
  362. disallowFlags |= DRC_DISALLOW_PADS;
  363. if( zone->GetDoNotAllowCopperPour() )
  364. disallowFlags |= DRC_DISALLOW_ZONES;
  365. if( zone->GetDoNotAllowFootprints() )
  366. disallowFlags |= DRC_DISALLOW_FOOTPRINTS;
  367. DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
  368. disallowConstraint.m_DisallowFlags = disallowFlags;
  369. rule->AddConstraint( disallowConstraint );
  370. }
  371. ReportAux( wxString::Format( wxT( "Building %d implicit netclass rules" ),
  372. (int) netclassClearanceRules.size() ) );
  373. }
  374. void DRC_ENGINE::loadRules( const wxFileName& aPath )
  375. {
  376. if( aPath.FileExists() )
  377. {
  378. std::vector<std::shared_ptr<DRC_RULE>> rules;
  379. FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) );
  380. if( fp )
  381. {
  382. DRC_RULES_PARSER parser( fp, aPath.GetFullPath() );
  383. parser.Parse( rules, m_reporter );
  384. }
  385. // Copy the rules into the member variable afterwards so that if Parse() throws then
  386. // the possibly malformed rules won't contaminate the current ruleset.
  387. for( std::shared_ptr<DRC_RULE>& rule : rules )
  388. m_rules.push_back( rule );
  389. }
  390. }
  391. void DRC_ENGINE::compileRules()
  392. {
  393. ReportAux( wxString::Format( wxT( "Compiling Rules (%d rules): " ), (int) m_rules.size() ) );
  394. for( std::shared_ptr<DRC_RULE>& rule : m_rules )
  395. {
  396. DRC_RULE_CONDITION* condition = nullptr;
  397. if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
  398. {
  399. condition = rule->m_Condition;
  400. condition->Compile( nullptr );
  401. }
  402. for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
  403. {
  404. if( !m_constraintMap.count( constraint.m_Type ) )
  405. m_constraintMap[ constraint.m_Type ] = new std::vector<DRC_ENGINE_CONSTRAINT*>();
  406. DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
  407. engineConstraint->layerTest = rule->m_LayerCondition;
  408. engineConstraint->condition = condition;
  409. engineConstraint->constraint = constraint;
  410. engineConstraint->parentRule = rule;
  411. m_constraintMap[ constraint.m_Type ]->push_back( engineConstraint );
  412. }
  413. }
  414. }
  415. void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
  416. {
  417. m_testProviders = DRC_TEST_PROVIDER_REGISTRY::Instance().GetTestProviders();
  418. for( DRC_TEST_PROVIDER* provider : m_testProviders )
  419. {
  420. ReportAux( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
  421. provider->SetDRCEngine( this );
  422. }
  423. m_rules.clear();
  424. m_rulesValid = false;
  425. for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
  426. {
  427. for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
  428. delete constraint;
  429. delete pair.second;
  430. }
  431. m_constraintMap.clear();
  432. m_board->IncrementTimeStamp(); // Clear board-level caches
  433. try // attempt to load full set of rules (implicit + user rules)
  434. {
  435. loadImplicitRules();
  436. loadRules( aRulePath );
  437. compileRules();
  438. }
  439. catch( PARSE_ERROR& original_parse_error )
  440. {
  441. try // try again with just our implicit rules
  442. {
  443. loadImplicitRules();
  444. compileRules();
  445. }
  446. catch( PARSE_ERROR& )
  447. {
  448. wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
  449. }
  450. throw original_parse_error;
  451. }
  452. for( int ii = DRCE_FIRST; ii < DRCE_LAST; ++ii )
  453. m_errorLimits[ ii ] = ERROR_LIMIT;
  454. m_rulesValid = true;
  455. }
  456. void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints )
  457. {
  458. m_userUnits = aUnits;
  459. m_reportAllTrackErrors = aReportAllTrackErrors;
  460. m_testFootprints = aTestFootprints;
  461. for( int ii = DRCE_FIRST; ii < DRCE_LAST; ++ii )
  462. {
  463. if( m_designSettings->Ignore( ii ) )
  464. m_errorLimits[ ii ] = 0;
  465. else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
  466. m_errorLimits[ ii ] = EXTENDED_ERROR_LIMIT;
  467. else
  468. m_errorLimits[ ii ] = ERROR_LIMIT;
  469. }
  470. DRC_TEST_PROVIDER::Init();
  471. m_board->IncrementTimeStamp(); // Invalidate all caches
  472. if( !ReportPhase( _( "Tessellating copper zones..." ) ) )
  473. return;
  474. // Cache zone bounding boxes, triangulation, copper zone rtrees, and footprint courtyards
  475. // before we start.
  476. //
  477. std::vector<ZONE*> allZones = m_board->Zones();
  478. for( FOOTPRINT* footprint : m_board->Footprints() )
  479. {
  480. for( ZONE* zone : footprint->Zones() )
  481. allZones.push_back( zone );
  482. footprint->BuildPolyCourtyards();
  483. }
  484. size_t count = allZones.size();
  485. std::atomic<size_t> next( 0 );
  486. std::atomic<size_t> done( 0 );
  487. std::atomic<size_t> threads_finished( 0 );
  488. size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
  489. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  490. {
  491. std::thread t = std::thread(
  492. [ this, &allZones, &done, &threads_finished, &next, count ]( )
  493. {
  494. for( size_t i = next.fetch_add( 1 ); i < count; i = next.fetch_add( 1 ) )
  495. {
  496. ZONE* zone = allZones[ i ];
  497. zone->CacheBoundingBox();
  498. zone->CacheTriangulation();
  499. if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
  500. {
  501. std::unique_ptr<DRC_RTREE> rtree = std::make_unique<DRC_RTREE>();
  502. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  503. {
  504. if( IsCopperLayer( layer ) )
  505. rtree->Insert( zone, layer );
  506. }
  507. std::unique_lock<std::mutex> cacheLock( m_board->m_CachesMutex );
  508. m_board->m_CopperZoneRTrees[ zone ] = std::move( rtree );
  509. }
  510. if( IsCancelled() )
  511. break;
  512. done.fetch_add( 1 );
  513. }
  514. threads_finished.fetch_add( 1 );
  515. } );
  516. t.detach();
  517. }
  518. while( threads_finished < parallelThreadCount )
  519. {
  520. ReportProgress( (double) done / (double) count );
  521. std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
  522. }
  523. // Now run the tests.
  524. //
  525. for( DRC_TEST_PROVIDER* provider : m_testProviders )
  526. {
  527. ReportAux( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
  528. if( !provider->Run() )
  529. break;
  530. }
  531. }
  532. #define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
  533. #define UNITS aReporter ? aReporter->GetUnits() : EDA_UNITS::MILLIMETRES
  534. #define REPORT_VALUE( v ) MessageTextFromValue( UNITS, v )
  535. DRC_CONSTRAINT DRC_ENGINE::EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
  536. PCB_LAYER_ID aLayer, REPORTER* aReporter )
  537. {
  538. DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
  539. REPORT( "" )
  540. REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
  541. EscapeHTML( PrintZoneConnection( constraint.m_ZoneConnection ) ) ) )
  542. if( constraint.m_ZoneConnection == ZONE_CONNECTION::THT_THERMAL )
  543. {
  544. const PAD* pad = nullptr;
  545. if( a->Type() == PCB_PAD_T )
  546. pad = static_cast<const PAD*>( a );
  547. else if( b->Type() == PCB_PAD_T )
  548. pad = static_cast<const PAD*>( b );
  549. if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
  550. {
  551. constraint.m_ZoneConnection = ZONE_CONNECTION::THERMAL;
  552. }
  553. else
  554. {
  555. REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
  556. EscapeHTML( PrintZoneConnection( ZONE_CONNECTION::FULL ) ) ) )
  557. constraint.m_ZoneConnection = ZONE_CONNECTION::FULL;
  558. }
  559. }
  560. return constraint;
  561. }
  562. DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a,
  563. const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
  564. REPORTER* aReporter )
  565. {
  566. /*
  567. * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
  568. * kills performance when running bulk DRC tests (where aReporter is nullptr).
  569. */
  570. const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ?
  571. static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
  572. const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ?
  573. static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
  574. bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
  575. bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
  576. const PAD* pad = nullptr;
  577. const ZONE* zone = nullptr;
  578. if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
  579. || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
  580. || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  581. {
  582. if( a && a->Type() == PCB_PAD_T )
  583. pad = static_cast<const PAD*>( a );
  584. else if( a && ( a->Type() == PCB_ZONE_T || a->Type() == PCB_FP_ZONE_T ) )
  585. zone = static_cast<const ZONE*>( a );
  586. if( b && b->Type() == PCB_PAD_T )
  587. pad = static_cast<const PAD*>( b );
  588. else if( b && ( b->Type() == PCB_ZONE_T || b->Type() == PCB_FP_ZONE_T ) )
  589. zone = static_cast<const ZONE*>( b );
  590. }
  591. DRC_CONSTRAINT constraint;
  592. constraint.m_Type = aConstraintType;
  593. // Local overrides take precedence over everything *except* board min clearance
  594. if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
  595. {
  596. int override = 0;
  597. if( ac && !b_is_non_copper )
  598. {
  599. int overrideA = ac->GetLocalClearanceOverrides( nullptr );
  600. if( overrideA > 0 )
  601. {
  602. REPORT( "" )
  603. REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
  604. EscapeHTML( a->GetSelectMenuText( UNITS ) ),
  605. REPORT_VALUE( overrideA ) ) )
  606. override = ac->GetLocalClearanceOverrides( &m_msg );
  607. }
  608. }
  609. if( bc && !a_is_non_copper )
  610. {
  611. int overrideB = bc->GetLocalClearanceOverrides( nullptr );
  612. if( overrideB > 0 )
  613. {
  614. REPORT( "" )
  615. REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
  616. EscapeHTML( b->GetSelectMenuText( UNITS ) ),
  617. EscapeHTML( REPORT_VALUE( overrideB ) ) ) )
  618. if( overrideB > override )
  619. override = bc->GetLocalClearanceOverrides( &m_msg );
  620. }
  621. }
  622. if( override )
  623. {
  624. if( aConstraintType == CLEARANCE_CONSTRAINT )
  625. {
  626. if( override < m_designSettings->m_MinClearance )
  627. {
  628. override = m_designSettings->m_MinClearance;
  629. m_msg = _( "board minimum" );
  630. REPORT( "" )
  631. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  632. REPORT_VALUE( override ) ) )
  633. }
  634. }
  635. else
  636. {
  637. if( override < m_designSettings->m_HoleClearance )
  638. {
  639. override = m_designSettings->m_HoleClearance;
  640. m_msg = _( "board minimum hole" );
  641. REPORT( "" )
  642. REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
  643. REPORT_VALUE( override ) ) )
  644. }
  645. }
  646. constraint.SetName( m_msg );
  647. constraint.m_Value.SetMin( override );
  648. return constraint;
  649. }
  650. }
  651. else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
  652. {
  653. if( pad && pad->GetLocalZoneConnectionOverride( nullptr ) != ZONE_CONNECTION::INHERITED )
  654. {
  655. ZONE_CONNECTION override = pad->GetLocalZoneConnectionOverride( &m_msg );
  656. REPORT( "" )
  657. REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
  658. EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
  659. EscapeHTML( PrintZoneConnection( override ) ) ) )
  660. constraint.SetName( m_msg );
  661. constraint.m_ZoneConnection = override;
  662. return constraint;
  663. }
  664. }
  665. else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
  666. {
  667. if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
  668. {
  669. int gap_override = pad->GetLocalThermalGapOverride( &m_msg );
  670. REPORT( "" )
  671. REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
  672. EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
  673. EscapeHTML( REPORT_VALUE( gap_override ) ) ) )
  674. constraint.SetName( m_msg );
  675. constraint.m_Value.SetMin( gap_override );
  676. return constraint;
  677. }
  678. }
  679. else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  680. {
  681. if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
  682. {
  683. int spoke_override = pad->GetLocalSpokeWidthOverride( &m_msg );
  684. REPORT( "" )
  685. REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
  686. EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
  687. EscapeHTML( REPORT_VALUE( spoke_override ) ) ) )
  688. if( zone && zone->GetMinThickness() > spoke_override )
  689. {
  690. spoke_override = zone->GetMinThickness();
  691. REPORT( "" )
  692. REPORT( wxString::Format( _( "%s min thickness: %s." ),
  693. EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
  694. EscapeHTML( REPORT_VALUE( spoke_override ) ) ) )
  695. }
  696. constraint.SetName( m_msg );
  697. constraint.m_Value.SetMin( spoke_override );
  698. return constraint;
  699. }
  700. }
  701. auto testAssertion =
  702. [&]( const DRC_ENGINE_CONSTRAINT* c )
  703. {
  704. REPORT( wxString::Format( _( "Checking assertion \"%s\"." ),
  705. EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
  706. if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer,
  707. aReporter ) )
  708. {
  709. REPORT( _( "Assertion passed." ) )
  710. }
  711. else
  712. {
  713. REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
  714. }
  715. };
  716. auto processConstraint =
  717. [&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
  718. {
  719. bool implicit = c->parentRule && c->parentRule->m_Implicit;
  720. REPORT( "" )
  721. switch( c->constraint.m_Type )
  722. {
  723. case CLEARANCE_CONSTRAINT:
  724. case COURTYARD_CLEARANCE_CONSTRAINT:
  725. case SILK_CLEARANCE_CONSTRAINT:
  726. case HOLE_CLEARANCE_CONSTRAINT:
  727. case EDGE_CLEARANCE_CONSTRAINT:
  728. case MECHANICAL_CLEARANCE_CONSTRAINT:
  729. case MECHANICAL_HOLE_CLEARANCE_CONSTRAINT:
  730. REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
  731. EscapeHTML( c->constraint.GetName() ),
  732. REPORT_VALUE( c->constraint.m_Value.Min() ) ) )
  733. break;
  734. case DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT:
  735. REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
  736. EscapeHTML( c->constraint.GetName() ),
  737. REPORT_VALUE( c->constraint.m_Value.Max() ) ) )
  738. break;
  739. case SKEW_CONSTRAINT:
  740. REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
  741. EscapeHTML( c->constraint.GetName() ),
  742. REPORT_VALUE( c->constraint.m_Value.Max() ) ) )
  743. break;
  744. case THERMAL_RELIEF_GAP_CONSTRAINT:
  745. REPORT( wxString::Format( _( "Checking %s gap: %s." ),
  746. EscapeHTML( c->constraint.GetName() ),
  747. REPORT_VALUE( c->constraint.m_Value.Min() ) ) )
  748. break;
  749. case THERMAL_SPOKE_WIDTH_CONSTRAINT:
  750. REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
  751. EscapeHTML( c->constraint.GetName() ),
  752. REPORT_VALUE( c->constraint.m_Value.Opt() ) ) )
  753. break;
  754. case MIN_RESOLVED_SPOKES_CONSTRAINT:
  755. REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
  756. EscapeHTML( c->constraint.GetName() ),
  757. MessageTextFromValue( EDA_UNITS::UNSCALED, c->constraint.m_Value.Min() ) ) )
  758. break;
  759. case ZONE_CONNECTION_CONSTRAINT:
  760. REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
  761. EscapeHTML( c->constraint.GetName() ),
  762. EscapeHTML( PrintZoneConnection( c->constraint.m_ZoneConnection ) ) ) )
  763. break;
  764. case TRACK_WIDTH_CONSTRAINT:
  765. case ANNULAR_WIDTH_CONSTRAINT:
  766. case VIA_DIAMETER_CONSTRAINT:
  767. case HOLE_SIZE_CONSTRAINT:
  768. case TEXT_HEIGHT_CONSTRAINT:
  769. case TEXT_THICKNESS_CONSTRAINT:
  770. case DIFF_PAIR_GAP_CONSTRAINT:
  771. case LENGTH_CONSTRAINT:
  772. {
  773. if( aReporter )
  774. {
  775. wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  776. wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  777. wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
  778. wxString msg;
  779. if( implicit )
  780. {
  781. min = StringFromValue( UNITS, c->constraint.m_Value.Min(), true );
  782. opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true );
  783. switch( c->constraint.m_Type )
  784. {
  785. case TRACK_WIDTH_CONSTRAINT:
  786. if( c->constraint.m_Value.HasOpt() )
  787. {
  788. REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
  789. EscapeHTML( c->constraint.GetName() ),
  790. opt ) )
  791. }
  792. else if( c->constraint.m_Value.HasMin() )
  793. {
  794. REPORT( wxString::Format( _( "Checking board setup constraints "
  795. "track width: min %s." ),
  796. min ) )
  797. }
  798. break;
  799. case ANNULAR_WIDTH_CONSTRAINT:
  800. REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
  801. EscapeHTML( c->constraint.GetName() ),
  802. opt ) )
  803. break;
  804. case VIA_DIAMETER_CONSTRAINT:
  805. if( c->constraint.m_Value.HasOpt() )
  806. {
  807. REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
  808. EscapeHTML( c->constraint.GetName() ),
  809. opt ) )
  810. }
  811. else if( c->constraint.m_Value.HasMin() )
  812. {
  813. REPORT( wxString::Format( _( "Checking board setup constraints "
  814. "via diameter: min %s." ),
  815. min ) )
  816. }
  817. break;
  818. case HOLE_SIZE_CONSTRAINT:
  819. if( c->constraint.m_Value.HasOpt() )
  820. {
  821. REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
  822. EscapeHTML( c->constraint.GetName() ),
  823. opt ) )
  824. }
  825. else if( c->constraint.m_Value.HasMin() )
  826. {
  827. REPORT( wxString::Format( _( "Checking board setup constraints "
  828. "hole size: min %s." ),
  829. min ) )
  830. }
  831. break;
  832. case TEXT_HEIGHT_CONSTRAINT:
  833. REPORT( wxString::Format( _( "Checking %s: min %s." ),
  834. EscapeHTML( c->constraint.GetName() ),
  835. min ) )
  836. break;
  837. case TEXT_THICKNESS_CONSTRAINT:
  838. REPORT( wxString::Format( _( "Checking %s: min %s." ),
  839. EscapeHTML( c->constraint.GetName() ),
  840. min ) )
  841. break;
  842. case DIFF_PAIR_GAP_CONSTRAINT:
  843. if( c->constraint.m_Value.HasOpt() )
  844. {
  845. REPORT( wxString::Format( _( "Checking %s diff pair gap: "
  846. "opt %s." ),
  847. EscapeHTML( c->constraint.GetName() ),
  848. opt ) )
  849. }
  850. else if( c->constraint.m_Value.HasMin() )
  851. {
  852. REPORT( wxString::Format( _( "Checking board setup constraints "
  853. "clearance: min %s." ),
  854. min ) )
  855. }
  856. break;
  857. default:
  858. REPORT( wxString::Format( _( "Checking %s." ),
  859. EscapeHTML( c->constraint.GetName() ) ) )
  860. }
  861. }
  862. else
  863. {
  864. if( c->constraint.m_Value.HasMin() )
  865. min = StringFromValue( UNITS, c->constraint.m_Value.Min(), true );
  866. if( c->constraint.m_Value.HasOpt() )
  867. opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true );
  868. if( c->constraint.m_Value.HasMax() )
  869. max = StringFromValue( UNITS, c->constraint.m_Value.Max(), true );
  870. REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
  871. EscapeHTML( c->constraint.GetName() ),
  872. min,
  873. opt,
  874. max ) )
  875. }
  876. }
  877. break;
  878. }
  879. default:
  880. REPORT( wxString::Format( _( "Checking %s." ),
  881. EscapeHTML( c->constraint.GetName() ) ) )
  882. }
  883. if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
  884. {
  885. if( implicit && ( a_is_non_copper || b_is_non_copper ) )
  886. {
  887. REPORT( _( "Board and netclass clearances apply only between copper "
  888. "items." ) );
  889. return true;
  890. }
  891. }
  892. else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
  893. {
  894. int mask;
  895. if( a->GetFlags() & HOLE_PROXY )
  896. {
  897. mask = DRC_DISALLOW_HOLES;
  898. }
  899. else if( a->Type() == PCB_VIA_T )
  900. {
  901. mask = DRC_DISALLOW_VIAS;
  902. switch( static_cast<const PCB_VIA*>( a )->GetViaType() )
  903. {
  904. case VIATYPE::BLIND_BURIED: mask |= DRC_DISALLOW_BB_VIAS; break;
  905. case VIATYPE::MICROVIA: mask |= DRC_DISALLOW_MICRO_VIAS; break;
  906. default: break;
  907. }
  908. }
  909. else
  910. {
  911. switch( a->Type() )
  912. {
  913. case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
  914. case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
  915. case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
  916. case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
  917. case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
  918. case PCB_FP_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
  919. case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
  920. case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
  921. case PCB_FP_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
  922. case PCB_FP_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
  923. case PCB_ZONE_T: mask = DRC_DISALLOW_ZONES; break;
  924. case PCB_FP_ZONE_T: mask = DRC_DISALLOW_ZONES; break;
  925. case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
  926. default: mask = 0; break;
  927. }
  928. }
  929. if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
  930. {
  931. if( implicit )
  932. REPORT( _( "Keepout constraint not met." ) )
  933. else
  934. REPORT( _( "Disallow constraint not met." ) )
  935. return false;
  936. }
  937. LSET itemLayers = a->GetLayerSet();
  938. if( a->Type() == PCB_FOOTPRINT_T )
  939. {
  940. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
  941. if( !footprint->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
  942. itemLayers |= LSET::FrontMask();
  943. if( !footprint->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
  944. itemLayers |= LSET::BackMask();
  945. }
  946. if( !( c->layerTest & itemLayers ).any() )
  947. {
  948. if( implicit )
  949. {
  950. REPORT( _( "Keepout layer(s) not matched." ) )
  951. }
  952. else if( c->parentRule )
  953. {
  954. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  955. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  956. }
  957. else
  958. {
  959. REPORT( _( "Rule layer not matched; rule ignored." ) )
  960. }
  961. return false;
  962. }
  963. }
  964. if( ( aLayer != UNDEFINED_LAYER && !c->layerTest.test( aLayer ) )
  965. || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
  966. {
  967. if( implicit )
  968. {
  969. REPORT( "Constraint layer not matched." )
  970. }
  971. else if( c->parentRule )
  972. {
  973. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  974. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  975. }
  976. else
  977. {
  978. REPORT( _( "Rule layer not matched; rule ignored." ) )
  979. }
  980. return false;
  981. }
  982. if( !c->condition || c->condition->GetExpression().IsEmpty() )
  983. {
  984. if( aReporter )
  985. {
  986. if( implicit )
  987. {
  988. REPORT( _( "Unconditional constraint applied." ) )
  989. }
  990. else if( constraint.m_Type == ASSERTION_CONSTRAINT )
  991. {
  992. REPORT( _( "Unconditional rule applied." ) )
  993. testAssertion( c );
  994. }
  995. else
  996. {
  997. REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
  998. }
  999. }
  1000. constraint = c->constraint;
  1001. return true;
  1002. }
  1003. else
  1004. {
  1005. if( implicit )
  1006. {
  1007. // Don't report on implicit rule conditions; they're synthetic.
  1008. }
  1009. else
  1010. {
  1011. REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
  1012. EscapeHTML( c->condition->GetExpression() ) ) )
  1013. }
  1014. if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
  1015. {
  1016. if( aReporter )
  1017. {
  1018. if( implicit )
  1019. {
  1020. REPORT( _( "Constraint applied." ) )
  1021. }
  1022. else if( constraint.m_Type == ASSERTION_CONSTRAINT )
  1023. {
  1024. REPORT( _( "Rule applied." ) )
  1025. testAssertion( c );
  1026. }
  1027. else
  1028. {
  1029. REPORT( _( "Rule applied; overrides previous constraints." ) )
  1030. }
  1031. }
  1032. if( c->constraint.m_Value.HasMin() )
  1033. constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
  1034. if( c->constraint.m_Value.HasOpt() )
  1035. constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
  1036. if( c->constraint.m_Value.HasMax() )
  1037. constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
  1038. // While the expectation would be to OR the disallow flags, we've already
  1039. // masked them down to aItem's type -- so we're really only looking for a
  1040. // boolean here.
  1041. constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
  1042. constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
  1043. constraint.SetParentRule( c->constraint.GetParentRule() );
  1044. return true;
  1045. }
  1046. else
  1047. {
  1048. REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
  1049. : _( "Condition not satisfied; rule ignored." ) )
  1050. return false;
  1051. }
  1052. }
  1053. };
  1054. if( m_constraintMap.count( aConstraintType ) )
  1055. {
  1056. std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
  1057. for( int ii = 0; ii < (int) ruleset->size(); ++ii )
  1058. processConstraint( ruleset->at( ii ) );
  1059. }
  1060. if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
  1061. return constraint;
  1062. // Unfortunately implicit rules don't work for local clearances (such as zones) because
  1063. // they have to be max'ed with netclass values (which are already implicit rules), and our
  1064. // rule selection paradigm is "winner takes all".
  1065. if( aConstraintType == CLEARANCE_CONSTRAINT )
  1066. {
  1067. int global = constraint.m_Value.Min();
  1068. int localA = ac ? ac->GetLocalClearance( nullptr ) : 0;
  1069. int localB = bc ? bc->GetLocalClearance( nullptr ) : 0;
  1070. int clearance = global;
  1071. if( localA > 0 )
  1072. {
  1073. REPORT( "" )
  1074. REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
  1075. EscapeHTML( a->GetSelectMenuText( UNITS ) ),
  1076. REPORT_VALUE( localA ) ) )
  1077. if( localA > clearance )
  1078. {
  1079. clearance = ac->GetLocalClearance( &m_msg );
  1080. constraint.SetParentRule( nullptr );
  1081. constraint.SetName( m_msg );
  1082. constraint.m_Value.SetMin( clearance );
  1083. }
  1084. }
  1085. if( localB > 0 )
  1086. {
  1087. REPORT( "" )
  1088. REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
  1089. EscapeHTML( b->GetSelectMenuText( UNITS ) ),
  1090. REPORT_VALUE( localB ) ) )
  1091. if( localB > clearance )
  1092. {
  1093. clearance = bc->GetLocalClearance( &m_msg );
  1094. constraint.SetParentRule( nullptr );
  1095. constraint.SetName( m_msg );
  1096. constraint.m_Value.SetMin( clearance );
  1097. }
  1098. }
  1099. REPORT( "" )
  1100. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  1101. REPORT_VALUE( m_designSettings->m_MinClearance ) ) )
  1102. if( clearance < m_designSettings->m_MinClearance )
  1103. {
  1104. constraint.SetParentRule( nullptr );
  1105. constraint.SetName( _( "board minimum" ) );
  1106. constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
  1107. }
  1108. return constraint;
  1109. }
  1110. else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
  1111. {
  1112. REPORT( "" )
  1113. REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
  1114. REPORT_VALUE( m_designSettings->m_MinClearance ) ) )
  1115. if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
  1116. {
  1117. constraint.SetParentRule( nullptr );
  1118. constraint.SetName( _( "board minimum" ) );
  1119. constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
  1120. }
  1121. return constraint;
  1122. }
  1123. else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
  1124. {
  1125. if( pad && pad->GetParent() )
  1126. {
  1127. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( pad->GetParent() );
  1128. ZONE_CONNECTION local = footprint->GetZoneConnection();
  1129. if( local != ZONE_CONNECTION::INHERITED )
  1130. {
  1131. REPORT( "" )
  1132. REPORT( wxString::Format( _( "%s zone connection: %s." ),
  1133. EscapeHTML( footprint->GetSelectMenuText( UNITS ) ),
  1134. EscapeHTML( PrintZoneConnection( local ) ) ) )
  1135. constraint.SetParentRule( nullptr );
  1136. constraint.SetName( _( "footprint" ) );
  1137. constraint.m_ZoneConnection = local;
  1138. return constraint;
  1139. }
  1140. }
  1141. if( zone )
  1142. {
  1143. ZONE_CONNECTION local = zone->GetPadConnection();
  1144. REPORT( "" )
  1145. REPORT( wxString::Format( _( "%s pad connection: %s." ),
  1146. EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
  1147. EscapeHTML( PrintZoneConnection( local ) ) ) )
  1148. constraint.SetParentRule( nullptr );
  1149. constraint.SetName( _( "zone" ) );
  1150. constraint.m_ZoneConnection = local;
  1151. return constraint;
  1152. }
  1153. }
  1154. else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
  1155. {
  1156. if( zone )
  1157. {
  1158. int local = zone->GetThermalReliefGap();
  1159. REPORT( "" )
  1160. REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
  1161. EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
  1162. EscapeHTML( REPORT_VALUE( local ) ) ) )
  1163. constraint.SetParentRule( nullptr );
  1164. constraint.SetName( _( "zone" ) );
  1165. constraint.m_Value.SetMin( local );
  1166. return constraint;
  1167. }
  1168. }
  1169. else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
  1170. {
  1171. if( zone )
  1172. {
  1173. int local = zone->GetThermalReliefSpokeWidth();
  1174. REPORT( "" )
  1175. REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
  1176. EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
  1177. EscapeHTML( REPORT_VALUE( local ) ) ) )
  1178. constraint.SetParentRule( nullptr );
  1179. constraint.SetName( _( "zone" ) );
  1180. constraint.m_Value.SetMin( local );
  1181. return constraint;
  1182. }
  1183. }
  1184. if( !constraint.GetParentRule() )
  1185. {
  1186. constraint.m_Type = NULL_CONSTRAINT;
  1187. constraint.m_DisallowFlags = 0;
  1188. }
  1189. return constraint;
  1190. }
  1191. void DRC_ENGINE::ProcessAssertions( const BOARD_ITEM* a,
  1192. std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
  1193. REPORTER* aReporter )
  1194. {
  1195. /*
  1196. * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
  1197. * kills performance when running bulk DRC tests (where aReporter is nullptr).
  1198. */
  1199. auto testAssertion =
  1200. [&]( const DRC_ENGINE_CONSTRAINT* c )
  1201. {
  1202. REPORT( wxString::Format( _( "Checking rule assertion \"%s\"." ),
  1203. EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
  1204. if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
  1205. a->GetLayer(), aReporter ) )
  1206. {
  1207. REPORT( _( "Assertion passed." ) )
  1208. }
  1209. else
  1210. {
  1211. REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
  1212. aFailureHandler( &c->constraint );
  1213. }
  1214. };
  1215. auto processConstraint =
  1216. [&]( const DRC_ENGINE_CONSTRAINT* c )
  1217. {
  1218. REPORT( "" )
  1219. REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
  1220. if( !( a->GetLayerSet() & c->layerTest ).any() )
  1221. {
  1222. REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
  1223. EscapeHTML( c->parentRule->m_LayerSource ) ) )
  1224. }
  1225. if( !c->condition || c->condition->GetExpression().IsEmpty() )
  1226. {
  1227. REPORT( _( "Unconditional rule applied." ) )
  1228. testAssertion( c );
  1229. }
  1230. else
  1231. {
  1232. REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
  1233. EscapeHTML( c->condition->GetExpression() ) ) )
  1234. if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
  1235. a->GetLayer(), aReporter ) )
  1236. {
  1237. REPORT( _( "Rule applied." ) )
  1238. testAssertion( c );
  1239. }
  1240. else
  1241. {
  1242. REPORT( _( "Condition not satisfied; rule ignored." ) )
  1243. }
  1244. }
  1245. };
  1246. if( m_constraintMap.count( ASSERTION_CONSTRAINT ) )
  1247. {
  1248. std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
  1249. for( int ii = 0; ii < (int) ruleset->size(); ++ii )
  1250. processConstraint( ruleset->at( ii ) );
  1251. }
  1252. }
  1253. #undef REPORT
  1254. #undef UNITS
  1255. #undef REPORT_VALUE
  1256. bool DRC_ENGINE::IsErrorLimitExceeded( int error_code )
  1257. {
  1258. assert( error_code >= 0 && error_code <= DRCE_LAST );
  1259. return m_errorLimits[ error_code ] <= 0;
  1260. }
  1261. void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
  1262. PCB_LAYER_ID aMarkerLayer )
  1263. {
  1264. m_errorLimits[ aItem->GetErrorCode() ] -= 1;
  1265. if( m_violationHandler )
  1266. m_violationHandler( aItem, aPos, aMarkerLayer );
  1267. if( m_reporter )
  1268. {
  1269. wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
  1270. aItem->GetViolatingTest()->GetName(),
  1271. aItem->GetErrorMessage(),
  1272. aItem->GetErrorCode() );
  1273. DRC_RULE* rule = aItem->GetViolatingRule();
  1274. if( rule )
  1275. msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
  1276. m_reporter->Report( msg );
  1277. wxString violatingItemsStr = wxT( "Violating items: " );
  1278. m_reporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
  1279. aPos.x,
  1280. aPos.y ) );
  1281. }
  1282. }
  1283. void DRC_ENGINE::ReportAux ( const wxString& aStr )
  1284. {
  1285. if( !m_reporter )
  1286. return;
  1287. m_reporter->Report( aStr, RPT_SEVERITY_INFO );
  1288. }
  1289. bool DRC_ENGINE::ReportProgress( double aProgress )
  1290. {
  1291. if( !m_progressReporter )
  1292. return true;
  1293. m_progressReporter->SetCurrentProgress( aProgress );
  1294. return m_progressReporter->KeepRefreshing( false );
  1295. }
  1296. bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
  1297. {
  1298. if( !m_progressReporter )
  1299. return true;
  1300. m_progressReporter->AdvancePhase( aMessage );
  1301. return m_progressReporter->KeepRefreshing( false );
  1302. }
  1303. bool DRC_ENGINE::IsCancelled() const
  1304. {
  1305. return m_progressReporter && m_progressReporter->IsCancelled();
  1306. }
  1307. bool DRC_ENGINE::HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID )
  1308. {
  1309. //drc_dbg(10,"hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size( ) );
  1310. if( m_constraintMap.count( constraintID ) )
  1311. return m_constraintMap[ constraintID ]->size() > 0;
  1312. return false;
  1313. }
  1314. bool DRC_ENGINE::QueryWorstConstraint( DRC_CONSTRAINT_T aConstraintId, DRC_CONSTRAINT& aConstraint )
  1315. {
  1316. int worst = 0;
  1317. if( m_constraintMap.count( aConstraintId ) )
  1318. {
  1319. for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
  1320. {
  1321. int current = c->constraint.GetValue().Min();
  1322. if( current > worst )
  1323. {
  1324. worst = current;
  1325. aConstraint = c->constraint;
  1326. }
  1327. }
  1328. }
  1329. return worst > 0;
  1330. }
  1331. // fixme: move two functions below to pcbcommon?
  1332. int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
  1333. wxString& aBaseDpName )
  1334. {
  1335. int rv = 0;
  1336. int count = 0;
  1337. for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
  1338. {
  1339. int ch = *it;
  1340. if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
  1341. {
  1342. continue;
  1343. }
  1344. else if( ch == '+' )
  1345. {
  1346. aComplementNet = wxT( "-" );
  1347. rv = 1;
  1348. }
  1349. else if( ch == '-' )
  1350. {
  1351. aComplementNet = wxT( "+" );
  1352. rv = -1;
  1353. }
  1354. else if( ch == 'N' )
  1355. {
  1356. aComplementNet = wxT( "P" );
  1357. rv = -1;
  1358. }
  1359. else if ( ch == 'P' )
  1360. {
  1361. aComplementNet = wxT( "N" );
  1362. rv = 1;
  1363. }
  1364. else
  1365. {
  1366. break;
  1367. }
  1368. }
  1369. if( rv != 0 && count >= 1 )
  1370. {
  1371. aBaseDpName = aNetName.Left( aNetName.Length() - count );
  1372. aComplementNet = aBaseDpName + aComplementNet + aNetName.Right( count - 1 );
  1373. }
  1374. return rv;
  1375. }
  1376. bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
  1377. {
  1378. wxString refName = aNet->GetNetname();
  1379. wxString dummy, coupledNetName;
  1380. if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
  1381. {
  1382. NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
  1383. if( !net )
  1384. return false;
  1385. if( polarity > 0 )
  1386. {
  1387. aNetP = aNet->GetNetCode();
  1388. aNetN = net->GetNetCode();
  1389. }
  1390. else
  1391. {
  1392. aNetP = net->GetNetCode();
  1393. aNetN = aNet->GetNetCode();
  1394. }
  1395. return true;
  1396. }
  1397. return false;
  1398. }
  1399. bool DRC_ENGINE::IsNetTie( BOARD_ITEM* aItem )
  1400. {
  1401. if( aItem->GetParent() && aItem->GetParent()->Type() == PCB_FOOTPRINT_T )
  1402. return static_cast<FOOTPRINT*>( aItem->GetParent() )->IsNetTie();
  1403. return false;
  1404. }
  1405. DRC_TEST_PROVIDER* DRC_ENGINE::GetTestProvider( const wxString& name ) const
  1406. {
  1407. for( auto prov : m_testProviders )
  1408. {
  1409. if( name == prov->GetName() )
  1410. return prov;
  1411. }
  1412. return nullptr;
  1413. }