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 // 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
405 m_board->SynchronizeNetsAndNetClasses( false );
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 m_logReporter->Report( wxT( "Compiling Rules" ) );
531
532 REPORTER error_semaphore;
533
534 for( std::shared_ptr<DRC_RULE>& rule : m_rules )
535 {
536 DRC_RULE_CONDITION* condition = nullptr;
537
538 if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
539 {
540 condition = rule->m_Condition;
541 condition->Compile( &error_semaphore );
542 }
543
544 if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
545 THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name,
546 TO_UTF8( rule->m_Condition->GetExpression() ), 0, 0 );
547
548 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
549 {
550 if( !m_constraintMap.count( constraint.m_Type ) )
551 m_constraintMap[ constraint.m_Type ] = new std::vector<DRC_ENGINE_CONSTRAINT*>();
552
553 DRC_ENGINE_CONSTRAINT* engineConstraint = new DRC_ENGINE_CONSTRAINT;
554
555 engineConstraint->layerTest = rule->m_LayerCondition;
556 engineConstraint->condition = condition;
557 engineConstraint->constraint = constraint;
558 engineConstraint->parentRule = rule;
559 m_constraintMap[ constraint.m_Type ]->push_back( engineConstraint );
560 }
561 }
562}
563
564
565void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
566{
568
569 for( DRC_TEST_PROVIDER* provider : m_testProviders )
570 {
571 if( m_logReporter )
572 m_logReporter->Report( wxString::Format( wxT( "Create DRC provider: '%s'" ), provider->GetName() ) );
573
574 provider->SetDRCEngine( this );
575 }
576
577 m_rules.clear();
578 m_rulesValid = false;
579
580 for( std::pair<DRC_CONSTRAINT_T, std::vector<DRC_ENGINE_CONSTRAINT*>*> pair : m_constraintMap )
581 {
582 for( DRC_ENGINE_CONSTRAINT* constraint : *pair.second )
583 delete constraint;
584
585 delete pair.second;
586 }
587
588 m_constraintMap.clear();
589
590 m_board->IncrementTimeStamp(); // Clear board-level caches
591
592 try // attempt to load full set of rules (implicit + user rules)
593 {
595 loadRules( aRulePath );
596 compileRules();
597 }
598 catch( PARSE_ERROR& original_parse_error )
599 {
600 m_rules.clear();
601
602 try // try again with just our implicit rules
603 {
605 compileRules();
606 }
607 catch( PARSE_ERROR& )
608 {
609 wxFAIL_MSG( wxT( "Compiling implicit rules failed." ) );
610 }
611
612 throw original_parse_error;
613 }
614
615 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
617
618 m_rulesValid = true;
619}
620
621
622void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints,
623 BOARD_COMMIT* aCommit )
624{
625 PROF_TIMER timer;
626
627 SetUserUnits( aUnits );
628
629 m_reportAllTrackErrors = aReportAllTrackErrors;
630 m_testFootprints = aTestFootprints;
631
632 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
633 {
634 if( m_designSettings->Ignore( ii ) )
635 m_errorLimits[ ii ] = 0;
636 else if( ii == DRCE_CLEARANCE || ii == DRCE_UNCONNECTED_ITEMS )
638 else
640 }
641
643
644 m_board->IncrementTimeStamp(); // Invalidate all caches...
645
646 DRC_CACHE_GENERATOR cacheGenerator;
647 cacheGenerator.SetDRCEngine( this );
648
649 if( !cacheGenerator.Run() ) // ... and regenerate them.
650 return;
651
652 // Recompute component classes
653 m_board->GetComponentClassManager().ForceComponentClassRecalculation();
654
655 int timestamp = m_board->GetTimeStamp();
656
657 for( DRC_TEST_PROVIDER* provider : m_testProviders )
658 {
659 if( m_logReporter )
660 m_logReporter->Report( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );
661
662 if( !provider->RunTests( aUnits ) )
663 break;
664 }
665
666 timer.Stop();
667 wxLogTrace( traceDrcProfile, "DRC took %0.3f ms", timer.msecs() );
668
669 // DRC tests are multi-threaded; anything that causes us to attempt to re-generate the
670 // caches while DRC is running is problematic.
671 wxASSERT( timestamp == m_board->GetTimeStamp() );
672}
673
674
675#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
676
678 PCB_LAYER_ID aLayer, REPORTER* aReporter )
679{
680 DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
681
682 REPORT( "" )
683 REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
685
687 {
688 const PAD* pad = nullptr;
689
690 if( a->Type() == PCB_PAD_T )
691 pad = static_cast<const PAD*>( a );
692 else if( b->Type() == PCB_PAD_T )
693 pad = static_cast<const PAD*>( b );
694
695 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
696 {
698 }
699 else
700 {
701 REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
704 }
705 }
706
707 return constraint;
708}
709
710
712 const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
713 REPORTER* aReporter )
714{
715 /*
716 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
717 * kills performance when running bulk DRC tests (where aReporter is nullptr).
718 */
719
720 const BOARD_CONNECTED_ITEM* ac = a && a->IsConnected() ?
721 static_cast<const BOARD_CONNECTED_ITEM*>( a ) : nullptr;
722 const BOARD_CONNECTED_ITEM* bc = b && b->IsConnected() ?
723 static_cast<const BOARD_CONNECTED_ITEM*>( b ) : nullptr;
724
725 bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
726 bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
727
728 const PAD* pad = nullptr;
729 const ZONE* zone = nullptr;
730 const FOOTPRINT* parentFootprint = nullptr;
731
732 if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
733 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
734 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
735 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
736 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
737 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
738 {
739 if( a && a->Type() == PCB_PAD_T )
740 pad = static_cast<const PAD*>( a );
741 else if( a && a->Type() == PCB_ZONE_T )
742 zone = static_cast<const ZONE*>( a );
743
744 if( b && b->Type() == PCB_PAD_T )
745 pad = static_cast<const PAD*>( b );
746 else if( b && b->Type() == PCB_ZONE_T )
747 zone = static_cast<const ZONE*>( b );
748
749 if( pad )
750 parentFootprint = pad->GetParentFootprint();
751 }
752
753 DRC_CONSTRAINT constraint;
754 constraint.m_Type = aConstraintType;
755
756 auto applyConstraint =
757 [&]( const DRC_ENGINE_CONSTRAINT* c )
758 {
759 if( c->constraint.m_Value.HasMin() )
760 {
761 if( c->parentRule && c->parentRule->m_Implicit )
762 constraint.m_ImplicitMin = true;
763
764 constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
765 }
766
767 if( c->constraint.m_Value.HasOpt() )
768 constraint.m_Value.SetOpt( c->constraint.m_Value.Opt() );
769
770 if( c->constraint.m_Value.HasMax() )
771 constraint .m_Value.SetMax( c->constraint.m_Value.Max() );
772
773 switch( c->constraint.m_Type )
774 {
782 if( constraint.m_Value.Min() > MAXIMUM_CLEARANCE )
783 constraint.m_Value.SetMin( MAXIMUM_CLEARANCE );
784
785 break;
786
787 default:
788 break;
789 }
790
791 // While the expectation would be to OR the disallow flags, we've already
792 // masked them down to aItem's type -- so we're really only looking for a
793 // boolean here.
794 constraint.m_DisallowFlags = c->constraint.m_DisallowFlags;
795
796 constraint.m_ZoneConnection = c->constraint.m_ZoneConnection;
797
798 constraint.SetParentRule( c->constraint.GetParentRule() );
799
800 constraint.SetOptionsFromOther( c->constraint );
801 };
802
803 const FOOTPRINT* footprints[2] = {a ? a->GetParentFootprint() : nullptr,
804 b ? b->GetParentFootprint() : nullptr};
805
806 // Handle Footprint net ties, which will zero out the clearance for footprint objects
807 if( aConstraintType == CLEARANCE_CONSTRAINT // Only zero clearance, other constraints still apply
808 && ( ( ( !ac ) ^ ( !bc ) )// Only apply to cases where we are comparing a connected item to a non-connected item
809 // and not both connected. Connected items of different nets still need to be checked
810 // for their standard clearance value
811 || ( ( footprints[0] == footprints[1] ) // Unless both items are in the same footprint
812 && footprints[0] ) ) // And that footprint exists
813 && !a_is_non_copper // Also, both elements need to be on copper layers
814 && !b_is_non_copper )
815 {
816 const BOARD_ITEM* child_items[2] = {a, b};
817
818 // These are the items being compared against, so the order is reversed
819 const BOARD_CONNECTED_ITEM* alt_items[2] = {bc, ac};
820
821 for( int ii = 0; ii < 2; ++ii )
822 {
823 // We need both a footprint item and a connected item to check for a net tie
824 if( !footprints[ii] || !alt_items[ii] )
825 continue;
826
827 const std::set<int>& netcodes = footprints[ii]->GetNetTieCache( child_items[ii] );
828
829 auto it = netcodes.find( alt_items[ii]->GetNetCode() );
830
831 if( it != netcodes.end() )
832 {
833 REPORT( "" )
834 REPORT( wxString::Format( _( "Net tie on %s; clearance: 0." ),
835 EscapeHTML( footprints[ii]->GetItemDescription( this, true ) ) ) )
836
837 constraint.SetName( _( "net tie" ) );
838 constraint.m_Value.SetMin( 0 );
839 return constraint;
840 }
841 }
842 }
843
844 // Local overrides take precedence over everything *except* board min clearance
845 if( aConstraintType == CLEARANCE_CONSTRAINT || aConstraintType == HOLE_CLEARANCE_CONSTRAINT )
846 {
847 int override_val = 0;
848 std::optional<int> overrideA;
849 std::optional<int> overrideB;
850
851 if( ac && !b_is_non_copper )
852 overrideA = ac->GetClearanceOverrides( nullptr );
853
854 if( bc && !a_is_non_copper )
855 overrideB = bc->GetClearanceOverrides( nullptr );
856
857 if( overrideA.has_value() || overrideB.has_value() )
858 {
859 wxString msg;
860
861 if( overrideA.has_value() )
862 {
863 REPORT( "" )
864 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
865 EscapeHTML( a->GetItemDescription( this, true ) ),
866 MessageTextFromValue( overrideA.value() ) ) )
867
868 override_val = ac->GetClearanceOverrides( &msg ).value();
869 }
870
871 if( overrideB.has_value() )
872 {
873 REPORT( "" )
874 REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
875 EscapeHTML( b->GetItemDescription( this, true ) ),
876 EscapeHTML( MessageTextFromValue( overrideB.value() ) ) ) )
877
878 if( overrideB > override_val )
879 override_val = bc->GetClearanceOverrides( &msg ).value();
880 }
881
882 if( override_val )
883 {
884 if( aConstraintType == CLEARANCE_CONSTRAINT )
885 {
886 if( override_val < m_designSettings->m_MinClearance )
887 {
888 override_val = m_designSettings->m_MinClearance;
889 msg = _( "board minimum" );
890
891 REPORT( "" )
892 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
893 MessageTextFromValue( override_val ) ) )
894 }
895 }
896 else
897 {
898 if( override_val < m_designSettings->m_HoleClearance )
899 {
900 override_val = m_designSettings->m_HoleClearance;
901 msg = _( "board minimum hole" );
902
903 REPORT( "" )
904 REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
905 MessageTextFromValue( override_val ) ) )
906 }
907 }
908
909 constraint.SetName( msg );
910 constraint.m_Value.SetMin( override_val );
911 return constraint;
912 }
913 }
914 }
915 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
916 {
917 if( pad && pad->GetLocalZoneConnection() != ZONE_CONNECTION::INHERITED )
918 {
919 wxString msg;
920 ZONE_CONNECTION override = pad->GetZoneConnectionOverrides( &msg );
921
922 REPORT( "" )
923 REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
924 EscapeHTML( pad->GetItemDescription( this, true ) ),
925 EscapeHTML( PrintZoneConnection( override ) ) ) )
926
927 constraint.SetName( msg );
928 constraint.m_ZoneConnection = override;
929 return constraint;
930 }
931 }
932 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
933 {
934 if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
935 {
936 wxString msg;
937 int gap_override = pad->GetLocalThermalGapOverride( &msg );
938
939 REPORT( "" )
940 REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
941 EscapeHTML( pad->GetItemDescription( this, true ) ),
942 EscapeHTML( MessageTextFromValue( gap_override ) ) ) )
943
944 constraint.SetName( msg );
945 constraint.m_Value.SetMin( gap_override );
946 return constraint;
947 }
948 }
949 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
950 {
951 if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
952 {
953 wxString msg;
954 int spoke_override = pad->GetLocalSpokeWidthOverride( &msg );
955
956 REPORT( "" )
957 REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
958 EscapeHTML( pad->GetItemDescription( this, true ) ),
959 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
960
961 if( zone && zone->GetMinThickness() > spoke_override )
962 {
963 spoke_override = zone->GetMinThickness();
964
965 REPORT( "" )
966 REPORT( wxString::Format( _( "%s min thickness: %s." ),
967 EscapeHTML( zone->GetItemDescription( this, true ) ),
968 EscapeHTML( MessageTextFromValue( spoke_override ) ) ) )
969 }
970
971 constraint.SetName( msg );
972 constraint.m_Value.SetMin( spoke_override );
973 return constraint;
974 }
975 }
976 else if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
977 {
978 std::optional<int> override;
979
980 if( pad )
981 override = pad->GetLocalSolderMaskMargin();
982 else if( a->Type() == PCB_SHAPE_T )
983 override = static_cast<const PCB_SHAPE*>( a )->GetLocalSolderMaskMargin();
984 else if( const PCB_TRACK* track = dynamic_cast<const PCB_TRACK*>( a ) )
985 override = track->GetLocalSolderMaskMargin();
986
987 if( override )
988 {
989 REPORT( "" )
990 REPORT( wxString::Format( _( "Local override on %s; solder mask expansion: %s." ),
991 EscapeHTML( pad->GetItemDescription( this, true ) ),
992 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
993
994 constraint.m_Value.SetOpt( override.value() );
995 return constraint;
996 }
997 }
998 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
999 {
1000 std::optional<int> override;
1001
1002 if( pad )
1003 override = pad->GetLocalSolderPasteMargin();
1004
1005 if( override )
1006 {
1007 REPORT( "" )
1008 REPORT( wxString::Format( _( "Local override on %s; solder paste absolute clearance: %s." ),
1009 EscapeHTML( pad->GetItemDescription( this, true ) ),
1010 EscapeHTML( MessageTextFromValue( override.value() ) ) ) )
1011
1012 constraint.m_Value.SetOpt( override.value_or( 0 ) );
1013 return constraint;
1014 }
1015 }
1016 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1017 {
1018 std::optional<double> overrideRatio;
1019
1020 if( pad )
1021 overrideRatio = pad->GetLocalSolderPasteMarginRatio();
1022
1023 if( overrideRatio )
1024 {
1025 REPORT( "" )
1026 REPORT( wxString::Format( _( "Local override on %s; solder paste relative clearance: %s." ),
1027 EscapeHTML( pad->GetItemDescription( this, true ) ),
1028 EscapeHTML( MessageTextFromValue( overrideRatio.value() * 100.0 ) ) ) )
1029
1030 constraint.m_Value.SetOpt( KiROUND( overrideRatio.value_or( 0 ) * 1000 ) );
1031 return constraint;
1032 }
1033 }
1034
1035 auto testAssertion =
1036 [&]( const DRC_ENGINE_CONSTRAINT* c )
1037 {
1038 REPORT( wxString::Format( _( "Checking assertion '%s'." ),
1039 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1040
1041 if( c->constraint.m_Test->EvaluateFor( a, b, c->constraint.m_Type, aLayer,
1042 aReporter ) )
1043 {
1044 REPORT( _( "Assertion passed." ) )
1045 }
1046 else
1047 {
1048 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1049 }
1050 };
1051
1052 auto processConstraint =
1053 [&]( const DRC_ENGINE_CONSTRAINT* c )
1054 {
1055 bool implicit = c->parentRule && c->parentRule->m_Implicit;
1056
1057 REPORT( "" )
1058
1059 switch( c->constraint.m_Type )
1060 {
1068 REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
1069 EscapeHTML( c->constraint.GetName() ),
1070 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1071 break;
1073 REPORT( wxString::Format( _( "Checking %s creepage: %s." ),
1074 EscapeHTML( c->constraint.GetName() ),
1075 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1076 break;
1078 REPORT( wxString::Format( _( "Checking %s max uncoupled length: %s." ),
1079 EscapeHTML( c->constraint.GetName() ),
1080 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1081 break;
1082
1083 case SKEW_CONSTRAINT:
1084 REPORT( wxString::Format( _( "Checking %s max skew: %s." ),
1085 EscapeHTML( c->constraint.GetName() ),
1086 MessageTextFromValue( c->constraint.m_Value.Max() ) ) )
1087 break;
1088
1090 REPORT( wxString::Format( _( "Checking %s gap: %s." ),
1091 EscapeHTML( c->constraint.GetName() ),
1092 MessageTextFromValue( c->constraint.m_Value.Min() ) ) )
1093 break;
1094
1096 REPORT( wxString::Format( _( "Checking %s thermal spoke width: %s." ),
1097 EscapeHTML( c->constraint.GetName() ),
1098 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1099 break;
1100
1102 REPORT( wxString::Format( _( "Checking %s solder mask expansion: %s." ),
1103 EscapeHTML( c->constraint.GetName() ),
1104 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1105 break;
1106
1108 REPORT( wxString::Format( _( "Checking %s solder paste absolute cleraance: %s." ),
1109 EscapeHTML( c->constraint.GetName() ),
1110 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1111 break;
1112
1114 REPORT( wxString::Format( _( "Checking %s solder paste relative clearance: %s." ),
1115 EscapeHTML( c->constraint.GetName() ),
1116 MessageTextFromValue( c->constraint.m_Value.Opt() ) ) )
1117 break;
1118
1120 REPORT( wxString::Format( _( "Checking %s min spoke count: %s." ),
1121 EscapeHTML( c->constraint.GetName() ),
1123 c->constraint.m_Value.Min() ) ) )
1124 break;
1125
1127 REPORT( wxString::Format( _( "Checking %s zone connection: %s." ),
1128 EscapeHTML( c->constraint.GetName() ),
1129 EscapeHTML( PrintZoneConnection( c->constraint.m_ZoneConnection ) ) ) )
1130 break;
1131
1139 case LENGTH_CONSTRAINT:
1142 {
1143 if( aReporter )
1144 {
1145 wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1146 wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1147 wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
1148
1149 if( implicit )
1150 {
1151 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1152 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1153
1154 switch( c->constraint.m_Type )
1155 {
1157 if( c->constraint.m_Value.HasOpt() )
1158 {
1159 REPORT( wxString::Format( _( "Checking %s track width: opt %s." ),
1160 EscapeHTML( c->constraint.GetName() ),
1161 opt ) )
1162 }
1163 else if( c->constraint.m_Value.HasMin() )
1164 {
1165 REPORT( wxString::Format( _( "Checking %s track width: min %s." ),
1166 EscapeHTML( c->constraint.GetName() ),
1167 min ) )
1168 }
1169
1170 break;
1171
1173 REPORT( wxString::Format( _( "Checking %s annular width: min %s." ),
1174 EscapeHTML( c->constraint.GetName() ),
1175 opt ) )
1176 break;
1177
1179 if( c->constraint.m_Value.HasOpt() )
1180 {
1181 REPORT( wxString::Format( _( "Checking %s via diameter: opt %s." ),
1182 EscapeHTML( c->constraint.GetName() ),
1183 opt ) )
1184 }
1185 else if( c->constraint.m_Value.HasMin() )
1186 {
1187 REPORT( wxString::Format( _( "Checking %s via diameter: min %s." ),
1188 EscapeHTML( c->constraint.GetName() ),
1189 min ) )
1190 }
1191 break;
1192
1194 if( c->constraint.m_Value.HasOpt() )
1195 {
1196 REPORT( wxString::Format( _( "Checking %s hole size: opt %s." ),
1197 EscapeHTML( c->constraint.GetName() ),
1198 opt ) )
1199 }
1200 else if( c->constraint.m_Value.HasMin() )
1201 {
1202 REPORT( wxString::Format( _( "Checking %s hole size: min %s." ),
1203 EscapeHTML( c->constraint.GetName() ),
1204 min ) )
1205 }
1206
1207 break;
1208
1212 REPORT( wxString::Format( _( "Checking %s: min %s." ),
1213 EscapeHTML( c->constraint.GetName() ),
1214 min ) )
1215 break;
1216
1218 if( c->constraint.m_Value.HasOpt() )
1219 {
1220 REPORT( wxString::Format( _( "Checking %s diff pair gap: opt %s." ),
1221 EscapeHTML( c->constraint.GetName() ),
1222 opt ) )
1223 }
1224 else if( c->constraint.m_Value.HasMin() )
1225 {
1226 REPORT( wxString::Format( _( "Checking %s clearance: min %s." ),
1227 EscapeHTML( c->constraint.GetName() ),
1228 min ) )
1229 }
1230
1231 break;
1232
1234 REPORT( wxString::Format( _( "Checking %s hole to hole: min %s." ),
1235 EscapeHTML( c->constraint.GetName() ),
1236 min ) )
1237 break;
1238
1239 default:
1240 REPORT( wxString::Format( _( "Checking %s." ),
1241 EscapeHTML( c->constraint.GetName() ) ) )
1242 }
1243 }
1244 else
1245 {
1246 if( c->constraint.m_Value.HasMin() )
1247 min = MessageTextFromValue( c->constraint.m_Value.Min() );
1248
1249 if( c->constraint.m_Value.HasOpt() )
1250 opt = MessageTextFromValue( c->constraint.m_Value.Opt() );
1251
1252 if( c->constraint.m_Value.HasMax() )
1253 max = MessageTextFromValue( c->constraint.m_Value.Max() );
1254
1255 REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
1256 EscapeHTML( c->constraint.GetName() ),
1257 min,
1258 opt,
1259 max ) )
1260 }
1261 }
1262 break;
1263 }
1264
1265 default:
1266 REPORT( wxString::Format( _( "Checking %s." ),
1267 EscapeHTML( c->constraint.GetName() ) ) )
1268 }
1269
1270 if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
1271 {
1272 if( a_is_non_copper || b_is_non_copper )
1273 {
1274 if( implicit )
1275 {
1276 REPORT( _( "Netclass clearances apply only between copper items." ) )
1277 }
1278 else if( a_is_non_copper )
1279 {
1280 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1281 EscapeHTML( a->GetItemDescription( this, true ) ) ) )
1282 }
1283 else if( b_is_non_copper )
1284 {
1285 REPORT( wxString::Format( _( "%s contains no copper. Rule ignored." ),
1286 EscapeHTML( b->GetItemDescription( this, true ) ) ) )
1287 }
1288
1289 return;
1290 }
1291 }
1292 else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
1293 {
1294 int mask;
1295
1296 if( a->GetFlags() & HOLE_PROXY )
1297 {
1298 mask = DRC_DISALLOW_HOLES;
1299 }
1300 else if( a->Type() == PCB_VIA_T )
1301 {
1302 mask = DRC_DISALLOW_VIAS;
1303
1304 switch( static_cast<const PCB_VIA*>( a )->GetViaType() )
1305 {
1306 case VIATYPE::BLIND_BURIED: mask |= DRC_DISALLOW_BB_VIAS; break;
1307 case VIATYPE::MICROVIA: mask |= DRC_DISALLOW_MICRO_VIAS; break;
1308 default: break;
1309 }
1310 }
1311 else
1312 {
1313 switch( a->Type() )
1314 {
1315 case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
1316 case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
1317 case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
1318 case PCB_FOOTPRINT_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
1319 case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
1320 case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
1321 case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
1322 case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
1323 case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
1324
1325 case PCB_ZONE_T:
1326 // Treat teardrop areas as tracks for DRC purposes
1327 if( static_cast<const ZONE*>( a )->IsTeardropArea() )
1328 mask = DRC_DISALLOW_TRACKS;
1329 else
1330 mask = DRC_DISALLOW_ZONES;
1331
1332 break;
1333
1334 case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
1335 default: mask = 0; break;
1336 }
1337 }
1338
1339 if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
1340 {
1341 if( implicit )
1342 REPORT( _( "Keepout constraint not met." ) )
1343 else
1344 REPORT( _( "Disallow constraint not met." ) )
1345
1346 return;
1347 }
1348
1349 LSET itemLayers = a->GetLayerSet();
1350
1351 if( a->Type() == PCB_FOOTPRINT_T )
1352 {
1353 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( a );
1354
1355 if( !footprint->GetCourtyard( F_CrtYd ).IsEmpty() )
1356 itemLayers |= LSET::FrontMask();
1357
1358 if( !footprint->GetCourtyard( B_CrtYd ).IsEmpty() )
1359 itemLayers |= LSET::BackMask();
1360 }
1361
1362 if( !( c->layerTest & itemLayers ).any() )
1363 {
1364 if( implicit )
1365 {
1366 REPORT( _( "Keepout layer(s) not matched." ) )
1367 }
1368 else if( c->parentRule )
1369 {
1370 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1371 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1372 }
1373 else
1374 {
1375 REPORT( _( "Rule layer not matched; rule ignored." ) )
1376 }
1377
1378 return;
1379 }
1380 }
1381
1382 if( ( aLayer != UNDEFINED_LAYER && !c->layerTest.test( aLayer ) )
1383 || ( m_board->GetEnabledLayers() & c->layerTest ).count() == 0 )
1384 {
1385 if( implicit )
1386 {
1387 REPORT( _( "Constraint layer not matched." ) )
1388 }
1389 else if( c->parentRule )
1390 {
1391 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1392 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1393 }
1394 else
1395 {
1396 REPORT( _( "Rule layer not matched; rule ignored." ) )
1397 }
1398 }
1399 else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
1400 && ( !a->HasDrilledHole() && !b->HasDrilledHole() ) )
1401 {
1402 // Report non-drilled-holes as an implicit condition
1403 REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
1404 a->GetItemDescription( this, true ) ) )
1405 }
1406 else if( !c->condition || c->condition->GetExpression().IsEmpty() )
1407 {
1408 if( aReporter )
1409 {
1410 if( implicit )
1411 {
1412 REPORT( _( "Unconditional constraint applied." ) )
1413 }
1414 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1415 {
1416 REPORT( _( "Unconditional rule applied." ) )
1417 testAssertion( c );
1418 }
1419 else
1420 {
1421 REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
1422 }
1423 }
1424
1425 applyConstraint( c );
1426 }
1427 else
1428 {
1429 if( implicit )
1430 {
1431 // Don't report on implicit rule conditions; they're synthetic.
1432 }
1433 else
1434 {
1435 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1436 EscapeHTML( c->condition->GetExpression() ) ) )
1437 }
1438
1439 if( c->condition->EvaluateFor( a, b, c->constraint.m_Type, aLayer, aReporter ) )
1440 {
1441 if( aReporter )
1442 {
1443 if( implicit )
1444 {
1445 REPORT( _( "Constraint applied." ) )
1446 }
1447 else if( constraint.m_Type == ASSERTION_CONSTRAINT )
1448 {
1449 REPORT( _( "Rule applied." ) )
1450 testAssertion( c );
1451 }
1452 else
1453 {
1454 REPORT( _( "Rule applied; overrides previous constraints." ) )
1455 }
1456 }
1457
1458 applyConstraint( c );
1459 }
1460 else
1461 {
1462 REPORT( implicit ? _( "Membership not satisfied; constraint ignored." )
1463 : _( "Condition not satisfied; rule ignored." ) )
1464 }
1465 }
1466 };
1467
1468 if( m_constraintMap.count( aConstraintType ) )
1469 {
1470 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1471
1472 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1473 processConstraint( rule );
1474 }
1475
1476 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1477 return constraint;
1478
1479 // Special case for properties which can be inherited from parent footprints. We've already
1480 // checked for local overrides, and there were no rules targetting the item itself, so we know
1481 // we're inheriting and need to see if there are any rules targetting the parent footprint.
1482 if( pad && parentFootprint && ( aConstraintType == ZONE_CONNECTION_CONSTRAINT
1483 || aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
1484 || aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT
1485 || aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT
1486 || aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
1487 || aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT ) )
1488 {
1489 REPORT( "" )
1490 REPORT( wxString::Format( _( "Inheriting from parent: %s." ),
1491 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ) ) )
1492
1493 if( a == pad )
1494 a = parentFootprint;
1495 else
1496 b = parentFootprint;
1497
1498 if( m_constraintMap.count( aConstraintType ) )
1499 {
1500 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
1501
1502 for( DRC_ENGINE_CONSTRAINT* rule : *ruleset )
1503 processConstraint( rule );
1504
1505 if( constraint.GetParentRule() && !constraint.GetParentRule()->m_Implicit )
1506 return constraint;
1507 }
1508
1509 // Found nothing again? Return the defaults.
1510 if( aConstraintType == SOLDER_MASK_EXPANSION_CONSTRAINT )
1511 {
1512 constraint.SetParentRule( nullptr );
1513 constraint.SetName( _( "board setup" ) );
1514 constraint.m_Value.SetOpt( m_designSettings->m_SolderMaskExpansion );
1515 return constraint;
1516 }
1517 else if( aConstraintType == SOLDER_PASTE_ABS_MARGIN_CONSTRAINT )
1518 {
1519 constraint.SetParentRule( nullptr );
1520 constraint.SetName( _( "board setup" ) );
1521 constraint.m_Value.SetOpt( m_designSettings->m_SolderPasteMargin );
1522 return constraint;
1523 }
1524 else if( aConstraintType == SOLDER_PASTE_REL_MARGIN_CONSTRAINT )
1525 {
1526 constraint.SetParentRule( nullptr );
1527 constraint.SetName( _( "board setup" ) );
1528 constraint.m_Value.SetOpt( KiROUND( m_designSettings->m_SolderPasteMarginRatio * 1000 ) );
1529 return constraint;
1530 }
1531 }
1532
1533 // Unfortunately implicit rules don't work for local clearances (such as zones) because
1534 // they have to be max'ed with netclass values (which are already implicit rules), and our
1535 // rule selection paradigm is "winner takes all".
1536 if( aConstraintType == CLEARANCE_CONSTRAINT )
1537 {
1538 int global = constraint.m_Value.Min();
1539 int clearance = global;
1540 bool needBlankLine = true;
1541
1542 if( ac && ac->GetLocalClearance().has_value() )
1543 {
1544 int localA = ac->GetLocalClearance().value();
1545
1546 if( needBlankLine )
1547 {
1548 REPORT( "" )
1549 needBlankLine = false;
1550 }
1551
1552 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1553 EscapeHTML( a->GetItemDescription( this, true ) ),
1554 MessageTextFromValue( localA ) ) )
1555
1556 if( localA > clearance )
1557 {
1558 wxString msg;
1559 clearance = ac->GetLocalClearance( &msg ).value();
1560 constraint.SetParentRule( nullptr );
1561 constraint.SetName( msg );
1562 constraint.m_Value.SetMin( clearance );
1563 }
1564 }
1565
1566 if( bc && bc->GetLocalClearance().has_value() )
1567 {
1568 int localB = bc->GetLocalClearance().value();
1569
1570 if( needBlankLine )
1571 {
1572 REPORT( "" )
1573 needBlankLine = false;
1574 }
1575
1576 REPORT( wxString::Format( _( "Local clearance on %s: %s." ),
1577 EscapeHTML( b->GetItemDescription( this, true ) ),
1578 MessageTextFromValue( localB ) ) )
1579
1580 if( localB > clearance )
1581 {
1582 wxString msg;
1583 clearance = bc->GetLocalClearance( &msg ).value();
1584 constraint.SetParentRule( nullptr );
1585 constraint.SetName( msg );
1586 constraint.m_Value.SetMin( clearance );
1587 }
1588 }
1589
1590 if( !a_is_non_copper && !b_is_non_copper )
1591 {
1592 if( needBlankLine )
1593 {
1594 REPORT( "" )
1595 needBlankLine = false;
1596 }
1597
1598 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1599 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1600
1601 if( clearance < m_designSettings->m_MinClearance )
1602 {
1603 constraint.SetParentRule( nullptr );
1604 constraint.SetName( _( "board minimum" ) );
1605 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1606 }
1607 }
1608
1609 return constraint;
1610 }
1611 else if( aConstraintType == DIFF_PAIR_GAP_CONSTRAINT )
1612 {
1613 REPORT( "" )
1614 REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
1615 MessageTextFromValue( m_designSettings->m_MinClearance ) ) )
1616
1617 if( constraint.m_Value.Min() < m_designSettings->m_MinClearance )
1618 {
1619 constraint.SetParentRule( nullptr );
1620 constraint.SetName( _( "board minimum" ) );
1621 constraint.m_Value.SetMin( m_designSettings->m_MinClearance );
1622 }
1623
1624 return constraint;
1625 }
1626 else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
1627 {
1628 if( pad && parentFootprint )
1629 {
1630 ZONE_CONNECTION local = parentFootprint->GetLocalZoneConnection();
1631
1632 if( local != ZONE_CONNECTION::INHERITED )
1633 {
1634 REPORT( "" )
1635 REPORT( wxString::Format( _( "%s zone connection: %s." ),
1636 EscapeHTML( parentFootprint->GetItemDescription( this, true ) ),
1637 EscapeHTML( PrintZoneConnection( local ) ) ) )
1638
1639 constraint.SetParentRule( nullptr );
1640 constraint.SetName( _( "footprint" ) );
1641 constraint.m_ZoneConnection = local;
1642 return constraint;
1643 }
1644 }
1645
1646 if( zone )
1647 {
1648 ZONE_CONNECTION local = zone->GetPadConnection();
1649
1650 REPORT( "" )
1651 REPORT( wxString::Format( _( "%s pad connection: %s." ),
1652 EscapeHTML( zone->GetItemDescription( this, true ) ),
1653 EscapeHTML( PrintZoneConnection( local ) ) ) )
1654
1655 constraint.SetParentRule( nullptr );
1656 constraint.SetName( _( "zone" ) );
1657 constraint.m_ZoneConnection = local;
1658 return constraint;
1659 }
1660 }
1661 else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
1662 {
1663 if( zone )
1664 {
1665 int local = zone->GetThermalReliefGap();
1666
1667 REPORT( "" )
1668 REPORT( wxString::Format( _( "%s thermal relief gap: %s." ),
1669 EscapeHTML( zone->GetItemDescription( this, true ) ),
1670 EscapeHTML( MessageTextFromValue( local ) ) ) )
1671
1672 constraint.SetParentRule( nullptr );
1673 constraint.SetName( _( "zone" ) );
1674 constraint.m_Value.SetMin( local );
1675 return constraint;
1676 }
1677 }
1678 else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
1679 {
1680 if( zone )
1681 {
1682 int local = zone->GetThermalReliefSpokeWidth();
1683
1684 REPORT( "" )
1685 REPORT( wxString::Format( _( "%s thermal spoke width: %s." ),
1686 EscapeHTML( zone->GetItemDescription( this, true ) ),
1687 EscapeHTML( MessageTextFromValue( local ) ) ) )
1688
1689 constraint.SetParentRule( nullptr );
1690 constraint.SetName( _( "zone" ) );
1691 constraint.m_Value.SetMin( local );
1692 return constraint;
1693 }
1694 }
1695
1696 if( !constraint.GetParentRule() )
1697 {
1698 constraint.m_Type = NULL_CONSTRAINT;
1699 constraint.m_DisallowFlags = 0;
1700 }
1701
1702 return constraint;
1703}
1704
1705
1707 std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
1708 REPORTER* aReporter )
1709{
1710 /*
1711 * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
1712 * kills performance when running bulk DRC tests (where aReporter is nullptr).
1713 */
1714
1715 auto testAssertion =
1716 [&]( const DRC_ENGINE_CONSTRAINT* c )
1717 {
1718 REPORT( wxString::Format( _( "Checking rule assertion '%s'." ),
1719 EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
1720
1721 if( c->constraint.m_Test->EvaluateFor( a, nullptr, c->constraint.m_Type,
1722 a->GetLayer(), aReporter ) )
1723 {
1724 REPORT( _( "Assertion passed." ) )
1725 }
1726 else
1727 {
1728 REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
1729 aFailureHandler( &c->constraint );
1730 }
1731 };
1732
1733 auto processConstraint =
1734 [&]( const DRC_ENGINE_CONSTRAINT* c )
1735 {
1736 REPORT( "" )
1737 REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
1738
1739 if( !( a->GetLayerSet() & c->layerTest ).any() )
1740 {
1741 REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
1742 EscapeHTML( c->parentRule->m_LayerSource ) ) )
1743 }
1744
1745 if( !c->condition || c->condition->GetExpression().IsEmpty() )
1746 {
1747 REPORT( _( "Unconditional rule applied." ) )
1748 testAssertion( c );
1749 }
1750 else
1751 {
1752 REPORT( wxString::Format( _( "Checking rule condition '%s'." ),
1753 EscapeHTML( c->condition->GetExpression() ) ) )
1754
1755 if( c->condition->EvaluateFor( a, nullptr, c->constraint.m_Type,
1756 a->GetLayer(), aReporter ) )
1757 {
1758 REPORT( _( "Rule applied." ) )
1759 testAssertion( c );
1760 }
1761 else
1762 {
1763 REPORT( _( "Condition not satisfied; rule ignored." ) )
1764 }
1765 }
1766 };
1767
1769 {
1770 std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
1771
1772 for( int ii = 0; ii < (int) ruleset->size(); ++ii )
1773 processConstraint( ruleset->at( ii ) );
1774 }
1775}
1776
1777
1778#undef REPORT
1779
1780
1782{
1783 assert( error_code >= 0 && error_code <= DRCE_LAST );
1784 return m_errorLimits[ error_code ] <= 0;
1785}
1786
1787
1788void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
1789 int aMarkerLayer, const std::vector<PCB_SHAPE>& aShapes )
1790{
1791 static std::mutex globalLock;
1792
1793 m_errorLimits[ aItem->GetErrorCode() ] -= 1;
1794
1795 if( m_violationHandler )
1796 {
1797 std::lock_guard<std::mutex> guard( globalLock );
1798 m_violationHandler( aItem, aPos, aMarkerLayer, aShapes );
1799 }
1800
1801 if( m_logReporter )
1802 {
1803 wxString msg = wxString::Format( wxT( "Test '%s': %s (code %d)" ),
1804 aItem->GetViolatingTest()->GetName(),
1805 aItem->GetErrorMessage(),
1806 aItem->GetErrorCode() );
1807
1808 DRC_RULE* rule = aItem->GetViolatingRule();
1809
1810 if( rule )
1811 msg += wxString::Format( wxT( ", violating rule: '%s'" ), rule->m_Name );
1812
1813 m_logReporter->Report( msg );
1814
1815 wxString violatingItemsStr = wxT( "Violating items: " );
1816
1817 m_logReporter->Report( wxString::Format( wxT( " |- violating position (%d, %d)" ),
1818 aPos.x,
1819 aPos.y ) );
1820 }
1821}
1822
1823
1825{
1826 if( !m_progressReporter )
1827 return true;
1828
1829 return m_progressReporter->KeepRefreshing( aWait );
1830}
1831
1832
1834{
1835 if( m_progressReporter )
1836 m_progressReporter->AdvanceProgress();
1837}
1838
1839
1841{
1842 if( m_progressReporter )
1843 m_progressReporter->SetMaxProgress( aSize );
1844}
1845
1846
1847bool DRC_ENGINE::ReportProgress( double aProgress )
1848{
1849 if( !m_progressReporter )
1850 return true;
1851
1852 m_progressReporter->SetCurrentProgress( aProgress );
1853 return m_progressReporter->KeepRefreshing( false );
1854}
1855
1856
1857bool DRC_ENGINE::ReportPhase( const wxString& aMessage )
1858{
1859 if( !m_progressReporter )
1860 return true;
1861
1862 m_progressReporter->AdvancePhase( aMessage );
1863 bool retval = m_progressReporter->KeepRefreshing( false );
1864 wxSafeYield( nullptr, true ); // Force an update for the message
1865 return retval;
1866}
1867
1868
1870{
1871 return m_progressReporter && m_progressReporter->IsCancelled();
1872}
1873
1874
1876{
1877 //drc_dbg( 10, "hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size() );
1878
1879 if( m_constraintMap.count( constraintID ) )
1880 return m_constraintMap[ constraintID ]->size() > 0;
1881
1882 return false;
1883}
1884
1885
1887{
1888 int worst = 0;
1889
1890 if( m_constraintMap.count( aConstraintId ) )
1891 {
1892 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1893 {
1894 int current = c->constraint.GetValue().Min();
1895
1896 if( current > worst )
1897 {
1898 worst = current;
1899 aConstraint = c->constraint;
1900 }
1901 }
1902 }
1903
1904 return worst > 0;
1905}
1906
1907
1909{
1910 std::set<int> distinctMinimums;
1911
1912 if( m_constraintMap.count( aConstraintId ) )
1913 {
1914 for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
1915 distinctMinimums.emplace( c->constraint.GetValue().Min() );
1916 }
1917
1918 return distinctMinimums;
1919}
1920
1921
1922// fixme: move two functions below to pcbcommon?
1923int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
1924 wxString& aBaseDpName )
1925{
1926 int rv = 0;
1927 int count = 0;
1928
1929 for( auto it = aNetName.rbegin(); it != aNetName.rend() && rv == 0; ++it, ++count )
1930 {
1931 int ch = *it;
1932
1933 if( ( ch >= '0' && ch <= '9' ) || ch == '_' )
1934 {
1935 continue;
1936 }
1937 else if( ch == '+' )
1938 {
1939 aComplementNet = wxT( "-" );
1940 rv = 1;
1941 }
1942 else if( ch == '-' )
1943 {
1944 aComplementNet = wxT( "+" );
1945 rv = -1;
1946 }
1947 else if( ch == 'N' )
1948 {
1949 aComplementNet = wxT( "P" );
1950 rv = -1;
1951 }
1952 else if ( ch == 'P' )
1953 {
1954 aComplementNet = wxT( "N" );
1955 rv = 1;
1956 }
1957 else
1958 {
1959 break;
1960 }
1961 }
1962
1963 if( rv != 0 && count >= 1 )
1964 {
1965 aBaseDpName = aNetName.Left( aNetName.Length() - count );
1966 aComplementNet = wxString( aBaseDpName ) << aComplementNet << aNetName.Right( count - 1 );
1967 }
1968
1969 return rv;
1970}
1971
1972
1973bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN )
1974{
1975 wxString refName = aNet->GetNetname();
1976 wxString dummy, coupledNetName;
1977
1978 if( int polarity = MatchDpSuffix( refName, coupledNetName, dummy ) )
1979 {
1980 NETINFO_ITEM* net = aBoard->FindNet( coupledNetName );
1981
1982 if( !net )
1983 return false;
1984
1985 if( polarity > 0 )
1986 {
1987 aNetP = aNet->GetNetCode();
1988 aNetN = net->GetNetCode();
1989 }
1990 else
1991 {
1992 aNetP = net->GetNetCode();
1993 aNetN = aNet->GetNetCode();
1994 }
1995
1996 return true;
1997 }
1998
1999 return false;
2000}
2001
2002
2007bool DRC_ENGINE::IsNetTieExclusion( int aTrackNetCode, PCB_LAYER_ID aTrackLayer,
2008 const VECTOR2I& aCollisionPos, BOARD_ITEM* aCollidingItem )
2009{
2010 FOOTPRINT* parentFootprint = aCollidingItem->GetParentFootprint();
2011
2012 if( parentFootprint && parentFootprint->IsNetTie() )
2013 {
2015 std::map<wxString, int> padToNetTieGroupMap = parentFootprint->MapPadNumbersToNetTieGroups();
2016
2017 for( PAD* pad : parentFootprint->Pads() )
2018 {
2019 if( padToNetTieGroupMap[ pad->GetNumber() ] >= 0 && aTrackNetCode == pad->GetNetCode() )
2020 {
2021 if( pad->GetEffectiveShape( aTrackLayer )->Collide( aCollisionPos, epsilon ) )
2022 return true;
2023 }
2024 }
2025 }
2026
2027 return false;
2028}
2029
2030
2032{
2033 for( DRC_TEST_PROVIDER* prov : m_testProviders )
2034 {
2035 if( name == prov->GetName() )
2036 return prov;
2037 }
2038
2039 return nullptr;
2040}
2041
2042
2043std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingCondition( const wxString& aExpression,
2044 DRC_CONSTRAINT_T aConstraint,
2045 REPORTER* aReporter )
2046{
2047 std::vector<BOARD_ITEM*> matches;
2048
2049 if( !m_board )
2050 return matches;
2051
2052 DRC_RULE_CONDITION condition( aExpression );
2053
2054 if( !condition.Compile( aReporter ? aReporter : m_logReporter ) )
2055 return matches;
2056
2057 BOARD_ITEM_SET items = m_board->GetItemSet();
2058
2059 for( auto& [kiid, item] : m_board->GetItemByIdCache() )
2060 {
2061 LSET itemLayers = item->GetLayerSet();
2062
2063 for( PCB_LAYER_ID layer : itemLayers )
2064 {
2065 if( condition.EvaluateFor( item, nullptr, static_cast<int>( aConstraint ), layer,
2066 aReporter ? aReporter : m_logReporter ) )
2067 {
2068 matches.push_back( item );
2069 break; // No need to check other layers
2070 }
2071 }
2072 }
2073
2074 return matches;
2075}
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:301
#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:317
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2152
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
int m_DisallowFlags
Definition drc_rule.h:205
void SetParentRule(DRC_RULE *aParentRule)
Definition drc_rule.h:165
MINOPTMAX< int > & Value()
Definition drc_rule.h:163
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:206
void SetName(const wxString &aName)
Definition drc_rule.h:168
MINOPTMAX< int > m_Value
Definition drc_rule.h:204
DRC_CONSTRAINT_T m_Type
Definition drc_rule.h:203
void SetOptionsFromOther(const DRC_CONSTRAINT &aOther)
Definition drc_rule.h:200
DRC_RULE * GetParentRule() const
Definition drc_rule.h:166
bool m_ImplicitMin
Definition drc_rule.h:208
std::map< DRC_CONSTRAINT_T, std::vector< DRC_ENGINE_CONSTRAINT * > * > m_constraintMap
Definition drc_engine.h:268
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:265
void addRule(std::shared_ptr< DRC_RULE > &rule)
Definition drc_engine.h:228
PROGRESS_REPORTER * m_progressReporter
Definition drc_engine.h:272
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:261
REPORTER * m_logReporter
Definition drc_engine.h:271
void compileRules()
std::set< int > QueryDistinctConstraints(DRC_CONSTRAINT_T aConstraintId)
DS_PROXY_VIEW_ITEM * m_drawingSheet
Definition drc_engine.h:256
NETLIST * m_schematicNetlist
Definition drc_engine.h:257
bool KeepRefreshing(bool aWait=false)
BOARD * m_board
Definition drc_engine.h:255
bool m_reportAllTrackErrors
Definition drc_engine.h:264
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:102
void SetMaxProgress(int aSize)
DRC_ENGINE(BOARD *aBoard=nullptr, BOARD_DESIGN_SETTINGS *aSettings=nullptr)
std::vector< int > m_errorLimits
Definition drc_engine.h:263
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:270
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:259
std::shared_ptr< DRC_RULE > createImplicitRule(const wxString &name)
void ReportViolation(const std::shared_ptr< DRC_ITEM > &aItem, const VECTOR2I &aPos, int aMarkerLayer, const std::vector< PCB_SHAPE > &aShapes={})
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:260
BOARD_DESIGN_SETTINGS * m_designSettings
Definition drc_engine.h:254
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:120
wxString m_Name
Definition drc_rule.h:122
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:249
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)
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:93
@ DRC_DISALLOW_VIAS
Definition drc_rule.h:89
@ DRC_DISALLOW_TEXTS
Definition drc_rule.h:95
@ DRC_DISALLOW_ZONES
Definition drc_rule.h:94
@ DRC_DISALLOW_HOLES
Definition drc_rule.h:97
@ DRC_DISALLOW_GRAPHICS
Definition drc_rule.h:96
@ DRC_DISALLOW_FOOTPRINTS
Definition drc_rule.h:98
@ DRC_DISALLOW_TRACKS
Definition drc_rule.h:92
@ DRC_DISALLOW_MICRO_VIAS
Definition drc_rule.h:90
@ DRC_DISALLOW_BB_VIAS
Definition drc_rule.h:91
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
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
@ BLIND_BURIED
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:69
@ 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:246
DRC_RULE_CONDITION * condition
Definition drc_engine.h:245
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: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:129
@ 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