KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_engine.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2014 Dick Hollenbeck, [email protected]
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <atomic>
27#include <wx/log.h>
28#include <reporter.h>
29#include <progress_reporter.h>
30#include <string_utils.h>
32#include <drc/drc_engine.h>
33#include <drc/drc_rtree.h>
34#include <drc/drc_rule_parser.h>
35#include <drc/drc_rule.h>
38#include <drc/drc_item.h>
40#include <footprint.h>
41#include <pad.h>
42#include <pcb_track.h>
43#include <pcb_shape.h>
44#include <core/profile.h>
45#include <thread_pool.h>
46#include <zone.h>
47
48
49// wxListBox's performance degrades horrifically with very large datasets. It's not clear
50// they're useful to the user anyway.
51#define ERROR_LIMIT 199
52#define EXTENDED_ERROR_LIMIT 499
53
54
62static const wxChar* traceDrcProfile = wxT( "KICAD_DRC_PROFILE" );
63
64
65void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
66{
67 wxString valueStr;
68
69 if( wxGetEnv( wxT( "DRC_DEBUG" ), &valueStr ) )
70 {
71 int setLevel = wxAtoi( valueStr );
72
73 if( level <= setLevel )
74 printf( "%-30s:%d | %s\n", function, line, (const char *) msg.c_str() );
75 }
76}
77
78
81 m_designSettings ( aSettings ),
82 m_board( aBoard ),
83 m_drawingSheet( nullptr ),
84 m_schematicNetlist( nullptr ),
85 m_rulesValid( false ),
87 m_testFootprints( false ),
88 m_logReporter( nullptr ),
89 m_progressReporter( nullptr )
90{
91 m_errorLimits.resize( DRCE_LAST + 1 );
92
93 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
95}
96
97
99{
100 m_rules.clear();
101
102 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
103 {
104 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
105 delete constraint;
106
107 delete pair.second;
108 }
109}
110
111
112static bool isKeepoutZone( const BOARD_ITEM* aItem, bool aCheckFlags )
113{
114 if( !aItem || aItem->Type() != PCB_ZONE_T )
115 return false;
116
117 const ZONE* zone = static_cast<const ZONE*>( aItem );
118
119 if( !zone->GetIsRuleArea() )
120 return false;
121
122 if( !zone->HasKeepoutParametersSet() )
123 return false;
124
125 if( aCheckFlags )
126 {
127 if( !zone->GetDoNotAllowTracks()
128 && !zone->GetDoNotAllowVias()
129 && !zone->GetDoNotAllowPads()
130 && !zone->GetDoNotAllowZoneFills()
131 && !zone->GetDoNotAllowFootprints() )
132 {
133 return false;
134 }
135 }
136
137 return true;
138}
139
140
141std::shared_ptr<DRC_RULE> DRC_ENGINE::createImplicitRule( const wxString& name )
142{
143 std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
144
145 rule->m_Name = name;
146 rule->m_Implicit = true;
147
148 addRule( rule );
149
150 return rule;
151}
152
153
155{
156 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
157
158 // 1) global defaults
159
160 std::shared_ptr<DRC_RULE> rule = createImplicitRule( _( "board setup constraints" ) );
161
162 DRC_CONSTRAINT widthConstraint( TRACK_WIDTH_CONSTRAINT );
163 widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
164 rule->AddConstraint( widthConstraint );
165
166 DRC_CONSTRAINT connectionConstraint( CONNECTION_WIDTH_CONSTRAINT );
167 connectionConstraint.Value().SetMin( bds.m_MinConn );
168 rule->AddConstraint( connectionConstraint );
169
170 DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
171 drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
172 rule->AddConstraint( drillConstraint );
173
174 DRC_CONSTRAINT annulusConstraint( ANNULAR_WIDTH_CONSTRAINT );
175 annulusConstraint.Value().SetMin( bds.m_ViasMinAnnularWidth );
176 rule->AddConstraint( annulusConstraint );
177
178 DRC_CONSTRAINT diameterConstraint( VIA_DIAMETER_CONSTRAINT );
179 diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
180 rule->AddConstraint( diameterConstraint );
181
182 DRC_CONSTRAINT holeToHoleConstraint( HOLE_TO_HOLE_CONSTRAINT );
183 holeToHoleConstraint.Value().SetMin( bds.m_HoleToHoleMin );
184 rule->AddConstraint( holeToHoleConstraint );
185
186 rule = createImplicitRule( _( "board setup constraints zone fill strategy" ) );
187 DRC_CONSTRAINT thermalSpokeCountConstraint( MIN_RESOLVED_SPOKES_CONSTRAINT );
188 thermalSpokeCountConstraint.Value().SetMin( bds.m_MinResolvedSpokes );
189 rule->AddConstraint( thermalSpokeCountConstraint );
190
191 rule = createImplicitRule( _( "board setup constraints silk" ) );
192 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
193 DRC_CONSTRAINT silkClearanceConstraint( SILK_CLEARANCE_CONSTRAINT );
194 silkClearanceConstraint.Value().SetMin( bds.m_SilkClearance );
195 rule->AddConstraint( silkClearanceConstraint );
196
197 rule = createImplicitRule( _( "board setup constraints silk text height" ) );
198 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
199 DRC_CONSTRAINT silkTextHeightConstraint( TEXT_HEIGHT_CONSTRAINT );
200 silkTextHeightConstraint.Value().SetMin( bds.m_MinSilkTextHeight );
201 rule->AddConstraint( silkTextHeightConstraint );
202
203 rule = createImplicitRule( _( "board setup constraints silk text thickness" ) );
204 rule->m_LayerCondition = LSET( { F_SilkS, B_SilkS } );
205 DRC_CONSTRAINT silkTextThicknessConstraint( TEXT_THICKNESS_CONSTRAINT );
206 silkTextThicknessConstraint.Value().SetMin( bds.m_MinSilkTextThickness );
207 rule->AddConstraint( silkTextThicknessConstraint );
208
209 rule = createImplicitRule( _( "board setup constraints hole" ) );
210 DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
211 holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
212 rule->AddConstraint( holeClearanceConstraint );
213
214 rule = createImplicitRule( _( "board setup constraints edge" ) );
215 DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
216 edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
217 rule->AddConstraint( edgeClearanceConstraint );
218
219 rule = createImplicitRule( _( "board setup constraints courtyard" ) );
220 DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
221 holeToHoleConstraint.Value().SetMin( 0 );
222 rule->AddConstraint( courtyardClearanceConstraint );
223
224 // 2a) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
225
226 std::shared_ptr<DRC_RULE> uViaRule = createImplicitRule( _( "board setup micro-via constraints" ) );
227
228 uViaRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Via_Type == 'Micro'" ) );
229
230 DRC_CONSTRAINT uViaDrillConstraint( HOLE_SIZE_CONSTRAINT );
231 uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
232 uViaRule->AddConstraint( uViaDrillConstraint );
233
234 DRC_CONSTRAINT uViaDiameterConstraint( VIA_DIAMETER_CONSTRAINT );
235 uViaDiameterConstraint.Value().SetMin( bds.m_MicroViasMinSize );
236 uViaRule->AddConstraint( uViaDiameterConstraint );
237
238 // 2b) barcode-specific defaults
239
240 std::shared_ptr<DRC_RULE> barcodeRule = createImplicitRule( _( "barcode visual separation default" ) );
241 DRC_CONSTRAINT barcodeSeparationConstraint( PHYSICAL_CLEARANCE_CONSTRAINT );
242 barcodeSeparationConstraint.Value().SetMin( GetIuScale().mmToIU( 1.0 ) );
243 barcodeRule->AddConstraint( barcodeSeparationConstraint );
244 barcodeRule->m_Condition = new DRC_RULE_CONDITION( wxT( "A.Type == 'Barcode'" ) );
245
246 // 3) per-netclass rules
247
248 std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
249 std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
250
251 auto makeNetclassRules =
252 [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
253 {
254 wxString ncName = nc->GetName();
255 wxString expr;
256
257 ncName.Replace( "'", "\\'" );
258
259 if( nc->HasClearance() )
260 {
261 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
262 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
263 nc->GetClearanceParent()->GetHumanReadableName() );
264 netclassRule->m_Implicit = true;
265
266 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
267 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
268 netclassClearanceRules.push_back( netclassRule );
269
271 constraint.Value().SetMin( nc->GetClearance() );
272 netclassRule->AddConstraint( constraint );
273 }
274
275 if( nc->HasTrackWidth() )
276 {
277 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
278 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
279 nc->GetTrackWidthParent()->GetHumanReadableName() );
280 netclassRule->m_Implicit = true;
281
282 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
283 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
284 netclassClearanceRules.push_back( netclassRule );
285
287 constraint.Value().SetMin( bds.m_TrackMinWidth );
288 constraint.Value().SetOpt( nc->GetTrackWidth() );
289 netclassRule->AddConstraint( constraint );
290 }
291
292 if( nc->HasDiffPairWidth() )
293 {
294 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
295 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
296 nc->GetDiffPairWidthParent()->GetHumanReadableName() );
297 netclassRule->m_Implicit = true;
298
299 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.inDiffPair('*')" ), ncName );
300 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
301 netclassItemSpecificRules.push_back( netclassRule );
302
304 constraint.Value().SetMin( bds.m_TrackMinWidth );
305 constraint.Value().SetOpt( nc->GetDiffPairWidth() );
306 netclassRule->AddConstraint( constraint );
307 }
308
309 if( nc->HasDiffPairGap() )
310 {
311 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
312 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
313 nc->GetDiffPairGapParent()->GetHumanReadableName() );
314 netclassRule->m_Implicit = true;
315
316 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
317 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
318 netclassItemSpecificRules.push_back( netclassRule );
319
321 constraint.Value().SetMin( bds.m_MinClearance );
322 constraint.Value().SetOpt( nc->GetDiffPairGap() );
323 netclassRule->AddConstraint( constraint );
324
325 // A narrower diffpair gap overrides the netclass min clearance
326 if( nc->GetDiffPairGap() < nc->GetClearance() )
327 {
328 netclassRule = std::make_shared<DRC_RULE>();
329 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
330 nc->GetDiffPairGapParent()->GetHumanReadableName() );
331 netclassRule->m_Implicit = true;
332
333 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && AB.isCoupledDiffPair()" ), ncName );
334 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
335 netclassItemSpecificRules.push_back( netclassRule );
336
337 DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
338 min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
339 netclassRule->AddConstraint( min_clearanceConstraint );
340 }
341 }
342
343 if( nc->HasViaDiameter() )
344 {
345 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
346 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
347 nc->GetViaDiameterParent()->GetHumanReadableName() );
348 netclassRule->m_Implicit = true;
349
350 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type != 'Micro'" ), ncName );
351 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
352 netclassItemSpecificRules.push_back( netclassRule );
353
355 constraint.Value().SetMin( bds.m_ViasMinSize );
356 constraint.Value().SetOpt( nc->GetViaDiameter() );
357 netclassRule->AddConstraint( constraint );
358 }
359
360 if( nc->HasViaDrill() )
361 {
362 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
363 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
364 nc->GetViaDrillParent()->GetHumanReadableName() );
365 netclassRule->m_Implicit = true;
366
367 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type != 'Micro'" ), ncName );
368 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
369 netclassItemSpecificRules.push_back( netclassRule );
370
372 constraint.Value().SetMin( bds.m_MinThroughDrill );
373 constraint.Value().SetOpt( nc->GetViaDrill() );
374 netclassRule->AddConstraint( constraint );
375 }
376
377 if( nc->HasuViaDiameter() )
378 {
379 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
380 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
381 nc->GetuViaDiameterParent()->GetHumanReadableName() );
382 netclassRule->m_Implicit = true;
383
384 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type == 'Micro'" ), ncName );
385 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
386 netclassItemSpecificRules.push_back( netclassRule );
387
389 constraint.Value().SetMin( bds.m_MicroViasMinSize );
390 constraint.Value().SetMin( nc->GetuViaDiameter() );
391 netclassRule->AddConstraint( constraint );
392 }
393
394 if( nc->HasuViaDrill() )
395 {
396 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
397 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
398 nc->GetuViaDrillParent()->GetHumanReadableName() );
399 netclassRule->m_Implicit = true;
400
401 expr = wxString::Format( wxT( "A.hasExactNetclass('%s') && A.Via_Type == 'Micro'" ), ncName );
402 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
403 netclassItemSpecificRules.push_back( netclassRule );
404
406 constraint.Value().SetMin( bds.m_MicroViasMinDrill );
407 constraint.Value().SetOpt( nc->GetuViaDrill() );
408 netclassRule->AddConstraint( constraint );
409 }
410 };
411
412 m_board->SynchronizeNetsAndNetClasses( false );
413 makeNetclassRules( bds.m_NetSettings->GetDefaultNetclass(), true );
414
415 for( const auto& [name, netclass] : bds.m_NetSettings->GetNetclasses() )
416 makeNetclassRules( netclass, false );
417
418 for( const auto& [name, netclass] : bds.m_NetSettings->GetCompositeNetclasses() )
419 makeNetclassRules( netclass, false );
420
421 // The netclass clearance rules have to be sorted by min clearance so the right one fires
422 // if 'A' and 'B' belong to two different netclasses.
423 //
424 // The item-specific netclass rules are all unary, so there's no 'A' vs 'B' issue.
425
426 std::sort( netclassClearanceRules.begin(), netclassClearanceRules.end(),
427 []( const std::shared_ptr<DRC_RULE>& lhs, const std::shared_ptr<DRC_RULE>& rhs )
428 {
429 return lhs->m_Constraints[0].m_Value.Min()
430 < rhs->m_Constraints[0].m_Value.Min();
431 } );
432
433 for( std::shared_ptr<DRC_RULE>& ncRule : netclassClearanceRules )
434 addRule( ncRule );
435
436 for( std::shared_ptr<DRC_RULE>& ncRule : netclassItemSpecificRules )
437 addRule( ncRule );
438
439 // 3) keepout area rules
440
441 std::vector<ZONE*> keepoutZones;
442
443 for( ZONE* zone : m_board->Zones() )
444 {
445 if( isKeepoutZone( zone, true ) )
446 keepoutZones.push_back( zone );
447 }
448
449 for( FOOTPRINT* footprint : m_board->Footprints() )
450 {
451 for( ZONE* zone : footprint->Zones() )
452 {
453 if( isKeepoutZone( zone, true ) )
454 keepoutZones.push_back( zone );
455 }
456 }
457
458 for( ZONE* zone : keepoutZones )
459 {
460 wxString name = zone->GetZoneName();
461
462 if( name.IsEmpty() )
463 rule = createImplicitRule( _( "keepout area" ) );
464 else
465 rule = createImplicitRule( wxString::Format( _( "keepout area '%s'" ), name ) );
466
467 rule->m_ImplicitItemId = zone->m_Uuid;
468
469 rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( wxT( "A.intersectsArea('%s')" ),
470 zone->m_Uuid.AsString() ) );
471
472 rule->m_LayerCondition = zone->GetLayerSet();
473
474 int disallowFlags = 0;
475
476 if( zone->GetDoNotAllowTracks() )
477 disallowFlags |= DRC_DISALLOW_TRACKS;
478
479 if( zone->GetDoNotAllowVias() )
480 disallowFlags |= DRC_DISALLOW_VIAS;
481
482 if( zone->GetDoNotAllowPads() )
483 disallowFlags |= DRC_DISALLOW_PADS;
484
485 if( zone->GetDoNotAllowZoneFills() )
486 disallowFlags |= DRC_DISALLOW_ZONES;
487
488 if( zone->GetDoNotAllowFootprints() )
489 disallowFlags |= DRC_DISALLOW_FOOTPRINTS;
490
491 DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
492 disallowConstraint.m_DisallowFlags = disallowFlags;
493 rule->AddConstraint( disallowConstraint );
494 }
495}
496
497
498void DRC_ENGINE::loadRules( const wxFileName& aPath )
499{
500 if( aPath.FileExists() )
501 {
502 std::vector<std::shared_ptr<DRC_RULE>> rules;
503
504 if( FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) ) )
505 {
506 FILE_LINE_READER lineReader( fp, aPath.GetFullPath() ); // Will close rules file
507 wxString rulesText;
508
509 std::function<bool( wxString* )> resolver =
510 [&]( wxString* token ) -> bool
511 {
512 if( m_board && m_board->GetProject() )
513 return m_board->GetProject()->TextVarResolver( token );
514
515 return false;
516 };
517
518 while( char* line = lineReader.ReadLine() )
519 rulesText << ExpandTextVars( line, &resolver ) << '\n';
520
521 DRC_RULES_PARSER parser( rulesText, aPath.GetFullPath() );
522 parser.Parse( rules, m_logReporter );
523 }
524
525 // Copy the rules into the member variable afterwards so that if Parse() throws then
526 // the possibly malformed rules won't contaminate the current ruleset.
527
528 for( std::shared_ptr<DRC_RULE>& rule : rules )
529 m_rules.push_back( rule );
530 }
531}
532
533
535{
536 if( m_logReporter )
537 m_logReporter->Report( wxT( "Compiling Rules" ) );
538
539 REPORTER error_semaphore;
540
541 for( std::shared_ptr<DRC_RULE>& rule : m_rules )
542 {
543 DRC_RULE_CONDITION* condition = nullptr;
544
545 if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
546 {
547 condition = rule->m_Condition;
548 condition->Compile( &error_semaphore );
549 }
550
551 if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
552 THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name,
553 TO_UTF8( rule->m_Condition->GetExpression() ), 0, 0 );
554
555 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
556 {
557 if( !m_constraintMap.count( constraint.m_Type ) )
558 m_constraintMap[ constraint.m_Type ] = new std::vector<DRC_ENGINE_CONSTRAINT*>();
559
560 DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
561
562 engineConstraint->layerTest = rule->m_LayerCondition;
563 engineConstraint->condition = condition;
564 engineConstraint->constraint = constraint;
565 engineConstraint->parentRule = rule;
566 m_constraintMap[ constraint.m_Type ]->push_back( engineConstraint );
567 }
568 }
569}
570
571
572void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
573{
575
576 for( DRC_TEST_PROVIDER* provider : m_testProviders )
577 {
578 if( m_logReporter )
579 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
580
581 provider->SetDRCEngine( this );
582 }
583
584 m_rules.clear();
585 m_rulesValid = false;
586
587 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
588 {
589 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
590 delete constraint;
591
592 delete pair.second;
593 }
594
595 m_constraintMap.clear();
596
597 m_board->IncrementTimeStamp(); // Clear board-level caches
598
599 try // attempt to load full set of rules (implicit + user rules)
600 {
602 loadRules( aRulePath );
603 compileRules();
604 }
605 catch( PARSE_ERROR& original_parse_error )
606 {
607 m_rules.clear();
608
609 try // try again with just our implicit rules
610 {
612 compileRules();
613 }
614 catch( PARSE_ERROR& )
615 {
616 wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
617 }
618
619 throw original_parse_error;
620 }
621
622 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
624
625 m_rulesValid = true;
626}
627
628
629void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
630 BOARD_COMMIT* aCommit )
631{
632 PROF_TIMER timer;
633
634 SetUserUnits( aUnits );
635
636 m_reportAllTrackErrors = aReportAllTrackErrors;
637 m_testFootprints = aTestFootprints;
638
639 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
640 {
641 if( m_designSettings->Ignore( ii ) )
642 m_errorLimits[ ii ] = 0;
643 else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
645 else
647 }
648
650
651 m_board->IncrementTimeStamp(); // Invalidate all caches...
652
653 DRC_CACHE_GENERATOR cacheGenerator;
654 cacheGenerator.SetDRCEngine( this );
655
656 if( !cacheGenerator.Run() ) // ... and regenerate them.
657 return;
658
659 // Recompute component classes
660 m_board->GetComponentClassManager().ForceComponentClassRecalculation();
661
662 int timestamp = m_board->GetTimeStamp();
663
664 for( DRC_TEST_PROVIDER* provider : m_testProviders )
665 {
666 if( m_logReporter )
667 m_logReporter->Report( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
668
669 if( !provider->RunTests( aUnits ) )
670 break;
671 }
672
673 timer.Stop();
674 wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
675
676 // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
677 // caches while DRC is running is problematic.
678 wxASSERT( timestamp == m_board->GetTimeStamp() );
679}
680
681
682#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
683
685 PCB_LAYER_ID aLayer, REPORTER* aReporter )
686{
687 DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
688
689 REPORT( "" )
690 REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
692
694 {
695 const PAD* pad = nullptr;
696
697 if( a->Type() == PCB_PAD_T )
698 pad = static_cast<const PAD*>( a );
699 else if( b->Type() == PCB_PAD_T )
700 pad = static_cast<const PAD*>( b );
701
702 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
703 {
705 }
706 else
707 {
708 REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
711 }
712 }
713
714 return constraint;
715}
716
717
719 const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
720 REPORTER* aReporter )
721{
722 /*
723 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
724 * kills performance when running bulk DRC tests (where aReporter is nullptr).
725 */
726
727 const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ?
728 static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
729 const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ?
730 static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
731
732 bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
733 bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
734
735 const PAD* pad = nullptr;
736 const ZONE* zone = nullptr;
737 const FOOTPRINT* parentFootprint = nullptr;
738
739 if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
740 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
741 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
742 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
743 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
744 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
745 {
746 if( a && a->Type() == PCB_PAD_T )
747 pad = static_cast<const PAD*>( a );
748 else if( a && a->Type() == PCB_ZONE_T )
749 zone = static_cast<const ZONE*>( a );
750
751 if( b && b->Type() == PCB_PAD_T )
752 pad = static_cast<const PAD*>( b );
753 else if( b && b->Type() == PCB_ZONE_T )
754 zone = static_cast<const ZONE*>( b );
755
756 if( pad )
757 parentFootprint = pad->GetParentFootprint();
758 }
759
760 DRC_CONSTRAINT constraint;
761 constraint.m_Type = aConstraintType;
762
763 auto applyConstraint =
764 [&]( const DRC_ENGINE_CONSTRAINT* c )
765 {
766 if( c->constraint.m_Value.HasMin() )
767 {
768 if( c->parentRule && c->parentRule->m_Implicit )
769 constraint.m_ImplicitMin = true;
770
771 constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
772 }
773
774 if( c->constraint.m_Value.HasOpt() )
775 constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
776
777 if( c->constraint.m_Value.HasMax() )
778 constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
779
780 switch( c->constraint.m_Type )
781 {
789 if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
790 constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
791
792 break;
793
794 default:
795 break;
796 }
797
798 // While the expectation would be to OR the disallow flags, we've already
799 // masked them down to aItem's type -- so we're really only looking for a
800 // boolean here.
801 constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
802
803 constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
804
805 constraint.SetParentRule( c->constraint.GetParentRule() );
806
807 constraint.SetOptionsFromOther( c->constraint );
808 };
809
810 const FOOTPRINT* footprints[2] = {a ? a->GetParentFootprint() : nullptr,
811 b ? b->GetParentFootprint() : nullptr};
812
813 // Handle Footprint net ties, which will zero out the clearance for footprint objects
814 if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
815 && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
816 // and not both connected. Connected items of different nets still need to be checked
817 // for their standard clearance value
818 || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
819 && footprints[0] ) ) // And that footprint exists
820 && !a_is_non_copper // Also, both elements need to be on copper layers
821 && !b_is_non_copper )
822 {
823 const BOARD_ITEM* child_items[2] = {a, b};
824
825 // These are the items being compared against, so the order is reversed
826 const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
827
828 for( int ii = 0; ii < 2; ++ii )
829 {
830 // We need both a footprint item and a connected item to check for a net tie
831 if( !footprints[ii] || !alt_items[ii] )
832 continue;
833
834 const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
835
836 auto it = netcodes.find( alt_items[ii]->GetNetCode() );
837
838 if( it != netcodes.end() )
839 {
840 REPORT( "" )
841 REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
842 EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
843
844 constraint.SetName( _( "net tie" ) );
845 constraint.m_Value.SetMin( 0 );
846 return constraint;
847 }
848 }
849 }
850
851 // Local overrides take precedence over everything *except* board min clearance
852 if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
853 {
854 int override_val = 0;
855 std::optional<int> overrideA;
856 std::optional<int> overrideB;
857
858 if( ac && !b_is_non_copper )
859 overrideA = ac->GetClearanceOverrides( nullptr );
860
861 if( bc && !a_is_non_copper )
862 overrideB = bc->GetClearanceOverrides( nullptr );
863
864 if( overrideA.has_value() || overrideB.has_value() )
865 {
866 wxString msg;
867
868 if( overrideA.has_value() )
869 {
870 REPORT( "" )
871 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
872 EscapeHTML( a->GetItemDescription( this, true ) ),
873 MessageTextFromValue( overrideA.value() ) ) )
874
875 override_val = ac->GetClearanceOverrides( &msg ).value();
876 }
877
878 if( overrideB.has_value() )
879 {
880 REPORT( "" )
881 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
882 EscapeHTML( b->GetItemDescription( this, true ) ),
883 EscapeHTML( MessageTextFromValue( overrideB.value() ) ) ) )
884
885 if( overrideB > override_val )
886 override_val = bc->GetClearanceOverrides( &msg ).value();
887 }
888
889 if( override_val )
890 {
891 if( aConstraintType == CLEARANCE_CONSTRAINT )
892 {
893 if( override_val < m_designSettings->m_MinClearance )
894 {
895 override_val = m_designSettings->m_MinClearance;
896 msg = _( "board minimum" );
897
898 REPORT( "" )
899 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
900 MessageTextFromValue( override_val ) ) )
901 }
902 }
903 else
904 {
905 if( override_val < m_designSettings->m_HoleClearance )
906 {
907 override_val = m_designSettings->m_HoleClearance;
908 msg = _( "board minimum hole" );
909
910 REPORT( "" )
911 REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
912 MessageTextFromValue( override_val ) ) )
913 }
914 }
915
916 constraint.SetName( msg );
917 constraint.m_Value.SetMin( override_val );
918 return constraint;
919 }
920 }
921 }
922 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
923 {
924 if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
925 {
926 wxString msg;
927 ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
928
929 REPORT( "" )
930 REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
931 EscapeHTML( pad->GetItemDescription( this, true ) ),
932 EscapeHTML( PrintZoneConnection( override ) ) ) )
933
934 constraint.SetName( msg );
935 constraint.m_ZoneConnection = override;
936 return constraint;
937 }
938 }
939 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
940 {
941 if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
942 {
943 wxString msg;
944 int gap_override = pad->GetLocalThermalGapOverride( &msg );
945
946 REPORT( "" )
947 REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
948 EscapeHTML( pad->GetItemDescription( this, true ) ),
949 EscapeHTML( MessageTextFromValue( gap_override ) ) ) )
950
951 constraint.SetName( msg );
952 constraint.m_Value.SetMin( gap_override );
953 return constraint;
954 }
955 }
956 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
957 {
958 if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
959 {
960 wxString msg;
961 int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
962
963 REPORT( "" )
964 REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
965 EscapeHTML( pad->GetItemDescription( this, true ) ),
966 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
967
968 if( zone && zone->GetMinThickness() > spoke_override )
969 {
970 spoke_override = zone->GetMinThickness();
971
972 REPORT( "" )
973 REPORT( wxString::Format( _( "%s min thickness: %s." ),
974 EscapeHTML( zone->GetItemDescription( this, true ) ),
975 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
976 }
977
978 constraint.SetName( msg );
979 constraint.m_Value.SetMin( spoke_override );
980 return constraint;
981 }
982 }
983 else if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
984 {
985 std::optional<int> override;
986
987 if( pad )
988 override = pad->GetLocalSolderMaskMargin();
989 else if( a->Type() == PCB_SHAPE_T )
990 override = static_cast<const PCB_SHAPE*>( a )->GetLocalSolderMaskMargin();
991 else if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( a ) )
992 override = track->GetLocalSolderMaskMargin();
993
994 if( override )
995 {
996 REPORT( "" )
997 REPORT( wxString::Format( _( "Local override on %s; solder mask expansion: %s." ),
998 EscapeHTML( pad->GetItemDescription( this, true ) ),
999 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
1000
1001 constraint.m_Value.SetOpt( override.value() );
1002 return constraint;
1003 }
1004 }
1005 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1006 {
1007 std::optional<int> override;
1008
1009 if( pad )
1010 override = pad->GetLocalSolderPasteMargin();
1011
1012 if( override )
1013 {
1014 REPORT( "" )
1015 REPORT( wxString::Format( _( "Local override on %s; solder paste absolute clearance: %s." ),
1016 EscapeHTML( pad->GetItemDescription( this, true ) ),
1017 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
1018
1019 constraint.m_Value.SetOpt( override.value_or( 0 ) );
1020 return constraint;
1021 }
1022 }
1023 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1024 {
1025 std::optional<double> overrideRatio;
1026
1027 if( pad )
1028 overrideRatio = pad->GetLocalSolderPasteMarginRatio();
1029
1030 if( overrideRatio )
1031 {
1032 REPORT( "" )
1033 REPORT( wxString::Format( _( "Local override on %s; solder paste relative clearance: %s." ),
1034 EscapeHTML( pad->GetItemDescription( this, true ) ),
1035 EscapeHTML( MessageTextFromValue( overrideRatio.value() * 100.0 ) ) ) )
1036
1037 constraint.m_Value.SetOpt( KiROUND( overrideRatio.value_or( 0 ) * 1000 ) );
1038 return constraint;
1039 }
1040 }
1041
1042 auto testAssertion =
1043 [&]( const DRC_ENGINE_CONSTRAINT* c )
1044 {
1045 REPORT( wxString::Format( _( "Checking assertion '%s'." ),
1046 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1047
1048 if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer,
1049 aReporter ) )
1050 {
1051 REPORT( _( "Assertion passed." ) )
1052 }
1053 else
1054 {
1055 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1056 }
1057 };
1058
1059 auto processConstraint =
1060 [&]( const DRC_ENGINE_CONSTRAINT* c )
1061 {
1062 bool implicit = c->parentRule && c->parentRule->m_Implicit;
1063
1064 REPORT( "" )
1065
1066 switch( c->constraint.m_Type )
1067 {
1075 REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
1076 EscapeHTML( c->constraint.GetName() ),
1077 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1078 break;
1080 REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
1081 EscapeHTML( c->constraint.GetName() ),
1082 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1083 break;
1085 REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
1086 EscapeHTML( c->constraint.GetName() ),
1087 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1088 break;
1089
1090 case SKEW_CONSTRAINT:
1091 REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
1092 EscapeHTML( c->constraint.GetName() ),
1093 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1094 break;
1095
1097 REPORT( wxString::Format( _( "Checking %s gap: %s." ),
1098 EscapeHTML( c->constraint.GetName() ),
1099 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1100 break;
1101
1103 REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
1104 EscapeHTML( c->constraint.GetName() ),
1105 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1106 break;
1107
1109 REPORT( wxString::Format( _( "Checking %s solder mask expansion: %s." ),
1110 EscapeHTML( c->constraint.GetName() ),
1111 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1112 break;
1113
1115 REPORT( wxString::Format( _( "Checking %s solder paste absolute cleraance: %s." ),
1116 EscapeHTML( c->constraint.GetName() ),
1117 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1118 break;
1119
1121 REPORT( wxString::Format( _( "Checking %s solder paste relative clearance: %s." ),
1122 EscapeHTML( c->constraint.GetName() ),
1123 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1124 break;
1125
1127 REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
1128 EscapeHTML( c->constraint.GetName() ),
1130 c->constraint.m_Value.Min() ) ) )
1131 break;
1132
1134 REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
1135 EscapeHTML( c->constraint.GetName() ),
1136 EscapeHTML( PrintZoneConnection( c->constraint.m_ZoneConnection ) ) ) )
1137 break;
1138
1146 case LENGTH_CONSTRAINT:
1149 {
1150 if( aReporter )
1151 {
1152 wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1153 wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1154 wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1155
1156 if( implicit )
1157 {
1158 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1159 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1160
1161 switch( c->constraint.m_Type )
1162 {
1164 if( c->constraint.m_Value.HasOpt() )
1165 {
1166 REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
1167 EscapeHTML( c->constraint.GetName() ),
1168 opt ) )
1169 }
1170 else if( c->constraint.m_Value.HasMin() )
1171 {
1172 REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
1173 EscapeHTML( c->constraint.GetName() ),
1174 min ) )
1175 }
1176
1177 break;
1178
1180 REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
1181 EscapeHTML( c->constraint.GetName() ),
1182 opt ) )
1183 break;
1184
1186 if( c->constraint.m_Value.HasOpt() )
1187 {
1188 REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
1189 EscapeHTML( c->constraint.GetName() ),
1190 opt ) )
1191 }
1192 else if( c->constraint.m_Value.HasMin() )
1193 {
1194 REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
1195 EscapeHTML( c->constraint.GetName() ),
1196 min ) )
1197 }
1198 break;
1199
1201 if( c->constraint.m_Value.HasOpt() )
1202 {
1203 REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
1204 EscapeHTML( c->constraint.GetName() ),
1205 opt ) )
1206 }
1207 else if( c->constraint.m_Value.HasMin() )
1208 {
1209 REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
1210 EscapeHTML( c->constraint.GetName() ),
1211 min ) )
1212 }
1213
1214 break;
1215
1219 REPORT( wxString::Format( _( "Checking %s: min %s." ),
1220 EscapeHTML( c->constraint.GetName() ),
1221 min ) )
1222 break;
1223
1225 if( c->constraint.m_Value.HasOpt() )
1226 {
1227 REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
1228 EscapeHTML( c->constraint.GetName() ),
1229 opt ) )
1230 }
1231 else if( c->constraint.m_Value.HasMin() )
1232 {
1233 REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
1234 EscapeHTML( c->constraint.GetName() ),
1235 min ) )
1236 }
1237
1238 break;
1239
1241 REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
1242 EscapeHTML( c->constraint.GetName() ),
1243 min ) )
1244 break;
1245
1246 default:
1247 REPORT( wxString::Format( _( "Checking %s." ),
1248 EscapeHTML( c->constraint.GetName() ) ) )
1249 }
1250 }
1251 else
1252 {
1253 if( c->constraint.m_Value.HasMin() )
1254 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1255
1256 if( c->constraint.m_Value.HasOpt() )
1257 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1258
1259 if( c->constraint.m_Value.HasMax() )
1260 max = MessageTextFromValue( c->constraint.m_Value.Max() );
1261
1262 REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
1263 EscapeHTML( c->constraint.GetName() ),
1264 min,
1265 opt,
1266 max ) )
1267 }
1268 }
1269 break;
1270 }
1271
1272 default:
1273 REPORT( wxString::Format( _( "Checking %s." ),
1274 EscapeHTML( c->constraint.GetName() ) ) )
1275 }
1276
1277 if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
1278 {
1279 if( a_is_non_copper || b_is_non_copper )
1280 {
1281 if( implicit )
1282 {
1283 REPORT( _( "Netclass clearances apply only between copper items." ) )
1284 }
1285 else if( a_is_non_copper )
1286 {
1287 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1288 EscapeHTML( a->GetItemDescription( this, true ) ) ) )
1289 }
1290 else if( b_is_non_copper )
1291 {
1292 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1293 EscapeHTML( b->GetItemDescription( this, true ) ) ) )
1294 }
1295
1296 return;
1297 }
1298 }
1299 else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
1300 {
1301 int mask;
1302
1303 if( a->GetFlags() & HOLE_PROXY )
1304 {
1305 mask = DRC_DISALLOW_HOLES;
1306 }
1307 else if( a->Type() == PCB_VIA_T )
1308 {
1309 const PCB_VIA* via = static_cast<const PCB_VIA*>( a );
1310
1311 if( via->IsMicroVia() )
1313 else if( via->IsBlindVia() )
1315 else if( via->IsBuriedVia() )
1317 else
1319 }
1320 else
1321 {
1322 switch( a->Type() )
1323 {
1324 case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
1325 case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
1326 case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
1327 case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
1328 case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1329 case PCB_BARCODE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1330 case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
1331 case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
1332 case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
1333 case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
1334
1335 case PCB_ZONE_T:
1336 // Treat teardrop areas as tracks for DRC purposes
1337 if( static_cast<const ZONE*>( a )->IsTeardropArea() )
1338 mask = DRC_DISALLOW_TRACKS;
1339 else
1340 mask = DRC_DISALLOW_ZONES;
1341
1342 break;
1343
1344 case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
1345 default: mask = 0; break;
1346 }
1347 }
1348
1349 if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
1350 {
1351 if( implicit )
1352 REPORT( _( "Keepout constraint not met." ) )
1353 else
1354 REPORT( _( "Disallow constraint not met." ) )
1355
1356 return;
1357 }
1358
1359 LSET itemLayers = a->GetLayerSet();
1360
1361 if( a->Type() == PCB_FOOTPRINT_T )
1362 {
1363 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
1364
1365 if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
1366 itemLayers |= LSET::FrontMask();
1367
1368 if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
1369 itemLayers |= LSET::BackMask();
1370 }
1371
1372 if( !( c->layerTest & itemLayers ).any() )
1373 {
1374 if( implicit )
1375 {
1376 REPORT( _( "Keepout layer(s) not matched." ) )
1377 }
1378 else if( c->parentRule )
1379 {
1380 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1381 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1382 }
1383 else
1384 {
1385 REPORT( _( "Rule layer not matched; rule ignored." ) )
1386 }
1387
1388 return;
1389 }
1390 }
1391
1392 if( ( aLayer != UNDEFINED_LAYER && !c->layerTest.test( aLayer ) )
1393 || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
1394 {
1395 if( implicit )
1396 {
1397 REPORT( _( "Constraint layer not matched." ) )
1398 }
1399 else if( c->parentRule )
1400 {
1401 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1402 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1403 }
1404 else
1405 {
1406 REPORT( _( "Rule layer not matched; rule ignored." ) )
1407 }
1408 }
1409 else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
1410 && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
1411 {
1412 // Report non-drilled-holes as an implicit condition
1413 REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
1414 a->GetItemDescription( this, true ) ) )
1415 }
1416 else if( !c->condition || c->condition->GetExpression().IsEmpty() )
1417 {
1418 if( aReporter )
1419 {
1420 if( implicit )
1421 {
1422 REPORT( _( "Unconditional constraint applied." ) )
1423 }
1424 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1425 {
1426 REPORT( _( "Unconditional rule applied." ) )
1427 testAssertion( c );
1428 }
1429 else
1430 {
1431 REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
1432 }
1433 }
1434
1435 applyConstraint( c );
1436 }
1437 else
1438 {
1439 if( implicit )
1440 {
1441 // Don't report on implicit rule conditions; they're synthetic.
1442 }
1443 else
1444 {
1445 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1446 EscapeHTML( c->condition->GetExpression() ) ) )
1447 }
1448
1449 if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1450 {
1451 if( aReporter )
1452 {
1453 if( implicit )
1454 {
1455 REPORT( _( "Constraint applied." ) )
1456 }
1457 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1458 {
1459 REPORT( _( "Rule applied." ) )
1460 testAssertion( c );
1461 }
1462 else
1463 {
1464 REPORT( _( "Rule applied; overrides previous constraints." ) )
1465 }
1466 }
1467
1468 applyConstraint( c );
1469 }
1470 else
1471 {
1472 REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
1473 : _( "Condition not satisfied; rule ignored." ) )
1474 }
1475 }
1476 };
1477
1478 if( m_constraintMap.count( aConstraintType ) )
1479 {
1480 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1481
1482 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1483 processConstraint( rule );
1484 }
1485
1486 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1487 return constraint;
1488
1489 // Special case for properties which can be inherited from parent footprints. We've already
1490 // checked for local overrides, and there were no rules targetting the item itself, so we know
1491 // we're inheriting and need to see if there are any rules targetting the parent footprint.
1492 if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1493 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1494 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1495 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1496 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1497 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT ) )
1498 {
1499 REPORT( "" )
1500 REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
1501 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
1502
1503 if( a == pad )
1504 a = parentFootprint;
1505 else
1506 b = parentFootprint;
1507
1508 if( m_constraintMap.count( aConstraintType ) )
1509 {
1510 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1511
1512 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1513 processConstraint( rule );
1514
1515 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1516 return constraint;
1517 }
1518
1519 // Found nothing again? Return the defaults.
1520 if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1521 {
1522 constraint.SetParentRule( nullptr );
1523 constraint.SetName( _( "board setup" ) );
1524 constraint.m_Value.SetOpt( m_designSettings->m_SolderMaskExpansion );
1525 return constraint;
1526 }
1527 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1528 {
1529 constraint.SetParentRule( nullptr );
1530 constraint.SetName( _( "board setup" ) );
1531 constraint.m_Value.SetOpt( m_designSettings->m_SolderPasteMargin );
1532 return constraint;
1533 }
1534 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1535 {
1536 constraint.SetParentRule( nullptr );
1537 constraint.SetName( _( "board setup" ) );
1538 constraint.m_Value.SetOpt( KiROUND( m_designSettings->m_SolderPasteMarginRatio * 1000 ) );
1539 return constraint;
1540 }
1541 }
1542
1543 // Unfortunately implicit rules don't work for local clearances (such as zones) because
1544 // they have to be max'ed with netclass values (which are already implicit rules), and our
1545 // rule selection paradigm is "winner takes all".
1546 if( aConstraintType == CLEARANCE_CONSTRAINT )
1547 {
1548 int global = constraint.m_Value.Min();
1549 int clearance = global;
1550 bool needBlankLine = true;
1551
1552 if( ac && ac->GetLocalClearance().has_value() )
1553 {
1554 int localA = ac->GetLocalClearance().value();
1555
1556 if( needBlankLine )
1557 {
1558 REPORT( "" )
1559 needBlankLine = false;
1560 }
1561
1562 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1563 EscapeHTML( a->GetItemDescription( this, true ) ),
1564 MessageTextFromValue( localA ) ) )
1565
1566 if( localA > clearance )
1567 {
1568 wxString msg;
1569 clearance = ac->GetLocalClearance( &msg ).value();
1570 constraint.SetParentRule( nullptr );
1571 constraint.SetName( msg );
1572 constraint.m_Value.SetMin( clearance );
1573 }
1574 }
1575
1576 if( bc && bc->GetLocalClearance().has_value() )
1577 {
1578 int localB = bc->GetLocalClearance().value();
1579
1580 if( needBlankLine )
1581 {
1582 REPORT( "" )
1583 needBlankLine = false;
1584 }
1585
1586 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1587 EscapeHTML( b->GetItemDescription( this, true ) ),
1588 MessageTextFromValue( localB ) ) )
1589
1590 if( localB > clearance )
1591 {
1592 wxString msg;
1593 clearance = bc->GetLocalClearance( &msg ).value();
1594 constraint.SetParentRule( nullptr );
1595 constraint.SetName( msg );
1596 constraint.m_Value.SetMin( clearance );
1597 }
1598 }
1599
1600 if( !a_is_non_copper && !b_is_non_copper )
1601 {
1602 if( needBlankLine )
1603 {
1604 REPORT( "" )
1605 needBlankLine = false;
1606 }
1607
1608 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1609 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1610
1611 if( clearance < m_designSettings->m_MinClearance )
1612 {
1613 constraint.SetParentRule( nullptr );
1614 constraint.SetName( _( "board minimum" ) );
1615 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1616 }
1617 }
1618
1619 return constraint;
1620 }
1621 else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
1622 {
1623 REPORT( "" )
1624 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1625 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1626
1627 if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
1628 {
1629 constraint.SetParentRule( nullptr );
1630 constraint.SetName( _( "board minimum" ) );
1631 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1632 }
1633
1634 return constraint;
1635 }
1636 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1637 {
1638 if( pad && parentFootprint )
1639 {
1640 ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
1641
1642 if( local != ZONE_CONNECTION::INHERITED )
1643 {
1644 REPORT( "" )
1645 REPORT( wxString::Format( _( "%s zone connection: %s." ),
1646 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
1647 EscapeHTML( PrintZoneConnection( local ) ) ) )
1648
1649 constraint.SetParentRule( nullptr );
1650 constraint.SetName( _( "footprint" ) );
1651 constraint.m_ZoneConnection = local;
1652 return constraint;
1653 }
1654 }
1655
1656 if( zone )
1657 {
1658 ZONE_CONNECTION local = zone->GetPadConnection();
1659
1660 REPORT( "" )
1661 REPORT( wxString::Format( _( "%s pad connection: %s." ),
1662 EscapeHTML( zone->GetItemDescription( this, true ) ),
1663 EscapeHTML( PrintZoneConnection( local ) ) ) )
1664
1665 constraint.SetParentRule( nullptr );
1666 constraint.SetName( _( "zone" ) );
1667 constraint.m_ZoneConnection = local;
1668 return constraint;
1669 }
1670 }
1671 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1672 {
1673 if( zone )
1674 {
1675 int local = zone->GetThermalReliefGap();
1676
1677 REPORT( "" )
1678 REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
1679 EscapeHTML( zone->GetItemDescription( this, true ) ),
1680 EscapeHTML( MessageTextFromValue( local ) ) ) )
1681
1682 constraint.SetParentRule( nullptr );
1683 constraint.SetName( _( "zone" ) );
1684 constraint.m_Value.SetMin( local );
1685 return constraint;
1686 }
1687 }
1688 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
1689 {
1690 if( zone )
1691 {
1692 int local = zone->GetThermalReliefSpokeWidth();
1693
1694 REPORT( "" )
1695 REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
1696 EscapeHTML( zone->GetItemDescription( this, true ) ),
1697 EscapeHTML( MessageTextFromValue( local ) ) ) )
1698
1699 constraint.SetParentRule( nullptr );
1700 constraint.SetName( _( "zone" ) );
1701 constraint.m_Value.SetMin( local );
1702 return constraint;
1703 }
1704 }
1705
1706 if( !constraint.GetParentRule() )
1707 {
1708 constraint.m_Type = NULL_CONSTRAINT;
1709 constraint.m_DisallowFlags = 0;
1710 }
1711
1712 return constraint;
1713}
1714
1715
1717 std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
1718 REPORTER* aReporter )
1719{
1720 /*
1721 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
1722 * kills performance when running bulk DRC tests (where aReporter is nullptr).
1723 */
1724
1725 auto testAssertion =
1726 [&]( const DRC_ENGINE_CONSTRAINT* c )
1727 {
1728 REPORT( wxString::Format( _( "Checking rule assertion '%s'." ),
1729 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1730
1731 if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
1732 a->GetLayer(), aReporter ) )
1733 {
1734 REPORT( _( "Assertion passed." ) )
1735 }
1736 else
1737 {
1738 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1739 aFailureHandler( &c->constraint );
1740 }
1741 };
1742
1743 auto processConstraint =
1744 [&]( const DRC_ENGINE_CONSTRAINT* c )
1745 {
1746 REPORT( "" )
1747 REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
1748
1749 if( !( a->GetLayerSet() & c->layerTest ).any() )
1750 {
1751 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1752 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1753 }
1754
1755 if( !c->condition || c->condition->GetExpression().IsEmpty() )
1756 {
1757 REPORT( _( "Unconditional rule applied." ) )
1758 testAssertion( c );
1759 }
1760 else
1761 {
1762 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1763 EscapeHTML( c->condition->GetExpression() ) ) )
1764
1765 if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
1766 a->GetLayer(), aReporter ) )
1767 {
1768 REPORT( _( "Rule applied." ) )
1769 testAssertion( c );
1770 }
1771 else
1772 {
1773 REPORT( _( "Condition not satisfied; rule ignored." ) )
1774 }
1775 }
1776 };
1777
1779 {
1780 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
1781
1782 for( int ii = 0; ii < (int) ruleset->size(); ++ii )
1783 processConstraint( ruleset->at( ii ) );
1784 }
1785}
1786
1787
1788#undef REPORT
1789
1790
1792{
1793 assert( error_code >= 0 && error_code <= DRCE_LAST );
1794 return m_errorLimits[ error_code ] <= 0;
1795}
1796
1797
1798void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
1799 int aMarkerLayer, const std::function<void( PCB_MARKER* )>& aPathGenerator )
1800{
1801 static std::mutex globalLock;
1802
1803 m_errorLimits[ aItem->GetErrorCode() ] -= 1;
1804
1805 if( m_violationHandler )
1806 {
1807 std::lock_guard<std::mutex> guard( globalLock );
1808 m_violationHandler( aItem, aPos, aMarkerLayer, aPathGenerator );
1809 }
1810
1811 if( m_logReporter )
1812 {
1813 wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
1814 aItem->GetViolatingTest()->GetName(),
1815 aItem->GetErrorMessage(),
1816 aItem->GetErrorCode() );
1817
1818 DRC_RULE* rule = aItem->GetViolatingRule();
1819
1820 if( rule )
1821 msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
1822
1823 m_logReporter->Report( msg );
1824
1825 wxString violatingItemsStr = wxT( "Violating items: " );
1826
1827 m_logReporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
1828 aPos.x,
1829 aPos.y ) );
1830 }
1831}
1832
1833
1835{
1836 if( !m_progressReporter )
1837 return true;
1838
1839 return m_progressReporter->KeepRefreshing( aWait );
1840}
1841
1842
1844{
1845 if( m_progressReporter )
1846 m_progressReporter->AdvanceProgress();
1847}
1848
1849
1851{
1852 if( m_progressReporter )
1853 m_progressReporter->SetMaxProgress( aSize );
1854}
1855
1856
1857bool DRC_ENGINE::ReportProgress( double aProgress )
1858{
1859 if( !m_progressReporter )
1860 return true;
1861
1862 m_progressReporter->SetCurrentProgress( aProgress );
1863 return m_progressReporter->KeepRefreshing( false );
1864}
1865
1866
1867bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
1868{
1869 if( !m_progressReporter )
1870 return true;
1871
1872 m_progressReporter->AdvancePhase( aMessage );
1873 bool retval = m_progressReporter->KeepRefreshing( false );
1874 wxSafeYield( nullptr, true ); // Force an update for the message
1875 return retval;
1876}
1877
1878
1880{
1881 return m_progressReporter && m_progressReporter->IsCancelled();
1882}
1883
1884
1886{
1887 //drc_dbg( 10, "hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size() );
1888
1889 if( m_constraintMap.count( constraintID ) )
1890 return m_constraintMap[ constraintID ]->size() > 0;
1891
1892 return false;
1893}
1894
1895
1897{
1898 int worst = 0;
1899
1900 if( m_constraintMap.count( aConstraintId ) )
1901 {
1902 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1903 {
1904 int current = c->constraint.GetValue().Min();
1905
1906 if( current > worst )
1907 {
1908 worst = current;
1909 aConstraint = c->constraint;
1910 }
1911 }
1912 }
1913
1914 return worst > 0;
1915}
1916
1917
1919{
1920 std::set<int> distinctMinimums;
1921
1922 if( m_constraintMap.count( aConstraintId ) )
1923 {
1924 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1925 distinctMinimums.emplace( c->constraint.GetValue().Min() );
1926 }
1927
1928 return distinctMinimums;
1929}
1930
1931
1932// fixme: move two functions below to pcbcommon?
1933int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
1934 wxString& aBaseDpName )
1935{
1936 int rv = 0;
1937 int count = 0;
1938
1939 for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
1940 {
1941 int ch = *it;
1942
1943 if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
1944 {
1945 continue;
1946 }
1947 else if( ch == '+' )
1948 {
1949 aComplementNet = wxT( "-" );
1950 rv = 1;
1951 }
1952 else if( ch == '-' )
1953 {
1954 aComplementNet = wxT( "+" );
1955 rv = -1;
1956 }
1957 else if( ch == 'N' )
1958 {
1959 aComplementNet = wxT( "P" );
1960 rv = -1;
1961 }
1962 else if ( ch == 'P' )
1963 {
1964 aComplementNet = wxT( "N" );
1965 rv = 1;
1966 }
1967 else
1968 {
1969 break;
1970 }
1971 }
1972
1973 if( rv != 0 && count >= 1 )
1974 {
1975 aBaseDpName = aNetName.Left( aNetName.Length() - count );
1976 aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
1977 }
1978
1979 return rv;
1980}
1981
1982
1983bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
1984{
1985 wxString refName = aNet->GetNetname();
1986 wxString dummy, coupledNetName;
1987
1988 if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
1989 {
1990 NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
1991
1992 if( !net )
1993 return false;
1994
1995 if( polarity > 0 )
1996 {
1997 aNetP = aNet->GetNetCode();
1998 aNetN = net->GetNetCode();
1999 }
2000 else
2001 {
2002 aNetP = net->GetNetCode();
2003 aNetN = aNet->GetNetCode();
2004 }
2005
2006 return true;
2007 }
2008
2009 return false;
2010}
2011
2012
2017bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
2018 const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
2019{
2020 FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
2021
2022 if( parentFootprint && parentFootprint->IsNetTie() )
2023 {
2025 std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
2026
2027 for( PAD* pad : parentFootprint->Pads() )
2028 {
2029 if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
2030 {
2031 if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
2032 return true;
2033 }
2034 }
2035 }
2036
2037 return false;
2038}
2039
2040
2042{
2043 for( DRC_TEST_PROVIDER* prov : m_testProviders )
2044 {
2045 if( name == prov->GetName() )
2046 return prov;
2047 }
2048
2049 return nullptr;
2050}
2051
2052
2053std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingCondition( const wxString& aExpression,
2054 DRC_CONSTRAINT_T aConstraint,
2055 REPORTER* aReporter )
2056{
2057 std::vector<BOARD_ITEM*> matches;
2058
2059 if( !m_board )
2060 return matches;
2061
2062 DRC_RULE_CONDITION condition( aExpression );
2063
2064 if( !condition.Compile( aReporter ? aReporter : m_logReporter ) )
2065 return matches;
2066
2067 BOARD_ITEM_SET items = m_board->GetItemSet();
2068
2069 for( auto& [kiid, item] : m_board->GetItemByIdCache() )
2070 {
2071 LSET itemLayers = item->GetLayerSet();
2072
2073 for( PCB_LAYER_ID layer : itemLayers )
2074 {
2075 if( condition.EvaluateFor( item, nullptr, static_cast<int>( aConstraint ), layer,
2076 aReporter ? aReporter : m_logReporter ) )
2077 {
2078 matches.push_back( item );
2079 break; // No need to check other layers
2080 }
2081 }
2082 }
2083
2084 return matches;
2085}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
constexpr EDA_IU_SCALE unityScale
Definition base_units.h:115
std::set< BOARD_ITEM *, CompareByUuid > BOARD_ITEM_SET
Set of BOARD_ITEMs ordered by UUID.
Definition board.h:306
#define MAXIMUM_CLEARANCE
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual std::optional< int > GetClearanceOverrides(wxString *aSource) const
Return any clearance overrides set in the "classic" (ie: pre-rule) system.
virtual std::optional< int > GetLocalClearance() const
Return any local clearances set in the "classic" (ie: pre-rule) system.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:232
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:134
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:252
virtual bool HasDrilledHole() const
Definition board_item.h:161
virtual bool IsOnCopperLayer() const
Definition board_item.h:151
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2282
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
int m_DisallowFlags
Definition drc_rule.h:209
void SetParentRule(DRC_RULE *aParentRule)
Definition drc_rule.h:169
MINOPTMAX< int > & Value()
Definition drc_rule.h:167
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:210
void SetName(const wxString &aName)
Definition drc_rule.h:172
MINOPTMAX< int > m_Value
Definition drc_rule.h:208
DRC_CONSTRAINT_T m_Type
Definition drc_rule.h:207
void SetOptionsFromOther(const DRC_CONSTRAINT &aOther)
Definition drc_rule.h:204
DRC_RULE * GetParentRule() const
Definition drc_rule.h:170
bool m_ImplicitMin
Definition drc_rule.h:212
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > * > m_constraintMap
Definition drc_engine.h:266
void AdvanceProgress()
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
bool m_testFootprints
Definition drc_engine.h:263
void addRule(std::shared_ptr< DRC_RULE > &rule)
Definition drc_engine.h:226
PROGRESS_REPORTER * m_progressReporter
Definition drc_engine.h:270
void loadRules(const wxFileName &aPath)
Load and parse a rule set from an sexpr text file.
std::vector< DRC_TEST_PROVIDER * > m_testProviders
Definition drc_engine.h:259
REPORTER * m_logReporter
Definition drc_engine.h:269
void compileRules()
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
DS_PROXY_VIEW_ITEM * m_drawingSheet
Definition drc_engine.h:254
NETLIST * m_schematicNetlist
Definition drc_engine.h:255
bool KeepRefreshing(bool aWait=false)
BOARD * m_board
Definition drc_engine.h:253
bool m_reportAllTrackErrors
Definition drc_engine.h:262
bool ReportProgress(double aProgress)
DRC_TEST_PROVIDER * GetTestProvider(const wxString &name) const
bool HasRulesForConstraintType(DRC_CONSTRAINT_T constraintID)
BOARD_DESIGN_SETTINGS * GetDesignSettings() const
Definition drc_engine.h:100
void SetMaxProgress(int aSize)
DRC_ENGINE(BOARD *aBoard=nullptr, BOARD_DESIGN_SETTINGS *aSettings=nullptr)
std::vector< int > m_errorLimits
Definition drc_engine.h:261
bool IsErrorLimitExceeded(int error_code)
void ProcessAssertions(const BOARD_ITEM *a, std::function< void(const DRC_CONSTRAINT *)> aFailureHandler, REPORTER *aReporter=nullptr)
void loadImplicitRules()
DRC_VIOLATION_HANDLER m_violationHandler
Definition drc_engine.h:268
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
std::vector< std::shared_ptr< DRC_RULE > > m_rules
Definition drc_engine.h:257
std::shared_ptr< DRC_RULE > createImplicitRule(const wxString &name)
bool IsCancelled() const
static bool IsNetADiffPair(BOARD *aBoard, NETINFO_ITEM *aNet, int &aNetP, int &aNetN)
bool IsNetTieExclusion(int aTrackNetCode, PCB_LAYER_ID aTrackLayer, const VECTOR2I &aCollisionPos, BOARD_ITEM *aCollidingItem)
Check if the given collision between a track and another item occurs during the track's entry into a ...
virtual ~DRC_ENGINE()
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
std::vector< BOARD_ITEM * > GetItemsMatchingCondition(const wxString &aExpression, DRC_CONSTRAINT_T aConstraint=ASSERTION_CONSTRAINT, REPORTER *aReporter=nullptr)
Evaluate a DRC condition against all board items and return matches.
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Check if the given net is a diff pair, returning its polarity and complement if so.
bool ReportPhase(const wxString &aMessage)
bool m_rulesValid
Definition drc_engine.h:258
BOARD_DESIGN_SETTINGS * m_designSettings
Definition drc_engine.h:252
void ReportViolation(const std::shared_ptr< DRC_ITEM > &aItem, const VECTOR2I &aPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator={})
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
bool Compile(REPORTER *aReporter, int aSourceLine=0, int aSourceOffset=0)
bool EvaluateFor(const BOARD_ITEM *aItemA, const BOARD_ITEM *aItemB, int aConstraint, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
bool m_Implicit
Definition drc_rule.h:124
wxString m_Name
Definition drc_rule.h:126
std::vector< DRC_TEST_PROVIDER * > GetTestProviders() const
static DRC_TEST_PROVIDER_REGISTRY & Instance()
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
void SetDRCEngine(DRC_ENGINE *engine)
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:144
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:145
A LINE_READER that reads from an open file.
Definition richio.h:185
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:251
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:309
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
std::deque< PAD * > & Pads()
Definition footprint.h:224
const std::set< int > & GetNetTieCache(const BOARD_ITEM *aItem) const
Get the set of net codes that are allowed to connect to a footprint item.
Definition footprint.h:556
bool IsNetTie() const
Definition footprint.h:340
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & FrontMask()
Return a mask holding all technical layers and the external CU layer on front side.
Definition lset.cpp:705
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition lset.cpp:712
T Min() const
Definition minoptmax.h:33
void SetMin(T v)
Definition minoptmax.h:41
void SetOpt(T v)
Definition minoptmax.h:43
void SetMax(T v)
Definition minoptmax.h:42
Handle the data for a net.
Definition netinfo.h:54
const wxString & GetNetname() const
Definition netinfo.h:112
int GetNetCode() const
Definition netinfo.h:106
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetCompositeNetclasses() const
Gets all composite (multiple assignment / missing defaults) netclasses.
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetNetclasses() const
Gets all netclasses.
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
Definition pad.h:54
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:198
A small class to help profiling.
Definition profile.h:49
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:88
double msecs(bool aSinceLast=false)
Definition profile.h:149
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:143
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
const EDA_IU_SCALE & GetIuScale() const
UNITS_PROVIDER(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
void SetUserUnits(EDA_UNITS aUnits)
Handle a list of polygons defining a copper zone.
Definition zone.h:74
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition zone.cpp:1154
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:704
bool GetDoNotAllowVias() const
Definition zone.h:720
bool GetDoNotAllowPads() const
Definition zone.h:722
bool GetDoNotAllowTracks() const
Definition zone.h:721
int GetMinThickness() const
Definition zone.h:301
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:298
int GetThermalReliefSpokeWidth() const
Definition zone.h:245
bool GetDoNotAllowFootprints() const
Definition zone.h:723
bool HasKeepoutParametersSet() const
Accessor to determine if any keepout parameters are set.
Definition zone.h:695
bool GetDoNotAllowZoneFills() const
Definition zone.h:719
int GetThermalReliefGap() const
Definition zone.h:234
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
void drcPrintDebugMessage(int level, const wxString &msg, const char *function, int line)
#define EXTENDED_ERROR_LIMIT
static bool isKeepoutZone(const BOARD_ITEM *aItem, bool aCheckFlags)
#define ERROR_LIMIT
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:39
@ DRCE_CLEARANCE
Definition drc_item.h:43
@ DRCE_FIRST
Definition drc_item.h:38
@ DRCE_LAST
Definition drc_item.h:112
@ DRC_DISALLOW_PADS
Definition drc_rule.h:94
@ DRC_DISALLOW_BURIED_VIAS
Definition drc_rule.h:92
@ DRC_DISALLOW_BLIND_VIAS
Definition drc_rule.h:91
@ DRC_DISALLOW_TEXTS
Definition drc_rule.h:96
@ DRC_DISALLOW_ZONES
Definition drc_rule.h:95
@ DRC_DISALLOW_HOLES
Definition drc_rule.h:98
@ DRC_DISALLOW_GRAPHICS
Definition drc_rule.h:97
@ DRC_DISALLOW_THROUGH_VIAS
Definition drc_rule.h:89
@ DRC_DISALLOW_FOOTPRINTS
Definition drc_rule.h:99
@ DRC_DISALLOW_TRACKS
Definition drc_rule.h:93
@ DRC_DISALLOW_MICRO_VIAS
Definition drc_rule.h:90
DRC_CONSTRAINT_T
Definition drc_rule.h:47
@ ANNULAR_WIDTH_CONSTRAINT
Definition drc_rule.h:61
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition drc_rule.h:55
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:70
@ ZONE_CONNECTION_CONSTRAINT
Definition drc_rule.h:62
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:73
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:69
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:59
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:56
@ EDGE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ MIN_RESOLVED_SPOKES_CONSTRAINT
Definition drc_rule.h:65
@ TEXT_THICKNESS_CONSTRAINT
Definition drc_rule.h:58
@ LENGTH_CONSTRAINT
Definition drc_rule.h:71
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:78
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:49
@ NULL_CONSTRAINT
Definition drc_rule.h:48
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
Definition drc_rule.h:64
@ CONNECTION_WIDTH_CONSTRAINT
Definition drc_rule.h:80
@ THERMAL_RELIEF_GAP_CONSTRAINT
Definition drc_rule.h:63
@ MAX_UNCOUPLED_CONSTRAINT
Definition drc_rule.h:74
@ ASSERTION_CONSTRAINT
Definition drc_rule.h:79
@ SKEW_CONSTRAINT
Definition drc_rule.h:72
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
Definition drc_rule.h:67
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:66
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:54
@ TEXT_HEIGHT_CONSTRAINT
Definition drc_rule.h:57
@ CREEPAGE_CONSTRAINT
Definition drc_rule.h:50
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition drc_rule.h:77
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
Definition drc_rule.h:68
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:52
constexpr int DRC_DISALLOW_VIAS
Definition drc_rule.h:102
#define _(s)
#define HOLE_PROXY
Indicates the BOARD_ITEM is a proxy for its hole.
EDA_UNITS
Definition eda_units.h:48
static FILENAME_RESOLVER * resolver
static const wxChar * traceDrcProfile
Flag to enable DRC profile timing logging.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
#define REPORT(msg)
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ RPT_SEVERITY_ERROR
const double epsilon
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
std::shared_ptr< DRC_RULE > parentRule
Definition drc_engine.h:244
DRC_RULE_CONDITION * condition
Definition drc_engine.h:243
A filename or source description, a problem input line, a line number, a byte offset,...
int clearance
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:101
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:94
@ PCB_LOCATE_HOLE_T
Definition typeinfo.h:131
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
wxString PrintZoneConnection(ZONE_CONNECTION aConnection)
Definition zones.h:56
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition zones.h:47
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:52
@ FULL
pads are covered by copper
Definition zones.h:51