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 ),
86 m_reportAllTrackErrors( 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{
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 // 2) 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 // 3) per-netclass rules
239
240 std::vector<std::shared_ptr<DRC_RULE>> netclassClearanceRules;
241 std::vector<std::shared_ptr<DRC_RULE>> netclassItemSpecificRules;
242
243 auto makeNetclassRules =
244 [&]( const std::shared_ptr<NETCLASS>& nc, bool isDefault )
245 {
246 wxString ncName = nc->GetName();
247 wxString expr;
248
249 ncName.Replace( "'", "\\'" );
250
251 if( nc->HasClearance() )
252 {
253 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
254 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
255 nc->GetClearanceParent()->GetHumanReadableName() );
256 netclassRule->m_Implicit = true;
257
258 expr = wxString::Format( wxT( "A.hasExactNetclass('%s')" ), ncName );
259 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
260 netclassClearanceRules.push_back( netclassRule );
261
263 constraint.Value().SetMin( nc->GetClearance() );
264 netclassRule->AddConstraint( constraint );
265 }
266
267 if( nc->HasTrackWidth() )
268 {
269 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
270 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
271 nc->GetTrackWidthParent()->GetHumanReadableName() );
272 netclassRule->m_Implicit = true;
273
274 expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
275 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
276 netclassClearanceRules.push_back( netclassRule );
277
279 constraint.Value().SetMin( bds.m_TrackMinWidth );
280 constraint.Value().SetOpt( nc->GetTrackWidth() );
281 netclassRule->AddConstraint( constraint );
282 }
283
284 if( nc->HasDiffPairWidth() )
285 {
286 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
287 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
288 nc->GetDiffPairWidthParent()->GetHumanReadableName() );
289 netclassRule->m_Implicit = true;
290
291 expr = wxString::Format( wxT( "A.NetClass == '%s' && A.inDiffPair('*')" ), ncName );
292 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
293 netclassItemSpecificRules.push_back( netclassRule );
294
296 constraint.Value().SetMin( bds.m_TrackMinWidth );
297 constraint.Value().SetOpt( nc->GetDiffPairWidth() );
298 netclassRule->AddConstraint( constraint );
299 }
300
301 if( nc->HasDiffPairGap() )
302 {
303 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
304 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
305 nc->GetDiffPairGapParent()->GetHumanReadableName() );
306 netclassRule->m_Implicit = true;
307
308 expr = wxString::Format( wxT( "A.NetClass == '%s'" ), ncName );
309 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
310 netclassItemSpecificRules.push_back( netclassRule );
311
313 constraint.Value().SetMin( bds.m_MinClearance );
314 constraint.Value().SetOpt( nc->GetDiffPairGap() );
315 netclassRule->AddConstraint( constraint );
316
317 // A narrower diffpair gap overrides the netclass min clearance
318 if( nc->GetDiffPairGap() < nc->GetClearance() )
319 {
320 netclassRule = std::make_shared<DRC_RULE>();
321 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (diff pair)" ),
322 nc->GetDiffPairGapParent()->GetHumanReadableName() );
323 netclassRule->m_Implicit = true;
324
325 expr = wxString::Format( wxT( "A.NetClass == '%s' && AB.isCoupledDiffPair()" ),
326 ncName );
327 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
328 netclassItemSpecificRules.push_back( netclassRule );
329
330 DRC_CONSTRAINT min_clearanceConstraint( CLEARANCE_CONSTRAINT );
331 min_clearanceConstraint.Value().SetMin( nc->GetDiffPairGap() );
332 netclassRule->AddConstraint( min_clearanceConstraint );
333 }
334 }
335
336 if( nc->HasViaDiameter() )
337 {
338 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
339 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
340 nc->GetViaDiameterParent()->GetHumanReadableName() );
341 netclassRule->m_Implicit = true;
342
343 expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
344 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
345 netclassItemSpecificRules.push_back( netclassRule );
346
348 constraint.Value().SetMin( bds.m_ViasMinSize );
349 constraint.Value().SetOpt( nc->GetViaDiameter() );
350 netclassRule->AddConstraint( constraint );
351 }
352
353 if( nc->HasViaDrill() )
354 {
355 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
356 netclassRule->m_Name = wxString::Format( _( "netclass '%s'" ),
357 nc->GetViaDrillParent()->GetHumanReadableName() );
358 netclassRule->m_Implicit = true;
359
360 expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type != 'Micro'" ), ncName );
361 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
362 netclassItemSpecificRules.push_back( netclassRule );
363
365 constraint.Value().SetMin( bds.m_MinThroughDrill );
366 constraint.Value().SetOpt( nc->GetViaDrill() );
367 netclassRule->AddConstraint( constraint );
368 }
369
370 if( nc->HasuViaDiameter() )
371 {
372 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
373 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
374 nc->GetuViaDiameterParent()->GetHumanReadableName() );
375 netclassRule->m_Implicit = true;
376
377 expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
378 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
379 netclassItemSpecificRules.push_back( netclassRule );
380
382 constraint.Value().SetMin( bds.m_MicroViasMinSize );
383 constraint.Value().SetMin( nc->GetuViaDiameter() );
384 netclassRule->AddConstraint( constraint );
385 }
386
387 if( nc->HasuViaDrill() )
388 {
389 std::shared_ptr<DRC_RULE> netclassRule = std::make_shared<DRC_RULE>();
390 netclassRule->m_Name = wxString::Format( _( "netclass '%s' (uvia)" ),
391 nc->GetuViaDrillParent()->GetHumanReadableName() );
392 netclassRule->m_Implicit = true;
393
394 expr = wxString::Format( wxT( "A.NetClass == '%s' && A.Via_Type == 'Micro'" ), ncName );
395 netclassRule->m_Condition = new DRC_RULE_CONDITION( expr );
396 netclassItemSpecificRules.push_back( netclassRule );
397
399 constraint.Value().SetMin( bds.m_MicroViasMinDrill );
400 constraint.Value().SetOpt( nc->GetuViaDrill() );
401 netclassRule->AddConstraint( constraint );
402 }
403 };
404
406 makeNetclassRules( bds.m_NetSettings->GetDefaultNetclass(), true );
407
408 for( const auto& [name, netclass] : bds.m_NetSettings->GetNetclasses() )
409 makeNetclassRules( netclass, false );
410
411 for( const auto& [name, netclass] : bds.m_NetSettings->GetCompositeNetclasses() )
412 makeNetclassRules( netclass, false );
413
414 // The netclass clearance rules have to be sorted by min clearance so the right one fires
415 // if 'A' and 'B' belong to two different netclasses.
416 //
417 // The item-specific netclass rules are all unary, so there's no 'A' vs 'B' issue.
418
419 std::sort( netclassClearanceRules.begin(), netclassClearanceRules.end(),
420 []( const std::shared_ptr<DRC_RULE>& lhs, const std::shared_ptr<DRC_RULE>& rhs )
421 {
422 return lhs->m_Constraints[0].m_Value.Min()
423 < rhs->m_Constraints[0].m_Value.Min();
424 } );
425
426 for( std::shared_ptr<DRC_RULE>& ncRule : netclassClearanceRules )
427 addRule( ncRule );
428
429 for( std::shared_ptr<DRC_RULE>& ncRule : netclassItemSpecificRules )
430 addRule( ncRule );
431
432 // 3) keepout area rules
433
434 std::vector<ZONE*> keepoutZones;
435
436 for( ZONE* zone : m_board->Zones() )
437 {
438 if( isKeepoutZone( zone, true ) )
439 keepoutZones.push_back( zone );
440 }
441
442 for( FOOTPRINT* footprint : m_board->Footprints() )
443 {
444 for( ZONE* zone : footprint->Zones() )
445 {
446 if( isKeepoutZone( zone, true ) )
447 keepoutZones.push_back( zone );
448 }
449 }
450
451 for( ZONE* zone : keepoutZones )
452 {
453 wxString name = zone->GetZoneName();
454
455 if( name.IsEmpty() )
456 rule = createImplicitRule( _( "keepout area" ) );
457 else
458 rule = createImplicitRule( wxString::Format( _( "keepout area '%s'" ), name ) );
459
460 rule->m_ImplicitItemId = zone->m_Uuid;
461
462 rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( wxT( "A.intersectsArea('%s')" ),
463 zone->m_Uuid.AsString() ) );
464
465 rule->m_LayerCondition = zone->GetLayerSet();
466
467 int disallowFlags = 0;
468
469 if( zone->GetDoNotAllowTracks() )
470 disallowFlags |= DRC_DISALLOW_TRACKS;
471
472 if( zone->GetDoNotAllowVias() )
473 disallowFlags |= DRC_DISALLOW_VIAS;
474
475 if( zone->GetDoNotAllowPads() )
476 disallowFlags |= DRC_DISALLOW_PADS;
477
478 if( zone->GetDoNotAllowZoneFills() )
479 disallowFlags |= DRC_DISALLOW_ZONES;
480
481 if( zone->GetDoNotAllowFootprints() )
482 disallowFlags |= DRC_DISALLOW_FOOTPRINTS;
483
484 DRC_CONSTRAINT disallowConstraint( DISALLOW_CONSTRAINT );
485 disallowConstraint.m_DisallowFlags = disallowFlags;
486 rule->AddConstraint( disallowConstraint );
487 }
488}
489
490
491void DRC_ENGINE::loadRules( const wxFileName& aPath )
492{
493 if( aPath.FileExists() )
494 {
495 std::vector<std::shared_ptr<DRC_RULE>> rules;
496
497 if( FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) ) )
498 {
499 FILE_LINE_READER lineReader( fp, aPath.GetFullPath() ); // Will close rules file
500 wxString rulesText;
501
502 std::function<bool( wxString* )> resolver =
503 [&]( wxString* token ) -> bool
504 {
505 if( m_board && m_board->GetProject() )
506 return m_board->GetProject()->TextVarResolver( token );
507
508 return false;
509 };
510
511 while( char* line = lineReader.ReadLine() )
512 rulesText << ExpandTextVars( line, &resolver ) << '\n';
513
514 DRC_RULES_PARSER parser( rulesText, aPath.GetFullPath() );
515 parser.Parse( rules, m_logReporter );
516 }
517
518 // Copy the rules into the member variable afterwards so that if Parse() throws then
519 // the possibly malformed rules won't contaminate the current ruleset.
520
521 for( std::shared_ptr<DRC_RULE>& rule : rules )
522 m_rules.push_back( rule );
523 }
524}
525
526
528{
529 if( m_logReporter )
530 {
531 m_logReporter->Report( ( wxString::Format( wxT( "Compiling Rules (%d rules): " ),
532 (int) m_rules.size() ) ) );
533 }
534
535 for( std::shared_ptr<DRC_RULE>& rule : m_rules )
536 {
537 DRC_RULE_CONDITION* condition = nullptr;
538
539 if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
540 {
541 condition = rule->m_Condition;
542 condition->Compile( nullptr );
543 }
544
545 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
546 {
547 if( !m_constraintMap.count( constraint.m_Type ) )
548 m_constraintMap[ constraint.m_Type ] = new std::vector<DRC_ENGINE_CONSTRAINT*>();
549
550 DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
551
552 engineConstraint->layerTest = rule->m_LayerCondition;
553 engineConstraint->condition = condition;
554 engineConstraint->constraint = constraint;
555 engineConstraint->parentRule = rule;
556 m_constraintMap[ constraint.m_Type ]->push_back( engineConstraint );
557 }
558 }
559}
560
561
562void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
563{
565
566 for( DRC_TEST_PROVIDER* provider : m_testProviders )
567 {
568 if( m_logReporter )
569 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
570
571 provider->SetDRCEngine( this );
572 }
573
574 m_rules.clear();
575 m_rulesValid = false;
576
577 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
578 {
579 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
580 delete constraint;
581
582 delete pair.second;
583 }
584
585 m_constraintMap.clear();
586
587 m_board->IncrementTimeStamp(); // Clear board-level caches
588
589 try // attempt to load full set of rules (implicit + user rules)
590 {
592 loadRules( aRulePath );
593 compileRules();
594 }
595 catch( PARSE_ERROR& original_parse_error )
596 {
597 try // try again with just our implicit rules
598 {
600 compileRules();
601 }
602 catch( PARSE_ERROR& )
603 {
604 wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
605 }
606
607 throw original_parse_error;
608 }
609
610 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
612
613 m_rulesValid = true;
614}
615
616
617void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
618 BOARD_COMMIT* aCommit )
619{
620 PROF_TIMER timer;
621
622 SetUserUnits( aUnits );
623
624 m_reportAllTrackErrors = aReportAllTrackErrors;
625 m_testFootprints = aTestFootprints;
626
627 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
628 {
629 if( m_designSettings->Ignore( ii ) )
630 m_errorLimits[ ii ] = 0;
631 else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
633 else
635 }
636
638
639 m_board->IncrementTimeStamp(); // Invalidate all caches...
640
641 DRC_CACHE_GENERATOR cacheGenerator;
642 cacheGenerator.SetDRCEngine( this );
643
644 if( !cacheGenerator.Run() ) // ... and regenerate them.
645 return;
646
647 // Recompute component classes
649
650 int timestamp = m_board->GetTimeStamp();
651
652 for( DRC_TEST_PROVIDER* provider : m_testProviders )
653 {
654 if( m_logReporter )
655 m_logReporter->Report( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
656
657 if( !provider->RunTests( aUnits ) )
658 break;
659 }
660
661 timer.Stop();
662 wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
663
664 // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
665 // caches while DRC is running is problematic.
666 wxASSERT( timestamp == m_board->GetTimeStamp() );
667}
668
669
670#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
671
673 PCB_LAYER_ID aLayer, REPORTER* aReporter )
674{
675 DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
676
677 REPORT( "" )
678 REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
680
681 if( constraint.m_ZoneConnection == ZONE_CONNECTION::THT_THERMAL )
682 {
683 const PAD* pad = nullptr;
684
685 if( a->Type() == PCB_PAD_T )
686 pad = static_cast<const PAD*>( a );
687 else if( b->Type() == PCB_PAD_T )
688 pad = static_cast<const PAD*>( b );
689
690 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
691 {
692 constraint.m_ZoneConnection = ZONE_CONNECTION::THERMAL;
693 }
694 else
695 {
696 REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
697 EscapeHTML( PrintZoneConnection( ZONE_CONNECTION::FULL ) ) ) )
698 constraint.m_ZoneConnection = ZONE_CONNECTION::FULL;
699 }
700 }
701
702 return constraint;
703}
704
705
707 const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
708 REPORTER* aReporter )
709{
710 /*
711 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
712 * kills performance when running bulk DRC tests (where aReporter is nullptr).
713 */
714
715 const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ?
716 static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
717 const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ?
718 static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
719
720 bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
721 bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
722
723 const PAD* pad = nullptr;
724 const ZONE* zone = nullptr;
725 const FOOTPRINT* parentFootprint = nullptr;
726
727 if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
728 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
729 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
730 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
731 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
732 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
733 {
734 if( a && a->Type() == PCB_PAD_T )
735 pad = static_cast<const PAD*>( a );
736 else if( a && a->Type() == PCB_ZONE_T )
737 zone = static_cast<const ZONE*>( a );
738
739 if( b && b->Type() == PCB_PAD_T )
740 pad = static_cast<const PAD*>( b );
741 else if( b && b->Type() == PCB_ZONE_T )
742 zone = static_cast<const ZONE*>( b );
743
744 if( pad )
745 parentFootprint = pad->GetParentFootprint();
746 }
747
748 DRC_CONSTRAINT constraint;
749 constraint.m_Type = aConstraintType;
750
751 auto applyConstraint =
752 [&]( const DRC_ENGINE_CONSTRAINT* c )
753 {
754 if( c->constraint.m_Value.HasMin() )
755 {
756 if( c->parentRule && c->parentRule->m_Implicit )
757 constraint.m_ImplicitMin = true;
758
759 constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
760 }
761
762 if( c->constraint.m_Value.HasOpt() )
763 constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
764
765 if( c->constraint.m_Value.HasMax() )
766 constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
767
768 switch( c->constraint.m_Type )
769 {
777 if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
778 constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
779
780 break;
781
782 default:
783 break;
784 }
785
786 // While the expectation would be to OR the disallow flags, we've already
787 // masked them down to aItem's type -- so we're really only looking for a
788 // boolean here.
789 constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
790
791 constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
792
793 constraint.SetParentRule( c->constraint.GetParentRule() );
794
795 constraint.SetOptionsFromOther( c->constraint );
796 };
797
798 const FOOTPRINT* footprints[2] = {a ? a->GetParentFootprint() : nullptr,
799 b ? b->GetParentFootprint() : nullptr};
800
801 // Handle Footprint net ties, which will zero out the clearance for footprint objects
802 if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
803 && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
804 // and not both connected. Connected items of different nets still need to be checked
805 // for their standard clearance value
806 || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
807 && footprints[0] ) ) // And that footprint exists
808 && !a_is_non_copper // Also, both elements need to be on copper layers
809 && !b_is_non_copper )
810 {
811 const BOARD_ITEM* child_items[2] = {a, b};
812
813 // These are the items being compared against, so the order is reversed
814 const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
815
816 for( int ii = 0; ii < 2; ++ii )
817 {
818 // We need both a footprint item and a connected item to check for a net tie
819 if( !footprints[ii] || !alt_items[ii] )
820 continue;
821
822 const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
823
824 auto it = netcodes.find( alt_items[ii]->GetNetCode() );
825
826 if( it != netcodes.end() )
827 {
828 REPORT( "" )
829 REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
830 EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
831
832 constraint.SetName( _( "net tie" ) );
833 constraint.m_Value.SetMin( 0 );
834 return constraint;
835 }
836 }
837 }
838
839 // Local overrides take precedence over everything *except* board min clearance
840 if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
841 {
842 int override_val = 0;
843 std::optional<int> overrideA;
844 std::optional<int> overrideB;
845
846 if( ac && !b_is_non_copper )
847 overrideA = ac->GetClearanceOverrides( nullptr );
848
849 if( bc && !a_is_non_copper )
850 overrideB = bc->GetClearanceOverrides( nullptr );
851
852 if( overrideA.has_value() || overrideB.has_value() )
853 {
854 wxString msg;
855
856 if( overrideA.has_value() )
857 {
858 REPORT( "" )
859 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
860 EscapeHTML( a->GetItemDescription( this, true ) ),
861 MessageTextFromValue( overrideA.value() ) ) )
862
863 override_val = ac->GetClearanceOverrides( &msg ).value();
864 }
865
866 if( overrideB.has_value() )
867 {
868 REPORT( "" )
869 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
870 EscapeHTML( b->GetItemDescription( this, true ) ),
871 EscapeHTML( MessageTextFromValue( overrideB.value() ) ) ) )
872
873 if( overrideB > override_val )
874 override_val = bc->GetClearanceOverrides( &msg ).value();
875 }
876
877 if( override_val )
878 {
879 if( aConstraintType == CLEARANCE_CONSTRAINT )
880 {
881 if( override_val < m_designSettings->m_MinClearance )
882 {
883 override_val = m_designSettings->m_MinClearance;
884 msg = _( "board minimum" );
885
886 REPORT( "" )
887 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
888 MessageTextFromValue( override_val ) ) )
889 }
890 }
891 else
892 {
893 if( override_val < m_designSettings->m_HoleClearance )
894 {
895 override_val = m_designSettings->m_HoleClearance;
896 msg = _( "board minimum hole" );
897
898 REPORT( "" )
899 REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
900 MessageTextFromValue( override_val ) ) )
901 }
902 }
903
904 constraint.SetName( msg );
905 constraint.m_Value.SetMin( override_val );
906 return constraint;
907 }
908 }
909 }
910 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
911 {
912 if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
913 {
914 wxString msg;
915 ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
916
917 REPORT( "" )
918 REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
919 EscapeHTML( pad->GetItemDescription( this, true ) ),
920 EscapeHTML( PrintZoneConnection( override ) ) ) )
921
922 constraint.SetName( msg );
923 constraint.m_ZoneConnection = override;
924 return constraint;
925 }
926 }
927 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
928 {
929 if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
930 {
931 wxString msg;
932 int gap_override = pad->GetLocalThermalGapOverride( &msg );
933
934 REPORT( "" )
935 REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
936 EscapeHTML( pad->GetItemDescription( this, true ) ),
937 EscapeHTML( MessageTextFromValue( gap_override ) ) ) )
938
939 constraint.SetName( msg );
940 constraint.m_Value.SetMin( gap_override );
941 return constraint;
942 }
943 }
944 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
945 {
946 if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
947 {
948 wxString msg;
949 int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
950
951 REPORT( "" )
952 REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
953 EscapeHTML( pad->GetItemDescription( this, true ) ),
954 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
955
956 if( zone && zone->GetMinThickness() > spoke_override )
957 {
958 spoke_override = zone->GetMinThickness();
959
960 REPORT( "" )
961 REPORT( wxString::Format( _( "%s min thickness: %s." ),
962 EscapeHTML( zone->GetItemDescription( this, true ) ),
963 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
964 }
965
966 constraint.SetName( msg );
967 constraint.m_Value.SetMin( spoke_override );
968 return constraint;
969 }
970 }
971 else if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
972 {
973 std::optional<int> override;
974
975 if( pad )
976 override = pad->GetLocalSolderMaskMargin();
977 else if( a->Type() == PCB_SHAPE_T )
978 override = static_cast<const PCB_SHAPE*>( a )->GetLocalSolderMaskMargin();
979 else if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( a ) )
980 override = track->GetLocalSolderMaskMargin();
981
982 if( override )
983 {
984 REPORT( "" )
985 REPORT( wxString::Format( _( "Local override on %s; solder mask expansion: %s." ),
986 EscapeHTML( pad->GetItemDescription( this, true ) ),
987 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
988
989 constraint.m_Value.SetOpt( override.value() );
990 return constraint;
991 }
992 }
993 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
994 {
995 std::optional<int> override;
996
997 if( pad )
998 override = pad->GetLocalSolderPasteMargin();
999
1000 if( override )
1001 {
1002 REPORT( "" )
1003 REPORT( wxString::Format( _( "Local override on %s; solder paste absolute clearance: %s." ),
1004 EscapeHTML( pad->GetItemDescription( this, true ) ),
1005 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
1006
1007 constraint.m_Value.SetOpt( override.value_or( 0 ) );
1008 return constraint;
1009 }
1010 }
1011 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1012 {
1013 std::optional<double> overrideRatio;
1014
1015 if( pad )
1016 overrideRatio = pad->GetLocalSolderPasteMarginRatio();
1017
1018 if( overrideRatio )
1019 {
1020 REPORT( "" )
1021 REPORT( wxString::Format( _( "Local override on %s; solder paste relative clearance: %s." ),
1022 EscapeHTML( pad->GetItemDescription( this, true ) ),
1023 EscapeHTML( MessageTextFromValue( overrideRatio.value() * 100.0 ) ) ) )
1024
1025 constraint.m_Value.SetOpt( KiROUND( overrideRatio.value_or( 0 ) * 1000 ) );
1026 return constraint;
1027 }
1028 }
1029
1030 auto testAssertion =
1031 [&]( const DRC_ENGINE_CONSTRAINT* c )
1032 {
1033 REPORT( wxString::Format( _( "Checking assertion \"%s\"." ),
1034 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1035
1036 if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer,
1037 aReporter ) )
1038 {
1039 REPORT( _( "Assertion passed." ) )
1040 }
1041 else
1042 {
1043 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1044 }
1045 };
1046
1047 auto processConstraint =
1048 [&]( const DRC_ENGINE_CONSTRAINT* c )
1049 {
1050 bool implicit = c->parentRule && c->parentRule->m_Implicit;
1051
1052 REPORT( "" )
1053
1054 switch( c->constraint.m_Type )
1055 {
1063 REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
1064 EscapeHTML( c->constraint.GetName() ),
1065 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1066 break;
1068 REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
1069 EscapeHTML( c->constraint.GetName() ),
1070 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1071 break;
1073 REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
1074 EscapeHTML( c->constraint.GetName() ),
1075 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1076 break;
1077
1078 case SKEW_CONSTRAINT:
1079 REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
1080 EscapeHTML( c->constraint.GetName() ),
1081 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1082 break;
1083
1085 REPORT( wxString::Format( _( "Checking %s gap: %s." ),
1086 EscapeHTML( c->constraint.GetName() ),
1087 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1088 break;
1089
1091 REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
1092 EscapeHTML( c->constraint.GetName() ),
1093 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1094 break;
1095
1097 REPORT( wxString::Format( _( "Checking %s solder mask expansion: %s." ),
1098 EscapeHTML( c->constraint.GetName() ),
1099 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1100 break;
1101
1103 REPORT( wxString::Format( _( "Checking %s solder paste absolute cleraance: %s." ),
1104 EscapeHTML( c->constraint.GetName() ),
1105 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1106 break;
1107
1109 REPORT( wxString::Format( _( "Checking %s solder paste relative clearance: %s." ),
1110 EscapeHTML( c->constraint.GetName() ),
1111 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1112 break;
1113
1115 REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
1116 EscapeHTML( c->constraint.GetName() ),
1118 c->constraint.m_Value.Min() ) ) )
1119 break;
1120
1122 REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
1123 EscapeHTML( c->constraint.GetName() ),
1124 EscapeHTML( PrintZoneConnection( c->constraint.m_ZoneConnection ) ) ) )
1125 break;
1126
1134 case LENGTH_CONSTRAINT:
1137 {
1138 if( aReporter )
1139 {
1140 wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1141 wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1142 wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1143
1144 if( implicit )
1145 {
1146 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1147 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1148
1149 switch( c->constraint.m_Type )
1150 {
1152 if( c->constraint.m_Value.HasOpt() )
1153 {
1154 REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
1155 EscapeHTML( c->constraint.GetName() ),
1156 opt ) )
1157 }
1158 else if( c->constraint.m_Value.HasMin() )
1159 {
1160 REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
1161 EscapeHTML( c->constraint.GetName() ),
1162 min ) )
1163 }
1164
1165 break;
1166
1168 REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
1169 EscapeHTML( c->constraint.GetName() ),
1170 opt ) )
1171 break;
1172
1174 if( c->constraint.m_Value.HasOpt() )
1175 {
1176 REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
1177 EscapeHTML( c->constraint.GetName() ),
1178 opt ) )
1179 }
1180 else if( c->constraint.m_Value.HasMin() )
1181 {
1182 REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
1183 EscapeHTML( c->constraint.GetName() ),
1184 min ) )
1185 }
1186 break;
1187
1189 if( c->constraint.m_Value.HasOpt() )
1190 {
1191 REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
1192 EscapeHTML( c->constraint.GetName() ),
1193 opt ) )
1194 }
1195 else if( c->constraint.m_Value.HasMin() )
1196 {
1197 REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
1198 EscapeHTML( c->constraint.GetName() ),
1199 min ) )
1200 }
1201
1202 break;
1203
1207 REPORT( wxString::Format( _( "Checking %s: min %s." ),
1208 EscapeHTML( c->constraint.GetName() ),
1209 min ) )
1210 break;
1211
1213 if( c->constraint.m_Value.HasOpt() )
1214 {
1215 REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
1216 EscapeHTML( c->constraint.GetName() ),
1217 opt ) )
1218 }
1219 else if( c->constraint.m_Value.HasMin() )
1220 {
1221 REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
1222 EscapeHTML( c->constraint.GetName() ),
1223 min ) )
1224 }
1225
1226 break;
1227
1229 REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
1230 EscapeHTML( c->constraint.GetName() ),
1231 min ) )
1232 break;
1233
1234 default:
1235 REPORT( wxString::Format( _( "Checking %s." ),
1236 EscapeHTML( c->constraint.GetName() ) ) )
1237 }
1238 }
1239 else
1240 {
1241 if( c->constraint.m_Value.HasMin() )
1242 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1243
1244 if( c->constraint.m_Value.HasOpt() )
1245 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1246
1247 if( c->constraint.m_Value.HasMax() )
1248 max = MessageTextFromValue( c->constraint.m_Value.Max() );
1249
1250 REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
1251 EscapeHTML( c->constraint.GetName() ),
1252 min,
1253 opt,
1254 max ) )
1255 }
1256 }
1257 break;
1258 }
1259
1260 default:
1261 REPORT( wxString::Format( _( "Checking %s." ),
1262 EscapeHTML( c->constraint.GetName() ) ) )
1263 }
1264
1265 if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
1266 {
1267 if( a_is_non_copper || b_is_non_copper )
1268 {
1269 if( implicit )
1270 {
1271 REPORT( _( "Netclass clearances apply only between copper items." ) )
1272 }
1273 else if( a_is_non_copper )
1274 {
1275 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1276 EscapeHTML( a->GetItemDescription( this, true ) ) ) )
1277 }
1278 else if( b_is_non_copper )
1279 {
1280 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1281 EscapeHTML( b->GetItemDescription( this, true ) ) ) )
1282 }
1283
1284 return;
1285 }
1286 }
1287 else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
1288 {
1289 int mask;
1290
1291 if( a->GetFlags() & HOLE_PROXY )
1292 {
1293 mask = DRC_DISALLOW_HOLES;
1294 }
1295 else if( a->Type() == PCB_VIA_T )
1296 {
1297 mask = DRC_DISALLOW_VIAS;
1298
1299 switch( static_cast<const PCB_VIA*>( a )->GetViaType() )
1300 {
1301 case VIATYPE::BLIND_BURIED: mask |= DRC_DISALLOW_BB_VIAS; break;
1302 case VIATYPE::MICROVIA: mask |= DRC_DISALLOW_MICRO_VIAS; break;
1303 default: break;
1304 }
1305 }
1306 else
1307 {
1308 switch( a->Type() )
1309 {
1310 case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
1311 case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
1312 case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
1313 case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
1314 case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1315 case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
1316 case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
1317 case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
1318 case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
1319
1320 case PCB_ZONE_T:
1321 // Treat teardrop areas as tracks for DRC purposes
1322 if( static_cast<const ZONE*>( a )->IsTeardropArea() )
1323 mask = DRC_DISALLOW_TRACKS;
1324 else
1325 mask = DRC_DISALLOW_ZONES;
1326
1327 break;
1328
1329 case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
1330 default: mask = 0; break;
1331 }
1332 }
1333
1334 if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
1335 {
1336 if( implicit )
1337 REPORT( _( "Keepout constraint not met." ) )
1338 else
1339 REPORT( _( "Disallow constraint not met." ) )
1340
1341 return;
1342 }
1343
1344 LSET itemLayers = a->GetLayerSet();
1345
1346 if( a->Type() == PCB_FOOTPRINT_T )
1347 {
1348 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
1349
1350 if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
1351 itemLayers |= LSET::FrontMask();
1352
1353 if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
1354 itemLayers |= LSET::BackMask();
1355 }
1356
1357 if( !( c->layerTest & itemLayers ).any() )
1358 {
1359 if( implicit )
1360 {
1361 REPORT( _( "Keepout layer(s) not matched." ) )
1362 }
1363 else if( c->parentRule )
1364 {
1365 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1366 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1367 }
1368 else
1369 {
1370 REPORT( _( "Rule layer not matched; rule ignored." ) )
1371 }
1372
1373 return;
1374 }
1375 }
1376
1377 if( ( aLayer != UNDEFINED_LAYER && !c->layerTest.test( aLayer ) )
1378 || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
1379 {
1380 if( implicit )
1381 {
1382 REPORT( _( "Constraint layer not matched." ) )
1383 }
1384 else if( c->parentRule )
1385 {
1386 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1387 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1388 }
1389 else
1390 {
1391 REPORT( _( "Rule layer not matched; rule ignored." ) )
1392 }
1393 }
1394 else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
1395 && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
1396 {
1397 // Report non-drilled-holes as an implicit condition
1398 REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
1399 a->GetItemDescription( this, true ) ) )
1400 }
1401 else if( !c->condition || c->condition->GetExpression().IsEmpty() )
1402 {
1403 if( aReporter )
1404 {
1405 if( implicit )
1406 {
1407 REPORT( _( "Unconditional constraint applied." ) )
1408 }
1409 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1410 {
1411 REPORT( _( "Unconditional rule applied." ) )
1412 testAssertion( c );
1413 }
1414 else
1415 {
1416 REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
1417 }
1418 }
1419
1420 applyConstraint( c );
1421 }
1422 else
1423 {
1424 if( implicit )
1425 {
1426 // Don't report on implicit rule conditions; they're synthetic.
1427 }
1428 else
1429 {
1430 REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
1431 EscapeHTML( c->condition->GetExpression() ) ) )
1432 }
1433
1434 if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1435 {
1436 if( aReporter )
1437 {
1438 if( implicit )
1439 {
1440 REPORT( _( "Constraint applied." ) )
1441 }
1442 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1443 {
1444 REPORT( _( "Rule applied." ) )
1445 testAssertion( c );
1446 }
1447 else
1448 {
1449 REPORT( _( "Rule applied; overrides previous constraints." ) )
1450 }
1451 }
1452
1453 applyConstraint( c );
1454 }
1455 else
1456 {
1457 REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
1458 : _( "Condition not satisfied; rule ignored." ) )
1459 }
1460 }
1461 };
1462
1463 if( m_constraintMap.count( aConstraintType ) )
1464 {
1465 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1466
1467 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1468 processConstraint( rule );
1469 }
1470
1471 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1472 return constraint;
1473
1474 // Special case for properties which can be inherited from parent footprints. We've already
1475 // checked for local overrides, and there were no rules targetting the item itself, so we know
1476 // we're inheriting and need to see if there are any rules targetting the parent footprint.
1477 if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1478 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1479 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1480 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1481 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1482 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT ) )
1483 {
1484 REPORT( "" )
1485 REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
1486 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
1487
1488 if( a == pad )
1489 a = parentFootprint;
1490 else
1491 b = parentFootprint;
1492
1493 if( m_constraintMap.count( aConstraintType ) )
1494 {
1495 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1496
1497 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1498 processConstraint( rule );
1499
1500 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1501 return constraint;
1502 }
1503
1504 // Found nothing again? Return the defaults.
1505 if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1506 {
1507 constraint.SetParentRule( nullptr );
1508 constraint.SetName( _( "board setup" ) );
1510 return constraint;
1511 }
1512 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1513 {
1514 constraint.SetParentRule( nullptr );
1515 constraint.SetName( _( "board setup" ) );
1517 return constraint;
1518 }
1519 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1520 {
1521 constraint.SetParentRule( nullptr );
1522 constraint.SetName( _( "board setup" ) );
1524 return constraint;
1525 }
1526 }
1527
1528 // Unfortunately implicit rules don't work for local clearances (such as zones) because
1529 // they have to be max'ed with netclass values (which are already implicit rules), and our
1530 // rule selection paradigm is "winner takes all".
1531 if( aConstraintType == CLEARANCE_CONSTRAINT )
1532 {
1533 int global = constraint.m_Value.Min();
1534 int clearance = global;
1535 bool needBlankLine = true;
1536
1537 if( ac && ac->GetLocalClearance().has_value() )
1538 {
1539 int localA = ac->GetLocalClearance().value();
1540
1541 if( needBlankLine )
1542 {
1543 REPORT( "" )
1544 needBlankLine = false;
1545 }
1546
1547 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1548 EscapeHTML( a->GetItemDescription( this, true ) ),
1549 MessageTextFromValue( localA ) ) )
1550
1551 if( localA > clearance )
1552 {
1553 wxString msg;
1554 clearance = ac->GetLocalClearance( &msg ).value();
1555 constraint.SetParentRule( nullptr );
1556 constraint.SetName( msg );
1557 constraint.m_Value.SetMin( clearance );
1558 }
1559 }
1560
1561 if( bc && bc->GetLocalClearance().has_value() )
1562 {
1563 int localB = bc->GetLocalClearance().value();
1564
1565 if( needBlankLine )
1566 {
1567 REPORT( "" )
1568 needBlankLine = false;
1569 }
1570
1571 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1572 EscapeHTML( b->GetItemDescription( this, true ) ),
1573 MessageTextFromValue( localB ) ) )
1574
1575 if( localB > clearance )
1576 {
1577 wxString msg;
1578 clearance = bc->GetLocalClearance( &msg ).value();
1579 constraint.SetParentRule( nullptr );
1580 constraint.SetName( msg );
1581 constraint.m_Value.SetMin( clearance );
1582 }
1583 }
1584
1585 if( !a_is_non_copper && !b_is_non_copper )
1586 {
1587 if( needBlankLine )
1588 {
1589 REPORT( "" )
1590 needBlankLine = false;
1591 }
1592
1593 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1595
1596 if( clearance < m_designSettings->m_MinClearance )
1597 {
1598 constraint.SetParentRule( nullptr );
1599 constraint.SetName( _( "board minimum" ) );
1601 }
1602 }
1603
1604 return constraint;
1605 }
1606 else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
1607 {
1608 REPORT( "" )
1609 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1611
1612 if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
1613 {
1614 constraint.SetParentRule( nullptr );
1615 constraint.SetName( _( "board minimum" ) );
1617 }
1618
1619 return constraint;
1620 }
1621 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1622 {
1623 if( pad && parentFootprint )
1624 {
1625 ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
1626
1627 if( local != ZONE_CONNECTION::INHERITED )
1628 {
1629 REPORT( "" )
1630 REPORT( wxString::Format( _( "%s zone connection: %s." ),
1631 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
1632 EscapeHTML( PrintZoneConnection( local ) ) ) )
1633
1634 constraint.SetParentRule( nullptr );
1635 constraint.SetName( _( "footprint" ) );
1636 constraint.m_ZoneConnection = local;
1637 return constraint;
1638 }
1639 }
1640
1641 if( zone )
1642 {
1643 ZONE_CONNECTION local = zone->GetPadConnection();
1644
1645 REPORT( "" )
1646 REPORT( wxString::Format( _( "%s pad connection: %s." ),
1647 EscapeHTML( zone->GetItemDescription( this, true ) ),
1648 EscapeHTML( PrintZoneConnection( local ) ) ) )
1649
1650 constraint.SetParentRule( nullptr );
1651 constraint.SetName( _( "zone" ) );
1652 constraint.m_ZoneConnection = local;
1653 return constraint;
1654 }
1655 }
1656 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1657 {
1658 if( zone )
1659 {
1660 int local = zone->GetThermalReliefGap();
1661
1662 REPORT( "" )
1663 REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
1664 EscapeHTML( zone->GetItemDescription( this, true ) ),
1665 EscapeHTML( MessageTextFromValue( local ) ) ) )
1666
1667 constraint.SetParentRule( nullptr );
1668 constraint.SetName( _( "zone" ) );
1669 constraint.m_Value.SetMin( local );
1670 return constraint;
1671 }
1672 }
1673 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
1674 {
1675 if( zone )
1676 {
1677 int local = zone->GetThermalReliefSpokeWidth();
1678
1679 REPORT( "" )
1680 REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
1681 EscapeHTML( zone->GetItemDescription( this, true ) ),
1682 EscapeHTML( MessageTextFromValue( local ) ) ) )
1683
1684 constraint.SetParentRule( nullptr );
1685 constraint.SetName( _( "zone" ) );
1686 constraint.m_Value.SetMin( local );
1687 return constraint;
1688 }
1689 }
1690
1691 if( !constraint.GetParentRule() )
1692 {
1693 constraint.m_Type = NULL_CONSTRAINT;
1694 constraint.m_DisallowFlags = 0;
1695 }
1696
1697 return constraint;
1698}
1699
1700
1702 std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
1703 REPORTER* aReporter )
1704{
1705 /*
1706 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
1707 * kills performance when running bulk DRC tests (where aReporter is nullptr).
1708 */
1709
1710 auto testAssertion =
1711 [&]( const DRC_ENGINE_CONSTRAINT* c )
1712 {
1713 REPORT( wxString::Format( _( "Checking rule assertion \"%s\"." ),
1714 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1715
1716 if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
1717 a->GetLayer(), aReporter ) )
1718 {
1719 REPORT( _( "Assertion passed." ) )
1720 }
1721 else
1722 {
1723 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1724 aFailureHandler( &c->constraint );
1725 }
1726 };
1727
1728 auto processConstraint =
1729 [&]( const DRC_ENGINE_CONSTRAINT* c )
1730 {
1731 REPORT( "" )
1732 REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
1733
1734 if( !( a->GetLayerSet() & c->layerTest ).any() )
1735 {
1736 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1737 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1738 }
1739
1740 if( !c->condition || c->condition->GetExpression().IsEmpty() )
1741 {
1742 REPORT( _( "Unconditional rule applied." ) )
1743 testAssertion( c );
1744 }
1745 else
1746 {
1747 REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
1748 EscapeHTML( c->condition->GetExpression() ) ) )
1749
1750 if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
1751 a->GetLayer(), aReporter ) )
1752 {
1753 REPORT( _( "Rule applied." ) )
1754 testAssertion( c );
1755 }
1756 else
1757 {
1758 REPORT( _( "Condition not satisfied; rule ignored." ) )
1759 }
1760 }
1761 };
1762
1764 {
1765 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
1766
1767 for( int ii = 0; ii < (int) ruleset->size(); ++ii )
1768 processConstraint( ruleset->at( ii ) );
1769 }
1770}
1771
1772
1773#undef REPORT
1774
1775
1777{
1778 assert( error_code >= 0 && error_code <= DRCE_LAST );
1779 return m_errorLimits[ error_code ] <= 0;
1780}
1781
1782
1783void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
1784 int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
1785{
1786 static std::mutex globalLock;
1787
1788 m_errorLimits[ aItem->GetErrorCode() ] -= 1;
1789
1790 if( m_violationHandler )
1791 {
1792 std::lock_guard<std::mutex> guard( globalLock );
1793 m_violationHandler( aItem, aPos, aMarkerLayer, aCustomHandler );
1794 }
1795
1796 if( m_logReporter )
1797 {
1798 wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
1799 aItem->GetViolatingTest()->GetName(),
1800 aItem->GetErrorMessage(),
1801 aItem->GetErrorCode() );
1802
1803 DRC_RULE* rule = aItem->GetViolatingRule();
1804
1805 if( rule )
1806 msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
1807
1808 m_logReporter->Report( msg );
1809
1810 wxString violatingItemsStr = wxT( "Violating items: " );
1811
1812 m_logReporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
1813 aPos.x,
1814 aPos.y ) );
1815 }
1816}
1817
1818
1820{
1821 if( !m_progressReporter )
1822 return true;
1823
1824 return m_progressReporter->KeepRefreshing( aWait );
1825}
1826
1827
1829{
1830 if( m_progressReporter )
1832}
1833
1834
1836{
1837 if( m_progressReporter )
1839}
1840
1841
1842bool DRC_ENGINE::ReportProgress( double aProgress )
1843{
1844 if( !m_progressReporter )
1845 return true;
1846
1848 return m_progressReporter->KeepRefreshing( false );
1849}
1850
1851
1852bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
1853{
1854 if( !m_progressReporter )
1855 return true;
1856
1857 m_progressReporter->AdvancePhase( aMessage );
1858 bool retval = m_progressReporter->KeepRefreshing( false );
1859 wxSafeYield( nullptr, true ); // Force an update for the message
1860 return retval;
1861}
1862
1863
1865{
1867}
1868
1869
1871{
1872 //drc_dbg( 10, "hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size() );
1873
1874 if( m_constraintMap.count( constraintID ) )
1875 return m_constraintMap[ constraintID ]->size() > 0;
1876
1877 return false;
1878}
1879
1880
1882{
1883 int worst = 0;
1884
1885 if( m_constraintMap.count( aConstraintId ) )
1886 {
1887 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1888 {
1889 int current = c->constraint.GetValue().Min();
1890
1891 if( current > worst )
1892 {
1893 worst = current;
1894 aConstraint = c->constraint;
1895 }
1896 }
1897 }
1898
1899 return worst > 0;
1900}
1901
1902
1904{
1905 std::set<int> distinctMinimums;
1906
1907 if( m_constraintMap.count( aConstraintId ) )
1908 {
1909 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1910 distinctMinimums.emplace( c->constraint.GetValue().Min() );
1911 }
1912
1913 return distinctMinimums;
1914}
1915
1916
1917// fixme: move two functions below to pcbcommon?
1918int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
1919 wxString& aBaseDpName )
1920{
1921 int rv = 0;
1922 int count = 0;
1923
1924 for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
1925 {
1926 int ch = *it;
1927
1928 if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
1929 {
1930 continue;
1931 }
1932 else if( ch == '+' )
1933 {
1934 aComplementNet = wxT( "-" );
1935 rv = 1;
1936 }
1937 else if( ch == '-' )
1938 {
1939 aComplementNet = wxT( "+" );
1940 rv = -1;
1941 }
1942 else if( ch == 'N' )
1943 {
1944 aComplementNet = wxT( "P" );
1945 rv = -1;
1946 }
1947 else if ( ch == 'P' )
1948 {
1949 aComplementNet = wxT( "N" );
1950 rv = 1;
1951 }
1952 else
1953 {
1954 break;
1955 }
1956 }
1957
1958 if( rv != 0 && count >= 1 )
1959 {
1960 aBaseDpName = aNetName.Left( aNetName.Length() - count );
1961 aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
1962 }
1963
1964 return rv;
1965}
1966
1967
1968bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
1969{
1970 wxString refName = aNet->GetNetname();
1971 wxString dummy, coupledNetName;
1972
1973 if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
1974 {
1975 NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
1976
1977 if( !net )
1978 return false;
1979
1980 if( polarity > 0 )
1981 {
1982 aNetP = aNet->GetNetCode();
1983 aNetN = net->GetNetCode();
1984 }
1985 else
1986 {
1987 aNetP = net->GetNetCode();
1988 aNetN = aNet->GetNetCode();
1989 }
1990
1991 return true;
1992 }
1993
1994 return false;
1995}
1996
1997
2002bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
2003 const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
2004{
2005 FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
2006
2007 if( parentFootprint && parentFootprint->IsNetTie() )
2008 {
2010 std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
2011
2012 for( PAD* pad : parentFootprint->Pads() )
2013 {
2014 if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
2015 {
2016 if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
2017 return true;
2018 }
2019 }
2020 }
2021
2022 return false;
2023}
2024
2025
2027{
2028 for( DRC_TEST_PROVIDER* prov : m_testProviders )
2029 {
2030 if( name == prov->GetName() )
2031 return prov;
2032 }
2033
2034 return nullptr;
2035}
const char * name
Definition: DXF_plotter.cpp:62
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
constexpr EDA_IU_SCALE unityScale
Definition: base_units.h:115
#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
bool Ignore(int aDRCErrorCode)
Return true if the DRC error code's severity is SEVERITY_IGNORE.
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
Definition: board_item.cpp:97
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:317
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:2070
const ZONES & Zones() const
Definition: board.h:362
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:2205
void IncrementTimeStamp()
Definition: board.cpp:253
const FOOTPRINTS & Footprints() const
Definition: board.h:358
PROJECT * GetProject() const
Definition: board.h:536
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1011
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:894
COMPONENT_CLASS_MANAGER & GetComponentClassManager()
Gets the component class manager.
Definition: board.h:1323
int GetTimeStamp() const
Definition: board.h:340
void ForceComponentClassRecalculation() const
Forces the component class for all footprints to be recalculated.
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
int m_DisallowFlags
Definition: drc_rule.h:203
void SetParentRule(DRC_RULE *aParentRule)
Definition: drc_rule.h:163
MINOPTMAX< int > & Value()
Definition: drc_rule.h:161
ZONE_CONNECTION m_ZoneConnection
Definition: drc_rule.h:204
void SetName(const wxString &aName)
Definition: drc_rule.h:166
MINOPTMAX< int > m_Value
Definition: drc_rule.h:202
DRC_CONSTRAINT_T m_Type
Definition: drc_rule.h:201
void SetOptionsFromOther(const DRC_CONSTRAINT &aOther)
Definition: drc_rule.h:198
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:164
bool m_ImplicitMin
Definition: drc_rule.h:206
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > * > m_constraintMap
Definition: drc_engine.h:253
void AdvanceProgress()
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
Definition: drc_engine.cpp:617
void ReportViolation(const std::shared_ptr< DRC_ITEM > &aItem, const VECTOR2I &aPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
bool m_testFootprints
Definition: drc_engine.h:250
void addRule(std::shared_ptr< DRC_RULE > &rule)
Definition: drc_engine.h:213
PROGRESS_REPORTER * m_progressReporter
Definition: drc_engine.h:257
void loadRules(const wxFileName &aPath)
Load and parse a rule set from an sexpr text file.
Definition: drc_engine.cpp:491
std::vector< DRC_TEST_PROVIDER * > m_testProviders
Definition: drc_engine.h:246
REPORTER * m_logReporter
Definition: drc_engine.h:256
void compileRules()
Definition: drc_engine.cpp:527
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
bool KeepRefreshing(bool aWait=false)
BOARD * m_board
Definition: drc_engine.h:240
bool m_reportAllTrackErrors
Definition: drc_engine.h:249
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:98
void SetMaxProgress(int aSize)
DRC_ENGINE(BOARD *aBoard=nullptr, BOARD_DESIGN_SETTINGS *aSettings=nullptr)
Definition: drc_engine.cpp:79
std::vector< int > m_errorLimits
Definition: drc_engine.h:248
bool IsErrorLimitExceeded(int error_code)
void ProcessAssertions(const BOARD_ITEM *a, std::function< void(const DRC_CONSTRAINT *)> aFailureHandler, REPORTER *aReporter=nullptr)
void loadImplicitRules()
Definition: drc_engine.cpp:154
DRC_VIOLATION_HANDLER m_violationHandler
Definition: drc_engine.h:255
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:706
std::vector< std::shared_ptr< DRC_RULE > > m_rules
Definition: drc_engine.h:244
std::shared_ptr< DRC_RULE > createImplicitRule(const wxString &name)
Definition: drc_engine.cpp:141
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()
Definition: drc_engine.cpp:98
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
Definition: drc_engine.cpp:562
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:672
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:245
BOARD_DESIGN_SETTINGS * m_designSettings
Definition: drc_engine.h:239
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
bool Compile(REPORTER *aReporter, int aSourceLine=0, int aSourceOffset=0)
bool m_Implicit
Definition: drc_rule.h:118
wxString m_Name
Definition: drc_rule.h:120
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:109
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
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:249
ZONE_CONNECTION GetLocalZoneConnection() const
Definition: footprint.h:291
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3190
std::deque< PAD * > & Pads()
Definition: footprint.h:209
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:522
bool IsNetTie() const
Definition: footprint.h:306
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition: footprint.cpp:2185
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2998
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:707
static const LSET & BackMask()
Return a mask holding all technical layers and the external CU layer on back side.
Definition: lset.cpp:714
T Min() const
Definition: minoptmax.h:33
void SetMin(T v)
Definition: minoptmax.h:41
void SetOpt(T v)
Definition: minoptmax.h:43
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
int GetNetCode() const
Definition: netinfo.h:108
Definition: pad.h:54
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_shape.h:193
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
virtual bool IsCancelled() const =0
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
virtual void AdvanceProgress()=0
Increment the progress bar length (inside the current virtual zone).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
virtual bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:75
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition: reporter.h:102
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
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:1109
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:699
bool GetDoNotAllowVias() const
Definition: zone.h:715
bool GetDoNotAllowPads() const
Definition: zone.h:717
bool GetDoNotAllowTracks() const
Definition: zone.h:716
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:718
bool HasKeepoutParametersSet() const
Accessor to determine if any keepout parameters are set.
Definition: zone.h:690
bool GetDoNotAllowZoneFills() const
Definition: zone.h:714
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)
Definition: drc_engine.cpp:65
#define EXTENDED_ERROR_LIMIT
Definition: drc_engine.cpp:52
static bool isKeepoutZone(const BOARD_ITEM *aItem, bool aCheckFlags)
Definition: drc_engine.cpp:112
#define ERROR_LIMIT
Definition: drc_engine.cpp:51
std::function< void(PCB_MARKER *aMarker)> DRC_CUSTOM_MARKER_HANDLER
Definition: drc_engine.h:68
@ 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:91
@ DRC_DISALLOW_VIAS
Definition: drc_rule.h:87
@ DRC_DISALLOW_TEXTS
Definition: drc_rule.h:93
@ DRC_DISALLOW_ZONES
Definition: drc_rule.h:92
@ DRC_DISALLOW_HOLES
Definition: drc_rule.h:95
@ DRC_DISALLOW_GRAPHICS
Definition: drc_rule.h:94
@ DRC_DISALLOW_FOOTPRINTS
Definition: drc_rule.h:96
@ DRC_DISALLOW_TRACKS
Definition: drc_rule.h:90
@ DRC_DISALLOW_MICRO_VIAS
Definition: drc_rule.h:88
@ DRC_DISALLOW_BB_VIAS
Definition: drc_rule.h:89
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
#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
Definition: export_idf.cpp:53
static const wxChar * traceDrcProfile
Flag to enable DRC profile timing logging.
Definition: drc_engine.cpp:62
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.
Definition: eda_units.cpp:404
const double epsilon
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
std::shared_ptr< DRC_RULE > parentRule
Definition: drc_engine.h:231
DRC_RULE_CONDITION * condition
Definition: drc_engine.h:230
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120
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:107
@ 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_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:128
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
wxString PrintZoneConnection(ZONE_CONNECTION aConnection)
Definition: zones.h:56
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:47