You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

779 lines
25 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2009-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "board_stackup.h"
  21. #include <base_units.h>
  22. #include <string_utils.h>
  23. #include <layer_ids.h>
  24. #include <board_design_settings.h>
  25. #include <board.h>
  26. #include <i18n_utility.h> // For _HKI definition
  27. #include "stackup_predefined_prms.h"
  28. BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
  29. {
  30. DIELECTRIC_PRMS item_prms;
  31. m_DielectricPrmsList.emplace_back( item_prms );
  32. m_LayerId = UNDEFINED_LAYER;
  33. m_Type = aType;
  34. SetDielectricLayerId( 1 );
  35. SetEnabled( true );
  36. // Initialize parameters to a usual value for allowed types:
  37. switch( m_Type )
  38. {
  39. case BS_ITEM_TYPE_COPPER:
  40. m_TypeName = KEY_COPPER;
  41. SetThickness( GetCopperDefaultThickness() );
  42. break;
  43. case BS_ITEM_TYPE_DIELECTRIC:
  44. m_TypeName = KEY_CORE; // or prepreg
  45. SetColor( NotSpecifiedPrm() );
  46. SetMaterial( wxT( "FR4" ) ); // or other dielectric name
  47. SetLossTangent( 0.02 ); // for FR4
  48. SetEpsilonR( 4.5 ); // for FR4
  49. break;
  50. case BS_ITEM_TYPE_SOLDERPASTE:
  51. m_TypeName = wxT( "solderpaste" );
  52. break;
  53. case BS_ITEM_TYPE_SOLDERMASK:
  54. m_TypeName = wxT( "soldermask" );
  55. SetColor( NotSpecifiedPrm() );
  56. SetMaterial( NotSpecifiedPrm() ); // or other solder mask material name
  57. SetThickness( GetMaskDefaultThickness() );
  58. SetEpsilonR( DEFAULT_EPSILON_R_SOLDERMASK );
  59. break;
  60. case BS_ITEM_TYPE_SILKSCREEN:
  61. m_TypeName = wxT( "silkscreen" );
  62. SetColor( NotSpecifiedPrm() );
  63. SetMaterial( NotSpecifiedPrm() ); // or other silkscreen material name
  64. SetEpsilonR( DEFAULT_EPSILON_R_SILKSCREEN );
  65. break;
  66. case BS_ITEM_TYPE_UNDEFINED:
  67. break;
  68. }
  69. }
  70. BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( const BOARD_STACKUP_ITEM& aOther )
  71. {
  72. m_LayerId = aOther.m_LayerId;
  73. m_DielectricLayerId = aOther.m_DielectricLayerId;
  74. m_Type = aOther.m_Type;
  75. m_enabled = aOther.m_enabled;
  76. m_DielectricPrmsList = aOther.m_DielectricPrmsList;
  77. m_TypeName = aOther.m_TypeName;
  78. m_LayerName = aOther.m_LayerName;
  79. }
  80. void BOARD_STACKUP_ITEM::AddDielectricPrms( int aDielectricPrmsIdx )
  81. {
  82. // add a DIELECTRIC_PRMS item to m_DielectricPrmsList
  83. DIELECTRIC_PRMS new_prms;
  84. m_DielectricPrmsList.emplace( m_DielectricPrmsList.begin() + aDielectricPrmsIdx, new_prms );
  85. }
  86. void BOARD_STACKUP_ITEM::RemoveDielectricPrms( int aDielectricPrmsIdx )
  87. {
  88. // Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList if possible
  89. if( GetSublayersCount() < 2
  90. || aDielectricPrmsIdx < 0
  91. || aDielectricPrmsIdx >= GetSublayersCount() )
  92. {
  93. return;
  94. }
  95. m_DielectricPrmsList.erase( m_DielectricPrmsList.begin() + aDielectricPrmsIdx );
  96. }
  97. int BOARD_STACKUP_ITEM::GetCopperDefaultThickness()
  98. {
  99. // A reasonable thickness for copper layers:
  100. return pcbIUScale.mmToIU( 0.035 );
  101. }
  102. int BOARD_STACKUP_ITEM::GetMaskDefaultThickness()
  103. {
  104. // A reasonable thickness for solder mask:
  105. return pcbIUScale.mmToIU( 0.01 );
  106. }
  107. // Getters:
  108. wxString BOARD_STACKUP_ITEM::GetColor( int aDielectricSubLayer ) const
  109. {
  110. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  111. return m_DielectricPrmsList[aDielectricSubLayer].m_Color;
  112. }
  113. int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) const
  114. {
  115. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  116. return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness;
  117. }
  118. double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) const
  119. {
  120. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  121. return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent;
  122. }
  123. double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) const
  124. {
  125. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  126. return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR;
  127. }
  128. bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) const
  129. {
  130. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  131. return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked;
  132. }
  133. wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) const
  134. {
  135. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  136. return m_DielectricPrmsList[aDielectricSubLayer].m_Material;
  137. }
  138. // Setters:
  139. void BOARD_STACKUP_ITEM::SetColor( const wxString& aColorName , int aDielectricSubLayer )
  140. {
  141. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  142. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  143. m_DielectricPrmsList[aDielectricSubLayer].m_Color = aColorName;
  144. }
  145. void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer )
  146. {
  147. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  148. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  149. m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness;
  150. }
  151. void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer )
  152. {
  153. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  154. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  155. m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg;
  156. }
  157. void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer )
  158. {
  159. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  160. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  161. m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon;
  162. }
  163. void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer )
  164. {
  165. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  166. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  167. m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked;
  168. }
  169. void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer )
  170. {
  171. wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
  172. if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
  173. m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName;
  174. }
  175. bool BOARD_STACKUP_ITEM::HasEpsilonRValue() const
  176. {
  177. return m_Type == BS_ITEM_TYPE_DIELECTRIC
  178. || m_Type == BS_ITEM_TYPE_SOLDERMASK;
  179. };
  180. bool BOARD_STACKUP_ITEM::HasLossTangentValue() const
  181. {
  182. return m_Type == BS_ITEM_TYPE_DIELECTRIC
  183. || m_Type == BS_ITEM_TYPE_SOLDERMASK;
  184. };
  185. bool BOARD_STACKUP_ITEM::HasMaterialValue( int aDielectricSubLayer ) const
  186. {
  187. // return true if the material is specified
  188. return IsMaterialEditable() && IsPrmSpecified( GetMaterial( aDielectricSubLayer ) );
  189. }
  190. bool BOARD_STACKUP_ITEM::IsMaterialEditable() const
  191. {
  192. return m_Type == BS_ITEM_TYPE_DIELECTRIC
  193. || m_Type == BS_ITEM_TYPE_SOLDERMASK
  194. || m_Type == BS_ITEM_TYPE_SILKSCREEN;
  195. }
  196. bool BOARD_STACKUP_ITEM::IsColorEditable() const
  197. {
  198. return m_Type == BS_ITEM_TYPE_DIELECTRIC
  199. || m_Type == BS_ITEM_TYPE_SOLDERMASK
  200. || m_Type == BS_ITEM_TYPE_SILKSCREEN;
  201. }
  202. bool BOARD_STACKUP_ITEM::IsThicknessEditable() const
  203. {
  204. return m_Type == BS_ITEM_TYPE_COPPER
  205. || m_Type == BS_ITEM_TYPE_DIELECTRIC
  206. || m_Type == BS_ITEM_TYPE_SOLDERMASK;
  207. }
  208. wxString BOARD_STACKUP_ITEM::FormatEpsilonR( int aDielectricSubLayer ) const
  209. {
  210. // return a wxString to print/display Epsilon R
  211. // note: we do not want scientific notation
  212. wxString txt = UIDouble2Str( GetEpsilonR( aDielectricSubLayer ) );
  213. return txt;
  214. }
  215. wxString BOARD_STACKUP_ITEM::FormatLossTangent( int aDielectricSubLayer ) const
  216. {
  217. // return a wxString to print/display Loss Tangent
  218. // note: we do not want scientific notation
  219. wxString txt = UIDouble2Str( GetLossTangent( aDielectricSubLayer ) );
  220. return txt;
  221. }
  222. wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() const
  223. {
  224. // return a wxString to print/display a dielectric name
  225. wxString lname;
  226. lname.Printf( _( "Dielectric %d" ), GetDielectricLayerId() );
  227. return lname;
  228. }
  229. BOARD_STACKUP::BOARD_STACKUP()
  230. {
  231. m_HasDielectricConstrains = false; // True if some dielectric layers have constrains
  232. // (Loss tg and Epison R)
  233. m_HasThicknessConstrains = false; // True if some dielectric or copper layers have constrains
  234. m_EdgeConnectorConstraints = BS_EDGE_CONNECTOR_NONE;
  235. m_CastellatedPads = false; // True if some castellated pads exist
  236. m_EdgePlating = false; // True if edge board is plated
  237. m_FinishType = wxT( "None" ); // undefined finish type
  238. }
  239. BOARD_STACKUP::BOARD_STACKUP( const BOARD_STACKUP& aOther )
  240. {
  241. m_HasDielectricConstrains = aOther.m_HasDielectricConstrains;
  242. m_HasThicknessConstrains = aOther.m_HasThicknessConstrains;
  243. m_EdgeConnectorConstraints = aOther.m_EdgeConnectorConstraints;
  244. m_CastellatedPads = aOther.m_CastellatedPads;
  245. m_EdgePlating = aOther.m_EdgePlating;
  246. m_FinishType = aOther.m_FinishType;
  247. // All items in aOther.m_list have to be duplicated, because aOther.m_list
  248. // manage pointers to these items
  249. for( BOARD_STACKUP_ITEM* item : aOther.m_list )
  250. {
  251. BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
  252. Add( dup_item );
  253. }
  254. }
  255. BOARD_STACKUP& BOARD_STACKUP::operator=( const BOARD_STACKUP& aOther )
  256. {
  257. m_HasDielectricConstrains = aOther.m_HasDielectricConstrains;
  258. m_HasThicknessConstrains = aOther.m_HasThicknessConstrains;
  259. m_EdgeConnectorConstraints = aOther.m_EdgeConnectorConstraints;
  260. m_CastellatedPads = aOther.m_CastellatedPads;
  261. m_EdgePlating = aOther.m_EdgePlating;
  262. m_FinishType = aOther.m_FinishType;
  263. RemoveAll();
  264. // All items in aOther.m_list have to be duplicated, because aOther.m_list
  265. // manage pointers to these items
  266. for( BOARD_STACKUP_ITEM* item : aOther.m_list )
  267. {
  268. BOARD_STACKUP_ITEM* dup_item = new BOARD_STACKUP_ITEM( *item );
  269. Add( dup_item );
  270. }
  271. return *this;
  272. }
  273. void BOARD_STACKUP::RemoveAll()
  274. {
  275. for( BOARD_STACKUP_ITEM* item : m_list )
  276. delete item;
  277. m_list.clear();
  278. }
  279. BOARD_STACKUP_ITEM* BOARD_STACKUP::GetStackupLayer( int aIndex )
  280. {
  281. if( aIndex < 0 || aIndex >= GetCount() )
  282. return nullptr;
  283. return GetList()[aIndex];
  284. }
  285. int BOARD_STACKUP::BuildBoardThicknessFromStackup() const
  286. {
  287. // return the board thickness from the thickness of BOARD_STACKUP_ITEM list
  288. int thickness = 0;
  289. for( BOARD_STACKUP_ITEM* item : m_list )
  290. {
  291. if( item->IsThicknessEditable() && item->IsEnabled() )
  292. {
  293. thickness += item->GetThickness();
  294. // dielectric layers can have more than one main layer
  295. // add thickness of all sublayers
  296. for( int idx = 1; idx < item->GetSublayersCount(); idx++ )
  297. {
  298. thickness += item->GetThickness( idx );
  299. }
  300. }
  301. }
  302. return thickness;
  303. }
  304. bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
  305. {
  306. bool change = false;
  307. // Build the suitable stackup:
  308. BOARD_STACKUP stackup;
  309. stackup.BuildDefaultStackupList( aSettings );
  310. // First, find removed layers:
  311. for( BOARD_STACKUP_ITEM* curr_item: m_list )
  312. {
  313. bool found = false;
  314. for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
  315. {
  316. if( curr_item->GetBrdLayerId() != UNDEFINED_LAYER )
  317. {
  318. if( item->GetBrdLayerId() == curr_item->GetBrdLayerId() )
  319. {
  320. found = true;
  321. break;
  322. }
  323. }
  324. else // curr_item = dielectric layer
  325. {
  326. if( item->GetBrdLayerId() != UNDEFINED_LAYER )
  327. continue;
  328. if( item->GetDielectricLayerId() == curr_item->GetDielectricLayerId() )
  329. {
  330. found = true;
  331. break;
  332. }
  333. }
  334. }
  335. if( !found ) // a layer was removed: a change is found
  336. {
  337. change = true;
  338. break;
  339. }
  340. }
  341. // Now initialize all stackup items to the initial values, when exist
  342. for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
  343. {
  344. bool found = false;
  345. // Search for initial settings:
  346. for( const BOARD_STACKUP_ITEM* initial_item : m_list )
  347. {
  348. if( item->GetBrdLayerId() != UNDEFINED_LAYER )
  349. {
  350. if( item->GetBrdLayerId() == initial_item->GetBrdLayerId() )
  351. {
  352. *item = *initial_item;
  353. found = true;
  354. break;
  355. }
  356. }
  357. else // dielectric layer: see m_DielectricLayerId for identification
  358. {
  359. // Compare dielectric layer with dielectric layer
  360. if( initial_item->GetBrdLayerId() != UNDEFINED_LAYER )
  361. continue;
  362. if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() )
  363. {
  364. *item = *initial_item;
  365. found = true;
  366. break;
  367. }
  368. }
  369. }
  370. if( !found )
  371. {
  372. change = true;
  373. }
  374. }
  375. // Transfer layer settings:
  376. *this = stackup;
  377. // Transfer other stackup settings from aSettings
  378. const BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
  379. m_HasDielectricConstrains = source_stackup.m_HasDielectricConstrains;
  380. m_EdgeConnectorConstraints = source_stackup.m_EdgeConnectorConstraints;
  381. m_CastellatedPads = source_stackup.m_CastellatedPads;
  382. m_EdgePlating = source_stackup.m_EdgePlating;
  383. m_FinishType = source_stackup.m_FinishType;
  384. return change;
  385. }
  386. void BOARD_STACKUP::BuildDefaultStackupList( const BOARD_DESIGN_SETTINGS* aSettings,
  387. int aActiveCopperLayersCount )
  388. {
  389. // Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
  390. // Note: the m_TypeName string is made translatable using _HKI marker, but is not
  391. // translated when building the stackup.
  392. // It will be used as this in files, and can be translated only in dialog
  393. // if aSettings == NULL, build a full stackup (with 32 copper layers)
  394. LSET enabledLayer = aSettings ? aSettings->GetEnabledLayers() : StackupAllowedBrdLayers();
  395. int copperLayerCount = aSettings ? aSettings->GetCopperLayerCount() : B_Cu+1;
  396. // We need to calculate a suitable dielectric layer thickness.
  397. // If no settings, and if aActiveCopperLayersCount is given, use it
  398. // (If no settings, and no aActiveCopperLayersCount, the full 32 layers are used)
  399. int activeCuLayerCount = copperLayerCount;
  400. if( aSettings == nullptr && aActiveCopperLayersCount > 0 )
  401. activeCuLayerCount = aActiveCopperLayersCount;
  402. int brd__thickness = aSettings ? aSettings->GetBoardThickness() : pcbIUScale.mmToIU( 1.6 );
  403. int diel_thickness = brd__thickness -
  404. ( BOARD_STACKUP_ITEM::GetCopperDefaultThickness() * activeCuLayerCount );
  405. // Take in account the solder mask thickness:
  406. int sm_count = ( enabledLayer & LSET( 2, F_Mask, B_Mask) ).count();
  407. diel_thickness -= BOARD_STACKUP_ITEM::GetMaskDefaultThickness() * sm_count;
  408. diel_thickness /= std::max( 1, activeCuLayerCount - 1 );
  409. int dielectric_idx = 0;
  410. // Add silk screen, solder mask and solder paste layers on top
  411. if( enabledLayer[F_SilkS] )
  412. {
  413. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SILKSCREEN );
  414. item->SetBrdLayerId( F_SilkS );
  415. item->SetTypeName( _HKI( "Top Silk Screen" ) );
  416. Add( item );
  417. }
  418. if( enabledLayer[F_Paste] )
  419. {
  420. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERPASTE );
  421. item->SetBrdLayerId( F_Paste );
  422. item->SetTypeName( _HKI( "Top Solder Paste" ) );
  423. Add( item );
  424. }
  425. if( enabledLayer[F_Mask] )
  426. {
  427. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERMASK );
  428. item->SetBrdLayerId( F_Mask );
  429. item->SetTypeName( _HKI( "Top Solder Mask" ) );
  430. Add( item );
  431. }
  432. // Add copper and dielectric layers
  433. for( int ii = 0; ii < copperLayerCount; ii++ )
  434. {
  435. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_COPPER );
  436. item->SetBrdLayerId( ( PCB_LAYER_ID )ii );
  437. item->SetTypeName( KEY_COPPER );
  438. Add( item );
  439. if( ii == copperLayerCount-1 )
  440. {
  441. item->SetBrdLayerId( B_Cu );
  442. break;
  443. }
  444. // Add the dielectric layer:
  445. item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_DIELECTRIC );
  446. item->SetThickness( diel_thickness );
  447. item->SetDielectricLayerId( dielectric_idx + 1 );
  448. // Display a dielectric default layer name:
  449. if( (dielectric_idx & 1) == 0 )
  450. {
  451. item->SetTypeName( KEY_CORE );
  452. item->SetMaterial( wxT( "FR4" ) );
  453. }
  454. else
  455. {
  456. item->SetTypeName( KEY_PREPREG );
  457. item->SetMaterial( wxT( "FR4" ) );
  458. }
  459. Add( item );
  460. dielectric_idx++;
  461. }
  462. // Add silk screen, solder mask and solder paste layers on bottom
  463. if( enabledLayer[B_Mask] )
  464. {
  465. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERMASK );
  466. item->SetBrdLayerId( B_Mask );
  467. item->SetTypeName( _HKI( "Bottom Solder Mask" ) );
  468. Add( item );
  469. }
  470. if( enabledLayer[B_Paste] )
  471. {
  472. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SOLDERPASTE );
  473. item->SetBrdLayerId( B_Paste );
  474. item->SetTypeName( _HKI( "Bottom Solder Paste" ) );
  475. Add( item );
  476. }
  477. if( enabledLayer[B_SilkS] )
  478. {
  479. BOARD_STACKUP_ITEM* item = new BOARD_STACKUP_ITEM( BS_ITEM_TYPE_SILKSCREEN );
  480. item->SetBrdLayerId( B_SilkS );
  481. item->SetTypeName( _HKI( "Bottom Silk Screen" ) );
  482. Add( item );
  483. }
  484. // Transfer other stackup settings from aSettings
  485. if( aSettings )
  486. {
  487. const BOARD_STACKUP& source_stackup = aSettings->GetStackupDescriptor();
  488. m_EdgeConnectorConstraints = source_stackup.m_EdgeConnectorConstraints;
  489. m_CastellatedPads = source_stackup.m_CastellatedPads;
  490. m_EdgePlating = source_stackup.m_EdgePlating;
  491. m_FinishType = source_stackup.m_FinishType;
  492. }
  493. }
  494. void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter,
  495. const BOARD* aBoard, int aNestLevel ) const
  496. {
  497. // Board stackup is the ordered list from top to bottom of
  498. // physical layers and substrate used to build the board.
  499. if( m_list.empty() )
  500. return;
  501. aFormatter->Print( aNestLevel, "(stackup\n" );
  502. int nest_level = aNestLevel+1;
  503. // Note:
  504. // Unspecified parameters are not stored in file.
  505. for( BOARD_STACKUP_ITEM* item: m_list )
  506. {
  507. wxString layer_name;
  508. if( item->GetBrdLayerId() == UNDEFINED_LAYER )
  509. layer_name.Printf( wxT( "dielectric %d" ), item->GetDielectricLayerId() );
  510. else
  511. layer_name = LSET::Name( item->GetBrdLayerId() );
  512. aFormatter->Print( nest_level, "(layer %s (type %s)",
  513. aFormatter->Quotew( layer_name ).c_str(),
  514. aFormatter->Quotew( item->GetTypeName() ).c_str() );
  515. // Output other parameters ( in sub layer list there is at least one item)
  516. for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
  517. {
  518. if( idx ) // not for the main (first) layer.
  519. {
  520. aFormatter->Print( 0, "\n" );
  521. aFormatter->Print( nest_level+1, "addsublayer" );
  522. }
  523. if( item->IsColorEditable() && IsPrmSpecified( item->GetColor( idx ) ) )
  524. {
  525. aFormatter->Print( 0, " (color %s)",
  526. aFormatter->Quotew( item->GetColor( idx ) ).c_str() );
  527. }
  528. if( item->IsThicknessEditable() )
  529. {
  530. if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
  531. aFormatter->Print( 0, " (thickness %s locked)",
  532. EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, item->GetThickness( idx ) ).c_str() );
  533. else
  534. aFormatter->Print( 0, " (thickness %s)",
  535. EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, item->GetThickness( idx ) ).c_str() );
  536. }
  537. if( item->HasMaterialValue( idx ) )
  538. aFormatter->Print( 0, " (material %s)",
  539. aFormatter->Quotew( item->GetMaterial( idx ) ).c_str() );
  540. if( item->HasEpsilonRValue() && item->HasMaterialValue( idx ) )
  541. aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR( idx ) );
  542. if( item->HasLossTangentValue() && item->HasMaterialValue( idx ) )
  543. aFormatter->Print( 0, " (loss_tangent %s)",
  544. FormatDouble2Str( item->GetLossTangent( idx ) ).c_str() );
  545. }
  546. aFormatter->Print( 0, ")\n" );
  547. }
  548. // Other infos about board, related to layers and other fabrication specifications
  549. if( IsPrmSpecified( m_FinishType ) )
  550. {
  551. aFormatter->Print( nest_level, "(copper_finish %s)\n",
  552. aFormatter->Quotew( m_FinishType ).c_str() );
  553. }
  554. aFormatter->Print( nest_level, "(dielectric_constraints %s)\n",
  555. m_HasDielectricConstrains ? "yes" : "no" );
  556. if( m_EdgeConnectorConstraints > 0 )
  557. {
  558. aFormatter->Print( nest_level, "(edge_connector %s)\n",
  559. m_EdgeConnectorConstraints > 1 ? "bevelled": "yes" );
  560. }
  561. if( m_CastellatedPads )
  562. aFormatter->Print( nest_level, "(castellated_pads yes)\n" );
  563. if( m_EdgePlating )
  564. aFormatter->Print( nest_level, "(edge_plating yes)\n" );
  565. aFormatter->Print( aNestLevel, ")\n" );
  566. }
  567. int BOARD_STACKUP::GetLayerDistance( PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer ) const
  568. {
  569. wxASSERT( IsCopperLayer( aFirstLayer ) && IsCopperLayer( aSecondLayer ) );
  570. if( aFirstLayer == aSecondLayer )
  571. return 0;
  572. if( aSecondLayer < aFirstLayer )
  573. std::swap( aFirstLayer, aSecondLayer );
  574. int total = 0;
  575. bool start = false;
  576. bool half = false;
  577. for( BOARD_STACKUP_ITEM* item : m_list )
  578. {
  579. // Will be UNDEFINED_LAYER for dielectrics
  580. PCB_LAYER_ID layer = item->GetBrdLayerId();
  581. if( layer != UNDEFINED_LAYER && !IsCopperLayer( layer ) )
  582. continue; // Silk/mask layer
  583. // Reached the start copper layer? Start counting the next dielectric after it
  584. if( !start && ( layer != UNDEFINED_LAYER && layer >= aFirstLayer ) )
  585. {
  586. start = true;
  587. half = true;
  588. }
  589. else if( !start )
  590. continue;
  591. // Reached the stop copper layer? we're done
  592. if( start && ( layer != UNDEFINED_LAYER && layer >= aSecondLayer ) )
  593. half = true;
  594. for( int sublayer = 0; sublayer < item->GetSublayersCount(); sublayer++ )
  595. {
  596. int subThickness = item->GetThickness( sublayer );
  597. total += half ? ( subThickness / 2 ) : subThickness;
  598. }
  599. half = false;
  600. if( layer != UNDEFINED_LAYER && layer >= aSecondLayer )
  601. break;
  602. }
  603. return total;
  604. }
  605. bool IsPrmSpecified( const wxString& aPrmValue )
  606. {
  607. // return true if the param value is specified:
  608. if( !aPrmValue.IsEmpty()
  609. && ( aPrmValue.CmpNoCase( NotSpecifiedPrm() ) != 0 )
  610. && aPrmValue != wxGetTranslation( NotSpecifiedPrm() ) )
  611. return true;
  612. return false;
  613. }