pyedb 0.51.2__py3-none-any.whl → 0.52.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -1
- pyedb/configuration/configuration.py +186 -340
- pyedb/dotnet/database/edb_data/padstacks_data.py +8 -2
- pyedb/dotnet/database/layout_validation.py +3 -13
- pyedb/dotnet/database/stackup.py +4 -3
- pyedb/dotnet/edb.py +35 -1
- pyedb/grpc/database/__init__.py +0 -1
- pyedb/grpc/database/definition/materials.py +7 -7
- pyedb/grpc/database/hierarchy/pin_pair_model.py +1 -1
- pyedb/grpc/database/net/differential_pair.py +2 -1
- pyedb/grpc/database/source_excitations.py +10 -10
- pyedb/grpc/edb.py +86 -173
- pyedb/grpc/edb_init.py +4 -2
- {pyedb-0.51.2.dist-info → pyedb-0.52.0.dist-info}/METADATA +4 -4
- {pyedb-0.51.2.dist-info → pyedb-0.52.0.dist-info}/RECORD +17 -17
- {pyedb-0.51.2.dist-info → pyedb-0.52.0.dist-info}/LICENSE +0 -0
- {pyedb-0.51.2.dist-info → pyedb-0.52.0.dist-info}/WHEEL +0 -0
pyedb/__init__.py
CHANGED
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
|
-
|
|
23
22
|
from datetime import datetime
|
|
24
23
|
import json
|
|
25
24
|
import os
|
|
26
25
|
from pathlib import Path
|
|
26
|
+
import warnings
|
|
27
27
|
|
|
28
28
|
import toml
|
|
29
29
|
|
|
@@ -34,341 +34,9 @@ from pyedb.dotnet.database.definition.package_def import PackageDef
|
|
|
34
34
|
class Configuration:
|
|
35
35
|
"""Enables export and import of a JSON configuration file that can be applied to a new or existing design."""
|
|
36
36
|
|
|
37
|
-
class Grpc:
|
|
38
|
-
def __init__(self, parent):
|
|
39
|
-
self.parent = parent
|
|
40
|
-
self._pedb = parent._pedb
|
|
41
|
-
|
|
42
|
-
def configuration_stackup(self, kwargs):
|
|
43
|
-
if kwargs.get("fix_padstack_def"):
|
|
44
|
-
from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
|
|
45
|
-
|
|
46
|
-
pedb_defs = self._pedb.padstacks.definitions
|
|
47
|
-
temp = []
|
|
48
|
-
for _, pdef in pedb_defs.items():
|
|
49
|
-
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
50
|
-
cfg_def.api.retrieve_parameters_from_edb()
|
|
51
|
-
temp.append(cfg_def)
|
|
52
|
-
self.parent.cfg_data.stackup.apply()
|
|
53
|
-
for cfg_pdef in temp:
|
|
54
|
-
cfg_pdef.api.set_parameters_to_edb()
|
|
55
|
-
else:
|
|
56
|
-
temp_pdef_data = {}
|
|
57
|
-
from ansys.edb.core.definition.padstack_def_data import (
|
|
58
|
-
PadType as GrpcPadType,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
62
|
-
for layer in pdef.data.layer_names:
|
|
63
|
-
result = pdef.data.get_pad_parameters(layer, GrpcPadType.REGULAR_PAD)
|
|
64
|
-
if len(result) == 4:
|
|
65
|
-
# polygon based
|
|
66
|
-
temp_pdef_data[pdef_name] = pdef.data
|
|
67
|
-
break
|
|
68
|
-
self.parent.cfg_data.stackup.apply()
|
|
69
|
-
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
70
|
-
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
71
|
-
pdef._padstack_def_data = pdef_data
|
|
72
|
-
|
|
73
|
-
def _load_stackup(self):
|
|
74
|
-
"""Imports stackup information from json."""
|
|
75
|
-
data = self.data["stackup"]
|
|
76
|
-
materials = data.get("materials")
|
|
77
|
-
|
|
78
|
-
if materials:
|
|
79
|
-
edb_materials = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
|
|
80
|
-
for mat in materials:
|
|
81
|
-
name = mat["name"].lower()
|
|
82
|
-
if name in edb_materials:
|
|
83
|
-
self._pedb.materials.delete_material(edb_materials[name])
|
|
84
|
-
for mat in materials:
|
|
85
|
-
self._pedb.materials.add_material(**mat)
|
|
86
|
-
|
|
87
|
-
layers = data.get("layers")
|
|
88
|
-
|
|
89
|
-
if layers:
|
|
90
|
-
input_signal_layers = [i for i in layers if i["type"].lower() == "signal"]
|
|
91
|
-
if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
|
|
92
|
-
self._pedb.logger.error("Input signal layer count do not match.")
|
|
93
|
-
return False
|
|
94
|
-
|
|
95
|
-
removal_list = []
|
|
96
|
-
lc_signal_layers = []
|
|
97
|
-
for name, obj in self._pedb.stackup.all_layers.items():
|
|
98
|
-
if obj.type == "dielectric":
|
|
99
|
-
removal_list.append(name)
|
|
100
|
-
elif obj.type == "signal":
|
|
101
|
-
lc_signal_layers.append(obj.id)
|
|
102
|
-
for l in removal_list:
|
|
103
|
-
self._pedb.stackup.remove_layer(l)
|
|
104
|
-
|
|
105
|
-
# update all signal layers
|
|
106
|
-
id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
|
|
107
|
-
signal_idx = 0
|
|
108
|
-
for l in layers:
|
|
109
|
-
if l["type"] == "signal":
|
|
110
|
-
layer_id = lc_signal_layers[signal_idx]
|
|
111
|
-
layer_name = id_name[layer_id]
|
|
112
|
-
self._pedb.stackup.layers[layer_name].update(**l)
|
|
113
|
-
signal_idx = signal_idx + 1
|
|
114
|
-
|
|
115
|
-
# add all dielectric layers. Dielectric layers must be added last. Otherwise,
|
|
116
|
-
# dielectric layer will occupy signal and document layer id.
|
|
117
|
-
prev_layer_clone = None
|
|
118
|
-
l = layers.pop(0)
|
|
119
|
-
if l["type"] == "signal":
|
|
120
|
-
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
121
|
-
else:
|
|
122
|
-
prev_layer_clone = self._pedb.stackup.add_layer_top(**l)
|
|
123
|
-
for idx, l in enumerate(layers):
|
|
124
|
-
if l["type"] == "dielectric":
|
|
125
|
-
prev_layer_clone = self._pedb.stackup.add_layer_below(
|
|
126
|
-
base_layer_name=prev_layer_clone.name, **l
|
|
127
|
-
)
|
|
128
|
-
elif l["type"] == "signal":
|
|
129
|
-
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
130
|
-
|
|
131
|
-
def _load_package_def(self):
|
|
132
|
-
"""Imports package definition information from JSON."""
|
|
133
|
-
comps = self._pedb.components.instances
|
|
134
|
-
for pkgd in self.parent.data["package_definitions"]:
|
|
135
|
-
name = pkgd["name"]
|
|
136
|
-
if name in self._pedb.definitions.package:
|
|
137
|
-
self._pedb.definitions.package[name].delete()
|
|
138
|
-
extent_bounding_box = pkgd.get("extent_bounding_box", None)
|
|
139
|
-
if extent_bounding_box:
|
|
140
|
-
package_def = PackageDef(self._pedb, name=name, extent_bounding_box=extent_bounding_box)
|
|
141
|
-
else:
|
|
142
|
-
package_def = PackageDef(self._pedb, name=name, component_part_name=pkgd["component_definition"])
|
|
143
|
-
package_def.maximum_power = pkgd["maximum_power"]
|
|
144
|
-
package_def.therm_cond = pkgd["therm_cond"]
|
|
145
|
-
package_def.theta_jb = pkgd["theta_jb"]
|
|
146
|
-
package_def.theta_jc = pkgd["theta_jc"]
|
|
147
|
-
package_def.height = pkgd["height"]
|
|
148
|
-
|
|
149
|
-
heatsink = pkgd.get("heatsink", None)
|
|
150
|
-
if heatsink:
|
|
151
|
-
package_def.set_heatsink(
|
|
152
|
-
heatsink["fin_base_height"],
|
|
153
|
-
heatsink["fin_height"],
|
|
154
|
-
heatsink["fin_orientation"],
|
|
155
|
-
heatsink["fin_spacing"],
|
|
156
|
-
heatsink["fin_thickness"],
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
comp_def_name = pkgd["component_definition"]
|
|
160
|
-
comp_def = self._pedb.definitions.component[comp_def_name]
|
|
161
|
-
|
|
162
|
-
comp_list = dict()
|
|
163
|
-
if pkgd["apply_to_all"]:
|
|
164
|
-
comp_list.update(
|
|
165
|
-
{
|
|
166
|
-
refdes: comp
|
|
167
|
-
for refdes, comp in comp_def.components.items()
|
|
168
|
-
if refdes not in pkgd["components"]
|
|
169
|
-
}
|
|
170
|
-
)
|
|
171
|
-
else:
|
|
172
|
-
comp_list.update(
|
|
173
|
-
{refdes: comp for refdes, comp in comp_def.components.items() if refdes in pkgd["components"]}
|
|
174
|
-
)
|
|
175
|
-
for _, i in comp_list.items():
|
|
176
|
-
i.package_def = name
|
|
177
|
-
|
|
178
|
-
def get_data_from_db(self, **kwargs):
|
|
179
|
-
"""Get configuration data from layout.
|
|
180
|
-
|
|
181
|
-
Parameters
|
|
182
|
-
----------
|
|
183
|
-
stackup
|
|
184
|
-
|
|
185
|
-
Returns
|
|
186
|
-
-------
|
|
187
|
-
|
|
188
|
-
"""
|
|
189
|
-
self._pedb.logger.info("Getting data from layout database.")
|
|
190
|
-
data = {}
|
|
191
|
-
if kwargs.get("general", False):
|
|
192
|
-
data["general"] = self.parent.cfg_data.general.get_data_from_db()
|
|
193
|
-
if kwargs.get("stackup", False):
|
|
194
|
-
data["stackup"] = self.parent.cfg_data.stackup.get_data_from_db()
|
|
195
|
-
if kwargs.get("package_definitions", False):
|
|
196
|
-
data["package_definitions"] = self.parent.cfg_data.package_definitions.get_data_from_db()
|
|
197
|
-
if kwargs.get("setups", False):
|
|
198
|
-
setups = self.parent.cfg_data.setups
|
|
199
|
-
setups.retrieve_parameters_from_edb()
|
|
200
|
-
data["setups"] = setups.to_dict()
|
|
201
|
-
if kwargs.get("sources", False):
|
|
202
|
-
data["sources"] = self.parent.cfg_data.sources.get_data_from_db()
|
|
203
|
-
if kwargs.get("ports", False):
|
|
204
|
-
data["ports"] = self.parent.cfg_data.ports.get_data_from_db()
|
|
205
|
-
if kwargs.get("components", False) or kwargs.get("s_parameters", False):
|
|
206
|
-
self.parent.cfg_data.components.retrieve_parameters_from_edb()
|
|
207
|
-
components = []
|
|
208
|
-
for i in self.parent.cfg_data.components.components:
|
|
209
|
-
if i.type == "io":
|
|
210
|
-
components.append(i.get_attributes())
|
|
211
|
-
components.append(i.get_attributes())
|
|
212
|
-
|
|
213
|
-
if kwargs.get("components", False):
|
|
214
|
-
data["components"] = components
|
|
215
|
-
elif kwargs.get("s_parameters", False):
|
|
216
|
-
data["s_parameters"] = self.parent.cfg_data.s_parameters.get_data_from_db(components)
|
|
217
|
-
if kwargs.get("nets", False):
|
|
218
|
-
data["nets"] = self.parent.cfg_data.nets.get_data_from_db()
|
|
219
|
-
if kwargs.get("pin_groups", False):
|
|
220
|
-
data["pin_groups"] = self.parent.cfg_data.pin_groups.get_data_from_db()
|
|
221
|
-
if kwargs.get("operations", False):
|
|
222
|
-
data["operations"] = self.parent.cfg_data.operations.get_data_from_db()
|
|
223
|
-
if kwargs.get("padstacks", False):
|
|
224
|
-
self.parent.cfg_data.padstacks.retrieve_parameters_from_edb()
|
|
225
|
-
definitions = []
|
|
226
|
-
for i in self.parent.cfg_data.padstacks.definitions:
|
|
227
|
-
definitions.append(i.get_attributes())
|
|
228
|
-
instances = []
|
|
229
|
-
for i in self.parent.cfg_data.padstacks.instances:
|
|
230
|
-
instances.append(i.get_attributes())
|
|
231
|
-
data["padstacks"] = dict()
|
|
232
|
-
data["padstacks"]["definitions"] = definitions
|
|
233
|
-
data["padstacks"]["instances"] = instances
|
|
234
|
-
|
|
235
|
-
if kwargs.get("boundaries", False):
|
|
236
|
-
data["boundaries"] = self.parent.cfg_data.boundaries.get_data_from_db()
|
|
237
|
-
|
|
238
|
-
return data
|
|
239
|
-
|
|
240
|
-
def export(
|
|
241
|
-
self,
|
|
242
|
-
file_path,
|
|
243
|
-
stackup=True,
|
|
244
|
-
package_definitions=False,
|
|
245
|
-
setups=True,
|
|
246
|
-
sources=True,
|
|
247
|
-
ports=True,
|
|
248
|
-
nets=True,
|
|
249
|
-
pin_groups=True,
|
|
250
|
-
operations=True,
|
|
251
|
-
components=True,
|
|
252
|
-
boundaries=True,
|
|
253
|
-
s_parameters=True,
|
|
254
|
-
padstacks=True,
|
|
255
|
-
general=True,
|
|
256
|
-
):
|
|
257
|
-
"""Export the configuration data from layout to a file.
|
|
258
|
-
|
|
259
|
-
Parameters
|
|
260
|
-
----------
|
|
261
|
-
file_path : str, Path
|
|
262
|
-
File path to export the configuration data.
|
|
263
|
-
stackup : bool
|
|
264
|
-
Whether to export stackup or not.
|
|
265
|
-
package_definitions : bool
|
|
266
|
-
Whether to export package definitions or not.
|
|
267
|
-
setups : bool
|
|
268
|
-
Whether to export setups or not.
|
|
269
|
-
sources : bool
|
|
270
|
-
Whether to export sources or not.
|
|
271
|
-
ports : bool
|
|
272
|
-
Whether to export ports or not.
|
|
273
|
-
nets : bool
|
|
274
|
-
Whether to export nets.
|
|
275
|
-
pin_groups : bool
|
|
276
|
-
Whether to export pin groups.
|
|
277
|
-
operations : bool
|
|
278
|
-
Whether to export operations.
|
|
279
|
-
components : bool
|
|
280
|
-
Whether to export component.
|
|
281
|
-
boundaries : bool
|
|
282
|
-
Whether to export boundaries.
|
|
283
|
-
s_parameters : bool
|
|
284
|
-
Whether to export s_parameters.
|
|
285
|
-
padstacks : bool
|
|
286
|
-
Whether to export padstacks.
|
|
287
|
-
general : bool
|
|
288
|
-
Whether to export general information.
|
|
289
|
-
Returns
|
|
290
|
-
-------
|
|
291
|
-
bool
|
|
292
|
-
"""
|
|
293
|
-
data = self.get_data_from_db(
|
|
294
|
-
stackup=stackup,
|
|
295
|
-
package_definitions=package_definitions,
|
|
296
|
-
setups=setups,
|
|
297
|
-
sources=sources,
|
|
298
|
-
ports=ports,
|
|
299
|
-
nets=nets,
|
|
300
|
-
pin_groups=pin_groups,
|
|
301
|
-
operations=operations,
|
|
302
|
-
components=components,
|
|
303
|
-
boundaries=boundaries,
|
|
304
|
-
s_parameters=s_parameters,
|
|
305
|
-
padstacks=padstacks,
|
|
306
|
-
general=general,
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
file_path = file_path if isinstance(file_path, Path) else Path(file_path)
|
|
310
|
-
file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
|
|
311
|
-
|
|
312
|
-
for comp in data["components"]:
|
|
313
|
-
for key, value in comp.items():
|
|
314
|
-
try:
|
|
315
|
-
json.dumps(value)
|
|
316
|
-
print(f"Key '{key}' is serializable.")
|
|
317
|
-
except TypeError as e:
|
|
318
|
-
print(f"Key '{key}' failed: {e}")
|
|
319
|
-
|
|
320
|
-
with open(file_path, "w") as f:
|
|
321
|
-
if file_path.suffix == ".json":
|
|
322
|
-
json.dump(data, f, ensure_ascii=False, indent=4)
|
|
323
|
-
else:
|
|
324
|
-
toml.dump(data, f)
|
|
325
|
-
return True if os.path.isfile(file_path) else False
|
|
326
|
-
|
|
327
|
-
class DotNet(Grpc):
|
|
328
|
-
def __init__(self, parent):
|
|
329
|
-
super().__init__(parent)
|
|
330
|
-
|
|
331
|
-
def configuration_stackup(self, kwargs):
|
|
332
|
-
if kwargs.get("fix_padstack_def"):
|
|
333
|
-
from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
|
|
334
|
-
|
|
335
|
-
pedb_defs = self._pedb.padstacks.definitions
|
|
336
|
-
temp = []
|
|
337
|
-
for _, pdef in pedb_defs.items():
|
|
338
|
-
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
339
|
-
cfg_def.api.retrieve_parameters_from_edb()
|
|
340
|
-
temp.append(cfg_def)
|
|
341
|
-
self.parent.cfg_data.stackup.apply()
|
|
342
|
-
for cfg_pdef in temp:
|
|
343
|
-
cfg_pdef.api.set_parameters_to_edb()
|
|
344
|
-
else:
|
|
345
|
-
temp_pdef_data = {}
|
|
346
|
-
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
347
|
-
pdef_data = pdef._padstack_def_data
|
|
348
|
-
for lyr_name in list(pdef_data.GetLayerNames()):
|
|
349
|
-
result = pdef_data.GetPadParametersValue(
|
|
350
|
-
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
351
|
-
)
|
|
352
|
-
flag, pad_shape, params, offset_x, offset_y, rotation = result
|
|
353
|
-
if flag is False:
|
|
354
|
-
result = pdef_data.GetPolygonalPadParameters(
|
|
355
|
-
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
356
|
-
)
|
|
357
|
-
flag, polygon_data, offset_x, offset_y, rotation = result
|
|
358
|
-
if flag:
|
|
359
|
-
temp_pdef_data[pdef_name] = pdef_data
|
|
360
|
-
break
|
|
361
|
-
self.parent.cfg_data.stackup.apply()
|
|
362
|
-
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
363
|
-
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
364
|
-
pdef._padstack_def_data = pdef_data
|
|
365
|
-
|
|
366
37
|
def __init__(self, pedb):
|
|
367
38
|
self._pedb = pedb
|
|
368
|
-
|
|
369
|
-
self.api = self.Grpc(self)
|
|
370
|
-
else:
|
|
371
|
-
self.api = self.DotNet(self)
|
|
39
|
+
|
|
372
40
|
self._components = self._pedb.components.instances
|
|
373
41
|
self.data = {}
|
|
374
42
|
self._s_parameter_library = ""
|
|
@@ -439,6 +107,8 @@ class Configuration:
|
|
|
439
107
|
|
|
440
108
|
def run(self, **kwargs):
|
|
441
109
|
"""Apply configuration settings to the current design"""
|
|
110
|
+
if kwargs.get("fix_padstack_def"):
|
|
111
|
+
warnings.warn("fix_padstack_def is deprecated.", DeprecationWarning)
|
|
442
112
|
|
|
443
113
|
if self.cfg_data.variables:
|
|
444
114
|
self.cfg_data.variables.apply()
|
|
@@ -480,7 +150,7 @@ class Configuration:
|
|
|
480
150
|
now = datetime.now()
|
|
481
151
|
|
|
482
152
|
# Configure stackup
|
|
483
|
-
self.
|
|
153
|
+
self.configuration_stackup()
|
|
484
154
|
self._pedb.logger.info(f"Updating stackup finished. Time lapse {datetime.now() - now}")
|
|
485
155
|
now = datetime.now()
|
|
486
156
|
|
|
@@ -523,13 +193,123 @@ class Configuration:
|
|
|
523
193
|
|
|
524
194
|
return True
|
|
525
195
|
|
|
196
|
+
def configuration_stackup(self):
|
|
197
|
+
temp_pdef_data = {}
|
|
198
|
+
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
199
|
+
pdef_edb_object = pdef._padstack_def_data
|
|
200
|
+
temp_pdef_data[pdef_name] = pdef_edb_object
|
|
201
|
+
|
|
202
|
+
temp_p_inst_layer_map = {}
|
|
203
|
+
for p_inst in self._pedb.layout.padstack_instances:
|
|
204
|
+
temp_p_inst_layer_map[p_inst.id] = p_inst._edb_object.GetLayerMap()
|
|
205
|
+
|
|
206
|
+
self.cfg_data.stackup.apply()
|
|
207
|
+
|
|
208
|
+
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
209
|
+
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
210
|
+
pdef._padstack_def_data = pdef_data
|
|
211
|
+
|
|
212
|
+
for p_inst in self._pedb.layout.padstack_instances:
|
|
213
|
+
p_inst._edb_object.SetLayerMap(temp_p_inst_layer_map[p_inst.id])
|
|
214
|
+
|
|
526
215
|
def _load_stackup(self):
|
|
527
216
|
"""Imports stackup information from json."""
|
|
528
|
-
self.
|
|
217
|
+
data = self.data["stackup"]
|
|
218
|
+
materials = data.get("materials")
|
|
219
|
+
|
|
220
|
+
if materials:
|
|
221
|
+
edb_materials = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
|
|
222
|
+
for mat in materials:
|
|
223
|
+
name = mat["name"].lower()
|
|
224
|
+
if name in edb_materials:
|
|
225
|
+
self._pedb.materials.delete_material(edb_materials[name])
|
|
226
|
+
for mat in materials:
|
|
227
|
+
self._pedb.materials.add_material(**mat)
|
|
228
|
+
|
|
229
|
+
layers = data.get("layers")
|
|
230
|
+
|
|
231
|
+
if layers:
|
|
232
|
+
input_signal_layers = [i for i in layers if i["type"].lower() == "signal"]
|
|
233
|
+
if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
|
|
234
|
+
self._pedb.logger.error("Input signal layer count do not match.")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
removal_list = []
|
|
238
|
+
lc_signal_layers = []
|
|
239
|
+
for name, obj in self._pedb.stackup.all_layers.items():
|
|
240
|
+
if obj.type == "dielectric":
|
|
241
|
+
removal_list.append(name)
|
|
242
|
+
elif obj.type == "signal":
|
|
243
|
+
lc_signal_layers.append(obj.id)
|
|
244
|
+
for l in removal_list:
|
|
245
|
+
self._pedb.stackup.remove_layer(l)
|
|
246
|
+
|
|
247
|
+
# update all signal layers
|
|
248
|
+
id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
|
|
249
|
+
signal_idx = 0
|
|
250
|
+
for l in layers:
|
|
251
|
+
if l["type"] == "signal":
|
|
252
|
+
layer_id = lc_signal_layers[signal_idx]
|
|
253
|
+
layer_name = id_name[layer_id]
|
|
254
|
+
self._pedb.stackup.layers[layer_name].update(**l)
|
|
255
|
+
signal_idx = signal_idx + 1
|
|
256
|
+
|
|
257
|
+
# add all dielectric layers. Dielectric layers must be added last. Otherwise,
|
|
258
|
+
# dielectric layer will occupy signal and document layer id.
|
|
259
|
+
prev_layer_clone = None
|
|
260
|
+
l = layers.pop(0)
|
|
261
|
+
if l["type"] == "signal":
|
|
262
|
+
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
263
|
+
else:
|
|
264
|
+
prev_layer_clone = self._pedb.stackup.add_layer_top(**l)
|
|
265
|
+
for idx, l in enumerate(layers):
|
|
266
|
+
if l["type"] == "dielectric":
|
|
267
|
+
prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **l)
|
|
268
|
+
elif l["type"] == "signal":
|
|
269
|
+
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
529
270
|
|
|
530
271
|
def _load_package_def(self):
|
|
531
272
|
"""Imports package definition information from JSON."""
|
|
532
|
-
self.
|
|
273
|
+
comps = self._pedb.components.instances
|
|
274
|
+
for pkgd in self.data["package_definitions"]:
|
|
275
|
+
name = pkgd["name"]
|
|
276
|
+
if name in self._pedb.definitions.package:
|
|
277
|
+
self._pedb.definitions.package[name].delete()
|
|
278
|
+
extent_bounding_box = pkgd.get("extent_bounding_box", None)
|
|
279
|
+
if extent_bounding_box:
|
|
280
|
+
package_def = PackageDef(self._pedb, name=name, extent_bounding_box=extent_bounding_box)
|
|
281
|
+
else:
|
|
282
|
+
package_def = PackageDef(self._pedb, name=name, component_part_name=pkgd["component_definition"])
|
|
283
|
+
package_def.maximum_power = pkgd["maximum_power"]
|
|
284
|
+
package_def.therm_cond = pkgd["therm_cond"]
|
|
285
|
+
package_def.theta_jb = pkgd["theta_jb"]
|
|
286
|
+
package_def.theta_jc = pkgd["theta_jc"]
|
|
287
|
+
package_def.height = pkgd["height"]
|
|
288
|
+
|
|
289
|
+
heatsink = pkgd.get("heatsink", None)
|
|
290
|
+
if heatsink:
|
|
291
|
+
package_def.set_heatsink(
|
|
292
|
+
heatsink["fin_base_height"],
|
|
293
|
+
heatsink["fin_height"],
|
|
294
|
+
heatsink["fin_orientation"],
|
|
295
|
+
heatsink["fin_spacing"],
|
|
296
|
+
heatsink["fin_thickness"],
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
comp_def_name = pkgd["component_definition"]
|
|
300
|
+
comp_def = self._pedb.definitions.component[comp_def_name]
|
|
301
|
+
|
|
302
|
+
comp_list = dict()
|
|
303
|
+
if pkgd["apply_to_all"]:
|
|
304
|
+
comp_list.update(
|
|
305
|
+
{refdes: comp for refdes, comp in comp_def.components.items() if refdes not in pkgd["components"]}
|
|
306
|
+
)
|
|
307
|
+
else:
|
|
308
|
+
comp_list.update(
|
|
309
|
+
{refdes: comp for refdes, comp in comp_def.components.items() if refdes in pkgd["components"]}
|
|
310
|
+
)
|
|
311
|
+
for _, i in comp_list.items():
|
|
312
|
+
i.package_def = name
|
|
533
313
|
|
|
534
314
|
def get_data_from_db(self, **kwargs):
|
|
535
315
|
"""Get configuration data from layout.
|
|
@@ -542,7 +322,56 @@ class Configuration:
|
|
|
542
322
|
-------
|
|
543
323
|
|
|
544
324
|
"""
|
|
545
|
-
|
|
325
|
+
self._pedb.logger.info("Getting data from layout database.")
|
|
326
|
+
data = {}
|
|
327
|
+
if kwargs.get("general", False):
|
|
328
|
+
data["general"] = self.cfg_data.general.get_data_from_db()
|
|
329
|
+
if kwargs.get("stackup", False):
|
|
330
|
+
data["stackup"] = self.cfg_data.stackup.get_data_from_db()
|
|
331
|
+
if kwargs.get("package_definitions", False):
|
|
332
|
+
data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
|
|
333
|
+
if kwargs.get("setups", False):
|
|
334
|
+
setups = self.cfg_data.setups
|
|
335
|
+
setups.retrieve_parameters_from_edb()
|
|
336
|
+
data["setups"] = setups.to_dict()
|
|
337
|
+
if kwargs.get("sources", False):
|
|
338
|
+
data["sources"] = self.cfg_data.sources.get_data_from_db()
|
|
339
|
+
if kwargs.get("ports", False):
|
|
340
|
+
data["ports"] = self.cfg_data.ports.get_data_from_db()
|
|
341
|
+
if kwargs.get("components", False) or kwargs.get("s_parameters", False):
|
|
342
|
+
self.cfg_data.components.retrieve_parameters_from_edb()
|
|
343
|
+
components = []
|
|
344
|
+
for i in self.cfg_data.components.components:
|
|
345
|
+
if i.type == "io":
|
|
346
|
+
components.append(i.get_attributes())
|
|
347
|
+
components.append(i.get_attributes())
|
|
348
|
+
|
|
349
|
+
if kwargs.get("components", False):
|
|
350
|
+
data["components"] = components
|
|
351
|
+
elif kwargs.get("s_parameters", False):
|
|
352
|
+
data["s_parameters"] = self.cfg_data.s_parameters.get_data_from_db(components)
|
|
353
|
+
if kwargs.get("nets", False):
|
|
354
|
+
data["nets"] = self.cfg_data.nets.get_data_from_db()
|
|
355
|
+
if kwargs.get("pin_groups", False):
|
|
356
|
+
data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db()
|
|
357
|
+
if kwargs.get("operations", False):
|
|
358
|
+
data["operations"] = self.cfg_data.operations.get_data_from_db()
|
|
359
|
+
if kwargs.get("padstacks", False):
|
|
360
|
+
self.cfg_data.padstacks.retrieve_parameters_from_edb()
|
|
361
|
+
definitions = []
|
|
362
|
+
for i in self.cfg_data.padstacks.definitions:
|
|
363
|
+
definitions.append(i.get_attributes())
|
|
364
|
+
instances = []
|
|
365
|
+
for i in self.cfg_data.padstacks.instances:
|
|
366
|
+
instances.append(i.get_attributes())
|
|
367
|
+
data["padstacks"] = dict()
|
|
368
|
+
data["padstacks"]["definitions"] = definitions
|
|
369
|
+
data["padstacks"]["instances"] = instances
|
|
370
|
+
|
|
371
|
+
if kwargs.get("boundaries", False):
|
|
372
|
+
data["boundaries"] = self.cfg_data.boundaries.get_data_from_db()
|
|
373
|
+
|
|
374
|
+
return data
|
|
546
375
|
|
|
547
376
|
def export(
|
|
548
377
|
self,
|
|
@@ -597,8 +426,7 @@ class Configuration:
|
|
|
597
426
|
-------
|
|
598
427
|
bool
|
|
599
428
|
"""
|
|
600
|
-
|
|
601
|
-
file_path,
|
|
429
|
+
data = self.get_data_from_db(
|
|
602
430
|
stackup=stackup,
|
|
603
431
|
package_definitions=package_definitions,
|
|
604
432
|
setups=setups,
|
|
@@ -613,3 +441,21 @@ class Configuration:
|
|
|
613
441
|
padstacks=padstacks,
|
|
614
442
|
general=general,
|
|
615
443
|
)
|
|
444
|
+
|
|
445
|
+
file_path = file_path if isinstance(file_path, Path) else Path(file_path)
|
|
446
|
+
file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
|
|
447
|
+
|
|
448
|
+
for comp in data["components"]:
|
|
449
|
+
for key, value in comp.items():
|
|
450
|
+
try:
|
|
451
|
+
json.dumps(value)
|
|
452
|
+
print(f"Key '{key}' is serializable.")
|
|
453
|
+
except TypeError as e:
|
|
454
|
+
print(f"Key '{key}' failed: {e}")
|
|
455
|
+
|
|
456
|
+
with open(file_path, "w") as f:
|
|
457
|
+
if file_path.suffix == ".json":
|
|
458
|
+
json.dump(data, f, ensure_ascii=False, indent=4)
|
|
459
|
+
else:
|
|
460
|
+
toml.dump(data, f)
|
|
461
|
+
return True if os.path.isfile(file_path) else False
|
|
@@ -1807,8 +1807,14 @@ class EDBPadstackInstance(Primitive):
|
|
|
1807
1807
|
|
|
1808
1808
|
val = String("")
|
|
1809
1809
|
_, name = self._edb_padstackinstance.GetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, val)
|
|
1810
|
-
|
|
1811
|
-
|
|
1810
|
+
aedt_name = str(name).strip("'")
|
|
1811
|
+
if aedt_name == "":
|
|
1812
|
+
if self.is_pin and self.component:
|
|
1813
|
+
aedt_name = f"{self.component.name}-{self.component_pin}"
|
|
1814
|
+
elif self.component_pin:
|
|
1815
|
+
aedt_name = self.component_pin
|
|
1816
|
+
self.aedt_name = aedt_name
|
|
1817
|
+
return aedt_name
|
|
1812
1818
|
|
|
1813
1819
|
@aedt_name.setter
|
|
1814
1820
|
def aedt_name(self, value):
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
import re
|
|
24
24
|
|
|
25
|
-
from pyedb.dotnet.clr_module import String
|
|
26
25
|
from pyedb.dotnet.database.edb_data.padstacks_data import EDBPadstackInstance
|
|
27
26
|
from pyedb.dotnet.database.edb_data.primitives_data import Primitive
|
|
28
27
|
from pyedb.generic.general_methods import generate_unique_name
|
|
@@ -325,21 +324,12 @@ class LayoutValidation:
|
|
|
325
324
|
return
|
|
326
325
|
|
|
327
326
|
def padstacks_no_name(self, fix=False):
|
|
327
|
+
"""Find and fix padstacks without aedt_name."""
|
|
328
328
|
pds = self._pedb.layout.padstack_instances
|
|
329
329
|
counts = 0
|
|
330
|
-
via_count = 1
|
|
331
330
|
for obj in pds:
|
|
332
|
-
|
|
333
|
-
_, name = obj._edb_object.GetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, val)
|
|
334
|
-
name = str(name).strip("'")
|
|
335
|
-
if name == "":
|
|
331
|
+
if obj.aedt_name == "":
|
|
336
332
|
counts += 1
|
|
337
333
|
if fix:
|
|
338
|
-
|
|
339
|
-
obj._edb_object.SetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, f"Via{via_count}")
|
|
340
|
-
via_count = via_count + 1
|
|
341
|
-
else:
|
|
342
|
-
obj._edb_object.SetProductProperty(
|
|
343
|
-
self._pedb.edb_api.ProductId.Designer, 11, f"{obj.component.name}-{obj.component_pin}"
|
|
344
|
-
)
|
|
334
|
+
obj.aedt_name = f"via_{obj.id}"
|
|
345
335
|
self._pedb._logger.info(f"Found {counts}/{len(pds)} padstacks have no name.")
|
pyedb/dotnet/database/stackup.py
CHANGED
|
@@ -2234,7 +2234,7 @@ class Stackup(LayerCollection):
|
|
|
2234
2234
|
material.loss_tanget = material_properties["DielectricLossTangent"]
|
|
2235
2235
|
return True
|
|
2236
2236
|
|
|
2237
|
-
def _import_xml(self, file_path
|
|
2237
|
+
def _import_xml(self, file_path):
|
|
2238
2238
|
"""Read external xml file and convert into json file.
|
|
2239
2239
|
You can use xml file to import layer stackup but using json file is recommended.
|
|
2240
2240
|
see :class:`pyedb.dotnet.database.edb_data.simulation_configuration.SimulationConfiguration´ class to
|
|
@@ -2304,7 +2304,8 @@ class Stackup(LayerCollection):
|
|
|
2304
2304
|
layers.append(layer)
|
|
2305
2305
|
stackup_dict["layers"] = layers
|
|
2306
2306
|
cfg = {"stackup": stackup_dict}
|
|
2307
|
-
|
|
2307
|
+
self._pedb.configuration.load(cfg)
|
|
2308
|
+
return self._pedb.configuration.run()
|
|
2308
2309
|
|
|
2309
2310
|
def _export_xml(self, file_path):
|
|
2310
2311
|
"""Export stackup information to an external XMLfile.
|
|
@@ -2391,7 +2392,7 @@ class Stackup(LayerCollection):
|
|
|
2391
2392
|
elif file_path.endswith(".json"):
|
|
2392
2393
|
return self._import_json(file_path, rename=rename)
|
|
2393
2394
|
elif file_path.endswith(".xml"):
|
|
2394
|
-
return self._import_xml(file_path
|
|
2395
|
+
return self._import_xml(file_path)
|
|
2395
2396
|
else:
|
|
2396
2397
|
return False
|
|
2397
2398
|
|