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.

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