From c1eee56785aeb8ba0293e71b97a173f449a85df3 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 23 Jun 2019 13:20:22 +0200 Subject: [PATCH] Prepare changes to support a better board stack management in .gbrjob file. In .gbrjob file, one can specify the thickness and color of some layers. Currently, there is no way to enter these parameters. This commit prepare a better support of .gbrjob files. --- common/CMakeLists.txt | 1 + include/board_design_settings.h | 18 + pcbnew/CMakeLists.txt | 6 + pcbnew/board_design_settings.cpp | 6 +- .../class_board_stackup.cpp | 484 ++++++++++++++++++ .../class_board_stackup.h | 191 +++++++ .../stackup_predefined_prms.cpp | 115 +++++ .../stackup_predefined_prms.h | 77 +++ pcbnew/class_board.h | 3 - pcbnew/exporters/gerber_jobfile_writer.cpp | 202 ++++---- 10 files changed, 999 insertions(+), 104 deletions(-) create mode 100644 pcbnew/board_stackup_manager/class_board_stackup.cpp create mode 100644 pcbnew/board_stackup_manager/class_board_stackup.h create mode 100644 pcbnew/board_stackup_manager/stackup_predefined_prms.cpp create mode 100644 pcbnew/board_stackup_manager/stackup_predefined_prms.h 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();