ADD: layer stack manager: support of multilayer dielectric between 2 copper layers

This feature is useful for advanced microwave applications.
This is the last missing feature in the board stackup management.
This commit is contained in:
jean-pierre charras 2019-11-14 16:26:05 +01:00
parent 39ef514ff9
commit 544867cf2a
8 changed files with 1092 additions and 757 deletions

View File

@ -25,6 +25,7 @@
# These are the keywords for the Pcbnew s-expression file format. # These are the keywords for the Pcbnew s-expression file format.
add_net add_net
addsublayer
allowed allowed
anchor anchor
angle angle

View File

@ -42,58 +42,76 @@ wxString BuildStackupReport( BOARD_STACKUP& aStackup, EDA_UNITS_T aUnits )
wxString report; wxString report;
wxString txt; wxString txt;
int row = 0;
LOCALE_IO toggle; // toggles on the C locale to write floating values, then off. LOCALE_IO toggle; // toggles on the C locale to write floating values, then off.
for( const auto item : aStackup.GetList() ) for( const BOARD_STACKUP_ITEM* item : aStackup.GetList() )
{ {
// Skip stackup items useless for the current board // Skip stackup items useless for the current board
if( !item->IsEnabled() ) if( !item->IsEnabled() )
{
row++;
continue; continue;
}
txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(), item->GetTypeName() ); if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
wxString sublayer_text;
if( item->GetSublayersCount() )
sublayer_text.Printf( "\n sublayer \"1/%d\"", item->GetSublayersCount() );
txt.Printf( "layer \"%s\" type \"%s\"%s",
item->FormatDielectricLayerName(),
item->GetTypeName(), sublayer_text );
}
else
txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(),
item->GetTypeName() );
report << txt; report << txt;
if( item->IsMaterialEditable() )
{
txt.Printf( " Material \"%s\"", item->GetMaterial() );
report << txt;
}
if( item->HasEpsilonRValue() )
{
txt.Printf( " EpsilonR %s", item->FormatEpsilonR() );
report << txt;
}
if( item->HasLossTangentValue() )
{
txt.Printf( " LossTg %s", item->FormatLossTangent() );
report << txt;
}
if( item->IsThicknessEditable() )
{
txt.Printf( " Thickness %s",
StringFromValue( aUnits, item->GetThickness(), true, true ) );
report << txt;
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() )
{
txt.Printf( " Locked" );
report << txt;
}
}
if( item->IsColorEditable() ) if( item->IsColorEditable() )
{ {
txt.Printf( " Color \"%s\"", item->GetColor() ); txt.Printf( " Color \"%s\"", item->GetColor() );
report << txt; report << txt;
} }
row++;
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
{
if( idx ) // not printed for the main (first) layer.
{
txt.Printf( "\n sublayer \"%d/%d\"", idx+1, item->GetSublayersCount() );
report << txt;
}
if( item->IsThicknessEditable() )
{
txt.Printf( " Thickness %s",
StringFromValue( aUnits, item->GetThickness( idx ), true, true ) );
report << txt;
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
{
txt.Printf( " Locked" );
report << txt;
}
}
if( item->IsMaterialEditable() )
{
txt.Printf( " Material \"%s\"", item->GetMaterial( idx ) );
report << txt;
}
if( item->HasEpsilonRValue() )
{
txt.Printf( " EpsilonR %s", item->FormatEpsilonR( idx ) );
report << txt;
}
if( item->HasLossTangentValue() )
{
txt.Printf( " LossTg %s", item->FormatLossTangent( idx ) );
report << txt;
}
}
report << '\n'; report << '\n';
} }

View File

@ -35,6 +35,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
m_DielectricPrmsList.emplace_back( item_prms ); m_DielectricPrmsList.emplace_back( item_prms );
m_LayerId = UNDEFINED_LAYER; m_LayerId = UNDEFINED_LAYER;
m_Type = aType; m_Type = aType;
SetDielectricLayerId( 1 );
SetEnabled( true ); SetEnabled( true );
// Initialize parameters to a usual value for allowed types: // Initialize parameters to a usual value for allowed types:
@ -48,7 +49,6 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
case BS_ITEM_TYPE_DIELECTRIC: case BS_ITEM_TYPE_DIELECTRIC:
m_TypeName = KEY_CORE; // or prepreg m_TypeName = KEY_CORE; // or prepreg
SetMaterial( "FR4" ); // or other dielectric name SetMaterial( "FR4" ); // or other dielectric name
SetDielectricLayerId( 1 );
SetLossTangent( 0.02 ); // for FR4 SetLossTangent( 0.02 ); // for FR4
SetEpsilonR( 4.5 ); // for FR4 SetEpsilonR( 4.5 ); // for FR4
break; break;
@ -81,6 +81,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther ) BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther )
{ {
m_LayerId = aOther.m_LayerId; m_LayerId = aOther.m_LayerId;
m_DielectricLayerId = aOther.m_DielectricLayerId;
m_Type = aOther.m_Type; m_Type = aOther.m_Type;
m_enabled = aOther.m_enabled; m_enabled = aOther.m_enabled;
m_DielectricPrmsList = aOther.m_DielectricPrmsList; m_DielectricPrmsList = aOther.m_DielectricPrmsList;
@ -90,6 +91,28 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther )
} }
void BOARD_STACKUP_ITEM::AddDielectricPrms( int aDielectricPrmsIdx )
{
// add a DIELECTRIC_PRMS item to m_DielectricPrmsList
DIELECTRIC_PRMS new_prms;
m_DielectricPrmsList.emplace( m_DielectricPrmsList.begin() + aDielectricPrmsIdx,
new_prms );
}
void BOARD_STACKUP_ITEM::RemoveDielectricPrms( int aDielectricPrmsIdx )
{
// Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList if possible
if( GetSublayersCount() < 2 || aDielectricPrmsIdx < 0 || aDielectricPrmsIdx >= GetSublayersCount() )
return;
m_DielectricPrmsList.erase( m_DielectricPrmsList.begin() + aDielectricPrmsIdx );
}
int BOARD_STACKUP_ITEM::GetCopperDefaultThickness() int BOARD_STACKUP_ITEM::GetCopperDefaultThickness()
{ {
// A reasonable thickness for copper layers: // A reasonable thickness for copper layers:
@ -104,38 +127,42 @@ int BOARD_STACKUP_ITEM::GetMaskDefaultThickness()
} }
// Getters: // Getters:
int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) const
{ {
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness; return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness;
} }
double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) const
{ {
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent; return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent;
} }
double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) const
{ {
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR; return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR;
} }
bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) const
{ {
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked; return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked;
} }
int BOARD_STACKUP_ITEM::GetDielectricLayerId( int aDielectricSubLayer ) wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) const
{ {
return m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
}
wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer )
{
return m_DielectricPrmsList[aDielectricSubLayer].m_Material; return m_DielectricPrmsList[aDielectricSubLayer].m_Material;
} }
@ -143,41 +170,50 @@ wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer )
// Setters: // Setters:
void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer ) void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer )
{ {
m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness;
} }
void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer ) void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer )
{ {
m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg;
} }
void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer ) void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer )
{ {
m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon;
} }
void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer ) void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer )
{ {
m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
}
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
void BOARD_STACKUP_ITEM::SetDielectricLayerId( int aLayerId, int aDielectricSubLayer ) m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked;
{
m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId = aLayerId;
} }
void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer ) void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer )
{ {
m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName; wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName;
} }
bool BOARD_STACKUP_ITEM::HasEpsilonRValue() bool BOARD_STACKUP_ITEM::HasEpsilonRValue() const
{ {
return m_Type == BS_ITEM_TYPE_DIELECTRIC return m_Type == BS_ITEM_TYPE_DIELECTRIC
|| m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SOLDERMASK
@ -186,23 +222,21 @@ bool BOARD_STACKUP_ITEM::HasEpsilonRValue()
}; };
bool BOARD_STACKUP_ITEM::HasLossTangentValue() bool BOARD_STACKUP_ITEM::HasLossTangentValue() const
{ {
return m_Type == BS_ITEM_TYPE_DIELECTRIC return m_Type == BS_ITEM_TYPE_DIELECTRIC
|| m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SOLDERMASK;
//|| m_Type == BS_ITEM_TYPE_SILKSCREEN
;
}; };
bool BOARD_STACKUP_ITEM::HasMaterialValue() bool BOARD_STACKUP_ITEM::HasMaterialValue( int aDielectricSubLayer ) const
{ {
// return true if the material is specified // return true if the material is specified
return IsMaterialEditable() && IsPrmSpecified( GetMaterial() ); return IsMaterialEditable() && IsPrmSpecified( GetMaterial( aDielectricSubLayer ) );
} }
bool BOARD_STACKUP_ITEM::IsMaterialEditable() bool BOARD_STACKUP_ITEM::IsMaterialEditable() const
{ {
// The material is editable only for dielectric // The material is editable only for dielectric
return m_Type == BS_ITEM_TYPE_DIELECTRIC || return m_Type == BS_ITEM_TYPE_DIELECTRIC ||
@ -211,13 +245,13 @@ bool BOARD_STACKUP_ITEM::IsMaterialEditable()
} }
bool BOARD_STACKUP_ITEM::IsColorEditable() bool BOARD_STACKUP_ITEM::IsColorEditable() const
{ {
return m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SILKSCREEN; return m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SILKSCREEN;
} }
bool BOARD_STACKUP_ITEM::IsThicknessEditable() bool BOARD_STACKUP_ITEM::IsThicknessEditable() const
{ {
switch( m_Type ) switch( m_Type )
{ {
@ -244,25 +278,25 @@ bool BOARD_STACKUP_ITEM::IsThicknessEditable()
} }
wxString BOARD_STACKUP_ITEM::FormatEpsilonR() wxString BOARD_STACKUP_ITEM::FormatEpsilonR( int aDielectricSubLayer ) const
{ {
// return a wxString to print/display Epsilon R // return a wxString to print/display Epsilon R
wxString txt; wxString txt;
txt.Printf( "%.1f", GetEpsilonR() ); txt.Printf( "%.1f", GetEpsilonR( aDielectricSubLayer ) );
return txt; return txt;
} }
wxString BOARD_STACKUP_ITEM::FormatLossTangent() wxString BOARD_STACKUP_ITEM::FormatLossTangent( int aDielectricSubLayer ) const
{ {
// return a wxString to print/display Loss Tangent // return a wxString to print/display Loss Tangent
wxString txt; wxString txt;
txt.Printf( "%g", GetLossTangent() ); txt.Printf( "%g", GetLossTangent( aDielectricSubLayer ) );
return txt; return txt;
} }
wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() const
{ {
// return a wxString to print/display a dielectriv name // return a wxString to print/display a dielectriv name
wxString lname; wxString lname;
@ -364,17 +398,31 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
BOARD_STACKUP stackup; BOARD_STACKUP stackup;
stackup.BuildDefaultStackupList( aSettings ); stackup.BuildDefaultStackupList( aSettings );
// First test for removed layers: // First, find removed layers:
for( BOARD_STACKUP_ITEM* old_item: m_list ) for( BOARD_STACKUP_ITEM* curr_item: m_list )
{ {
bool found = false; bool found = false;
for( BOARD_STACKUP_ITEM* item: stackup.GetList() ) for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
{ {
if( item->GetBrdLayerId() == old_item->GetBrdLayerId() ) if( curr_item->GetBrdLayerId() != UNDEFINED_LAYER )
{ {
found = true; if( item->GetBrdLayerId() == curr_item->GetBrdLayerId() )
break; {
found = true;
break;
}
}
else // curr_item = dielectric layer
{
if( item->GetBrdLayerId() != UNDEFINED_LAYER )
continue;
if( item->GetDielectricLayerId() == curr_item->GetDielectricLayerId() )
{
found = true;
break;
}
} }
} }
@ -403,6 +451,10 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
} }
else // dielectric layer: see m_DielectricLayerId for identification else // dielectric layer: see m_DielectricLayerId for identification
{ {
// Compare dielectric layer with dielectric layer
if( initial_item->GetBrdLayerId() != UNDEFINED_LAYER )
continue;
if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() ) if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() )
{ {
*item = *initial_item; *item = *initial_item;
@ -413,7 +465,9 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
} }
if( !found ) if( !found )
{
change = true; change = true;
}
} }
// Transfer other stackup settings from aSettings // Transfer other stackup settings from aSettings
@ -586,31 +640,40 @@ void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter,
aFormatter->Quotew( layer_name ).c_str(), aFormatter->Quotew( layer_name ).c_str(),
aFormatter->Quotew( item->GetTypeName() ).c_str() ); aFormatter->Quotew( item->GetTypeName() ).c_str() );
if( item->IsThicknessEditable() )
{
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() )
aFormatter->Print( 0, " (thickness %s locked)",
FormatInternalUnits( item->GetThickness() ).c_str() );
else
aFormatter->Print( 0, " (thickness %s)",
FormatInternalUnits( item->GetThickness() ).c_str() );
}
if( item->HasMaterialValue() )
aFormatter->Print( 0, " (material %s)",
aFormatter->Quotew( item->GetMaterial() ).c_str() );
if( item->HasEpsilonRValue() && item->HasMaterialValue() )
aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR() );
if( item->HasLossTangentValue() && item->HasMaterialValue() )
aFormatter->Print( 0, " (loss_tangent %s)",
Double2Str(item->GetLossTangent() ).c_str() );
if( item->IsColorEditable() && IsPrmSpecified( item->GetColor() ) ) if( item->IsColorEditable() && IsPrmSpecified( item->GetColor() ) )
aFormatter->Print( 0, " (color %s)", aFormatter->Print( 0, " (color %s)",
aFormatter->Quotew( item->GetColor() ).c_str() ); aFormatter->Quotew( item->GetColor() ).c_str() );
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
{
if( idx ) // not for the main (first) layer.
{
aFormatter->Print( 0, "\n" );
aFormatter->Print( nest_level+1, "addsublayer" );
}
if( item->IsThicknessEditable() )
{
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
aFormatter->Print( 0, " (thickness %s locked)",
FormatInternalUnits( item->GetThickness( idx ) ).c_str() );
else
aFormatter->Print( 0, " (thickness %s)",
FormatInternalUnits( item->GetThickness( idx ) ).c_str() );
}
if( item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (material %s)",
aFormatter->Quotew( item->GetMaterial( idx ) ).c_str() );
if( item->HasEpsilonRValue() && item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR( idx ) );
if( item->HasLossTangentValue() && item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (loss_tangent %s)",
Double2Str(item->GetLossTangent( idx ) ).c_str() );
}
aFormatter->Print( 0, ")\n" ); aFormatter->Print( 0, ")\n" );
} }

View File

@ -58,15 +58,13 @@ enum BS_EDGE_CONNECTOR_CONSTRAINTS
/** /**
* A helper class to manage a dielectric parameters * A helper class to manage a dielectric layer set of parameters
*/ */
class DIELECTRIC_PRMS class DIELECTRIC_PRMS
{ {
friend class BOARD_STACKUP_ITEM; friend class BOARD_STACKUP_ITEM;
private: private:
int m_DielectricLayerId;/// the "layer" id for dielectric layers,
/// from 1 (top) to 31 (bottom)
wxString m_Material; /// type of material (for dielectric and solder mask) wxString m_Material; /// type of material (for dielectric and solder mask)
int m_Thickness; /// the physical layer thickness in internal units int m_Thickness; /// the physical layer thickness in internal units
bool m_ThicknessLocked; /// true for dielectric layers with a fixed thickness bool m_ThicknessLocked; /// true for dielectric layers with a fixed thickness
@ -76,7 +74,6 @@ private:
public: public:
DIELECTRIC_PRMS() : DIELECTRIC_PRMS() :
m_DielectricLayerId(-1),
m_Thickness(0), m_ThicknessLocked( false ), m_Thickness(0), m_ThicknessLocked( false ),
m_EpsilonR( 1.0 ), m_LossTangent( 0.0 ) m_EpsilonR( 1.0 ), m_LossTangent( 0.0 )
{} {}
@ -102,6 +99,9 @@ private:
PCB_LAYER_ID m_LayerId; /// the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask) PCB_LAYER_ID m_LayerId; /// the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask)
/// and UNDEFINED_LAYER (-1) for dielectic layers that are not /// and UNDEFINED_LAYER (-1) for dielectic layers that are not
/// really layers for the board editor /// really layers for the board editor
int m_DielectricLayerId;/// the "layer" id for dielectric layers,
/// from 1 (top) to 31 (bottom)
/// (only 31 dielectric layers for 32 copper layers)
/// List of dielectric parameters /// List of dielectric parameters
/// usually only one item, but in complex (microwave) boards, one can have /// usually only one item, but in complex (microwave) boards, one can have
/// more than one dielectic layer between 2 copper layers, and therfore /// more than one dielectic layer between 2 copper layers, and therfore
@ -112,25 +112,40 @@ private:
/// false to ignore it. Mainly used in dialog stackup editor. /// false to ignore it. Mainly used in dialog stackup editor.
public: public:
/**
* add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList
* all values are set to default
* @param aDielectricPrmsIdx is a index in m_DielectricPrmsList
* the new item will be inserted at this position
*/
void AddDielectricPrms( int aDielectricPrmsIdx );
/**
* Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList
* @param aDielectricPrmsIdx is the index of the parameters set
* to remove in m_DielectricPrmsList
*/
void RemoveDielectricPrms( int aDielectricPrmsIdx );
/// @return true if the layer has a meaningfull Epsilon R parameter /// @return true if the layer has a meaningfull Epsilon R parameter
/// namely dielectric layers: dielectric and solder mask /// namely dielectric layers: dielectric and solder mask
bool HasEpsilonRValue(); bool HasEpsilonRValue() const;
/// @return true if the layer has a meaningfull Dielectric Loss parameter /// @return true if the layer has a meaningfull Dielectric Loss parameter
/// namely dielectric layers: dielectric and solder mask /// namely dielectric layers: dielectric and solder mask
bool HasLossTangentValue(); bool HasLossTangentValue() const;
/// @return true if the material is specified /// @return true if the material is specified
bool HasMaterialValue(); bool HasMaterialValue( int aDielectricSubLayer = 0 ) const;
/// @return true if the material is editable /// @return true if the material is editable
bool IsMaterialEditable(); bool IsMaterialEditable() const;
/// @return true if the color is editable /// @return true if the color is editable
bool IsColorEditable(); bool IsColorEditable() const;
/// @return true if Thickness is editable /// @return true if Thickness is editable
bool IsThicknessEditable(); bool IsThicknessEditable() const;
/// @return a reasonable default value for a copper layer thickness /// @return a reasonable default value for a copper layer thickness
static int GetCopperDefaultThickness(); static int GetCopperDefaultThickness();
@ -138,30 +153,34 @@ public:
/// @return a reasonable default value for a solder mask layer thickness /// @return a reasonable default value for a solder mask layer thickness
static int GetMaskDefaultThickness(); static int GetMaskDefaultThickness();
/// @return a the number of sublayers in a dielectric layer.
/// the count is >= 1 (there is at least one layer)
int GetSublayersCount() const { return m_DielectricPrmsList.size(); }
/// @return a wxString to print/display Epsilon R /// @return a wxString to print/display Epsilon R
wxString FormatEpsilonR(); wxString FormatEpsilonR( int aDielectricSubLayer = 0 ) const;
/// @return a wxString to print/display Loss Tangent /// @return a wxString to print/display Loss Tangent
wxString FormatLossTangent(); wxString FormatLossTangent( int aDielectricSubLayer = 0 ) const;
/// @return a wxString to print/display a dielectric name /// @return a wxString to print/display a dielectric name
wxString FormatDielectricLayerName(); wxString FormatDielectricLayerName() const;
// Getters: // Getters:
bool IsEnabled() {return m_enabled; } bool IsEnabled() const {return m_enabled; }
BOARD_STACKUP_ITEM_TYPE GetType() { return m_Type; } BOARD_STACKUP_ITEM_TYPE GetType() const { return m_Type; }
PCB_LAYER_ID GetBrdLayerId() { return m_LayerId; } PCB_LAYER_ID GetBrdLayerId() const { return m_LayerId; }
wxString GetColor(){ return m_Color; } wxString GetColor() const { return m_Color; }
wxString GetLayerName() { return m_LayerName; } wxString GetLayerName() const { return m_LayerName; }
wxString GetTypeName() { return m_TypeName; } wxString GetTypeName() const { return m_TypeName; }
int GetDielectricLayerId() const { return m_DielectricLayerId; }
int GetThickness( int aDielectricSubLayer = 0 ); int GetThickness( int aDielectricSubLayer = 0 ) const;
bool IsThicknessLocked( int aDielectricSubLayer = 0 ); bool IsThicknessLocked( int aDielectricSubLayer = 0 ) const;
double GetEpsilonR( int aDielectricSubLayer = 0 ); double GetEpsilonR( int aDielectricSubLayer = 0 ) const;
double GetLossTangent( int aDielectricSubLayer = 0 ); double GetLossTangent( int aDielectricSubLayer = 0 ) const;
int GetDielectricLayerId( int aDielectricSubLayer = 0 ); wxString GetMaterial( int aDielectricSubLayer = 0 ) const;
wxString GetMaterial( int aDielectricSubLayer = 0 );
// Setters: // Setters:
void SetEnabled( bool aEnable) { m_enabled = aEnable; } void SetEnabled( bool aEnable) { m_enabled = aEnable; }
@ -169,12 +188,12 @@ public:
void SetColor( const wxString& aColorName ){ m_Color = aColorName; } void SetColor( const wxString& aColorName ){ m_Color = aColorName; }
void SetLayerName( const wxString& aName ) { m_LayerName = aName; } void SetLayerName( const wxString& aName ) { m_LayerName = aName; }
void SetTypeName( const wxString& aName ) { m_TypeName = aName; } void SetTypeName( const wxString& aName ) { m_TypeName = aName; }
void SetDielectricLayerId( int aLayerId ) { m_DielectricLayerId = aLayerId; }
void SetThickness( int aThickness, int aDielectricSubLayer = 0 ); void SetThickness( int aThickness, int aDielectricSubLayer = 0 );
void SetThicknessLocked( bool aLocked, int aDielectricSubLayer = 0 ); void SetThicknessLocked( bool aLocked, int aDielectricSubLayer = 0 );
void SetEpsilonR( double aEpsilon, int aDielectricSubLayer = 0 ); void SetEpsilonR( double aEpsilon, int aDielectricSubLayer = 0 );
void SetLossTangent( double aTg, int aDielectricSubLayer = 0 ); void SetLossTangent( double aTg, int aDielectricSubLayer = 0 );
void SetDielectricLayerId( int aLayerId, int aDielectricSubLayer = 0 );
void SetMaterial( const wxString& aName, int aDielectricSubLayer = 0 ); void SetMaterial( const wxString& aName, int aDielectricSubLayer = 0 );
}; };

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,9 @@ class PANEL_SETUP_LAYERS;
struct BOARD_STACKUP_ROW_UI_ITEM struct BOARD_STACKUP_ROW_UI_ITEM
{ {
BOARD_STACKUP_ITEM* m_Item; // The BOARD_STACKUP_ITEM managed by this BOARD_STACKUP_ROW_UI_ITEM BOARD_STACKUP_ITEM* m_Item; // The BOARD_STACKUP_ITEM managed by this BOARD_STACKUP_ROW_UI_ITEM
int m_SubItem; // For multilayer dielectric, the index in sublayer list.
// Must be >= 0 and < m_Item sublayer count. Used only for dielectic
// 0 is the base list of parameters (always existing)
bool m_isEnabled; // True if the row is in board bool m_isEnabled; // True if the row is in board
// false if not (this row is not shown on the panel) // false if not (this row is not shown on the panel)
wxStaticBitmap* m_Icon; // Color icon in first column (column 1) wxStaticBitmap* m_Icon; // Color icon in first column (column 1)
@ -62,8 +65,8 @@ struct BOARD_STACKUP_ROW_UI_ITEM
wxControl* m_EpsilonCtrl; // control shown in column 8 wxControl* m_EpsilonCtrl; // control shown in column 8
wxControl* m_LossTgCtrl; // control shown in column 9 wxControl* m_LossTgCtrl; // control shown in column 9
BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem ) : BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem, int aSubItem = 1 ) :
m_Item( aItem ), m_Item( aItem ), m_SubItem( aSubItem ),
m_isEnabled( true ), m_Icon( nullptr ), m_LayerName( nullptr ), m_isEnabled( true ), m_Icon( nullptr ), m_LayerName( nullptr ),
m_LayerTypeCtrl( nullptr ), m_LayerTypeCtrl( nullptr ),
m_MaterialCtrl( nullptr ),m_MaterialButt( nullptr ), m_MaterialCtrl( nullptr ),m_MaterialButt( nullptr ),
@ -92,13 +95,16 @@ public:
*/ */
void OnLayersOptionsChanged( LSET aNewLayerSet ); void OnLayersOptionsChanged( LSET aNewLayerSet );
BOARD_STACKUP_ITEM* GetStackupItem( int aIndex ); /// @return the BOARD_STACKUP_ITEM managed by the row aRow
BOARD_STACKUP_ITEM* GetStackupItem( int aRow );
/// @return the BOARD_STACKUP_ITEM sublayermanaged by the row aRow
int GetSublayerId( int aRow );
/// Return the color currently selected for the row aRow /// Return the color currently selected for the row aRow
wxColor GetSelectedColor( int aRow ) const; wxColor GetSelectedColor( int aRow ) const;
BOARD_STACKUP& GetStackup() { return m_stackup; } BOARD_STACKUP& GetStackup() { return m_stackup; }
int GetPcbTickness(); int GetPcbThickness();
// Called by wxWidgets: transfer current settings stored in m_stackup to the board // Called by wxWidgets: transfer current settings stored in m_stackup to the board
bool TransferDataFromWindow() override; bool TransferDataFromWindow() override;
@ -106,6 +112,18 @@ public:
std::vector<wxColor> m_UserColors; // the list of user colors for each grid row std::vector<wxColor> m_UserColors; // the list of user colors for each grid row
// other colors are defined colors, and are not stored // other colors are defined colors, and are not stored
private: private:
/** Creates a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem.
* @return a BOARD_STACKUP_ROW_UI_ITEM filled with corresponding widgets
* @param aRow is the row index in the row list
* @param aStackupItem is the stackup item controlled by the created
* BOARD_STACKUP_ROW_UI_ITEM.
* @param aSublayerIdx is used only for BS_ITEM_TYPE_DIELECTRIC stackup items.
* this is the index of the sublayer to used inside aStackupItem
* (from 0 to sub layer count - 1)
*/
BOARD_STACKUP_ROW_UI_ITEM createRowData( int aRow, BOARD_STACKUP_ITEM* aStackupItem,
int aSublayerIdx );
/** add a Spacer in m_fgGridSizer when a empty cell is needed /** add a Spacer in m_fgGridSizer when a empty cell is needed
*/ */
wxControl* addSpacer(); wxControl* addSpacer();
@ -124,20 +142,31 @@ private:
* all copper layers and all tech layers that are supported by the stackup * all copper layers and all tech layers that are supported by the stackup
* items not in the current board stackup will be not shown, but they are * items not in the current board stackup will be not shown, but they are
* existing in list * existing in list
* @param aCreatedInitialStackup = true to create a initial stackup list for the dialog
* false to build the stackup panel from the existing stackup list.
*/ */
void buildLayerStackPanel(); void buildLayerStackPanel( bool aCreatedInitialStackup );
/** Show or do not show items in m_fgGridSizer according to the stackup of the /** Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the
* current board and optionally update the stackup params (thickness, color ... ) * current board and optionally update the stackup params (thickness, color ... )
* @param aFullSync = true to update stackup params, false to only update the list * @param aFullSync = true to update stackup params, false to only update the list
* of shown items * of shown items
*/ */
void synchronizeWithBoard( bool aFullSync ); void synchronizeWithBoard( bool aFullSync );
/** Show or do not show items in m_fgGridSizer according to the stackup of the
* current board.
* The panel stackup stores all posible layers (because the number of layers is set
* from an other panel), but only some of them must be actually shown on screen
*/
void showOnlyActiveLayers();
/** Populate m_fgGridSizer with items to handle stackup parameters /** Populate m_fgGridSizer with items to handle stackup parameters
* If previous items are in list, remove old items * If previous items are in list, remove old items
* New prms are added
* must be called after adding or deleting a dielectric parameter set
*/ */
void RebuildLayerStackPanel(); void rebuildLayerStackPanel();
/** Transfer current UI settings to m_stackup but not to the board /** Transfer current UI settings to m_stackup but not to the board
*/ */

View File

@ -614,150 +614,173 @@ void GERBER_JOBFILE_WRITER::addJSONMaterialStackup()
for( int ii = 0; ii < brd_stackup.GetCount(); ++ii ) for( int ii = 0; ii < brd_stackup.GetCount(); ++ii )
{ {
BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii ); BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii );
double thickness = item->GetThickness()*m_conversionUnits; // layer thickness is always in mm
wxString layer_type;
std::string layer_name; // for comment
switch( item->GetType() ) int sub_layer_count = item->GetType() == BS_ITEM_TYPE_DIELECTRIC
? item->GetSublayersCount() : 1;
for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ )
{ {
case BS_ITEM_TYPE_COPPER: // layer thickness is always in mm
layer_type = "Copper"; double thickness = item->GetThickness( sub_idx )*m_conversionUnits;
layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) ); wxString layer_type;
last_copper_layer = item->GetBrdLayerId(); std::string layer_name; // for comment
break;
case BS_ITEM_TYPE_SILKSCREEN: switch( item->GetType() )
layer_type = "Legend";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERMASK:
layer_type = "SolderMask";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERPASTE:
layer_type = "SolderPaste";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_DIELECTRIC:
layer_type = "Dielectric";
// The option core or prepreg is not added here, as it creates constraints
// in build process, not necessary wanted.
layer_name = formatStringFromUTF32( wxString::Format( "dielectric layer %d",
item->GetDielectricLayerId() ) );
break;
default:
break;
}
openBlock();
addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) );
if( item->IsColorEditable() && uptodate )
{
if( IsPrmSpecified( item->GetColor() ) )
{ {
wxString colorName = item->GetColor(); case BS_ITEM_TYPE_COPPER:
layer_type = "Copper";
layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) );
last_copper_layer = item->GetBrdLayerId();
break;
if( colorName.StartsWith( "#" ) ) // This is a user defined color. case BS_ITEM_TYPE_SILKSCREEN:
layer_type = "Legend";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERMASK:
layer_type = "SolderMask";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERPASTE:
layer_type = "SolderPaste";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_DIELECTRIC:
layer_type = "Dielectric";
// The option core or prepreg is not added here, as it creates constraints
// in build process, not necessary wanted.
if( sub_layer_count > 1 )
{ {
// In job file a color can be given by its RGB values (0...255) layer_name = formatStringFromUTF32(
wxColor color( colorName ); wxString::Format( "dielectric layer %d - %d/%d",
colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() ); item->GetDielectricLayerId(),
sub_idx+1, sub_layer_count ) );
} }
else
layer_name = formatStringFromUTF32(
wxString::Format( "dielectric layer %d",
item->GetDielectricLayerId() ) );
break;
addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) ); default:
break;
} }
}
if( item->IsThicknessEditable() && uptodate ) openBlock();
addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) ); addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) );
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC ) if( item->IsColorEditable() && uptodate )
{
if( item->HasMaterialValue() )
{ {
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) ); if( IsPrmSpecified( item->GetColor() ) )
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
{ {
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not wxString colorName = item->GetColor();
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not if( colorName.StartsWith( "#" ) ) // This is a user defined color.
// a possible value {
if( item->GetLossTangent() > 0.0 ) // In job file a color can be given by its RGB values (0...255)
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", wxColor color( colorName );
item->FormatLossTangent() ) ); colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() );
}
addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) );
} }
} }
PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1); if( item->IsThicknessEditable() && uptodate )
addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) );
// If the next_copper_layer is the last copper layer, the next layer id is B_Cu if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 ) {
next_copper_layer = B_Cu; if( item->HasMaterialValue() )
{
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n",
item->GetMaterial( sub_idx ) ) );
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
{
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR( sub_idx ) ) );
addJSONObject( wxString::Format( "\"Name\": \"%s/%s\",\n", // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent( sub_idx ) ) );
}
}
PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1);
// If the next_copper_layer is the last copper layer, the next layer id is B_Cu
if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 )
next_copper_layer = B_Cu;
wxString subLayerName;
if( sub_layer_count > 1 )
subLayerName.Printf( " (%d/%d)",sub_idx+1, sub_layer_count );
addJSONObject( wxString::Format( "\"Name\": \"%s/%s%s\",\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ),
subLayerName )
);
// Add a comment ("Notes"):
wxString note = "\"Notes\": ";
note << wxString::Format( " \"Type: %s", layer_name.c_str() );
note << wxString::Format( " (from %s to %s)\"\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ), formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) ) formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
);
// Add a comment ("Notes"): addJSONObject( note );
wxString note = "\"Notes\": "; }
else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
note << wxString::Format( " \"Type: %s", layer_name.c_str() );
note << wxString::Format( " (from %s to %s)\"\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
addJSONObject( note );
}
else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
{
if( item->HasMaterialValue() )
{ {
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) ); if( item->HasMaterialValue() )
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
{ {
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) );
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not // These constrains are only written if the board has impedance controlled tracks.
// a possible value // If the board is not impedance controlled, they are useless.
if( item->GetLossTangent() > 0.0 ) // Do not add constrains that create more expensive boards.
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n", if( brd_stackup.m_HasDielectricConstrains )
item->FormatLossTangent() ) ); {
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent() ) );
}
} }
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
}
else
{
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
} }
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) ); removeJSONSepararator();
closeBlockWithSep();
} }
else
{
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
}
removeJSONSepararator();
closeBlockWithSep();
} }
removeJSONSepararator(); removeJSONSepararator();

View File

@ -1075,72 +1075,90 @@ void PCB_PARSER::parseBoardStackup()
else else
Expecting( "layer_name" ); Expecting( "layer_name" );
// Dielectric thickness can be locked (for impedance controled layers) bool has_next_sublayer = true;
bool thickness_locked = false; int sublayer_idx = 0; // the index of dielectric sub layers
// sublayer 0 is always existing (main sublayer)
for( token = NextTok(); token != T_RIGHT; token = NextTok() ) while( has_next_sublayer )
{ {
if( token == T_LEFT ) has_next_sublayer = false;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{ {
token = NextTok(); if( token == T_addsublayer )
switch( token )
{ {
case T_type: has_next_sublayer = true;
NeedSYMBOL();
item->SetTypeName( FromUTF8() );
NeedRIGHT();
break; break;
}
case T_thickness: if( token == T_LEFT )
item->SetThickness( parseBoardUnits( T_thickness ) ); {
token = NextTok(); token = NextTok();
if( token == T_LEFT ) switch( token )
{
case T_type:
NeedSYMBOL();
item->SetTypeName( FromUTF8() );
NeedRIGHT();
break; break;
if( token == T_locked ) case T_thickness:
{ item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
thickness_locked = true; token = NextTok();
if( token == T_LEFT )
break;
if( token == T_locked )
{
// Dielectric thickness can be locked (for impedance controled layers)
if( type == BS_ITEM_TYPE_DIELECTRIC )
item->SetThicknessLocked( true, sublayer_idx );
NeedRIGHT();
}
break;
case T_material:
NeedSYMBOL();
item->SetMaterial( FromUTF8(), sublayer_idx );
NeedRIGHT(); NeedRIGHT();
break;
case T_epsilon_r:
NextTok();
item->SetEpsilonR( parseDouble(), sublayer_idx );
NeedRIGHT();
break;
case T_loss_tangent:
NextTok();
item->SetLossTangent( parseDouble(), sublayer_idx );
NeedRIGHT();
break;
case T_color:
NeedSYMBOL();
item->SetColor( FromUTF8() );
NeedRIGHT();
break;
default:
// Currently, skip this item if not defined, because the stackup def
// is a moving target
//Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
skipCurrent();
} }
break;
case T_material:
NeedSYMBOL();
item->SetMaterial( FromUTF8() );
NeedRIGHT();
break;
case T_epsilon_r:
NextTok();
item->SetEpsilonR( parseDouble() );
NeedRIGHT();
break;
case T_loss_tangent:
NextTok();
item->SetLossTangent( parseDouble() );
NeedRIGHT();
break;
case T_color:
NeedSYMBOL();
item->SetColor( FromUTF8() );
NeedRIGHT();
break;
default:
// Currently, skip this item if not defined, because the stackup def
// is a moving target
//Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
skipCurrent();
} }
} }
}
if( type == BS_ITEM_TYPE_DIELECTRIC && thickness_locked ) if( has_next_sublayer ) // Prepare reading the next sublayer description
item->SetThicknessLocked( true ); {
sublayer_idx++;
item->AddDielectricPrms( sublayer_idx );
}
}
} }
if( token != T_RIGHT ) if( token != T_RIGHT )