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 covered.insert( std::min( top->second, bot->second ) );
138 }
139
140 if( broken || (int) covered.size() != spanLen - 1 )
141 {
142 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_MICROVIA_STACK_SPAN );
143 drcItem->SetItems( aStack );
144 reportViolation( drcItem, aStack->GetPosition(), aStack->GetLayer() );
145 }
146 }
147}
148
149
150// Depth and fill are properties of the copper, not of the object that placed it.
151void DRC_TEST_PROVIDER_VIA_STACK::checkColumn( const std::vector<PCB_VIA*>& aColumn )
152{
153 // Not standardised. A fabricator quotes its own limit, and a designer may accept a deeper
154 // stack in one area than another, so the limit is resolved per via rather than read once.
155 PCB_VIA* top = aColumn.front();
156 DRC_CONSTRAINT depthConstraint =
157 m_drcEngine->EvalRules( MICROVIA_STACK_DEPTH_CONSTRAINT, top, nullptr, top->GetLayer() );
158
159 if( depthConstraint.GetValue().HasMax() && (int) aColumn.size() > depthConstraint.GetValue().Max()
160 && !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH ) )
161 {
162 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_STACK_DEPTH );
163 drcItem->SetErrorMessage( wxString::Format( _( "Stack is %d hops deep, limit is %d" ), (int) aColumn.size(),
164 depthConstraint.GetValue().Max() ) );
165 drcItem->SetItems( top );
166 drcItem->SetViolatingRule( depthConstraint.GetParentRule() );
167 reportViolation( drcItem, top->GetPosition(), top->GetLayer() );
168 }
169
170 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED ) )
171 return;
172
173 // IPC-6012 requires the hops of a stacked microvia to be filled. An unfilled hop is a dimple,
174 // and the joint separates under thermal cycling.
175 // An unset fill takes the board default.
176 bool boardFillsVias = m_drcEngine->GetBoard()->GetDesignSettings().m_FillVias;
177
178 for( PCB_VIA* via : aColumn )
179 {
180 if( !via->Padstack().Drill().is_filled.value_or( boardFillsVias ) )
181 {
182 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_STACK_NOT_FILLED );
183 drcItem->SetItems( via );
184 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
185 }
186 }
187}
188
189
191{
192 std::vector<std::vector<PCB_VIA*>> columns = PCB_VIA::CollectMicroviaColumns( aBoard );
193
194 const int progressDelta = 250;
195 int ii = 0;
196
197 for( const std::vector<PCB_VIA*>& column : columns )
198 {
199 if( !reportProgress( ii++, columns.size(), progressDelta ) )
200 return;
201
202 checkColumn( column );
203 }
204}
205
206
207// The dielectric a microvia crosses is the stackup item between its two copper layers, so both
208// its thickness and its declared material come from the same walk.
210{
212
213 bool checkRatio = !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO );
214 bool checkCore = !m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE );
215
216 if( !checkRatio && !checkCore )
217 return;
218
219 const std::vector<BOARD_STACKUP_ITEM*>& stack = bds.GetStackupDescriptor().GetList();
220
221 const int progressDelta = 500;
222 int ii = 0;
223
224 for( PCB_TRACK* track : aBoard->Tracks() )
225 {
226 if( !reportProgress( ii++, aBoard->Tracks().size(), progressDelta ) )
227 return;
228
229 if( track->Type() != PCB_VIA_T )
230 continue;
231
232 PCB_VIA* via = static_cast<PCB_VIA*>( track );
233
234 if( via->GetViaType() != VIATYPE::MICROVIA || via->GetDrillValue() <= 0 )
235 continue;
236
237 // Find the copper items the via runs between, and the dielectric they enclose.
238 int first = -1;
239 int last = -1;
240
241 for( int i = 0; i < (int) stack.size(); ++i )
242 {
243 if( stack[i]->GetType() != BS_ITEM_TYPE_COPPER )
244 continue;
245
246 PCB_LAYER_ID layer = stack[i]->GetBrdLayerId();
247
248 if( layer == via->TopLayer() || layer == via->BottomLayer() )
249 {
250 if( first < 0 )
251 first = i;
252 else
253 last = i;
254 }
255 }
256
257 if( first < 0 || last < 0 )
258 continue;
259
260 int thickness = 0;
261 wxString dielectric;
262 bool crossesCore = false;
263
264 for( int i = first + 1; i < last; ++i )
265 {
266 if( stack[i]->GetType() != BS_ITEM_TYPE_DIELECTRIC || !stack[i]->IsEnabled() )
267 continue;
268
269 // A dielectric can be built from several sublayers.
270 for( int sub = 0; sub < stack[i]->GetSublayersCount(); ++sub )
271 thickness += stack[i]->GetThickness( sub );
272
273 if( stack[i]->GetTypeName() == KEY_CORE )
274 crossesCore = true;
275
276 if( dielectric.IsEmpty() )
277 dielectric = stack[i]->GetTypeName();
278 }
279
280 // IPC-2226 builds microvias in the layers laminated onto the core, not through it.
281 // Laser drilling relies on the resin ablating, and core is glass reinforced.
282 if( crossesCore && checkCore )
283 {
284 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_CROSSES_CORE );
285
286 drcItem->SetErrorMessage(
287 wxString::Format( _( "The stackup gives the dielectric between %s and %s as core" ),
288 LSET::Name( via->TopLayer() ), LSET::Name( via->BottomLayer() ) ) );
289 drcItem->SetItems( via );
290 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
291 }
292
293 if( !checkRatio || thickness <= 0 )
294 continue;
295
296 DRC_CONSTRAINT ratioConstraint =
297 m_drcEngine->EvalRules( MICROVIA_ASPECT_RATIO_CONSTRAINT, via, nullptr, via->GetLayer() );
298
299 if( !ratioConstraint.GetValue().HasMax() )
300 continue;
301
302 // Ratios are parsed to three decimals, so the stored maximum is scaled by 1000.
303 double limit = ratioConstraint.GetValue().Max() / 1000.0;
304
305 if( limit <= 0.0 )
306 continue;
307
308 // IPC-2226 measures the hole against the dielectric plus the copper foil over it.
309 // The laser enters from the outer side.
310 bool fromBack = IsExternalCopperLayer( stack[last]->GetBrdLayerId() )
311 && !IsExternalCopperLayer( stack[first]->GetBrdLayerId() );
312
313 thickness += stack[fromBack ? last : first]->GetThickness();
314
315 double ratio = (double) thickness / (double) via->GetDrillValue();
316
317 if( ratio <= limit )
318 continue;
319
320 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO ) )
321 {
322 if( !checkCore )
323 return;
324
325 checkRatio = false;
326 continue;
327 }
328
329 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MICROVIA_ASPECT_RATIO );
330
331 drcItem->SetErrorMessage(
332 wxString::Format( _( "Crosses %s dielectric: aspect ratio %.2f exceeds the %.2f limit" ),
333 dielectric.IsEmpty() ? _( "an unnamed" ) : dielectric, ratio, limit ) );
334
335 drcItem->SetItems( via );
336 drcItem->SetViolatingRule( ratioConstraint.GetParentRule() );
337 reportViolation( drcItem, via->GetPosition(), via->GetLayer() );
338 }
339}
340
341
343{
344 if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_MICROVIA_STACK_SPAN )
345 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED )
346 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH )
347 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO )
348 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE ) )
349 {
350 return true;
351 }
352
353 if( !reportPhase( _( "Checking microvia stacks..." ) ) )
354 return false;
355
356 BOARD* board = m_drcEngine->GetBoard();
357 int copperLayers = board->GetCopperLayerCount();
358
359 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_MICROVIA_STACK_SPAN ) )
360 {
361 for( PCB_GENERATOR* gen : board->Generators() )
362 {
363 if( m_drcEngine->IsCancelled() )
364 break;
365
366 if( PCB_VIA_STACK* stack = dynamic_cast<PCB_VIA_STACK*>( gen ) )
367 checkSpan( stack, copperLayers );
368 }
369 }
370
371 if( !m_drcEngine->IsCancelled()
372 && !( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_NOT_FILLED )
373 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_STACK_DEPTH ) ) )
374 {
375 checkLooseMicrovias( board );
376 }
377
378 if( !m_drcEngine->IsCancelled()
379 && !( m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_ASPECT_RATIO )
380 && m_drcEngine->IsErrorLimitExceeded( DRCE_MICROVIA_CROSSES_CORE ) ) )
381 {
382 checkAspectRatio( board );
383 }
384
385 return !m_drcEngine->IsCancelled();
386}
387
388
389namespace detail
390{
392}
@ 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:409
const GENERATORS & Generators() const
Definition board.h:476
int GetCopperLayerCount() const
Definition board.cpp:1131
const TRACKS & Tracks() const
Definition board.h:461
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
DRC_RULE * GetParentRule() const
Definition drc_rule.h:204
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:35
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:411
#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