KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_drc_via_stack.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <fstream>
21#include <memory>
22
23#include <wx/filename.h>
24
25#include <qa_utils/file_utils.h>
28
29#include <board.h>
33#include <common.h>
34#include <lset.h>
35#include <padstack.h>
36#include <pcb_track.h>
38#include <drc/drc_engine.h>
39#include <drc/drc_item.h>
40#include <drc/drc_rule_parser.h>
42#include <reporter.h>
45
46
48{
50 std::unique_ptr<BOARD> m_board;
51
53 int m_notFilled = 0;
54 int m_depth = 0;
55 int m_aspect = 0;
57
58 std::vector<int> m_ignored;
59
60 wxFileName m_rulePath;
61
62 // The temp dir has to outlive run(), so it is kept here rather than in each test.
63 std::unique_ptr<KI_TEST::SCOPED_TEMP_DIR> m_ruleDir;
64
65 // The limits these checks read come from rules, so a test states them the way a user would.
66 void setRule( const std::string& aConstraint, const std::string& aCondition = "",
67 const std::string& aSeverity = "" )
68 {
69 m_ruleDir = std::make_unique<KI_TEST::SCOPED_TEMP_DIR>( "microvia_rule" );
70
71 wxFileName rulePath( m_ruleDir->PathStr(), "test.kicad_dru" );
72
73 {
74 std::ofstream dru( rulePath.GetFullPath().ToStdString() );
75 dru << "(version 1)\n"
76 << "(rule \"via stack limits\"\n";
77
78 if( !aCondition.empty() )
79 dru << " (condition \"" << aCondition << "\")\n";
80
81 if( !aSeverity.empty() )
82 dru << " (severity " << aSeverity << ")\n";
83
84 dru << " (constraint " << aConstraint << ")\n"
85 << ")\n";
86 }
87
88 m_rulePath = rulePath;
89 }
90
91 // Cases whose subject is the stackup still build their board here: a
92 // synthesized stackup is not written to the board file, so a fixture would
93 // come back with no thicknesses and quietly test something else.
94 //
95 // Each case's board is stored under drc_via_stack/ named after the test case,
96 // so a renamed case fails loudly here rather than silently reading the wrong
97 // board. Via stacks do not exist in v10, so these fixtures are written by
98 // the development build.
99 void loadBoard()
100 {
102 KI_TEST::GetPcbnewTestDataDir() + "drc_via_stack/"
103 + boost::unit_test::framework::current_test_case().p_name.get()
104 + ".kicad_pcb" );
105
107 }
108
109
110 void run()
111 {
112 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
113
114 if( !bds.m_DRCEngine )
115 bds.m_DRCEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
116
118
119 for( int code = DRCE_FIRST; code <= DRCE_LAST; ++code )
121
127
128 for( int code : m_ignored )
130
132 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I&, int,
133 const std::function<void( PCB_MARKER* )>& )
134 {
135 switch( aItem->GetErrorCode() )
136 {
139 case DRCE_MICROVIA_STACK_DEPTH: m_depth++; break;
142 default: break;
143 }
144 } );
145
146 bds.m_DRCEngine->RunTests( EDA_UNITS::MM, true, false );
148 }
149};
150
151
152static PCB_VIA* makeMicrovia( BOARD* aBoard, const VECTOR2I& aPos, PCB_LAYER_ID aTop, PCB_LAYER_ID aBottom,
153 bool aFilled )
154{
155 PCB_VIA* via = new PCB_VIA( aBoard );
156 via->SetViaType( VIATYPE::MICROVIA );
157 via->SetLayerPair( aTop, aBottom );
158 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.3 ) );
159 via->SetDrill( pcbIUScale.mmToIU( 0.15 ) );
160 via->SetPosition( aPos );
161 via->Padstack().Drill().is_filled = aFilled;
162 aBoard->Add( via );
163
164 return via;
165}
166
167
168static PCB_VIA_STACK* makeStack( BOARD* aBoard, PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd )
169{
170 PCB_VIA_STACK* stack = new PCB_VIA_STACK( aBoard, F_Cu );
171 stack->SetStartLayer( aStart );
172 stack->SetEndLayer( aEnd );
174 aBoard->Add( stack );
175
176 return stack;
177}
178
179
180BOOST_AUTO_TEST_SUITE( DRCViaStack )
181
182
184{
185 m_board = std::make_unique<BOARD>();
186 m_board->SetCopperLayerCount( 4 );
187 m_board->SetEnabledLayers( LSET::AllCuMask( 4 ) | LSET::AllTechMask() );
188
189 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
190 bds.SetCopperLayerCount( 4 );
192 setRule( "microvia_aspect_ratio (max 1.0)" );
193
194 for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
195 {
196 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
197 continue;
198
199 item->SetThickness( pcbIUScale.mmToIU( 0.1 ) );
200 item->SetTypeName( KEY_PREPREG );
201 }
202
203 PCB_VIA_STACK* stack = makeStack( m_board.get(), F_Cu, In2_Cu );
204 stack->SetViaSize( pcbIUScale.mmToIU( 0.3 ) );
205 stack->SetViaDrill( pcbIUScale.mmToIU( 0.15 ) );
206 stack->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
207 stack->Regenerate( m_board.get(), nullptr );
208
209 run();
210
211 BOOST_CHECK_EQUAL( m_malformedSpans, 0 );
212 BOOST_CHECK_EQUAL( m_notFilled, 0 );
213 BOOST_CHECK_EQUAL( m_depth, 0 );
214 BOOST_CHECK_EQUAL( m_aspect, 0 );
215 BOOST_CHECK_EQUAL( m_crossesCore, 0 );
216}
217
218
219BOOST_FIXTURE_TEST_CASE( StackNotTilingItsSpanReported, VIA_STACK_DRC_FIXTURE )
220{
221 loadBoard();
222
223 run();
224
225 BOOST_CHECK_EQUAL( m_malformedSpans, 1 );
226}
227
228
230{
231 m_board = std::make_unique<BOARD>();
232 m_board->SetCopperLayerCount( 4 );
233 m_board->SetEnabledLayers( LSET::AllCuMask( 4 ) | LSET::AllTechMask() );
234
235 VECTOR2I pos( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) );
236
237 PCB_VIA_STACK* stack = makeStack( m_board.get(), F_Cu, In2_Cu );
238 stack->SetViaSize( pcbIUScale.mmToIU( 0.3 ) );
239 stack->SetViaDrill( pcbIUScale.mmToIU( 0.15 ) );
240 stack->SetPosition( pos );
241 stack->Regenerate( m_board.get(), nullptr );
242
243 stack->AddItem( makeMicrovia( m_board.get(), pos, F_Cu, In1_Cu, true ) );
244
245 run();
246
247 BOOST_CHECK_EQUAL( m_malformedSpans, 1 );
248}
249
250
252{
253 loadBoard();
254
255 run();
256
257 BOOST_CHECK_EQUAL( m_malformedSpans, 0 );
258 BOOST_CHECK_EQUAL( m_notFilled, 1 );
259}
260
261
263{
264 loadBoard();
265
266 setRule( "microvia_stack_depth (max 1)" );
267
268 run();
269
270 BOOST_CHECK_EQUAL( m_malformedSpans, 0 );
271 BOOST_CHECK_EQUAL( m_depth, 1 );
272}
273
274
275BOOST_FIXTURE_TEST_CASE( LooseStackedMicroviasReportNotFilled, VIA_STACK_DRC_FIXTURE )
276{
277 loadBoard();
278
279 run();
280
281 BOOST_CHECK_EQUAL( m_notFilled, 1 );
282}
283
284
285BOOST_FIXTURE_TEST_CASE( LooseStackedMicroviasReportDepth, VIA_STACK_DRC_FIXTURE )
286{
287 loadBoard();
288
289 setRule( "microvia_stack_depth (max 2)" );
290
291 run();
292
293 BOOST_CHECK_EQUAL( m_depth, 1 );
294}
295
296
297// Two microvias sharing an x,y but not touching are two structures, not one three deep.
298BOOST_FIXTURE_TEST_CASE( CoaxialButSeparatedMicroviasAreNotOneStack, VIA_STACK_DRC_FIXTURE )
299{
300 loadBoard();
301
302 setRule( "microvia_stack_depth (max 1)" );
303
304 run();
305
306 BOOST_CHECK_EQUAL( m_depth, 0 );
307 BOOST_CHECK_EQUAL( m_notFilled, 0 );
308}
309
310
311// A generator stack must be judged once, not once as a generator and again as loose geometry.
312BOOST_FIXTURE_TEST_CASE( GeneratorStackIsNotReportedTwice, VIA_STACK_DRC_FIXTURE )
313{
314 loadBoard();
315
316 run();
317
318 BOOST_CHECK_EQUAL( m_notFilled, 2 );
319}
320
321
322// A hair of offset must not split one structure into two. The upper via still lands on the one
323// below, so both rules still apply.
324BOOST_FIXTURE_TEST_CASE( OverlappingButOffsetMicroviasAreOneStructure, VIA_STACK_DRC_FIXTURE )
325{
326 loadBoard();
327
328 run();
329
330 BOOST_CHECK_EQUAL( m_notFilled, 1 );
331}
332
333
334// A real stagger clears the via below, so the fill rule does not apply to it.
335BOOST_FIXTURE_TEST_CASE( ClearOfTheViaBelowIsAStaggerNotAStack, VIA_STACK_DRC_FIXTURE )
336{
337 loadBoard();
338
339 run();
340
341 BOOST_CHECK_EQUAL( m_notFilled, 0 );
342 BOOST_CHECK_EQUAL( m_depth, 0 );
343}
344
345
346// IPC-6012 requires the hops of a stacked microvia to be filled. That applies to the whole
347// structure and not only to the ones carrying another hop.
348BOOST_FIXTURE_TEST_CASE( EveryHopOfAStackMustBeFilled, VIA_STACK_DRC_FIXTURE )
349{
350 loadBoard();
351
352 run();
353
354 BOOST_CHECK_EQUAL( m_notFilled, 2 );
355}
356
357
358// A lone microvia carries nothing, so it may be unfilled.
359BOOST_FIXTURE_TEST_CASE( ALoneMicroviaMayBeUnfilled, VIA_STACK_DRC_FIXTURE )
360{
361 loadBoard();
362
363 run();
364
365 BOOST_CHECK_EQUAL( m_notFilled, 0 );
366}
367
368
369// IPC-2226 caps a microvia at 1:1. A default 6 layer stackup has dielectrics far thicker than a
370// 0.1 mm hole, so a microvia across one is well outside that.
371BOOST_FIXTURE_TEST_CASE( MicroviaTooDeepForItsDiameterIsReported, VIA_STACK_DRC_FIXTURE )
372{
373 m_board = std::make_unique<BOARD>();
374 m_board->SetCopperLayerCount( 6 );
375 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
376
377 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
378 bds.SetCopperLayerCount( 6 );
380 setRule( "microvia_aspect_ratio (max 1.0)" );
381
382 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), F_Cu, In1_Cu, true );
383
384 run();
385
386 BOOST_CHECK_EQUAL( m_aspect, 1 );
387}
388
389
390// With no limit set the check says nothing, however thick the dielectric.
391BOOST_FIXTURE_TEST_CASE( AspectRatioIsSilentWithoutALimit, VIA_STACK_DRC_FIXTURE )
392{
393 m_board = std::make_unique<BOARD>();
394 m_board->SetCopperLayerCount( 6 );
395 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
396
397 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
398 bds.SetCopperLayerCount( 6 );
400
401
402 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), F_Cu, In1_Cu, true );
403
404 run();
405
406 BOOST_CHECK_EQUAL( m_aspect, 0 );
407
408 // The same via violates once a limit exists, so the silence came from the missing limit.
409 m_aspect = 0;
410 setRule( "microvia_aspect_ratio (max 1.0)" );
411
412 run();
413
414 BOOST_CHECK_EQUAL( m_aspect, 1 );
415}
416
417
418// The aspect ratio check is independent of the other three, so silencing them must not
419// silence it.
420BOOST_FIXTURE_TEST_CASE( AspectRatioSurvivesTheOtherChecksBeingIgnored, VIA_STACK_DRC_FIXTURE )
421{
422 m_board = std::make_unique<BOARD>();
423 m_board->SetCopperLayerCount( 6 );
424 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
425
426 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
427 bds.SetCopperLayerCount( 6 );
429 setRule( "microvia_aspect_ratio (max 1.0)" );
430
432
433 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), F_Cu, In1_Cu, true );
434
435 run();
436
437 BOOST_CHECK_EQUAL( m_aspect, 1 );
438}
439
440
441// Without a stackup there are no thicknesses to divide, so the check stands down.
442// With stackup thicknesses to divide, the limit applies; with none, the check stands down.
443BOOST_FIXTURE_TEST_CASE( AspectRatioNeedsStackupThicknesses, VIA_STACK_DRC_FIXTURE )
444{
445 m_board = std::make_unique<BOARD>();
446 m_board->SetCopperLayerCount( 4 );
447 m_board->SetEnabledLayers( LSET::AllCuMask( 4 ) | LSET::AllTechMask() );
448
449 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
450 setRule( "microvia_aspect_ratio (max 0.5)" );
451
452 VECTOR2I at( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) );
453 makeMicrovia( m_board.get(), at, F_Cu, In1_Cu, true );
454
455 // No stackup list at all, so there is nothing to measure.
456 run();
457 BOOST_CHECK_EQUAL( m_aspect, 0 );
458
459 // The same via against a real stackup exceeds the same limit.
460 m_aspect = 0;
461 bds.SetCopperLayerCount( 4 );
463 run();
464 BOOST_CHECK_MESSAGE( m_aspect == 1, "with thicknesses present the limit must apply" );
465}
466
467
468BOOST_FIXTURE_TEST_CASE( MicroviaStackDepthRuleIsHonoured, VIA_STACK_DRC_FIXTURE )
469{
470 loadBoard();
471
472 setRule( "microvia_stack_depth (max 2)" );
473
474 run();
475 BOOST_CHECK_EQUAL( m_depth, 1 );
476
477 m_depth = 0;
478 setRule( "microvia_stack_depth (max 4)" );
479
480 // A rule that allows four leaves the same three hops alone.
481 run();
482 BOOST_CHECK_EQUAL( m_depth, 0 );
483
484 // With a condition the rule still has to match, so a limit of two bites again.
485 m_depth = 0;
486 setRule( "microvia_stack_depth (max 2)", "A.isStackedVia()" );
487
488 run();
489 BOOST_CHECK_MESSAGE( m_depth == 1, "the rule condition did not select the stack" );
490}
491
492
494{
495 m_board = std::make_unique<BOARD>();
496 m_board->SetCopperLayerCount( 4 );
497 m_board->SetEnabledLayers( LSET::AllCuMask( 4 ) | LSET::AllTechMask() );
498
499 PCB_VIA_STACK* stack = makeStack( m_board.get(), F_Cu, In2_Cu );
500 stack->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ) );
501 stack->Regenerate( m_board.get(), nullptr );
502
503 setRule( "microvia_stack_depth (max 1)", "", "ignore" );
504
505 run();
506
507 BOOST_CHECK_EQUAL( m_depth, 0 );
508}
509
510
512{
513 m_board = std::make_unique<BOARD>();
514 m_board->SetCopperLayerCount( 4 );
515 m_board->SetEnabledLayers( LSET::AllCuMask( 4 ) | LSET::AllTechMask() );
516
517 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
519
520 for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
521 {
522 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
523 item->SetThickness( pcbIUScale.mmToIU( 0.1 ) );
524 }
525
526 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), F_Cu, In1_Cu, true );
527
528 setRule( "microvia_aspect_ratio (max 0.1)", "", "ignore" );
529
530 run();
531
532 BOOST_CHECK_EQUAL( m_aspect, 0 );
533}
534
535
536// A microvia whose two layers are the same spans no dielectric. Such vias point at each other
537// in the chain walk, so a handful of them must not multiply into a pile of violations.
538BOOST_FIXTURE_TEST_CASE( ZeroSpanMicroviasDoNotMultiplyViolations, VIA_STACK_DRC_FIXTURE )
539{
540 loadBoard();
541
542 run();
543
544 // A via spanning one layer forms no column, so there is nothing to report at all.
545 BOOST_CHECK_MESSAGE( m_notFilled == 0, "3 vias produced " << m_notFilled << " violations" );
546}
547
548
549// A dielectric built from several sublayers is as deep as all of them together.
550BOOST_FIXTURE_TEST_CASE( AspectRatioCountsEveryDielectricSublayer, VIA_STACK_DRC_FIXTURE )
551{
552 m_board = std::make_unique<BOARD>();
553 m_board->SetCopperLayerCount( 6 );
554 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
555
556 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
557 bds.SetCopperLayerCount( 6 );
559
560 // Split the dielectric under F.Cu into two 0.1 mm sublayers.
561 for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
562 {
563 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
564 continue;
565
566 item->AddDielectricPrms( 1 );
567 item->SetThickness( pcbIUScale.mmToIU( 0.1 ), 0 );
568 item->SetThickness( pcbIUScale.mmToIU( 0.1 ), 1 );
569 break;
570 }
571
572 // 0.2 mm of dielectric over a 0.15 mm drill is a ratio above 1, one sublayer alone is not.
573 setRule( "microvia_aspect_ratio (max 1.2)" );
574
575 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), F_Cu, In1_Cu, true );
576
577 run();
578
579 BOOST_CHECK_MESSAGE( m_aspect == 1, "both sublayers must count toward the via depth" );
580}
581
582
583// A microvia reaching B.Cu is drilled from the back, so it passes through the B.Cu foil.
584BOOST_FIXTURE_TEST_CASE( AspectRatioUsesTheFoilTheLaserEnters, VIA_STACK_DRC_FIXTURE )
585{
586 m_board = std::make_unique<BOARD>();
587 m_board->SetCopperLayerCount( 6 );
588 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
589
590 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
591 bds.SetCopperLayerCount( 6 );
593
594 const std::vector<BOARD_STACKUP_ITEM*>& stack = bds.GetStackupDescriptor().GetList();
595
596 // 1 oz outer foil, half that inside, and a known dielectric between In4.Cu and B.Cu.
597 for( BOARD_STACKUP_ITEM* item : stack )
598 {
599 if( item->GetType() != BS_ITEM_TYPE_COPPER )
600 continue;
601
602 bool outer = item->GetBrdLayerId() == F_Cu || item->GetBrdLayerId() == B_Cu;
603 item->SetThickness( pcbIUScale.mmToIU( outer ? 0.035 : 0.0175 ) );
604 }
605
606 for( int i = (int) stack.size() - 1; i >= 0; --i )
607 {
608 if( stack[i]->GetType() == BS_ITEM_TYPE_DIELECTRIC )
609 {
610 stack[i]->SetThickness( pcbIUScale.mmToIU( 0.1 ) );
611 break;
612 }
613 }
614
615 // Through the B.Cu foil the depth is 0.135 mm over a 0.1 mm drill, a ratio of 1.35.
616 // Through the In4.Cu foil it would read 1.175 and slip under the limit.
617 setRule( "microvia_aspect_ratio (max 1.25)" );
618
619 PCB_VIA* via = makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), In4_Cu,
620 B_Cu, true );
621 via->SetDrill( pcbIUScale.mmToIU( 0.1 ) );
622
623 run();
624
625 BOOST_CHECK_MESSAGE( m_aspect == 1, "a back-side microvia must be measured through the B.Cu foil" );
626}
627
628
629// An unset fill means "use the board default", not "unfilled".
630BOOST_FIXTURE_TEST_CASE( FillFromBoardHonoursTheBoardDefault, VIA_STACK_DRC_FIXTURE )
631{
632 loadBoard();
633
634 run();
635
636 BOOST_CHECK_MESSAGE( m_notFilled == 0, "board default is filled, but got " << m_notFilled << " not-filled errors" );
637}
638
639
640// Two overlapping hops can both land on the one below them, and it must not be reported once
641// for each.
642BOOST_FIXTURE_TEST_CASE( AViaCarriedTwiceIsReportedOnce, VIA_STACK_DRC_FIXTURE )
643{
644 loadBoard();
645
646 run();
647
648 BOOST_CHECK_EQUAL( m_notFilled, 1 );
649}
650
651
652// Holes a drill diameter apart touch, leaving no wall between them.
654{
655 loadBoard();
656
657 run();
658
659 BOOST_CHECK_EQUAL( m_notFilled, 1 );
660}
661
662
663// Laser drilling needs the resin to ablate, so a microvia belongs in the build-up layers
664// rather than through the glass reinforced core.
665BOOST_FIXTURE_TEST_CASE( MicroviaThroughCoreIsReported, VIA_STACK_DRC_FIXTURE )
666{
667 m_board = std::make_unique<BOARD>();
668 m_board->SetCopperLayerCount( 6 );
669 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
670
671 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
672 bds.SetCopperLayerCount( 6 );
674
675 const std::vector<BOARD_STACKUP_ITEM*>& stack = bds.GetStackupDescriptor().GetList();
676
677 // Name the copper layers either side of the first core, and of the first prepreg.
678 PCB_LAYER_ID coreTop = UNDEFINED_LAYER, coreBot = UNDEFINED_LAYER;
679 PCB_LAYER_ID pregTop = UNDEFINED_LAYER, pregBot = UNDEFINED_LAYER;
680 PCB_LAYER_ID lastCopper = UNDEFINED_LAYER;
681
682 for( size_t i = 0; i < stack.size(); ++i )
683 {
684 if( stack[i]->GetType() == BS_ITEM_TYPE_COPPER )
685 {
686 lastCopper = stack[i]->GetBrdLayerId();
687 continue;
688 }
689
690 if( stack[i]->GetType() != BS_ITEM_TYPE_DIELECTRIC )
691 continue;
692
694
695 for( size_t j = i + 1; j < stack.size(); ++j )
696 {
697 if( stack[j]->GetType() == BS_ITEM_TYPE_COPPER )
698 {
699 below = stack[j]->GetBrdLayerId();
700 break;
701 }
702 }
703
704 if( stack[i]->GetTypeName() == KEY_CORE && coreTop == UNDEFINED_LAYER )
705 {
706 coreTop = lastCopper;
707 coreBot = below;
708 }
709 else if( stack[i]->GetTypeName() == KEY_PREPREG && pregTop == UNDEFINED_LAYER )
710 {
711 pregTop = lastCopper;
712 pregBot = below;
713 }
714 }
715
716 BOOST_REQUIRE( coreTop != UNDEFINED_LAYER && coreBot != UNDEFINED_LAYER );
717 BOOST_REQUIRE( pregTop != UNDEFINED_LAYER && pregBot != UNDEFINED_LAYER );
718
719 VECTOR2I overCore( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) );
720 VECTOR2I overPrepreg( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 10 ) );
721
722 makeMicrovia( m_board.get(), overCore, coreTop, coreBot, true );
723 makeMicrovia( m_board.get(), overPrepreg, pregTop, pregBot, true );
724
725 run();
726
727 // Only the one crossing core is reported, and it does not need an aspect ratio limit set.
728 BOOST_CHECK_EQUAL( m_crossesCore, 1 );
729}
730
731
732// Two hops landing on one shared via below are two structures. The second must still be
733// fill checked rather than dropped for sharing.
734BOOST_FIXTURE_TEST_CASE( ASecondHopSharingALandingViaIsStillChecked, VIA_STACK_DRC_FIXTURE )
735{
736 loadBoard();
737
738 run();
739
740 BOOST_CHECK_EQUAL( m_notFilled, 2 );
741}
742
743
744// The core check shares the aspect ratio walk, so an exhausted aspect limit must not end it.
745BOOST_FIXTURE_TEST_CASE( AnIgnoredAspectRatioDoesNotStopTheCoreCheck, VIA_STACK_DRC_FIXTURE )
746{
747 m_board = std::make_unique<BOARD>();
748 m_board->SetCopperLayerCount( 6 );
749 m_board->SetEnabledLayers( LSET::AllCuMask( 6 ) | LSET::AllTechMask() );
750
751 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
752 bds.SetCopperLayerCount( 6 );
754
755 // A limit is set, but its severity is off. The first via would otherwise end the walk.
756 setRule( "microvia_aspect_ratio (max 0.1)" );
757 m_ignored = { DRCE_MICROVIA_ASPECT_RATIO };
758
759 const std::vector<BOARD_STACKUP_ITEM*>& stack = bds.GetStackupDescriptor().GetList();
760 PCB_LAYER_ID coreTop = UNDEFINED_LAYER, coreBot = UNDEFINED_LAYER, lastCopper = UNDEFINED_LAYER;
761
762 for( size_t i = 0; i < stack.size(); ++i )
763 {
764 if( stack[i]->GetType() == BS_ITEM_TYPE_COPPER )
765 {
766 lastCopper = stack[i]->GetBrdLayerId();
767 continue;
768 }
769
770 if( stack[i]->GetType() == BS_ITEM_TYPE_DIELECTRIC && stack[i]->GetTypeName() == KEY_CORE
771 && coreTop == UNDEFINED_LAYER )
772 {
773 coreTop = lastCopper;
774
775 for( size_t j = i + 1; j < stack.size(); ++j )
776 {
777 if( stack[j]->GetType() == BS_ITEM_TYPE_COPPER )
778 {
779 coreBot = stack[j]->GetBrdLayerId();
780 break;
781 }
782 }
783 }
784 }
785
786 BOOST_REQUIRE( coreTop != UNDEFINED_LAYER && coreBot != UNDEFINED_LAYER );
787
788 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), coreTop, coreBot, true );
789 makeMicrovia( m_board.get(), VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 10 ) ), coreTop, coreBot, true );
790
791 run();
792
793 BOOST_CHECK_EQUAL( m_aspect, 0 );
794 BOOST_CHECK_MESSAGE( m_crossesCore == 2, "the walk stopped early, only " << m_crossesCore << " reported" );
795}
796
797
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
General utilities for PCB file IO for QA programs.
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_DIELECTRIC
Container for design settings for a BOARD object.
std::map< int, SEVERITY > m_DRCSeverities
std::shared_ptr< DRC_ENGINE > m_DRCEngine
BOARD_STACKUP & GetStackupDescriptor()
void SetCopperLayerCount(int aNewLayerCount)
Set the copper layer count to aNewLayerCount.
Manage one layer needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
void SetViolationHandler(DRC_VIOLATION_HANDLER aHandler)
Set an optional DRC violation handler (receives DRC_ITEMs and positions).
Definition drc_engine.h:164
void ClearViolationHandler()
Definition drc_engine.h:169
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition lset.cpp:672
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
void SetPosition(const VECTOR2I &aPos) override
A microvia stack: several single hop microvias, plus connecting traces for staggered stacks,...
void Regenerate(BOARD *aBoard, BOARD_COMMIT *aCommit)
Rebuild the member vias (and, for a staggered stack, the connecting traces) from the current stack se...
void SetStartLayer(PCB_LAYER_ID aLayer)
void SetViaSize(int aSize)
void SetViaDrill(int aDrill)
void SetStyle(VIA_STACK_STYLE aStyle)
void SetEndLayer(PCB_LAYER_ID aLayer)
@ DRCE_MICROVIA_STACK_DEPTH
Definition drc_item.h:65
@ DRCE_MICROVIA_CROSSES_CORE
Definition drc_item.h:67
@ DRCE_MALFORMED_MICROVIA_STACK_SPAN
Definition drc_item.h:63
@ DRCE_MICROVIA_ASPECT_RATIO
Definition drc_item.h:66
@ DRCE_MICROVIA_STACK_NOT_FILLED
Definition drc_item.h:64
@ DRCE_FIRST
Definition drc_item.h:36
@ DRCE_LAST
Definition drc_item.h:131
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ In2_Cu
Definition layer_ids.h:63
@ In4_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ In1_Cu
Definition layer_ids.h:62
@ F_Cu
Definition layer_ids.h:60
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
#define KEY_PREPREG
#define KEY_CORE
std::unique_ptr< BOARD > m_board
SETTINGS_MANAGER m_settingsManager
void setRule(const std::string &aConstraint, const std::string &aCondition="", const std::string &aSeverity="")
std::vector< int > m_ignored
std::unique_ptr< KI_TEST::SCOPED_TEMP_DIR > m_ruleDir
BOOST_FIXTURE_TEST_CASE(ServerStartsAndResponds, API_SERVER_E2E_FIXTURE)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
static PCB_VIA_STACK * makeStack(BOARD *aBoard, PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd)
static PCB_VIA * makeMicrovia(BOARD *aBoard, const VECTOR2I &aPos, PCB_LAYER_ID aTop, PCB_LAYER_ID aBottom, bool aFilled)
BOOST_FIXTURE_TEST_CASE(GoodStackNoViolations, VIA_STACK_DRC_FIXTURE)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708