KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include "board_stackup.h"
23#include <base_units.h>
24#include <string_utils.h>
25#include <layer_ids.h>
27#include <board.h>
28#include <i18n_utility.h> // For _HKI definition
31#include <richio.h>
32#include <google/protobuf/any.pb.h>
33#include <api/board/board.pb.h>
34#include <api/api_enums.h>
35#include <api/api_utils.h>
36
37
39{
40 if( m_Material != aOther.m_Material ) return false;
41 if( m_Thickness != aOther.m_Thickness ) return false;
42 if( m_ThicknessLocked != aOther.m_ThicknessLocked ) return false;
43 if( m_EpsilonR != aOther.m_EpsilonR ) return false;
44 if( m_LossTangent != aOther.m_LossTangent ) return false;
45 if( m_SpecFreq != aOther.m_SpecFreq ) return false;
46 if( m_DielectricModel != aOther.m_DielectricModel ) return false;
47 if( m_Color != aOther.m_Color ) return false;
48
49 return true;
50}
51
52
54{
55 DIELECTRIC_PRMS item_prms;
56 m_DielectricPrmsList.emplace_back( item_prms );
58 m_Type = aType;
60 SetEnabled( true );
61
62 // Initialize parameters to a usual value for allowed types:
63 switch( m_Type )
64 {
68 break;
69
71 m_TypeName = KEY_CORE; // or prepreg
73 SetMaterial( wxT( "FR4" ) ); // or other dielectric name
74 SetLossTangent( 0.02 ); // for FR4
75 SetEpsilonR( 4.5 ); // for FR4
76 SetSpecFreq( 1e9 ); // for FR4
78 break;
79
81 m_TypeName = wxT( "solderpaste" );
82 break;
83
85 m_TypeName = wxT( "soldermask" );
87 SetMaterial( NotSpecifiedPrm() ); // or other solder mask material name
90 break;
91
93 m_TypeName = wxT( "silkscreen" );
95 SetMaterial( NotSpecifiedPrm() ); // or other silkscreen material name
97 break;
98
100 break;
101 }
102}
103
104
115
116
118{
119 if( m_Type != aOther.m_Type ) return false;
120 if( m_LayerName != aOther.m_LayerName ) return false;
121 if( m_TypeName != aOther.m_TypeName ) return false;
122 if( m_LayerId != aOther.m_LayerId ) return false;
123 if( m_DielectricLayerId != aOther.m_DielectricLayerId ) return false;
124 if( m_enabled != aOther.m_enabled ) return false;
125
126 if( !std::equal( std::begin( m_DielectricPrmsList ), std::end( m_DielectricPrmsList ),
127 std::begin( aOther.m_DielectricPrmsList ),
128 []( const DIELECTRIC_PRMS& aA, const DIELECTRIC_PRMS& aB )
129 {
130 return aA == aB;
131 } ) )
132 {
133 return false;
134 }
135
136 return true;
137}
138
139
140void BOARD_STACKUP_ITEM::AddDielectricPrms( int aDielectricPrmsIdx )
141{
142 // add a DIELECTRIC_PRMS item to m_DielectricPrmsList
143 DIELECTRIC_PRMS new_prms;
144
145 m_DielectricPrmsList.emplace( m_DielectricPrmsList.begin() + aDielectricPrmsIdx, new_prms );
146}
147
148
149void BOARD_STACKUP_ITEM::RemoveDielectricPrms( int aDielectricPrmsIdx )
150{
151 // Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList if possible
152
153 if( GetSublayersCount() < 2
154 || aDielectricPrmsIdx < 0
155 || aDielectricPrmsIdx >= GetSublayersCount() )
156 {
157 return;
158 }
159
160 m_DielectricPrmsList.erase( m_DielectricPrmsList.begin() + aDielectricPrmsIdx );
161}
162
163
164
166{
167 // A reasonable thickness for copper layers:
168 return pcbIUScale.mmToIU( 0.035 );
169}
170
171
173{
174 // A reasonable thickness for solder mask:
175 return pcbIUScale.mmToIU( 0.01 );
176}
177
178
179// Getters:
180wxString BOARD_STACKUP_ITEM::GetColor( int aDielectricSubLayer ) const
181{
182 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
183
184 return m_DielectricPrmsList[aDielectricSubLayer].m_Color;
185}
186
187int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) const
188{
189 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
190
191 return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness;
192}
193
194
195double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) const
196{
197 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
198
199 return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent;
200}
201
202
203double BOARD_STACKUP_ITEM::GetSpecFreq( int aDielectricSubLayer ) const
204{
205 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
206
207 return m_DielectricPrmsList[aDielectricSubLayer].m_SpecFreq;
208}
209
210
212{
213 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
214
215 return m_DielectricPrmsList[aDielectricSubLayer].m_DielectricModel;
216}
217
218
219double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) const
220{
221 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
222
223 return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR;
224}
225
226
227bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) const
228{
229 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
230
231 return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked;
232}
233
234
235wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) const
236{
237 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
238
239 return m_DielectricPrmsList[aDielectricSubLayer].m_Material;
240}
241
242
243// Setters:
244void BOARD_STACKUP_ITEM::SetColor( const wxString& aColorName , int aDielectricSubLayer )
245{
246 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
247
248 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
249 m_DielectricPrmsList[aDielectricSubLayer].m_Color = aColorName;
250}
251
252
253void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer )
254{
255 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
256
257 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
258 m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness;
259}
260
261
262void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer )
263{
264 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
265
266 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
267 m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg;
268}
269
270
271void BOARD_STACKUP_ITEM::SetSpecFreq( const double aSpecFreq, const int aDielectricSubLayer )
272{
273 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
274
275 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
276 m_DielectricPrmsList[aDielectricSubLayer].m_SpecFreq = aSpecFreq;
277}
278
279
280void BOARD_STACKUP_ITEM::SetDielectricModel( DIELECTRIC_MODEL aModel, int aDielectricSubLayer )
281{
282 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
283
284 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
285 m_DielectricPrmsList[aDielectricSubLayer].m_DielectricModel = aModel;
286}
287
288
289void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer )
290{
291 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
292
293 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
294 m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon;
295}
296
297
298void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer )
299{
300 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
301
302 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
303 m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked;
304}
305
306
307void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer )
308{
309 wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
310
311 if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
312 m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName;
313}
314
315
321
322
328
329
334
335
336bool BOARD_STACKUP_ITEM::HasMaterialValue( int aDielectricSubLayer ) const
337{
338 // return true if the material is specified
339 return IsMaterialEditable() && IsPrmSpecified( GetMaterial( aDielectricSubLayer ) );
340}
341
342
349
350
357
358
365
366
367wxString BOARD_STACKUP_ITEM::FormatEpsilonR( int aDielectricSubLayer ) const
368{
369 // return a wxString to print/display Epsilon R
370 // note: we do not want scientific notation
371 wxString txt = UIDouble2Str( GetEpsilonR( aDielectricSubLayer ) );
372 return txt;
373}
374
375
376wxString BOARD_STACKUP_ITEM::FormatLossTangent( int aDielectricSubLayer ) const
377{
378 // return a wxString to print/display Loss Tangent
379 // note: we do not want scientific notation
380 wxString txt = UIDouble2Str( GetLossTangent( aDielectricSubLayer ) );
381 return txt;
382}
383
384
386{
387 // return a wxString to print/display a dielectric name
388 wxString lname;
389 lname.Printf( _( "Dielectric %d" ), GetDielectricLayerId() );
390
391 return lname;
392}
393
394
396{
397 m_HasDielectricConstrains = false; // True if some dielectric layers have constrains
398 // (Loss tg and Epison R)
399 m_HasThicknessConstrains = false; // True if some dielectric or copper layers have constrains
401 m_EdgePlating = false; // True if edge board is plated
402 m_FinishType = wxT( "None" ); // undefined finish type
403}
404
405
407{
412 m_FinishType = aOther.m_FinishType;
413
414 // All items in aOther.m_list have to be duplicated, because aOther.m_list
415 // manage pointers to these items
416 for( BOARD_STACKUP_ITEM* item : aOther.m_list )
417 {
418 BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
419 Add( dup_item );
420 }
421}
422
423
425{
430 m_FinishType = aOther.m_FinishType;
431
432 RemoveAll();
433
434 // All items in aOther.m_list have to be duplicated, because aOther.m_list
435 // manage pointers to these items
436 for( BOARD_STACKUP_ITEM* item : aOther.m_list )
437 {
438 BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
439 Add( dup_item );
440 }
441
442 return *this;
443}
444
445
446bool BOARD_STACKUP::operator==( const BOARD_STACKUP& aOther ) const
447{
448 if( m_HasDielectricConstrains != aOther.m_HasDielectricConstrains ) return false;
449 if( m_HasThicknessConstrains != aOther.m_HasThicknessConstrains ) return false;
450 if( m_EdgeConnectorConstraints != aOther.m_EdgeConnectorConstraints ) return false;
451 if( m_EdgePlating != aOther.m_EdgePlating ) return false;
452 if( m_FinishType != aOther.m_FinishType ) return false;
453
454 if( m_list.size() != aOther.m_list.size() )
455 return false;
456
457 if( !std::equal( std::begin( m_list ), std::end( m_list ), std::begin( aOther.m_list ),
458 []( const BOARD_STACKUP_ITEM* aA, const BOARD_STACKUP_ITEM* aB )
459 {
460 return *aA == *aB;
461 } ) )
462 {
463 return false;
464 }
465
466 return true;
467}
468
469
470void BOARD_STACKUP::Serialize( kiapi::board::BoardStackup& stackup ) const
471{
472 using namespace kiapi::board;
473
474 for( const BOARD_STACKUP_ITEM* item : m_list )
475 {
476 BoardStackupLayer* layer = stackup.mutable_layers()->Add();
477
478 layer->mutable_thickness()->set_value_nm( item->GetThickness() );
479 layer->set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( item->GetBrdLayerId() ) );
480 layer->set_enabled( item->IsEnabled() );
481 layer->set_type( ToProtoEnum<BOARD_STACKUP_ITEM_TYPE, BoardStackupLayerType>( item->GetType() ) );
482
483 if( item->IsColorEditable() )
484 {
485 wxString colorStr = item->GetColor( 0 );
486
487 if( IsPrmSpecified( colorStr ) )
488 {
489 KIGFX::COLOR4D color( colorStr );
490 kiapi::common::PackColor( *layer->mutable_color(), color );
491 }
492 }
493
494 switch( item->GetType() )
495 {
497 {
498 layer->set_material_name( "copper" );
499 // (no copper params yet...)
500 break;
501 }
502
504 {
505 BoardStackupDielectricLayer* dielectric = layer->mutable_dielectric();
506
507 const wxString& typeName = item->GetTypeName();
508
509 if( typeName == KEY_CORE )
510 dielectric->set_type( BoardStackupDielectricType::BSDT_CORE );
511 else if( typeName == KEY_PREPREG )
512 dielectric->set_type( BoardStackupDielectricType::BSDT_PREPREG );
513 else
514 dielectric->set_type( BoardStackupDielectricType::BSDT_NONE );
515
516 for( int i = 0; i < item->GetSublayersCount(); ++i )
517 {
518 BoardStackupDielectricProperties* props = dielectric->mutable_layer()->Add();
519 props->set_epsilon_r( item->GetEpsilonR( i ) );
520 props->set_loss_tangent( item->GetLossTangent( i ) );
521 props->set_material_name( item->GetMaterial( i ).ToUTF8() );
522 props->mutable_thickness()->set_value_nm( item->GetThickness( i ) );
523
524 props->set_thickness_locked( item->IsThicknessLocked( i ) );
525
526 if( item->GetSpecFreq( i ) != 0.0 )
527 props->set_spec_frequency( item->GetSpecFreq( i ) );
528
529 props->set_dielectric_model(
530 ToProtoEnum<DIELECTRIC_MODEL, DielectricModel>( item->GetDielectricModel( i ) ) );
531 }
532
533 break;
534 }
535
537 {
538 BoardStackupSoldermaskLayer* soldermask = layer->mutable_soldermask();
539 soldermask->set_epsilon_r( item->GetEpsilonR( 0 ) );
540 soldermask->set_loss_tangent( item->GetLossTangent( 0 ) );
541 soldermask->set_material_name( item->GetMaterial( 0 ).ToUTF8() );
542 soldermask->mutable_thickness()->set_value_nm( item->GetThickness( 0 ) );
543 break;
544 }
545
547 {
548 BoardStackupSilkscreenLayer* silkscreen = layer->mutable_silkscreen();
549 silkscreen->set_material_name( item->GetMaterial( 0 ).ToUTF8() );
550 break;
551 }
552
553 default:
554 break;
555 }
556 }
557
558 stackup.mutable_finish()->set_type_name( m_FinishType.ToUTF8() );
559 stackup.mutable_impedance()->set_is_controlled( m_HasDielectricConstrains );
560
561 BoardEdgeSettings* edge = stackup.mutable_edge();
562 edge->mutable_connector()->set_type(
564 edge->mutable_plating()->set_has_edge_plating( m_EdgePlating );
565
566}
567
568
569void BOARD_STACKUP::Serialize( google::protobuf::Any& aContainer ) const
570{
571 kiapi::board::BoardStackup stackup;
572 Serialize( stackup );
573 aContainer.PackFrom( stackup );
574}
575
576
577bool BOARD_STACKUP::Deserialize( const google::protobuf::Any& aContainer )
578{
579 // Read-only for now
580 kiapi::board::BoardStackup stackup;
581
582 if( !aContainer.UnpackTo( &stackup ) )
583 return false;
584
585 return Deserialize( stackup );
586}
587
588
589bool BOARD_STACKUP::Deserialize( const kiapi::board::BoardStackup& aInput )
590{
591 // Read-only for now
592 return false;
593}
594
595
597{
598 for( BOARD_STACKUP_ITEM* item : m_list )
599 delete item;
600
601 m_list.clear();
602}
603
604
606{
607 if( aIndex < 0 || aIndex >= GetCount() )
608 return nullptr;
609
610 return GetList()[aIndex];
611}
612
613
615{
616 // return the board thickness from the thickness of BOARD_STACKUP_ITEM list
617 int thickness = 0;
618
619 for( BOARD_STACKUP_ITEM* item : m_list )
620 {
621 if( item->IsThicknessEditable() && item->IsEnabled() )
622 {
623 thickness += item->GetThickness();
624
625 // dielectric layers can have more than one main layer
626 // add thickness of all sublayers
627 for( int idx = 1; idx < item->GetSublayersCount(); idx++ )
628 {
629 thickness += item->GetThickness( idx );
630 }
631 }
632 }
633
634 return thickness;
635}
636
637
639{
640 bool change = false;
641 // Build the suitable stackup:
642 BOARD_STACKUP stackup;
643 stackup.BuildDefaultStackupList( aSettings );
644
645 // First, find removed layers:
646 for( BOARD_STACKUP_ITEM* curr_item: m_list )
647 {
648 bool found = false;
649
650 for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
651 {
652 if( curr_item->GetBrdLayerId() != UNDEFINED_LAYER )
653 {
654 if( item->GetBrdLayerId() == curr_item->GetBrdLayerId() )
655 {
656 found = true;
657 break;
658 }
659 }
660 else // curr_item = dielectric layer
661 {
662 if( item->GetBrdLayerId() != UNDEFINED_LAYER )
663 continue;
664
665 if( item->GetDielectricLayerId() == curr_item->GetDielectricLayerId() )
666 {
667 found = true;
668 break;
669 }
670 }
671 }
672
673 if( !found ) // a layer was removed: a change is found
674 {
675 change = true;
676 break;
677 }
678 }
679
680 // Now initialize all stackup items to the initial values, when exist
681 for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
682 {
683 bool found = false;
684 // Search for initial settings:
685 for( const BOARD_STACKUP_ITEM* initial_item : m_list )
686 {
687 if( item->GetBrdLayerId() != UNDEFINED_LAYER )
688 {
689 if( item->GetBrdLayerId() == initial_item->GetBrdLayerId() )
690 {
691 *item = *initial_item;
692 found = true;
693 break;
694 }
695 }
696 else // dielectric layer: see m_DielectricLayerId for identification
697 {
698 // Compare dielectric layer with dielectric layer
699 if( initial_item->GetBrdLayerId() != UNDEFINED_LAYER )
700 continue;
701
702 if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() )
703 {
704 *item = *initial_item;
705 found = true;
706 break;
707 }
708 }
709 }
710
711 if( !found )
712 {
713 change = true;
714 }
715 }
716
717 // Transfer layer settings:
718 *this = stackup;
719
720 // Transfer other stackup settings from aSettings
721 const BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
724 m_EdgePlating = source_stackup.m_EdgePlating;
725 m_FinishType = source_stackup.m_FinishType;
726
727 return change;
728}
729
730
732 int aActiveCopperLayersCount )
733{
734 // Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
735 // Note: the m_TypeName string is made translatable using _HKI marker, but is not
736 // translated when building the stackup.
737 // It will be used as this in files, and can be translated only in dialog
738 // if aSettings == NULL, build a full stackup (with 32 copper layers)
739 LSET enabledLayer = aSettings ? aSettings->GetEnabledLayers() : StackupAllowedBrdLayers();
740 int copperLayerCount = aSettings ? aSettings->GetCopperLayerCount() : 32;
741
742 // We need to calculate a suitable dielectric layer thickness.
743 // If no settings, and if aActiveCopperLayersCount is given, use it
744 // (If no settings, and no aActiveCopperLayersCount, the full 32 layers are used)
745 int activeCuLayerCount = copperLayerCount;
746
747 if( aSettings == nullptr && aActiveCopperLayersCount > 0 )
748 activeCuLayerCount = aActiveCopperLayersCount;
749
750 int brd__thickness = aSettings ? aSettings->GetBoardThickness() : pcbIUScale.mmToIU( 1.6 );
751 int diel_thickness = brd__thickness -
752 ( BOARD_STACKUP_ITEM::GetCopperDefaultThickness() * activeCuLayerCount );
753
754 // Take in account the solder mask thickness:
755 int sm_count = ( enabledLayer & LSET( { F_Mask, B_Mask } ) ).count();
756 diel_thickness -= BOARD_STACKUP_ITEM::GetMaskDefaultThickness() * sm_count;
757 diel_thickness /= std::max( 1, activeCuLayerCount - 1 );
758
759 int dielectric_idx = 0;
760
761 // Add silk screen, solder mask and solder paste layers on top
762 if( enabledLayer[F_SilkS] )
763 {
765 item->SetBrdLayerId( F_SilkS );
766 item->SetTypeName( _HKI( "Top Silk Screen" ) );
767 Add( item );
768 }
769
770 if( enabledLayer[F_Paste] )
771 {
773 item->SetBrdLayerId( F_Paste );
774 item->SetTypeName( _HKI( "Top Solder Paste" ) );
775 Add( item );
776 }
777
778 if( enabledLayer[F_Mask] )
779 {
781 item->SetBrdLayerId( F_Mask );
782 item->SetTypeName( _HKI( "Top Solder Mask" ) );
783 Add( item );
784 }
785
786 // Add copper and dielectric layers
787 for( PCB_LAYER_ID layer : enabledLayer.CuStack() )
788 {
790 item->SetBrdLayerId( layer );
791 item->SetTypeName( KEY_COPPER );
792 Add( item );
793
794 if( layer == B_Cu )
795 break;
796
797 // Add the dielectric layer:
799 item->SetThickness( diel_thickness );
800 item->SetDielectricLayerId( dielectric_idx + 1 );
801
802 // Display a dielectric default layer name:
803 if( (dielectric_idx & 1) == 0 )
804 {
805 item->SetTypeName( KEY_CORE );
806 item->SetMaterial( wxT( "FR4" ) );
807 }
808 else
809 {
810 item->SetTypeName( KEY_PREPREG );
811 item->SetMaterial( wxT( "FR4" ) );
812 }
813
814 Add( item );
815 dielectric_idx++;
816 }
817
818 // Add silk screen, solder mask and solder paste layers on bottom
819 if( enabledLayer[B_Mask] )
820 {
822 item->SetBrdLayerId( B_Mask );
823 item->SetTypeName( _HKI( "Bottom Solder Mask" ) );
824 Add( item );
825 }
826
827 if( enabledLayer[B_Paste] )
828 {
830 item->SetBrdLayerId( B_Paste );
831 item->SetTypeName( _HKI( "Bottom Solder Paste" ) );
832 Add( item );
833 }
834
835 if( enabledLayer[B_SilkS] )
836 {
838 item->SetBrdLayerId( B_SilkS );
839 item->SetTypeName( _HKI( "Bottom Silk Screen" ) );
840 Add( item );
841 }
842
843 // Transfer other stackup settings from aSettings
844 if( aSettings )
845 {
846 const BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
849 m_EdgePlating = source_stackup.m_EdgePlating;
850 m_FinishType = source_stackup.m_FinishType;
851 }
852}
853
854
855void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter, const BOARD* aBoard ) const
856{
857 // Board stackup is the ordered list from top to bottom of
858 // physical layers and substrate used to build the board.
859 if( m_list.empty() )
860 return;
861
862 aFormatter->Print( "(stackup" );
863
864 // Note:
865 // Unspecified parameters are not stored in file.
866 for( BOARD_STACKUP_ITEM* item: m_list )
867 {
868 wxString layer_name;
869
870 if( item->GetBrdLayerId() == UNDEFINED_LAYER )
871 layer_name.Printf( wxT( "dielectric %d" ), item->GetDielectricLayerId() );
872 else
873 layer_name = LSET::Name( item->GetBrdLayerId() );
874
875 aFormatter->Print( "(layer %s (type %s)",
876 aFormatter->Quotew( layer_name ).c_str(),
877 aFormatter->Quotew( item->GetTypeName() ).c_str() );
878
879 // Output other parameters (in sub layer list there is at least one item)
880 for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
881 {
882 if( idx ) // not for the main (first) layer.
883 aFormatter->Print( " addsublayer" );
884
885 if( item->IsColorEditable() && IsPrmSpecified( item->GetColor( idx ) ) )
886 {
887 aFormatter->Print( "(color %s)",
888 aFormatter->Quotew( item->GetColor( idx ) ).c_str() );
889 }
890
891 if( item->IsThicknessEditable() )
892 {
893 aFormatter->Print( "(thickness %s",
894 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, item->GetThickness( idx ) ).c_str() );
895
896 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
897 aFormatter->Print( " locked" );
898
899 aFormatter->Print( ")" );
900 }
901
902 if( item->HasMaterialValue( idx ) )
903 {
904 aFormatter->Print( "(material %s)",
905 aFormatter->Quotew( item->GetMaterial( idx ) ).c_str() );
906 }
907
908 if( item->HasEpsilonRValue() && item->HasMaterialValue( idx ) )
909 aFormatter->Print( "(epsilon_r %s)", FormatDouble2Str( item->GetEpsilonR( idx ) ).c_str() );
910
911 if( item->HasLossTangentValue() && item->HasMaterialValue( idx ) )
912 {
913 aFormatter->Print( "(loss_tangent %s)",
914 FormatDouble2Str( item->GetLossTangent( idx ) ).c_str() );
915 }
916
917 if( item->HasSpecFreqValue() && item->HasMaterialValue( idx ) )
918 {
919 aFormatter->Print( "(spec_frequency %s)", FormatDouble2Str( item->GetSpecFreq( idx ) ).c_str() );
920
921 wxString modelToken = wxT( "constant" );
922
923 if( item->GetDielectricModel( idx ) == DIELECTRIC_MODEL::DJORDJEVIC_SARKAR )
924 modelToken = wxT( "djordjevic_sarkar" );
925
926 aFormatter->Print( "(dielectric_model %s)", modelToken.ToStdString().c_str() );
927 }
928 }
929
930 aFormatter->Print( ")" );
931 }
932
933 // Other infos about board, related to layers and other fabrication specifications
935 aFormatter->Print( "(copper_finish %s)", aFormatter->Quotew( m_FinishType ).c_str() );
936
937 KICAD_FORMAT::FormatBool( aFormatter, "dielectric_constraints", m_HasDielectricConstrains );
938
940 {
941 aFormatter->Print( "(edge_connector %s)",
942 m_EdgeConnectorConstraints > 1 ? "bevelled": "yes" );
943 }
944
945 if( m_EdgePlating )
946 KICAD_FORMAT::FormatBool( aFormatter, "edge_plating", true );
947
948 aFormatter->Print( ")" );
949}
950
951
952int BOARD_STACKUP::GetLayerDistance( PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer ) const
953{
954 wxASSERT( IsCopperLayer( aFirstLayer ) && IsCopperLayer( aSecondLayer ) );
955
956 if( aFirstLayer == aSecondLayer )
957 return 0;
958
959 // B_Cu is always the last copper layer but doesn't have the last numerical value
960 if( aSecondLayer != B_Cu && ( aSecondLayer < aFirstLayer || aFirstLayer == B_Cu ) )
961 std::swap( aFirstLayer, aSecondLayer );
962
963 int total = 0;
964 bool start = false;
965 bool half = false;
966
967 for( const BOARD_STACKUP_ITEM* item : m_list )
968 {
969 // Will be UNDEFINED_LAYER for dielectrics
970 const PCB_LAYER_ID layer = item->GetBrdLayerId();
971
972 if( layer != UNDEFINED_LAYER && !IsCopperLayer( layer ) )
973 continue; // Silk/mask layer
974
975 // Reached the start copper layer? Start counting the next dielectric after it
976 if( !start && ( layer != UNDEFINED_LAYER && layer == aFirstLayer ) )
977 {
978 start = true;
979
980 // Only count half of each internal copper layer
981 if( aFirstLayer != F_Cu && aFirstLayer != B_Cu )
982 half = true;
983 }
984 else if( !start )
985 continue;
986
987 // Reached the stop copper layer? we're done
988 if( start && ( layer != UNDEFINED_LAYER && layer == aSecondLayer ) )
989 {
990 // Only count half of each internal copper layer
991 if( aSecondLayer != F_Cu && aSecondLayer != B_Cu )
992 half = true;
993 }
994
995 for( int sublayer = 0; sublayer < item->GetSublayersCount(); sublayer++ )
996 {
997 const int subThickness = item->GetThickness( sublayer );
998 total += half ? ( subThickness / 2 ) : subThickness;
999 }
1000
1001 half = false;
1002
1003 if( layer != UNDEFINED_LAYER && layer == aSecondLayer )
1004 break;
1005 }
1006
1007 return total;
1008}
1009
1010
1011bool IsPrmSpecified( const wxString& aPrmValue )
1012{
1013 // return true if the param value is specified:
1014
1015 if( !aPrmValue.IsEmpty()
1016 && ( aPrmValue.CmpNoCase( NotSpecifiedPrm() ) != 0 )
1017 && aPrmValue != wxGetTranslation( NotSpecifiedPrm() ) )
1018 return true;
1019
1020 return false;
1021}
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_EDGE_CONNECTOR_NONE
BOARD_STACKUP_ITEM_TYPE
@ BS_ITEM_TYPE_UNDEFINED
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_SILKSCREEN
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERPASTE
@ BS_ITEM_TYPE_SOLDERMASK
Container for design settings for a BOARD object.
const LSET & GetEnabledLayers() const
Return a bit-mask of all the layers that are enabled.
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
Manage one layer needed to make a physical board.
void AddDielectricPrms(int aDielectricPrmsIdx)
Add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList all values are set to default.
DIELECTRIC_MODEL GetDielectricModel(int aDielectricSubLayer=0) const
PCB_LAYER_ID m_LayerId
type name of layer (copper, silk screen, core, prepreg ...)
int GetSublayersCount() const
void SetDielectricLayerId(int aLayerId)
bool HasSpecFreqValue() const
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString GetColor(int aDielectricSubLayer=0) const
bool HasEpsilonRValue() const
void SetThickness(int aThickness, int aDielectricSubLayer=0)
bool IsMaterialEditable() const
BOARD_STACKUP_ITEM_TYPE m_Type
void SetDielectricModel(DIELECTRIC_MODEL aModel, int aDielectricSubLayer=0)
bool HasMaterialValue(int aDielectricSubLayer=0) const
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
wxString FormatDielectricLayerName() const
void SetSpecFreq(double aSpecFreq, int aDielectricSubLayer=0)
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
BOARD_STACKUP_ITEM(BOARD_STACKUP_ITEM_TYPE aType)
bool HasLossTangentValue() const
bool IsThicknessEditable() const
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
int GetThickness(int aDielectricSubLayer=0) const
void SetEnabled(bool aEnable)
std::vector< DIELECTRIC_PRMS > m_DielectricPrmsList
the "layer" id for dielectric layers, from 1 (top) to 31 (bottom) (only 31 dielectric layers for 32 c...
static int GetMaskDefaultThickness()
wxString GetMaterial(int aDielectricSubLayer=0) const
wxString m_TypeName
name of layer as shown in layer manager. Useful to create reports
void SetBrdLayerId(PCB_LAYER_ID aBrdLayerId)
void SetTypeName(const wxString &aName)
bool IsThicknessLocked(int aDielectricSubLayer=0) const
wxString FormatEpsilonR(int aDielectricSubLayer=0) const
int m_DielectricLayerId
the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask) and UNDEFINED_LAYER (-1) for dielectric l...
void SetColor(const wxString &aColorName, int aDielectricSubLayer=0)
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
double GetSpecFreq(int aDielectricSubLayer=0) const
void RemoveDielectricPrms(int aDielectricPrmsIdx)
Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList.
int GetDielectricLayerId() const
bool operator==(const BOARD_STACKUP_ITEM &aOther) const
bool IsColorEditable() const
wxString FormatLossTangent(int aDielectricSubLayer=0) const
double GetLossTangent(int aDielectricSubLayer=0) const
static int GetCopperDefaultThickness()
void RemoveAll()
Delete all items in list and clear the list.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
static LSET StackupAllowedBrdLayers()
int GetCount() const
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
bool operator==(const BOARD_STACKUP &aOther) const
int BuildBoardThicknessFromStackup() const
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
BOARD_STACKUP & operator=(const BOARD_STACKUP &aOther)
BOARD_STACKUP_ITEM * GetStackupLayer(int aIndex)
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
std::vector< BOARD_STACKUP_ITEM * > m_list
bool m_EdgePlating
True if the edge board is plated.
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard) const
Write the stackup info on board file.
bool m_HasThicknessConstrains
True if some layers (copper and/or dielectric) have specific thickness.
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
A helper class to manage a dielectric layer set of parameters.
double m_EpsilonR
true for dielectric layers with a fixed thickness (for impedance controlled purposes),...
double m_SpecFreq
For dielectric (and solder mask) the dielectric loss.
int m_Thickness
type of material (for dielectric and solder mask)
wxString m_Material
bool operator==(const DIELECTRIC_PRMS &aOther) const
wxString m_Color
For dielectric the dielectric frequency correction model.
DIELECTRIC_MODEL m_DielectricModel
For dielectric the frequency (Hz) at which E_R is specified.
bool m_ThicknessLocked
the physical layer thickness in internal units
double m_LossTangent
For dielectric (and solder mask) the dielectric constant.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:259
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
An interface used to output 8 bit text in a convenient way.
Definition richio.h:294
std::string Quotew(const wxString &aWrapee) const
Definition richio.cpp:505
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:432
#define _(s)
Some functions to handle hotkeys in KiCad.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_Paste
Definition layer_ids.h:100
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_SilkS
Definition layer_ids.h:96
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
Writes a boolean to the formatter, in the style (aKey [yes|no])
KICOMMON_API void PackColor(types::Color &aOutput, const KIGFX::COLOR4D &aInput)
#define _HKI(x)
Definition page_info.cpp:40
wxString NotSpecifiedPrm()
#define KEY_PREPREG
#define KEY_COPPER
#define KEY_CORE
#define DEFAULT_EPSILON_R_SILKSCREEN
#define DEFAULT_EPSILON_R_SOLDERMASK
std::string UIDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
DIELECTRIC_MODEL
Frequency-domain model used for the substrate dielectric properties.