KiCad PCB EDA Suite
class_board_stackup.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) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2009-2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include "class_board_stackup.h"
23 #include <convert_to_biu.h>
24 #include <base_units.h>
26 #include <board_design_settings.h>
27 #include <class_board.h>
28 #include <i18n_utility.h> // For _HKI definition
30 
31 
33 {
35  m_Type = aType;
36  m_Enabled = true;
38  m_EpsilonR = 0;
39  m_LossTangent = 0.0;
40  m_ThicknessLocked = false;
41 
42  // Initialize parameters to a usual value for allowed types:
43  switch( m_Type )
44  {
48  break;
49 
51  m_TypeName = KEY_CORE; // or prepreg
52  m_Material = "FR4"; // or other dielectric name
54  m_Thickness = 0; // will be set later
55  m_LossTangent = 0.02; // for FR4
56  m_EpsilonR = 4.5; // for FR4
57  break;
58 
60  m_TypeName = "solderpaste";
61  m_Thickness = 0.0; // Not used
62  break;
63 
65  m_TypeName = "soldermask";
66  m_Color = "Green";
67  m_Material = NotSpecifiedPrm(); // or other solder mask material name
70  m_LossTangent = 0.0;
71  break;
72 
74  m_TypeName = "silkscreen";
76  m_Material = NotSpecifiedPrm(); // or other silkscreen material name
78  m_Thickness = 0.0; // to be specified
79  break;
80 
82  m_Thickness = 0.0;
83  break;
84  }
85 }
86 
87 
89 {
90  m_LayerId = aOther.m_LayerId;
91  m_Type = aOther.m_Type;
92  m_Enabled = aOther.m_Enabled;
94  m_TypeName = aOther.m_TypeName;
95  m_LayerName = aOther.m_LayerName;
96  m_Material = aOther.m_Material;
97  m_Color = aOther.m_Color;
98  m_Thickness = aOther.m_Thickness;
100  m_EpsilonR = aOther.m_EpsilonR;
101  m_LossTangent = aOther.m_LossTangent;
102 }
103 
104 
106 {
107  // A reasonable thickness for copper layers:
108  return Millimeter2iu( 0.035 );
109 }
110 
111 
113 {
114  // A reasonable thickness for solder mask:
115  return Millimeter2iu( 0.01 );
116 }
117 
118 
120 {
123  //|| m_Type == BS_ITEM_TYPE_SILKSCREEN
124  ;
125 };
126 
127 
129 {
132  //|| m_Type == BS_ITEM_TYPE_SILKSCREEN
133  ;
134 };
135 
136 
138 {
139  // return true if the material is specified
141 }
142 
143 
145 {
146  // The material is editable only for dielectric
147  return m_Type == BS_ITEM_TYPE_DIELECTRIC ||
150 }
151 
152 
154 {
156 }
157 
158 
160 {
161  switch( m_Type )
162  {
163  case BS_ITEM_TYPE_COPPER:
164  return true;
165 
167  return true;
168 
170  return true;
171 
173  return false;
174 
176  return false;
177 
178  default:
179  break;
180  }
181 
182  return false;
183 }
184 
185 
187 {
188  // return a wxString to print/display Epsilon R
189  wxString txt;
190  txt.Printf( "%.1f", m_EpsilonR );
191  return txt;
192 }
193 
194 
196 {
197  // return a wxString to print/display Loss Tangent
198  wxString txt;
199  txt.Printf( "%g", m_LossTangent );
200  return txt;
201 }
202 
203 
204 
206 {
207  m_HasDielectricConstrains = false; // True if some dielectric layers have constrains
208  // (Loss tg and Epison R)
209  m_HasThicknessConstrains = false; // True if some dielectric or copper layers have constrains
211  m_CastellatedPads = false; // True if some castellated pads exist
212  m_EdgePlating = false; // True if edge board is plated
213  m_FinishType = "None"; // undefined finish type
214 }
215 
216 
218 {
222  m_EdgePlating = aOther.m_EdgePlating;
223  m_FinishType = aOther.m_FinishType;
224 
225  // All items in aOther.m_list have to be duplicated, because aOther.m_list
226  // manage pointers to these items
227  for( auto item : aOther.m_list )
228  {
229  BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
230  Add( dup_item );
231  }
232 }
233 
234 
236 {
240  m_EdgePlating = aOther.m_EdgePlating;
241  m_FinishType = aOther.m_FinishType;
242 
243  RemoveAll();
244 
245  // All items in aOther.m_list have to be duplicated, because aOther.m_list
246  // manage pointers to these items
247  for( auto item : aOther.m_list )
248  {
249  BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
250  Add( dup_item );
251  }
252 
253  return *this;
254 }
255 
256 
258 {
259  for( auto item : m_list )
260  delete item;
261 
262  m_list.clear();
263 }
264 
265 
267 {
268  if( aIndex < 0 || aIndex >= GetCount() )
269  return nullptr;
270 
271  return GetList()[aIndex];
272 }
273 
274 
276 {
277  // return the board thickness from the thickness of BOARD_STACKUP_ITEM list
278  int thickness = 0;
279 
280  for( auto item : m_list )
281  {
282  if( item->IsThicknessEditable() && item->m_Enabled )
283  thickness += item->m_Thickness;
284  }
285 
286  return thickness;
287 }
288 
289 
291 {
292  bool change = false;
293  // Build the suitable stackup:
294  BOARD_STACKUP stackup;
295  stackup.BuildDefaultStackupList( aSettings );
296 
297  // First test for removed layers:
298  for( BOARD_STACKUP_ITEM* old_item: m_list )
299  {
300  bool found = false;
301 
302  for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
303  {
304  if( item->m_LayerId == old_item->m_LayerId )
305  {
306  found = true;
307  break;
308  }
309  }
310 
311  if( !found ) // a layer was removed: a change is found
312  {
313  change = true;
314  break;
315  }
316  }
317 
318  // Now initialize all stackup items to the initial values, when exist
319  for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
320  {
321  bool found = false;
322  // Search for initial settings:
323  for( BOARD_STACKUP_ITEM* initial_item: m_list )
324  {
325  if( item->m_LayerId != UNDEFINED_LAYER )
326  {
327  if( item->m_LayerId == initial_item->m_LayerId )
328  {
329  *item = *initial_item;
330  found = true;
331  break;
332  }
333  }
334  else // dielectric layer: see m_DielectricLayerId for identification
335  {
336  if( item->m_DielectricLayerId == initial_item->m_DielectricLayerId )
337  {
338  *item = *initial_item;
339  found = true;
340  break;
341  }
342  }
343  }
344 
345  if( !found )
346  change = true;
347  }
348 
349  // Transfer other stackup settings from aSettings
350  BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
353  m_CastellatedPads = source_stackup.m_CastellatedPads;
354  m_EdgePlating = source_stackup.m_EdgePlating;
355  m_FinishType = source_stackup.m_FinishType;
356 
357  *this = stackup;
358 
359  return change;
360 }
361 
362 
364  int aActiveCopperLayersCount )
365 {
366  // Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
367  // Note: the m_TypeName string is made translatable using _HKI marker, but is not
368  // translated when building the stackup.
369  // It will be used as this in files, and can be translated only in dialog
370  // if aSettings == NULL, build a full stackup (with 32 copper layers)
371  LSET enabledLayer = aSettings ? aSettings->GetEnabledLayers() : StackupAllowedBrdLayers();
372  int copperLayerCount = aSettings ? aSettings->GetCopperLayerCount() : B_Cu+1;
373 
374  // We need to calculate a suitable dielectric layer thickness.
375  // If no settings, and if aActiveCopperLayersCount is given, use it
376  // (If no settings, and no aActiveCopperLayersCount, the full 32 layers are used)
377  int activeCuLayerCount = copperLayerCount;
378 
379  if( aSettings == nullptr && aActiveCopperLayersCount > 0 )
380  activeCuLayerCount = aActiveCopperLayersCount;
381 
382  int brd__thickness = aSettings ? aSettings->GetBoardThickness() : Millimeter2iu( 1.6 );
383  int diel_thickness = brd__thickness -
384  ( BOARD_STACKUP_ITEM::GetCopperDefaultThickness() * activeCuLayerCount );
385 
386  // Take in account the solder mask thickness:
387  int sm_count = ( enabledLayer & LSET( 2, F_Mask, B_Mask) ).count();
388  diel_thickness -= BOARD_STACKUP_ITEM::GetMaskDefaultThickness() * sm_count;
389 
390  int dielectric_idx = 0;
391 
392  // Add silk screen, solder mask and solder paste layers on top
393  if( enabledLayer[F_SilkS] )
394  {
396  item->m_LayerId = F_SilkS;
397  item->m_TypeName = _HKI( "Top Silk Screen" );
398  Add( item );
399  }
400 
401  if( enabledLayer[F_Paste] )
402  {
404  item->m_LayerId = F_Paste;
405  item->m_TypeName = _HKI( "Top Solder Paste" );
406  Add( item );
407  }
408 
409  if( enabledLayer[F_Mask] )
410  {
412  item->m_LayerId = F_Mask;
413  item->m_TypeName = _HKI( "Top Solder Mask" );
414  Add( item );
415  }
416 
417  // Add copper and dielectric layers
418  for( int ii = 0; ii < copperLayerCount; ii++ )
419  {
421  item->m_LayerId = ( PCB_LAYER_ID )ii;
422  item->m_TypeName = KEY_COPPER;
423  Add( item );
424 
425  if( ii == copperLayerCount-1 )
426  {
427  item->m_LayerId = B_Cu;
428  break;
429  }
430 
431  // Add the dielectric layer:
433  item->m_Thickness = diel_thickness;
434  item->m_DielectricLayerId = dielectric_idx + 1;
435 
436  // Display a dielectric default layer name:
437  if( (dielectric_idx & 1) == 0 )
438  {
439  item->m_TypeName = KEY_CORE;
440  item->m_Material = "FR4";
441  }
442  else
443  {
444  item->m_TypeName = KEY_PREPREG;
445  item->m_Material = "FR4";
446  }
447 
448  Add( item );
449  dielectric_idx++;
450  }
451 
452  // Add silk screen, solder mask and solder paste layers on bottom
453  if( enabledLayer[B_Mask] )
454  {
456  item->m_LayerId = B_Mask;
457  item->m_TypeName = _HKI( "Bottom Solder Mask" );
458  Add( item );
459  }
460 
461  if( enabledLayer[B_Paste] )
462  {
464  item->m_LayerId = B_Paste;
465  item->m_TypeName = _HKI( "Bottom Solder Paste" );
466  Add( item );
467  }
468 
469  if( enabledLayer[B_SilkS] )
470  {
472  item->m_LayerId = B_SilkS;
473  item->m_TypeName = _HKI( "Bottom Silk Screen" );
474  Add( item );
475  }
476 
477  // Transfer other stackup settings from aSettings
478  if( aSettings )
479  {
480  BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
483  m_CastellatedPads = source_stackup.m_CastellatedPads;
484  m_EdgePlating = source_stackup.m_EdgePlating;
485  m_FinishType = source_stackup.m_FinishType;
486  }
487 }
488 
489 
490 
492  BOARD* aBoard, int aNestLevel ) const
493 {
494  // Board stackup is the ordered list from top to bottom of
495  // physical layers and substrate used to build the board.
496  if( m_list.empty() )
497  return;
498 
499  aFormatter->Print( aNestLevel, "(stackup\n" );
500  int nest_level = aNestLevel+1;
501 
502  // Note:
503  // Unspecified parameters are not stored in file.
504  for( BOARD_STACKUP_ITEM* item: m_list )
505  {
506  wxString layer_name;
507 
508  if( item->m_LayerId == UNDEFINED_LAYER )
509  {
510  layer_name.Printf( "dielectric %d", item->m_DielectricLayerId );
511  }
512  else
513  layer_name = aBoard->GetLayerName( item->m_LayerId );
514 
515  aFormatter->Print( nest_level, "(layer %s (type %s)",
516  aFormatter->Quotew( layer_name ).c_str(),
517  aFormatter->Quotew( item->m_TypeName ).c_str() );
518 
519  if( item->IsThicknessEditable() )
520  {
521  if( item->m_Type == BS_ITEM_TYPE_DIELECTRIC && item->m_ThicknessLocked )
522  aFormatter->Print( 0, " (thickness %s locked)",
523  FormatInternalUnits( (int)item->m_Thickness ).c_str() );
524  else
525  aFormatter->Print( 0, " (thickness %s)",
526  FormatInternalUnits( (int)item->m_Thickness ).c_str() );
527  }
528 
529  if( item->HasMaterialValue() )
530  aFormatter->Print( 0, " (material %s)",
531  aFormatter->Quotew( item->m_Material ).c_str() );
532 
533  if( item->HasEpsilonRValue() && item->HasMaterialValue() )
534  aFormatter->Print( 0, " (epsilon_r %g)", item->m_EpsilonR );
535 
536  if( item->HasLossTangentValue() && item->HasMaterialValue() )
537  aFormatter->Print( 0, " (loss_tangent %s)",
538  Double2Str(item->m_LossTangent ).c_str() );
539 
540  if( item->IsColorEditable() && IsPrmSpecified( item->m_Color ) )
541  aFormatter->Print( 0, " (color %s)",
542  aFormatter->Quotew( item->m_Color ).c_str() );
543 
544  aFormatter->Print( 0, ")\n" );
545  }
546 
547  // Other infos about board, related to layers and other fabrication specifications
549  aFormatter->Print( nest_level, "(copper_finish %s)\n",
550  aFormatter->Quotew( m_FinishType ).c_str() );
551 
552  aFormatter->Print( nest_level, "(dielectric_constraints %s)\n",
553  m_HasDielectricConstrains ? "yes" : "no" );
554 
556  aFormatter->Print( nest_level, "(edge_connector %s)\n",
557  m_EdgeConnectorConstraints > 1 ? "bevelled": "yes" );
558 
559  if( m_CastellatedPads )
560  aFormatter->Print( nest_level, "(castellated_pads yes)\n" );
561 
562  if( m_EdgePlating )
563  aFormatter->Print( nest_level, "(edge_plating yes)\n" );
564 
565  aFormatter->Print( aNestLevel, ")\n" );
566 }
567 
568 
569 bool IsPrmSpecified( const wxString& aPrmValue )
570 {
571  // return true if the param value is specified:
572 
573  if( !aPrmValue.IsEmpty()
574  && ( aPrmValue.CmpNoCase( NotSpecifiedPrm() ) != 0 )
575  && aPrmValue != wxGetTranslation( NotSpecifiedPrm() ) )
576  return true;
577 
578  return false;
579 }
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, BOARD *aBoard, int aNestLevel) const
Writes the stackup info on board file.
int BuildBoardTicknessFromStackup() const
bool m_ThicknessLocked
the physical layer thickness in internal units
#define DEFAULT_EPSILON_R_SOLDERMASK
wxString m_LayerName
true if this stackup item must be taken in account, false to ignore it.
double m_EpsilonR
true for dielectric layers with a fixed thickness (for impendace controled purposes),...
BOARD_STACKUP_ITEM * GetStackupLayer(int aIndex)
std::vector< BOARD_STACKUP_ITEM * > & GetList()
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Function GetLayerName returns the name of a layer given by aLayer.
this class manage the layers needed to make a physical board they are solder mask,...
Implementation of conversion functions that require both schematic and board internal units.
wxString m_FinishType
The name of external copper finish.
bool m_EdgePlating
True if the edge board is plated.
bool IsPrmSpecified(const wxString &aPrmValue)
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specifed in job file: BS_EDGE_CONNECTOR...
bool m_CastellatedPads
True if castellated pads exist.
#define KEY_COPPER
int m_Thickness
mainly for silkscreen and solder mask
Class OUTPUTFORMATTER is an important interface (abstract class) used to output 8 bit text in a conve...
Definition: richio.h:327
wxString m_TypeName
name of layer as shown in layer manager. Usefull to create reports
std::string Double2Str(double aValue)
Helper function Double2Str to print a float number without using scientific notation and no trailing ...
Definition: base_units.cpp:63
wxString m_Material
type name of layer (copper, silk screen, core, prepreg ...)
BOARD_STACKUP & operator=(const BOARD_STACKUP &aOther)
static int GetCopperDefaultThickness()
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
BOARD_STACKUP & GetStackupDescriptor()
PCB_LAYER_ID
A quick note on layer IDs:
Class LSET is a set of PCB_LAYER_IDs.
bool HasEpsilonRValue()
the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask) and UNDEFINED_LAYER (-1) for dielectic la...
static LSET StackupAllowedBrdLayers()
wxString NotSpecifiedPrm()
PCB_LAYER_ID m_LayerId
For dielectric (and solder mask) the dielectric loss.
std::string Quotew(const wxString &aWrapee)
Definition: richio.cpp:472
std::vector< BOARD_STACKUP_ITEM * > m_list
#define KEY_PREPREG
bool m_HasThicknessConstrains
True if some layers (copper and/or dielectric) have specific thickness.
this class manage one layer needed to make a physical board it can be a solder mask,...
Some functions to handle hotkeys in KiCad.
static int GetMaskDefaultThickness()
#define DEFAULT_EPSILON_R_SILKSCREEN
wxString m_Color
the "layer" id for dielectric layers, from 1 (top) to 31 (bottom)
void RemoveAll()
Delete all items in list and clear the list.
BOARD_STACKUP_ITEM(BOARD_STACKUP_ITEM_TYPE aType)
double m_LossTangent
For dielectric (and solder mask) the dielectric constant.
Board layer functions and definitions.
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:161
LSET GetEnabledLayers() const
Function GetEnabledLayers returns a bit-mask of all the layers that are enabled.
BOARD_STACKUP_ITEM_TYPE
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Function Print formats and writes text to the output stream.
Definition: richio.cpp:404
#define KEY_CORE
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
BOARD_STACKUP_ITEM_TYPE m_Type
#define _HKI(x)
int GetCopperLayerCount() const
Function GetCopperLayerCount.
int m_DielectricLayerId
type of material (has meaning only for dielectric and solder mask)
std::string FormatInternalUnits(int aValue)
Function FormatInternalUnits converts aValue from internal units to a string appropriate for writing ...
Definition: base_units.cpp:478
void BuildDefaultStackupList(BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Class BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.