diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5b286473d7..2f675467d0 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -447,6 +447,7 @@ set( PCB_COMMON_SRCS ../pcbnew/class_pad.cpp ../pcbnew/class_pcb_target.cpp ../pcbnew/class_pcb_text.cpp + ../pcbnew/board_stackup_manager/class_board_stackup.cpp ../pcbnew/class_text_mod.cpp ../pcbnew/class_track.cpp ../pcbnew/class_zone.cpp diff --git a/include/board_design_settings.h b/include/board_design_settings.h index 3452a4ad66..a4bb145a17 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -29,6 +29,7 @@ #include #include #include +#include #define DEFAULT_SILK_LINE_WIDTH 0.12 #define DEFAULT_COPPER_LINE_WIDTH 0.20 @@ -250,6 +251,15 @@ public: D_PAD m_Pad_Master; ///< A dummy pad to store all default parameters // when importing values or create a new pad + /** Set to true if the board has a stackup management. + * if m_hasStackup is false, a default basic stackup witll be used to + * generate the ;gbrjob file. + * if m_hasStackup is true, the stackup defined for the board is used. + * if not up to date, a error message will be set + * Could be removed later, or at least always set to true + */ + bool m_HasStackup; + private: // Indicies into the trackWidth, viaSizes and diffPairDimensions lists. // The 0 index is always the current netclass value(s) @@ -278,9 +288,17 @@ private: /// This is also the last used netclass after starting a track. wxString m_currentNetClassName; + /** the description of layers stackup, for board fabrication + * only physical layers are in layers stackup. + * It includes not only layers enabled for the board edition, but also dielectic layers + */ + BOARD_STACKUP m_stackup; + public: BOARD_DESIGN_SETTINGS(); + BOARD_STACKUP& GetStackupDescriptor() { return m_stackup; } + /** * Function GetDefault * @return the default netclass. diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 92ac8c4045..2d45bbc24b 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -180,6 +180,10 @@ if( KICAD_SCRIPTING AND KICAD_SCRIPTING_ACTION_MENU ) ) endif() +set( PCBNEW_BRDSTACKUP_MGR + board_stackup_manager/stackup_predefined_prms.cpp + ) + set( PCBNEW_IMPORT_GFX import_gfx/dialog_import_gfx_base.cpp import_gfx/dialog_import_gfx.cpp @@ -192,6 +196,7 @@ set( PCBNEW_IMPORT_GFX import_gfx/nanosvg.cpp ) + set( PCBNEW_EXPORTERS exporters/export_hyperlynx.cpp exporters/export_d356.cpp @@ -222,6 +227,7 @@ set( PCBNEW_CLASS_SRCS ${PCBNEW_EXPORTERS} ${PCBNEW_IMPORT_GFX} ${PCBNEW_DRC_SRCS} + ${PCBNEW_BRDSTACKUP_MGR} autorouter/rect_placement/rect_placement.cpp autorouter/spread_footprints.cpp diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 2875758575..991763b52a 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -423,8 +423,9 @@ public: BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : m_Pad_Master( NULL ) { - LSET all_set = LSET().set(); + m_HasStackup = false; // no stackup defined by default + LSET all_set = LSET().set(); m_enabledLayers = all_set; // All layers enabled at first. // SetCopperLayerCount() will adjust this. SetVisibleLayers( all_set ); @@ -433,7 +434,6 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : m_visibleElements = ~( 1 << GAL_LAYER_INDEX( LAYER_MOD_TEXT_INVISIBLE ) ); SetCopperLayerCount( 2 ); // Default design is a double sided board - m_CurrentViaType = VIA_THROUGH; // if true, when creating a new track starting on an existing track, use this track width diff --git a/pcbnew/board_stackup_manager/class_board_stackup.cpp b/pcbnew/board_stackup_manager/class_board_stackup.cpp new file mode 100644 index 0000000000..cff7df7366 --- /dev/null +++ b/pcbnew/board_stackup_manager/class_board_stackup.cpp @@ -0,0 +1,484 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2009-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + + +#include "class_board_stackup.h" +#include +#include +#include +#include +#include +#include "stackup_predefined_prms.h" + +// A reasonable thickness for copper layers: +const int copperDefaultThickness = Millimeter2iu( 0.035 ); +// A reasonable thickness for solder mask: +const int maskDefaultThickness = Millimeter2iu( 0.01 ); + +BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType ) +{ + m_LayerId = UNDEFINED_LAYER; + m_Type = aType; + m_DielectricLayerId = 0; + m_EpsilonR = 0; + m_LossTangent = 0.0; + + // Initialize parameters to a usual value for allowed types: + switch( m_Type ) + { + case BS_ITEM_TYPE_COPPER: + m_TypeName = "copper"; + m_Thickness = copperDefaultThickness; + break; + + case BS_ITEM_TYPE_DIELECTRIC: + m_TypeName = "core"; // or prepreg + m_Material = "FR4"; // or other dielectric name + m_DielectricLayerId = 1; + m_Thickness = 0; // will be set later + m_LossTangent = 0.02; // for FR4 + m_EpsilonR = 4.5; // for FR4 + break; + + case BS_ITEM_TYPE_SOLDERPASTE: + m_TypeName = "solderpaste"; + m_Thickness = 0.0; // Not used + break; + + case BS_ITEM_TYPE_SOLDERMASK: + m_TypeName = "soldermask"; + m_Color = NOT_SPECIFIED; + m_Thickness = maskDefaultThickness; + m_EpsilonR = 3.5; + m_LossTangent = 0.0; + break; + + case BS_ITEM_TYPE_SILKSCREEN: + m_TypeName = "silkscreen"; + m_Color = NOT_SPECIFIED; + m_Thickness = 0.0; // Not used + break; + + case BS_ITEM_TYPE_UNDEFINED: + m_Thickness = 0.0; + break; + } +} + + +BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther ) +{ + m_LayerId = aOther.m_LayerId; + m_Type = aOther.m_Type; + m_DielectricLayerId = aOther.m_DielectricLayerId; + m_TypeName = aOther.m_TypeName; + m_Material = aOther.m_Material; + m_Color = aOther.m_Color; + m_Thickness = aOther.m_Thickness; + m_EpsilonR = aOther.m_EpsilonR; + m_LossTangent = aOther.m_LossTangent; +} + + +bool BOARD_STACKUP_ITEM::HasEpsilonRValue() +{ + return m_Type == BS_ITEM_TYPE_DIELECTRIC || m_Type == BS_ITEM_TYPE_SOLDERMASK; +}; + + +bool BOARD_STACKUP_ITEM::HasLossTangentValue() +{ + return m_Type == BS_ITEM_TYPE_DIELECTRIC || m_Type == BS_ITEM_TYPE_SOLDERMASK; +}; + + +bool BOARD_STACKUP_ITEM::IsMaterialEditable() +{ + // The material is editable only for dielectric + return m_Type == BS_ITEM_TYPE_DIELECTRIC; +} + + +bool BOARD_STACKUP_ITEM::IsColorEditable() +{ + return m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SILKSCREEN; +} + + +bool BOARD_STACKUP_ITEM::IsThicknessEditable() +{ + switch( m_Type ) + { + case BS_ITEM_TYPE_COPPER: + return true; + + case BS_ITEM_TYPE_DIELECTRIC: + return true; + + case BS_ITEM_TYPE_SOLDERMASK: + return true; + + case BS_ITEM_TYPE_SOLDERPASTE: + return false; + + case BS_ITEM_TYPE_SILKSCREEN: + return false; + + default: + break; + } + + return false; +} + + +BOARD_STACKUP::BOARD_STACKUP() +{ + m_HasDielectricConstrains = false; // True if some dielectric layers have constrains + // (Loss tg and Epison R) + m_HasThicknessConstrains = false; // True if some dielectric or copper layers have constrains + m_EdgeConnectorConstraints = BS_EDGE_CONNECTOR_NONE; + m_CastellatedPads = false; // True if some castellated pads exist + m_EdgePlating = false; // True if edge board is plated + m_FinishType = "None"; // undefined finish type +} + + +BOARD_STACKUP::BOARD_STACKUP( BOARD_STACKUP& aOther ) +{ + m_HasDielectricConstrains = aOther.m_HasDielectricConstrains; + m_EdgeConnectorConstraints = aOther.m_EdgeConnectorConstraints; + m_CastellatedPads = aOther.m_CastellatedPads; + m_EdgePlating = aOther.m_EdgePlating; + m_FinishType = aOther.m_FinishType; + + // All items in aOther.m_list have to be duplicated, because aOther.m_list + // manage pointers to these items + for( auto item : aOther.m_list ) + { + BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item ); + Add( dup_item ); + } +} + + +BOARD_STACKUP& BOARD_STACKUP::operator=( const BOARD_STACKUP& aOther ) +{ + m_HasDielectricConstrains = aOther.m_HasDielectricConstrains; + m_EdgeConnectorConstraints = aOther.m_EdgeConnectorConstraints; + m_CastellatedPads = aOther.m_CastellatedPads; + m_EdgePlating = aOther.m_EdgePlating; + m_FinishType = aOther.m_FinishType; + + RemoveAll(); + + // All items in aOther.m_list have to be duplicated, because aOther.m_list + // manage pointers to these items + for( auto item : aOther.m_list ) + { + BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item ); + Add( dup_item ); + } + + return *this; +} + + +void BOARD_STACKUP::RemoveAll() +{ + for( auto item : m_list ) + delete item; + + m_list.clear(); +} + + +BOARD_STACKUP_ITEM* BOARD_STACKUP::GetStackupLayer( int aIndex ) +{ + if( aIndex < 0 || aIndex >= GetCount() ) + return nullptr; + + return GetList()[aIndex]; +} + + +int BOARD_STACKUP::BuildBoardTicknessFromStackup() const +{ + // return the board thickness from the thickness of BOARD_STACKUP_ITEM list + int thickness = 0; + + for( auto item : m_list ) + { + if( item->IsThicknessEditable() ) + thickness += item->m_Thickness; + } + + return thickness; +} + + +bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings ) +{ + bool change = false; + // Build the suitable stackup: + BOARD_STACKUP stackup; + stackup.BuildDefaultStackupList( aSettings ); + + // First test for removed layers: + for( BOARD_STACKUP_ITEM* old_item: m_list ) + { + bool found = false; + + for( BOARD_STACKUP_ITEM* item: stackup.GetList() ) + { + if( item->m_LayerId == old_item->m_LayerId ) + { + found = true; + break; + } + } + + if( !found ) // a layer was removed: a change is found + { + change = true; + break; + } + } + + // Now initialize all stackup items to the initial values, when exist + for( BOARD_STACKUP_ITEM* item: stackup.GetList() ) + { + bool found = false; + // Search for initial settings: + for( BOARD_STACKUP_ITEM* initial_item: m_list ) + { + if( item->m_LayerId != UNDEFINED_LAYER ) + { + if( item->m_LayerId == initial_item->m_LayerId ) + { + *item = *initial_item; + found = true; + break; + } + } + else // dielectric layer: see m_DielectricLayerId for identification + { + if( item->m_DielectricLayerId == initial_item->m_DielectricLayerId ) + { + *item = *initial_item; + found = true; + break; + } + } + } + + if( !found ) + change = true; + } + + // Transfer other stackup settings from aSettings + BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor(); + m_HasDielectricConstrains = source_stackup.m_HasDielectricConstrains; + m_EdgeConnectorConstraints = source_stackup.m_EdgeConnectorConstraints; + m_CastellatedPads = source_stackup.m_CastellatedPads; + m_EdgePlating = source_stackup.m_EdgePlating; + m_FinishType = source_stackup.m_FinishType; + + *this = stackup; + + return change; +} + + +void BOARD_STACKUP::BuildDefaultStackupList( BOARD_DESIGN_SETTINGS* aSettings ) +{ + // Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings. + LSET enabledLayer = aSettings->GetEnabledLayers(); + int copperLayerCount = aSettings->GetCopperLayerCount(); + double diel_thickness = aSettings->GetBoardThickness() + - (copperDefaultThickness * copperLayerCount); + diel_thickness /= copperLayerCount - 1; + + int dielectric_idx = 0; + + // Add silk screen, solder mask and solder paste layers on top + if( enabledLayer[F_SilkS] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SILKSCREEN ); + item->m_LayerId = F_SilkS; + item->m_TypeName = _( "Top Silk Screen" ); + Add( item ); + } + + if( enabledLayer[F_Paste] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERPASTE ); + item->m_LayerId = F_Paste; + item->m_TypeName = _( "Top Solder Paste" ); + Add( item ); + } + + if( enabledLayer[F_Mask] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERMASK ); + item->m_LayerId = F_Mask; + item->m_TypeName = _( "Top Solder Mask" ); + Add( item ); + } + + // Add copper and dielectric layers + for( int ii = 0; ii < copperLayerCount; ii++ ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_COPPER ); + item->m_LayerId = ( PCB_LAYER_ID )ii; + Add( item ); + + if( ii == copperLayerCount-1 ) + { + item->m_LayerId = B_Cu; + break; + } + + // Add the dielectric layer: + item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_DIELECTRIC ); + item->m_Thickness = diel_thickness; + item->m_DielectricLayerId = dielectric_idx + 1; + + // Display a dielectric default layer name: + if( (dielectric_idx & 1) == 0 ) + { + item->m_TypeName = _( "core" ); + item->m_Material = "FR4"; + } + else + { + item->m_TypeName = _( "prepreg" ); + item->m_Material = "FR4"; + } + + Add( item ); + dielectric_idx++; + } + + // Add silk screen, solder mask and solder paste layers on bottom + if( enabledLayer[B_Mask] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERMASK ); + item->m_LayerId = B_Mask; + item->m_TypeName = _( "Bottom Solder Mask" ); + Add( item ); + } + + if( enabledLayer[B_Paste] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERPASTE ); + item->m_LayerId = B_Paste; + item->m_TypeName = _( "Bottom Solder Paste" ); + Add( item ); + } + + if( enabledLayer[B_SilkS] ) + { + BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SILKSCREEN ); + item->m_LayerId = B_SilkS; + item->m_TypeName = _( "Bottom Silk Screen" ); + Add( item ); + } + + // Transfer other stackup settings from aSettings + BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor(); + m_HasDielectricConstrains = source_stackup.m_HasDielectricConstrains; + m_EdgeConnectorConstraints = source_stackup.m_EdgeConnectorConstraints; + m_CastellatedPads = source_stackup.m_CastellatedPads; + m_EdgePlating = source_stackup.m_EdgePlating; + m_FinishType = source_stackup.m_FinishType; +} + + + +void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter, + BOARD* aBoard, int aNestLevel ) const +{ + // Board stackup is the ordered list from top to bottom of + // physical layers and substrate used to build the board. + if( m_list.empty() ) + return; + + aFormatter->Print( aNestLevel, "(board_stackup\n" ); + int nest_level = aNestLevel+1; + + for( BOARD_STACKUP_ITEM* item: m_list ) + { + wxString layer_name; + + if( item->m_LayerId == UNDEFINED_LAYER ) + { + layer_name.Printf( "dielectric %d", item->m_DielectricLayerId ); + } + else + layer_name = aBoard->GetLayerName( item->m_LayerId ); + + aFormatter->Print( nest_level, "(layer %s (type %s)", + aFormatter->Quotew( layer_name ).c_str(), + aFormatter->Quotew( item->m_TypeName ).c_str() ); + + if( item->IsThicknessEditable() ) + aFormatter->Print( 0, " (thickness %s)", + FormatInternalUnits( (int)item->m_Thickness ).c_str() ); + + if( item->m_Type == BS_ITEM_TYPE_DIELECTRIC ) + aFormatter->Print( 0, " (material %s)", + aFormatter->Quotew( item->m_Material ).c_str() ); + + if( item->HasEpsilonRValue() ) + aFormatter->Print( 0, " (epsilon_r %g)", item->m_EpsilonR ); + + if( item->HasLossTangentValue() ) + aFormatter->Print( 0, " (loss %s)", + Double2Str(item->m_LossTangent ).c_str() ); + + if( item->IsColorEditable() && !item->m_Color.IsEmpty() + && item->m_Color != NOT_SPECIFIED ) + aFormatter->Print( 0, " (color %s)", + aFormatter->Quotew( item->m_Color ).c_str() ); + + aFormatter->Print( 0, ")\n" ); + } + + // Other infos about board, related to layers and other fabrication specifications + if( !m_FinishType.IsEmpty() && m_FinishType != NOT_SPECIFIED ) + aFormatter->Print( nest_level, "(copper_finish %s)\n", + aFormatter->Quotew( m_FinishType ).c_str() ); + + aFormatter->Print( nest_level, "(dielectric_constrains %s)\n", + m_HasDielectricConstrains ? "yes" : "no" ); + + if( m_EdgeConnectorConstraints > 0 ) + aFormatter->Print( nest_level, "(edge_connector %s)\n", + m_EdgeConnectorConstraints > 1 ? "bevelled": "yes" ); + + if( m_CastellatedPads ) + aFormatter->Print( nest_level, "(castellated_pads yes)\n" ); + + if( m_EdgePlating ) + aFormatter->Print( nest_level, "(edge_plating yes)\n" ); + + aFormatter->Print( aNestLevel, ")\n" ); +} diff --git a/pcbnew/board_stackup_manager/class_board_stackup.h b/pcbnew/board_stackup_manager/class_board_stackup.h new file mode 100644 index 0000000000..5a01c51875 --- /dev/null +++ b/pcbnew/board_stackup_manager/class_board_stackup.h @@ -0,0 +1,191 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2009-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * @file class_board_stackup.h + */ + +#ifndef CLASS_BOARD_STACKUP_H +#define CLASS_BOARD_STACKUP_H + + +#include +#include +#include + +class BOARD_DESIGN_SETTINGS; +class OUTPUTFORMATTER; + +// A enum to manage the different layers inside the stackup layers. +// Note the stackup layers include both dielectric and some layers handled by the board editor +// Therfore a stackup layer item is not exactely like a board layer +enum BOARD_STACKUP_ITEM_TYPE +{ + BS_ITEM_TYPE_UNDEFINED, // For not yet initialized BOARD_STACKUP_ITEM item + BS_ITEM_TYPE_COPPER, // A initialized BOARD_STACKUP_ITEM item for copper layers + BS_ITEM_TYPE_DIELECTRIC, // A initialized BOARD_STACKUP_ITEM item for the + // dielectric between copper layers + BS_ITEM_TYPE_SOLDERPASTE, // A initialized BOARD_STACKUP_ITEM item for solder paste layers + BS_ITEM_TYPE_SOLDERMASK, // A initialized BOARD_STACKUP_ITEM item for solder mask layers + BS_ITEM_TYPE_SILKSCREEN, // A initialized BOARD_STACKUP_ITEM item for silkscreen layers +}; + +// A enum to manage edge connector fab info +enum BS_EDGE_CONNECTOR_CONSTRAINTS +{ + BS_EDGE_CONNECTOR_NONE, // No edge connector in board + BS_EDGE_CONNECTOR_IN_USE, // some edge connector in board + BS_EDGE_CONNECTOR_BEVELLED // Some connector in board, and the connector must be bevelled +}; + +/** + * this class manage one layer needed to make a physical board + * it can be a solder mask, silk screen, copper or a dielectric + */ +class BOARD_STACKUP_ITEM +{ +public: + BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType ); + BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther ); + + BOARD_STACKUP_ITEM_TYPE m_Type; + wxString m_TypeName; /// type name of layer (copper, silk screen, core, prepreg ...) + wxString m_Material; /// type of material (has meaning only for dielectric + int m_DielectricLayerId;/// the "layer" id for dielectric layers, from 1 (top) to 32 (bottom) + wxString m_Color; /// mainly for silkscreen and solder mask + int m_Thickness; /// the physical layer thickness in internal units + double m_EpsilonR; /// For dielectric (and solder mask) the dielectric constant + double m_LossTangent; /// For dielectric (and solder mask) the dielectric loss + 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 + /// really layers for the board editor + + /// @return true if the layer has a meaningfull Epsilon R parameter + /// namely dielectric layers: dielectric and solder mask + bool HasEpsilonRValue(); + + /// @return true if the layer has a meaningfull Dielectric Loss parameter + /// namely dielectric layers: dielectric and solder mask + bool HasLossTangentValue(); + + /// @return true if the material is editable + bool IsMaterialEditable(); + + /// @return true if the color is editable + bool IsColorEditable(); + + /// @return true if Thickness is editable + bool IsThicknessEditable(); +}; + + +/** + * this class manage the layers needed to make a physical board + * they are solder mask, silk screen, copper and dielectric + * Some other layers, used in fabrication, are not managed here because they + * are not used to make a physical board itself + * Note also there are a few other parameters realed to the physical stackup, + * like finish type, impedance control and a few others + */ +class BOARD_STACKUP +{ + // The list of items describing the stackup for fabrication. + // this is not just copper layers, but also mask dielectric layers + std::vector m_list; + +public: + /** The name of external copper finish + */ + wxString m_FinishType; + + /** True if some layers have impedance controlled tracks or have specific + * constrains for micro-wave applications + * If the board has dielectric constrains, the .gbrjob will contain + * info about dielectric constrains: loss tangent and Epsilon rel. + * If not, these values will be not specified in job file. + */ + bool m_HasDielectricConstrains; + + /** True if some layers (copper and/or dielectric) have specific thickness + */ + bool m_HasThicknessConstrains; + + /** If the board has edge connector cards, some constrains can be specifed + * in job file: + * BS_EDGE_CONNECTOR_NONE = no edge connector + * BS_EDGE_CONNECTOR_IN_USE = board has edge connectors + * BS_EDGE_CONNECTOR_BEVELLED = edge connectors are bevelled + */ + BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints; + + bool m_CastellatedPads; ///< True if castellated pads exist + bool m_EdgePlating; ///< True if the edge board is plated + +public: + BOARD_STACKUP(); + BOARD_STACKUP( BOARD_STACKUP& aOther ); + BOARD_STACKUP& operator=( const BOARD_STACKUP& aOther ); + + ~BOARD_STACKUP() { RemoveAll(); } + + std::vector& GetList() { return m_list; } + + /// @return a reference to the layer aIndex, or nullptr if not exists + BOARD_STACKUP_ITEM* GetStackupLayer( int aIndex ); + + /// Delete all items in list and clear the list + void RemoveAll(); + + /// @return the number of layers in the stackup + int GetCount() { return (int) m_list.size(); } + + /// @return the board thickness ( in UI) from the thickness of BOARD_STACKUP_ITEM list + int BuildBoardTicknessFromStackup() const; + + /// Add a new item in stackup layer + void Add( BOARD_STACKUP_ITEM* aItem ) { m_list.push_back( aItem ); } + + /** + * Synchronize the BOARD_STACKUP_ITEM* list with the board. + * Not enabled layers are removed + * Missing layers are added + * @param aSettings, is the current board setting. + * @return true if changes are made + */ + bool SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings ); + + /** + * Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings. + * @param aSettings is the current board setting. + */ + void BuildDefaultStackupList( BOARD_DESIGN_SETTINGS* aSettings ); + + /** + * Writes the stackup info on board file + * @param aFormatter is the OUTPUTFORMATTER used to create the file + * @param aBoard is the board + * @param aNestLevel is the index to nest level to indent the lines in file + */ + void FormatBoardStackup( OUTPUTFORMATTER* aFormatter, + BOARD* aBoard, int aNestLevel ) const; +}; + + +#endif // #ifndef CLASS_BOARD_STACKUP_H diff --git a/pcbnew/board_stackup_manager/stackup_predefined_prms.cpp b/pcbnew/board_stackup_manager/stackup_predefined_prms.cpp new file mode 100644 index 0000000000..0bec1efca9 --- /dev/null +++ b/pcbnew/board_stackup_manager/stackup_predefined_prms.cpp @@ -0,0 +1,115 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2009-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +/** + * @file stackup_predefined_prms.cpp + */ + +#include "class_board_stackup.h" +#include +#include +#include +#include // _HKI definition +#include "stackup_predefined_prms.h" + +// A list of copper finish standard type names +// They are standard names in .gbdjob files, so avoid changing them or +// ensure they are compatible with .gbrjob file spec. +static wxString CopperFinishType[] = +{ + _HKI( NOT_SPECIFIED ), // Not specified, not in .gbrjob file + _HKI("ENIG"), // used in .gbrjob file + _HKI("ENEPIG"), // used in .gbrjob file + _HKI("HAL SnPb"), // used in .gbrjob file + _HKI("HAL lead-free"), // used in .gbrjob file + _HKI("Hard gold"), // used in .gbrjob file + _HKI("Immersion tin"), // used in .gbrjob file + _HKI("Immersion nickel"), // used in .gbrjob file + _HKI("Immersion silver"), // used in .gbrjob file + _HKI("Immersion gold"), // used in .gbrjob file + _HKI("HT_OSP"), // used in .gbrjob file + _HKI("OSP"), // used in .gbrjob file + _HKI("None"), // used in .gbrjob file + _HKI("User defined") // keep this option at end +}; + + +// A list of available colors for solder mask and silkscreen +// These names are used in .gbrjob file, so they are not fully free. +// Use only what is allowed in .gbrjob files. +static FAB_LAYER_COLOR solderMaskColors[] = +{ + { _HKI( NOT_SPECIFIED ), wxColor( 0, 128, 0 ) }, // Not specified, not in .gbrjob file + { _HKI( "Green" ), wxColor( 0, 128, 0 ) }, // used in .gbrjob file + { _HKI( "Red" ), wxColor( 128, 0, 0 ) }, // used in .gbrjob file + { _HKI( "Blue" ), wxColor( 0, 0, 128 ) }, // used in .gbrjob file + { _HKI( "Black" ), wxColor( 20, 20, 20 ) }, // used in .gbrjob file + { _HKI( "White" ), wxColor( 200, 200, 200 ) }, // used in .gbrjob file + { _HKI( "Yellow" ), wxColor( 128, 128, 0 ) }, // used in .gbrjob file + { _HKI( "Purple" ), wxColor( 100, 0, 100 ) }, // used in .gbrjob file + { _HKI( "user defined" ), wxColor( 128, 128, 128 ) }, //free + { "", wxColor( 0, 0, 0 ) } // Sentinel +}; + + +// A list of available substrate material +// These names are used in .gbrjob file, so they are not fully free. +// Use only what is allowed in .gbrjob files. +static FAB_SUBSTRATE substrateMaterial[] = +{ + { _HKI( NOT_SPECIFIED ), 0.0, 0.0 }, // Not specified, not in .gbrjob file + { _HKI( "FR4" ), 4.5, 0.02 }, // used in .gbrjob file + { _HKI( "Polyimide" ), 1.0, 0.0 }, // used in .gbrjob file + { _HKI( "Polyolefin" ), 1.0, 0.0 }, // used in .gbrjob file + { _HKI( "Al" ), 8.7, 0.001 }, // used in .gbrjob file + { _HKI( "PTFE" ), 2.1, 0.0002 }, // used in .gbrjob file + { _HKI( "Teflon" ), 2.1, 0.0002 }, // used in .gbrjob file + { _HKI( "Ceramic" ), 1.0, 0.0 }, // used in .gbrjob file + { _HKI( "user defined" ), 1.0, 0.0 }, // Free + { "", 0.0, 0.0 } // Sentinel +}; + + +wxArrayString GetCopperFinishStandardList( bool aTranslate ) +{ + wxArrayString list; + + for( unsigned ii = 0; ii < arrayDim( CopperFinishType ); ii++ ) + list.Add( aTranslate ? wxGetTranslation( CopperFinishType[ii] ) : CopperFinishType[ii] ); + + return list; +} + + +const FAB_LAYER_COLOR* GetColorStandardList() +{ + return solderMaskColors; +} + + +const FAB_SUBSTRATE* GetSubstrateMaterialStandardList() +{ + return substrateMaterial; +} diff --git a/pcbnew/board_stackup_manager/stackup_predefined_prms.h b/pcbnew/board_stackup_manager/stackup_predefined_prms.h new file mode 100644 index 0000000000..260908aa38 --- /dev/null +++ b/pcbnew/board_stackup_manager/stackup_predefined_prms.h @@ -0,0 +1,77 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2009-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file stackup_predefined_prms.h + */ + +#ifndef STACKUP_PREDEFINED_PRMS_H +#define STACKUP_PREDEFINED_PRMS_H + + +#include +#include + +// Keyword used in file to identify the dielectric layer type +#define KEY_CORE "core" +#define KEY_PREPREG "prepreg" + +// key string used for not specified parameters +#define NOT_SPECIFIED "Undefined" + +// A minor struct to handle color in gerber job file and dialog +struct FAB_LAYER_COLOR +{ + wxString m_ColorName; // the name (in job file) of the color + wxColor m_Color; // the color in r,g,b values (0..255) +}; + + +// A minor struct to handle substrates prms in gerber job file and dialog +struct FAB_SUBSTRATE +{ + wxString m_Name; // the name (in job file) of material + double m_EpsilonR; // the epsilon r of this material + double m_Loss; // the loss (tanD) of this material +}; + + + +/** + * @return a wxArray of standard copper finish names. + * @param aTranslate = false for the initial names, true for translated names + */ +wxArrayString GetCopperFinishStandardList( bool aTranslate ); + +/** + * @return a list of standard FAB_LAYER_COLOR items for silkscreen and solder mask. + */ +const FAB_LAYER_COLOR* GetColorStandardList(); + +/** + * @return a list of standard material items for dielectric. + */ +const FAB_SUBSTRATE* GetSubstrateMaterialStandardList(); + +#endif // #ifndef STACKUP_PREDEFINED_PRMS_H diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index d06e6da586..be0bb8329f 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -111,11 +111,8 @@ struct LAYER */ wxString m_name; ///< The name of the layer, there should be no spaces in this name. - LAYER_T m_type; ///< The type of the layer - bool m_visible; - int m_number; /** diff --git a/pcbnew/exporters/gerber_jobfile_writer.cpp b/pcbnew/exporters/gerber_jobfile_writer.cpp index ebc5744ca1..67f7ed6051 100644 --- a/pcbnew/exporters/gerber_jobfile_writer.cpp +++ b/pcbnew/exporters/gerber_jobfile_writer.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean_Pierre Charras - * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,6 +45,7 @@ #include #include #include +#include GERBER_JOBFILE_WRITER::GERBER_JOBFILE_WRITER( BOARD* aPcb, REPORTER* aReporter ) @@ -280,6 +281,29 @@ void GERBER_JOBFILE_WRITER::addJSONGeneralSpecs() addJSONObject( wxString::Format( "\"BoardThickness\": %.3f,\n", m_pcb->GetDesignSettings().GetBoardThickness()*m_conversionUnits ) ); + + // Copper finish + BOARD_STACKUP brd_stackup = m_pcb->GetDesignSettings().GetStackupDescriptor(); + + if( !brd_stackup.m_FinishType.IsEmpty() ) + addJSONObject( wxString::Format( "\"Finish\": \"%s\",\n", brd_stackup.m_FinishType ) ); + + if( brd_stackup.m_CastellatedPads ) + addJSONObject( "\"Castellated\": \"true\",\n" ); + + if( brd_stackup.m_EdgePlating ) + addJSONObject( "\"EdgePlating\": \"true\",\n" ); + + if( brd_stackup.m_EdgeConnectorConstraints ) + { + addJSONObject( "\"EdgeConnector\": \"true\",\n" ); + + if( brd_stackup.m_EdgeConnectorConstraints == BS_EDGE_CONNECTOR_BEVELLED ) + addJSONObject( "\"EdgeConnectorBevelled\": \"true\",\n" ); + else + addJSONObject( "\"EdgeConnectorBevelled\": \"false\",\n" ); + } + #if 0 // Not yet in use /* The board type according to IPC-2221. There are six primary board types: - Type 1 - Single-sided @@ -555,126 +579,108 @@ void GERBER_JOBFILE_WRITER::addJSONMaterialStackup() addJSONObject( "\"MaterialStackup\":\n" ); openArrayBlock(); - // Build the candidates: only layers on a board are candidates: + // Build the candidates list: LSET maskLayer; + BOARD_STACKUP brd_stackup = m_pcb->GetDesignSettings().GetStackupDescriptor(); - for( unsigned ii = 0; ii < m_params.m_GerberFileList.GetCount(); ii ++ ) - { - PCB_LAYER_ID layer = m_params.m_LayerId[ii]; + // Ensure brd_stackup is up to date (i.e. no change made by SynchronizeWithBoard() ) + bool uptodate = not brd_stackup.SynchronizeWithBoard( &m_pcb->GetDesignSettings() ); - if( layer <= B_Cu ) - maskLayer.set( layer ); - else - { - switch( layer ) - { - case B_Paste: - case F_Paste: - case B_SilkS: - case F_SilkS: - case B_Mask: - case F_Mask: - maskLayer.set( layer ); - break; + if( !uptodate && m_pcb->GetDesignSettings().m_HasStackup ) + m_reporter->Report( _( "Board stackup settings not up to date\n" + "Please fix the stackup" ), REPORTER::RPT_ERROR ); - case Edge_Cuts: - case B_Adhes: - case F_Adhes: - case B_Fab: - case F_Fab: - case Dwgs_User: - case Cmts_User: - case Eco1_User: - case Eco2_User: - case Margin: - case B_CrtYd: - case F_CrtYd: - break; + PCB_LAYER_ID last_copper_layer = F_Cu; - default: - m_reporter->Report( - wxString::Format( "Unexpected layer id %d in job file", layer ), - REPORTER::RPT_ERROR ); - break; - } - } - } - - // build a candidate list (in reverse order: bottom to top): - LSEQ list = maskLayer.SeqStackupBottom2Top(); // Generate the list (top to bottom): - for( int ii = list.size()-1; ii >= 0; --ii ) + for( int ii = 0; ii < brd_stackup.GetCount(); ++ii ) { - PCB_LAYER_ID layer = list[ii]; + BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii ); + double thickness = item->m_Thickness*m_conversionUnits; // layer thickness is always in mm wxString layer_type; - wxString color; - wxString dielectric; - double thickness = 0.0; // layer thickness in mm + std::string layer_name; // for comment - if( layer <= B_Cu ) + switch( item->m_Type ) { + case BS_ITEM_TYPE_COPPER: layer_type = "Copper"; - //thickness = 0.035; - } - else - { - switch( layer ) - { - case B_Paste: - case F_Paste: - layer_type = "SolderPaste"; - break; + layer_name = formatStringToGerber( m_pcb->GetLayerName( item->m_LayerId ) ); + last_copper_layer = item->m_LayerId; + break; - case B_SilkS: - case F_SilkS: - //color = "White"; - layer_type = "Legend"; - break; + case BS_ITEM_TYPE_SILKSCREEN: + layer_type = "Legend"; + layer_name = formatStringToGerber( item->m_TypeName ); + break; - case B_Mask: - case F_Mask: - //color = "Green"; - //thickness = 0.025; - layer_type = "SolderMask"; - break; + case BS_ITEM_TYPE_SOLDERMASK: + layer_type = "SolderMask"; + layer_name = formatStringToGerber( item->m_TypeName ); + break; - default: - break; - } + case BS_ITEM_TYPE_SOLDERPASTE: + layer_type = "SolderPaste"; + layer_name = formatStringToGerber( item->m_TypeName ); + break; + + case BS_ITEM_TYPE_DIELECTRIC: + layer_type = "Dielectric"; + layer_name = formatStringToGerber( wxString::Format( "dielectric layer %d (%s)", + item->m_DielectricLayerId, item->m_TypeName ) ); + break; + + default: + break; } openBlock(); addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) ); - if( !color.IsEmpty() ) - addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", color ) ); + if( item->IsColorEditable() && uptodate ) + { + if( !item->m_Color.IsEmpty() && item->m_Color != NOT_SPECIFIED ) + addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", item->m_Color ) ); + } - if( thickness > 0.0 ) - addJSONObject( wxString::Format( "\"Thickness\": %f,\n", thickness ) ); + if( item->IsThicknessEditable() && uptodate ) + addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) ); + + if( item->m_Type == BS_ITEM_TYPE_DIELECTRIC ) + { + addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->m_Material ) ); + + // 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 ) + { + addJSONObject( wxString::Format( "\"DielectricConstant\": %.1f,\n", item->m_EpsilonR ) ); + addJSONObject( wxString::Format( "\"LossTangent\": %f,\n", item->m_LossTangent ) ); + } + + 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; + + // Add a comment ("Notes"): + wxString note = "\"Notes\": "; + + if( uptodate ) // We can add the dielectric variant ("core" "prepreg" ...): + note << wxString::Format( " \"Type: %s", layer_name.c_str() ); + + note << wxString::Format( " (from %s to %s)\"\n", + formatStringToGerber( m_pcb->GetLayerName( last_copper_layer ) ), + formatStringToGerber( m_pcb->GetLayerName( next_copper_layer ) ) ); + + addJSONObject( note ); + } + else + addJSONObject( wxString::Format( "\"Notes\": \"Layer: %s\",\n", layer_name.c_str() ) ); - std::string strname = formatStringToGerber( m_pcb->GetLayerName( layer ) ); - addJSONObject( wxString::Format( "\"Notes\": \"Layer %s\",\n", strname.c_str() ) ); removeJSONSepararator(); closeBlockWithSep(); - - if( layer < B_Cu ) // Add dielectric between copper layers - { - dielectric = "FR4"; // Temporary - - openBlock(); - addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", "Dielectric" ) ); - - if( thickness > 0.0 ) - addJSONObject( wxString::Format( "\"Thickness\": %f,\n", color ) ); - - if( !dielectric.IsEmpty() ) - addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", dielectric ) ); - - addJSONObject( wxString::Format( "\"Notes\": \"Layers L%d/L%d\",\n", - layer+1, layer+2 ) ); - removeJSONSepararator(); - closeBlockWithSep(); - } } removeJSONSepararator();