-
Notifications
You must be signed in to change notification settings - Fork 0
/
Layers.py
208 lines (176 loc) · 7.44 KB
/
Layers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
from PCModel import PCModel
import Html
import yaml
# OLD CODE: These are layer properties only.
#
# TODO: The code has to understand that:
# component height varies, and is specified in the IDF file
# thermal pad displaces air
# solder displaces air and the thermal pad
# solder mask displaces air, thermal pad, and solder
# copper displaces prepreg
# solder mask coats the top layer and the top layer copper
# TODO: Handle coatings
# TODO: Handle thermal pads -
# Is the way that thermal pads expand and flow easy to model?
# Are MEs already able to do this?
# If not, would they be interested in a tool that does this?
# TODO: Handle attached parts with maximum size
# TODO: Display graphical visualization of the stackup that is in the layers.js file.
# TODO: Attached parts, especially resistors, might be handled with
# vias for electrodes, alumina layer, metal resistance material layer.
# The idea would be to get a picture of the oval hotspot on the top of the part.
# In this case the layer thickness would vary with different part sizes.
class Layers(PCModel):
def __init__(self, fn):
# The old code
# self.loadConfig(config)
# The new code
self.rowName= "layer"
self.colName= "property"
self.config_js_fn= fn
with open (self.config_js_fn, "r") as jsonHandle:
jsonContents= jsonHandle.read()
self.layerConfig= yaml.load(jsonContents)
# print yaml.dump(self.layerConfig)
self.layers= self.layerConfig['Stackup']
self.setLayerTableCols()
self.checkProperties(self.layers, self.tableCols)
self.setLayerTableUnits()
self.convertUnits(self.layers, self.tableCols, self.tableUnits)
self.propDict= self.createTableDictionary(self.layers, self.tableCols)
self.calculateBoardThickness()
self.calculateLayerZCoords()
return
def setLayerTableCols(self):
self.tableCols= ['name', 'matl', 'type', 'thickness', 'displaces', 'coverage',
'z_bottom', 'z_top', 'adheres_to']
self.tableColSQLTypes= {'name':'TEXT', 'matl':'TEXT', 'type':'TEXT', 'thickness':'real',
'displaces':'TEXT', 'coverage':'real', 'z_bottom':'real', 'z_top':'real',
'adheres_to':'TEXT'}
def setLayerTableUnits(self):
self.tableUnits= {'name':'', 'matl':'', 'type':'', 'thickness':'m', 'displaces':'', 'coverage':'',
'start':'', 'stop':'', 'z_bottom':'m', 'z_top':'m', 'adheres_to':'' }
def calculateBoardThickness(self):
# Store coverage and thickness for adjacent fill layer calculations
coverage= []
thickness= []
for layer in self.layers:
cov= 1.0
if layer['coverage'] != '-':
cov= float(layer['coverage'])
coverage.append(cov)
thickness.append(layer['thickness'])
# Calculate amounts of fill due to embeddeding prepreg into adjacent layers,
# which contain a variable coverage of copper (0 to 100%)
layerIdx=0
fillFromAbove=[]
fillFromBelow=[]
fillLayer=[]
for layer in self.layers:
if layer['type'] == 'Fill':
fillAbove= 0.0
if layerIdx > 0:
fillAbove= (1.0 - coverage[layerIdx-1]) * thickness[layerIdx-1]
fillBelow= 0.0
if layerIdx < len(coverage) - 1:
fillBelow= (1.0 - coverage[layerIdx+1]) * thickness[layerIdx+1]
fillFromBelow.append(fillBelow)
fillFromAbove.append(fillAbove)
fillLayer.append(layerIdx)
layerIdx= layerIdx + 1
# Calculate laminated thickness of prepreg layers due to being embedded into an
# adjacent trace layer with less than 100% coverage.
# Modify the layer table to reflect new calculated thickness,
# which is just for the material between the copper layers.
# This prepreg thickness does not include the thickness of the copper layers.
fillIdx=0
for layerIdx in fillLayer:
layer= self.layers[layerIdx]
laminatedThickness= layer['thickness'] - fillFromAbove[fillIdx] - fillFromBelow[fillIdx]
# print "Fill Layer Name: " + str(layer['name'])
# print " Unlaminated layer Thickness: " + str(layer['thickness'])
# print " Fill from above: " + str(fillFromAbove[fillIdx])
# print " Fill from below: " + str(fillFromBelow[fillIdx])
# print " Laminated thickness: " + str(laminatedThickness) + "\n"
layer['thickness']= laminatedThickness
self.propDict[layer['name']]['thickness']= laminatedThickness
fillIdx = fillIdx + 1
# Board thickness with embedding
self.boardThickness=0
for layer in self.layers:
self.boardThickness= self.boardThickness + layer['thickness']
return
def calculateLayerZCoords(self):
height= self.boardThickness
for layer in self.layers:
layer['z_top']= height
self.propDict[layer['name']]['z_top']= height;
height= height - layer['thickness']
layer['z_bottom']= height
self.propDict[layer['name']]['z_bottom']= height;
return
# HTML Generation
def genHTMLLayersTable(self, matl, h):
layerHtml= ''
row= ''
for prop in self.tableCols:
row += h.tdh(prop)
layerHtml += h.tr(row)
row= ''
for prop in self.tableCols:
row += h.tdh(self.tableUnits[prop])
layerHtml += h.tr(row)
# How to set a default for a layer property? Would like to have default coverage=1
thisLayerName=''
for layer in self.layers:
row= ''
for prop in self.tableCols:
if prop in layer:
if prop == 'name':
row += h.tdh(layer[prop])
thisLayerName= layer[prop]
elif prop == 'matl':
thisMaterialName= layer[prop]
if thisMaterialName in matl.propDict:
if 'color' in matl.propDict[thisMaterialName]:
row += h.tdc(layer[prop], matl.propDict[thisMaterialName]['color'])
else:
print "No color set for material: " + str(thisMaterialName) + " in layer " + str(thisLayerName)
row += h.tdc(layer[prop], 'yellow')
else:
print "Unrecognized material name: " + str(thisMaterialName) + " in layer " + str(thisLayerName)
row += h.tdc(thisMaterialName, 'red')
else:
row += h.td(layer[prop])
else:
# Default layer properties
if prop == 'coverage':
row += h.td('1.0')
row += h.td(' ')
layerHtml += h.tr(row)
return h.h3('Stackup') + h.table(layerHtml)
# The old code.
# These are actually the simulation layers, and should be a different object from the
# PC board physical stackup.
#def loadConfig(self, config):
#self.numdoublelayers= 0
#self.numintlayers= 0
#for lyr in config:
#self.__dict__[lyr['name']]= lyr['index']
#if (lyr['type'] == 'double'):
#self.numdoublelayers = self.numdoublelayers + 1
#if (lyr['type'] == 'int'):
#self.numintlayers = self.numintlayers + 1
def helpString(self):
return """
Layers are specified in a JSON file with this format:
{
"Stackup":
[
{ "name":"topside_cu", "matl":"Cu", "thickness":"1.2mil", "type":"Rigid"},
{ "name":"core", "matl":"Core", "thickness":"12mil" , "type":"Rigid"}
],
}
The filename of the JSON file is passed into the __init__ and a Layers object is returned.
"""