KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_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 <cstdlib>
21#include <map>
22#include <set>
23#include <vector>
24
25#include <board.h>
27#include <layer_range.h>
28#include <padstack.h>
29#include <pcb_generator.h>
30#include <pcb_track.h>
32#include <drc/drc_engine.h>
33#include <drc/drc_item.h>
37
38/*
39 Microvia stack topology tests.
40
41 Errors generated:
42 - DRCE_MALFORMED_MICROVIA_STACK_SPAN
43 - DRCE_MICROVIA_STACK_NOT_FILLED
44 - DRCE_MICROVIA_STACK_DEPTH
45 - DRCE_MICROVIA_ASPECT_RATIO
46 - DRCE_MICROVIA_CROSSES_CORE
47
48 The span test asks whether a generator's hops still tile the span it declares. Nothing
49 rebuilds a stack on load, so a file can carry one that does not. The other three ask whether
50 the copper can be built, and apply to every microvia however it was placed.
51
52 Where these rules come from:
53
54 IPC-2226 defines a microvia as a hole of aspect ratio 1:1 or better, no deeper than 0.25 mm.
55 That is why a hop spans one dielectric and why deeper structures are built by stacking.
56
57 IPC-4761 names the via protection types. A filled via is Type V and a filled and capped via is
58 Type VII, which is the via in pad treatment used for BGA fanout.
59
60 IPC-6012 carries the reliability requirements for microvias, including the thermal cycling a
61 stacked structure has to survive. Unfilled stacked microvias separate at the target pad.
62
63 Maximum stack depth is deliberately not standardised. It is a fabricator capability, so it is a
64 board setting rather than a fixed number here.
65*/
66
68{
69public:
71
72 virtual ~DRC_TEST_PROVIDER_VIA_STACK() = default;
73
74 virtual bool Run() override;
75
76 virtual const wxString GetName() const override { return wxT( "microvia_stack" ); };
77
78private:
79 void checkSpan( PCB_VIA_STACK* aStack, int aCopperLayers );
80 void checkColumn( const std::vector<PCB_VIA*>& aColumn );
81 void checkLooseMicrovias( BOARD* aBoard );
82 void checkAspectRatio( BOARD* aBoard );
83};
84
85
86void DRC_TEST_PROVIDER_VIA_STACK::checkSpan( PCB_VIA_STACK* aStack, int aCopperLayers )
87{
88 std::vector<PCB_VIA*> vias;
89
90 for( BOARD_ITEM* item : aStack->GetBoardItems() )
91 {
92 if( item->Type() == PCB_VIA_T )
93 {
94 PCB_VIA* via = static_cast<PCB_VIA*>( item );
95
96 if( via->GetViaType() == VIATYPE::MICROVIA )
97 vias.push_back( via );
98 }
99 }
100
101 if( vias.empty() )
102 return;
103
104 // IPC-2226 confines a microvia to one dielectric, so the hops must tile the declared span
105 // with single layer steps and no gap. A span naming layers absent from the board is broken
106 // by definition, and iterating it would never terminate.
107 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_MICROVIA_STACK_SPAN ) )
108 {
109 if( !PCB_VIA_STACK::IsSpanValid( m_drcEngine->GetBoard(), aStack->GetStartLayer(), aStack->GetEndLayer() ) )
110 {
111 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_MICROVIA_STACK_SPAN );
112 drcItem->SetItems( aStack );
113 reportViolation( drcItem, aStack->GetPosition(), aStack->GetLayer() );
114 return;
115 }
116
117 std::map<int, int> layerToIndex;
118 int spanLen = 0;
119
120 for( PCB_LAYER_ID layer : LAYER_RANGE( aStack->GetStartLayer(), aStack->GetEndLayer(), aCopperLayers ) )
121 layerToIndex[layer] = spanLen++;
122
123 std::set<int> covered;
124 bool broken = false;
125
126 for( PCB_VIA* via : vias )
127 {
128 auto top = layerToIndex.find( via->TopLayer() );
129 auto bot = layerToIndex.find( via->BottomLayer() );
130
131 if( top == layerToIndex.end() || bot == layerToIndex.end() || std::abs( top->second - bot->second ) != 1 )
132 {
133 broken = true;
134 break;
135 }
136
137 if( !covered.insert( std::min( top->second, bot->second ) ).second )
138 {
139 broken = true;
140 break;
141 }
142 }
143
144 if( broken || (int) covered.size() != spanLen - 1 )
145 {
146 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_MICROVIA_STACK_SPAN );
147 drcItem->SetItems( aStack );
148 reportViolation( drcItem, aStack->GetPosition(), aStack->GetLayer() );
149 }
150 }
151}
152
153
154// Depth and fill are properties of the copper, not of the object that placed it.
155void DRC_TEST_PROVIDER_VIA_STACK::checkColumn( const std::vector<PCB_VIA*>& aColumn )
156{
157 // Not standardised. A fabricator quotes its own limit, and a designer may accept a deeper
158 // stack in one area than another, so the limit is resolved per via rather than read once.
159 PCB_VIA* top = aColumn.front();
160 DRC_CONSTRAINT depthConstraint =
161 m_drcEngine->EvalRules( MICROVIA_STACK_DEPTH_CONSTRAINT, top, nullptr, top->GetLayer() );
162
163 if( depthConstraint.GetSeverity() != RPT_SEVERITY_IGNORE && depthConstraint.GetValue().HasMax()
164 && (int) aColumn.size() > depthConstraint.GetValue().Max()
165 && !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH ) )
166 {
167 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_STACK_DEPTH );
168 drcItem->SetErrorMessage( wxString::Format( _( "Stack is %d hops deep, limit is %d" ), (int) aColumn.size(),
169 depthConstraint.GetValue().Max() ) );
170 drcItem->SetItems( top );
171 drcItem->SetViolatingRule( depthConstraint.GetParentRule() );
172 reportViolation( drcItem, top->GetPosition(), top->GetLayer() );
173 }
174
175 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED ) )
176 return;
177
178 // IPC-6012 requires the hops of a stacked microvia to be filled. An unfilled hop is a dimple,
179 // and the joint separates under thermal cycling.
180 // An unset fill takes the board default.
181 bool boardFillsVias = m_drcEngine->GetBoard()->GetDesignSettings().m_FillVias;
182
183 for( PCB_VIA* via : aColumn )
184 {
185 if( !via->Padstack().Drill().is_filled.value_or( boardFillsVias ) )
186 {
187 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_STACK_NOT_FILLED );
188 drcItem->SetItems( via );
189 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
190 }
191 }
192}
193
194
196{
197 std::vector<std::vector<PCB_VIA*>> columns = PCB_VIA::CollectMicroviaColumns( aBoard );
198
199 const int progressDelta = 250;
200 int ii = 0;
201
202 for( const std::vector<PCB_VIA*>& column : columns )
203 {
204 if( !reportProgress( ii++, columns.size(), progressDelta ) )
205 return;
206
207 checkColumn( column );
208 }
209}
210
211
212// The dielectric a microvia crosses is the stackup item between its two copper layers, so both
213// its thickness and its declared material come from the same walk.
215{
217
218 bool checkRatio = !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO );
219 bool checkCore = !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE );
220
221 if( !checkRatio && !checkCore )
222 return;
223
224 const std::vector<BOARD_STACKUP_ITEM*>& stack = bds.GetStackupDescriptor().GetList();
225
226 const int progressDelta = 500;
227 int ii = 0;
228
229 for( PCB_TRACK* track : aBoard->Tracks() )
230 {
231 if( !reportProgress( ii++, aBoard->Tracks().size(), progressDelta ) )
232 return;
233
234 if( track->Type() != PCB_VIA_T )
235 continue;
236
237 PCB_VIA* via = static_cast<PCB_VIA*>( track );
238
239 if( via->GetViaType() != VIATYPE::MICROVIA || via->GetDrillValue() <= 0 )
240 continue;
241
242 // Find the copper items the via runs between, and the dielectric they enclose.
243 int first = -1;
244 int last = -1;
245
246 for( int i = 0; i < (int) stack.size(); ++i )
247 {
248 if( stack[i]->GetType() != BS_ITEM_TYPE_COPPER )
249 continue;
250
251 PCB_LAYER_ID layer = stack[i]->GetBrdLayerId();
252
253 if( layer == via->TopLayer() || layer == via->BottomLayer() )
254 {
255 if( first < 0 )
256 first = i;
257 else
258 last = i;
259 }
260 }
261
262 if( first < 0 || last < 0 )
263 continue;
264
265 int thickness = 0;
266 wxString dielectric;
267 bool crossesCore = false;
268
269 for( int i = first + 1; i < last; ++i )
270 {
271 if( stack[i]->GetType() != BS_ITEM_TYPE_DIELECTRIC || !stack[i]->IsEnabled() )
272 continue;
273
274 // A dielectric can be built from several sublayers.
275 for( int sub = 0; sub < stack[i]->GetSublayersCount(); ++sub )
276 thickness += stack[i]->GetThickness( sub );
277
278 if( stack[i]->GetTypeName() == KEY_CORE )
279 crossesCore = true;
280
281 if( dielectric.IsEmpty() )
282 dielectric = stack[i]->GetTypeName();
283 }
284
285 // IPC-2226 builds microvias in the layers laminated onto the core, not through it.
286 // Laser drilling relies on the resin ablating, and core is glass reinforced.
287 if( crossesCore && checkCore )
288 {
289 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_CROSSES_CORE );
290
291 drcItem->SetErrorMessage(
292 wxString::Format( _( "The stackup gives the dielectric between %s and %s as core" ),
293 LSET::Name( via->TopLayer() ), LSET::Name( via->BottomLayer() ) ) );
294 drcItem->SetItems( via );
295 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
296 }
297
298 if( !checkRatio || thickness <= 0 )
299 continue;
300
301 DRC_CONSTRAINT ratioConstraint =
302 m_drcEngine->EvalRules( MICROVIA_ASPECT_RATIO_CONSTRAINT, via, nullptr, via->GetLayer() );
303
304 if( ratioConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || !ratioConstraint.GetValue().HasMax() )
305 continue;
306
307 // Ratios are parsed to three decimals, so the stored maximum is scaled by 1000.
308 double limit = ratioConstraint.GetValue().Max() / 1000.0;
309
310 if( limit <= 0.0 )
311 continue;
312
313 // IPC-2226 measures the hole against the dielectric plus the copper foil over it.
314 // The laser enters from the outer side.
315 bool fromBack = IsExternalCopperLayer( stack[last]->GetBrdLayerId() )
316 && !IsExternalCopperLayer( stack[first]->GetBrdLayerId() );
317
318 thickness += stack[fromBack ? last : first]->GetThickness();
319
320 double ratio = (double) thickness / (double) via->GetDrillValue();
321
322 if( ratio <= limit )
323 continue;
324
325 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO ) )
326 {
327 if( !checkCore )
328 return;
329
330 checkRatio = false;
331 continue;
332 }
333
334 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_ASPECT_RATIO );
335
336 drcItem->SetErrorMessage(
337 wxString::Format( _( "Crosses %s dielectric: aspect ratio %.2f exceeds the %.2f limit" ),
338 dielectric.IsEmpty() ? _( "an unnamed" ) : dielectric, ratio, limit ) );
339
340 drcItem->SetItems( via );
341 drcItem->SetViolatingRule( ratioConstraint.GetParentRule() );
342 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
343 }
344}
345
346
348{
349 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_MICROVIA_STACK_SPAN )
350 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED )
351 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH )
352 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO )
353 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE ) )
354 {
355 return true;
356 }
357
358 if( !reportPhase( _( "Checking microvia stacks..." ) ) )
359 return false;
360
361 BOARD* board = m_drcEngine->GetBoard();
362 int copperLayers = board->GetCopperLayerCount();
363
364 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_MICROVIA_STACK_SPAN ) )
365 {
366 for( PCB_GENERATOR* gen : board->Generators() )
367 {
368 if( m_drcEngine->IsCancelled() )
369 break;
370
371 if( PCB_VIA_STACK* stack = dynamic_cast<PCB_VIA_STACK*>( gen ) )
372 checkSpan( stack, copperLayers );
373 }
374 }
375
376 if( !m_drcEngine->IsCancelled()
377 && !( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED )
378 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH ) ) )
379 {
380 checkLooseMicrovias( board );
381 }
382
383 if( !m_drcEngine->IsCancelled()
384 && !( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO )
385 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE ) ) )
386 {
387 checkAspectRatio( board );
388 }
389
390 return !m_drcEngine->IsCancelled();
391}
392
393
394namespace detail
395{
397}
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_DIELECTRIC
Container for design settings for a BOARD object.
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const GENERATORS & Generators() const
Definition board.h:484
int GetCopperLayerCount() const
Definition board.cpp:1138
const TRACKS & Tracks() const
Definition board.h:462
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
SEVERITY GetSeverity() const
Definition drc_rule.h:256
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:204
DRC_RULE * GetParentRule() const
Definition drc_rule.h:222
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
virtual const wxString GetName() const override
virtual ~DRC_TEST_PROVIDER_VIA_STACK()=default
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void checkColumn(const std::vector< PCB_VIA * > &aColumn)
void checkSpan(PCB_VIA_STACK *aStack, int aCopperLayers)
virtual bool reportPhase(const wxString &aStageName)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
bool HasMax() const
Definition minoptmax.h:38
T Max() const
Definition minoptmax.h:30
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
VECTOR2I GetPosition() const override
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
A microvia stack: several single hop microvias, plus connecting traces for staggered stacks,...
PCB_LAYER_ID GetStartLayer() const
PCB_LAYER_ID GetEndLayer() const
static bool IsSpanValid(BOARD *aBoard, PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd)
True when both span ends are copper layers within the board's copper layer count.
static std::vector< std::vector< PCB_VIA * > > CollectMicroviaColumns(BOARD *aBoard)
Runs of microvias that land on one another, each ordered from its outermost hop down.
@ 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
@ MICROVIA_ASPECT_RATIO_CONSTRAINT
Definition drc_rule.h:91
@ MICROVIA_STACK_DEPTH_CONSTRAINT
Definition drc_rule.h:90
#define _(s)
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ RPT_SEVERITY_IGNORE
#define KEY_CORE
KIBIS top(path, &reporter)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89