pyedb 0.42.0__py3-none-any.whl → 0.44.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/cfg_boundaries.py +155 -72
- pyedb/configuration/cfg_components.py +1 -1
- pyedb/configuration/cfg_general.py +34 -16
- pyedb/configuration/cfg_modeler.py +162 -67
- pyedb/configuration/cfg_nets.py +33 -17
- pyedb/configuration/cfg_operations.py +63 -31
- pyedb/configuration/cfg_package_definition.py +113 -52
- pyedb/configuration/cfg_padstacks.py +611 -256
- pyedb/configuration/cfg_pin_groups.py +75 -33
- pyedb/configuration/cfg_ports_sources.py +75 -15
- pyedb/configuration/cfg_s_parameter_models.py +125 -70
- pyedb/configuration/cfg_setup.py +301 -257
- pyedb/configuration/cfg_stackup.py +166 -90
- pyedb/configuration/configuration.py +342 -209
- pyedb/dotnet/database/edb_data/design_options.py +19 -1
- pyedb/dotnet/database/edb_data/padstacks_data.py +16 -6
- pyedb/dotnet/database/geometry/polygon_data.py +4 -2
- pyedb/dotnet/database/padstack.py +6 -2
- pyedb/dotnet/database/sim_setup_data/data/sweep_data.py +63 -10
- pyedb/dotnet/database/utilities/simulation_setup.py +14 -30
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +30 -0
- pyedb/dotnet/edb.py +75 -105
- pyedb/grpc/database/components.py +1 -1
- pyedb/grpc/database/definition/component_def.py +15 -0
- pyedb/grpc/database/definition/component_pin.py +1 -1
- pyedb/grpc/database/definition/materials.py +27 -0
- pyedb/grpc/database/definition/package_def.py +20 -2
- pyedb/grpc/database/definition/padstack_def.py +5 -2
- pyedb/grpc/database/hierarchy/component.py +4 -2
- pyedb/grpc/database/hierarchy/pingroup.py +12 -8
- pyedb/grpc/database/layers/layer.py +28 -0
- pyedb/grpc/database/layers/stackup_layer.py +281 -40
- pyedb/grpc/database/layout/layout.py +12 -6
- pyedb/grpc/database/modeler.py +8 -8
- pyedb/grpc/database/primitive/bondwire.py +3 -3
- pyedb/grpc/database/primitive/circle.py +1 -1
- pyedb/grpc/database/primitive/padstack_instance.py +13 -3
- pyedb/grpc/database/primitive/path.py +2 -2
- pyedb/grpc/database/primitive/polygon.py +3 -3
- pyedb/grpc/database/primitive/primitive.py +1 -1
- pyedb/grpc/database/primitive/rectangle.py +2 -2
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +73 -30
- pyedb/grpc/database/source_excitations.py +7 -7
- pyedb/grpc/database/stackup.py +14 -6
- pyedb/grpc/database/terminal/bundle_terminal.py +3 -3
- pyedb/grpc/database/terminal/edge_terminal.py +2 -2
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +42 -2
- pyedb/grpc/database/terminal/pingroup_terminal.py +35 -2
- pyedb/grpc/database/terminal/point_terminal.py +10 -1
- pyedb/grpc/database/terminal/terminal.py +4 -4
- pyedb/grpc/database/utility/hfss_extent_info.py +14 -10
- pyedb/grpc/edb.py +8 -8
- pyedb/misc/misc.py +13 -0
- {pyedb-0.42.0.dist-info → pyedb-0.44.0.dist-info}/METADATA +1 -1
- {pyedb-0.42.0.dist-info → pyedb-0.44.0.dist-info}/RECORD +58 -58
- {pyedb-0.42.0.dist-info → pyedb-0.44.0.dist-info}/LICENSE +0 -0
- {pyedb-0.42.0.dist-info → pyedb-0.44.0.dist-info}/WHEEL +0 -0
|
@@ -33,8 +33,331 @@ from pyedb.dotnet.database.definition.package_def import PackageDef
|
|
|
33
33
|
class Configuration:
|
|
34
34
|
"""Enables export and import of a JSON configuration file that can be applied to a new or existing design."""
|
|
35
35
|
|
|
36
|
+
class Grpc:
|
|
37
|
+
def __init__(self, parent):
|
|
38
|
+
self.parent = parent
|
|
39
|
+
self._pedb = parent._pedb
|
|
40
|
+
|
|
41
|
+
def configuration_stackup(self, kwargs):
|
|
42
|
+
if kwargs.get("fix_padstack_def"):
|
|
43
|
+
from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
|
|
44
|
+
|
|
45
|
+
pedb_defs = self._pedb.padstacks.definitions
|
|
46
|
+
temp = []
|
|
47
|
+
for _, pdef in pedb_defs.items():
|
|
48
|
+
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
49
|
+
cfg_def.api.retrieve_parameters_from_edb()
|
|
50
|
+
temp.append(cfg_def)
|
|
51
|
+
self.parent.cfg_data.stackup.apply()
|
|
52
|
+
for cfg_pdef in temp:
|
|
53
|
+
cfg_pdef.api.set_parameters_to_edb()
|
|
54
|
+
else:
|
|
55
|
+
temp_pdef_data = {}
|
|
56
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
57
|
+
PadType as GrpcPadType,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
61
|
+
for layer in pdef.data.layer_names:
|
|
62
|
+
result = pdef.data.get_pad_parameters(layer, GrpcPadType.REGULAR_PAD)
|
|
63
|
+
if len(result) == 4:
|
|
64
|
+
# polygon based
|
|
65
|
+
temp_pdef_data[pdef_name] = pdef.data
|
|
66
|
+
break
|
|
67
|
+
self.parent.cfg_data.stackup.apply()
|
|
68
|
+
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
69
|
+
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
70
|
+
pdef._padstack_def_data = pdef_data
|
|
71
|
+
|
|
72
|
+
def _load_stackup(self):
|
|
73
|
+
"""Imports stackup information from json."""
|
|
74
|
+
data = self.data["stackup"]
|
|
75
|
+
materials = data.get("materials")
|
|
76
|
+
|
|
77
|
+
if materials:
|
|
78
|
+
edb_materials = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
|
|
79
|
+
for mat in materials:
|
|
80
|
+
name = mat["name"].lower()
|
|
81
|
+
if name in edb_materials:
|
|
82
|
+
self._pedb.materials.delete_material(edb_materials[name])
|
|
83
|
+
for mat in materials:
|
|
84
|
+
self._pedb.materials.add_material(**mat)
|
|
85
|
+
|
|
86
|
+
layers = data.get("layers")
|
|
87
|
+
|
|
88
|
+
if layers:
|
|
89
|
+
input_signal_layers = [i for i in layers if i["type"].lower() == "signal"]
|
|
90
|
+
if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
|
|
91
|
+
self._pedb.logger.error("Input signal layer count do not match.")
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
removal_list = []
|
|
95
|
+
lc_signal_layers = []
|
|
96
|
+
for name, obj in self._pedb.stackup.all_layers.items():
|
|
97
|
+
if obj.type == "dielectric":
|
|
98
|
+
removal_list.append(name)
|
|
99
|
+
elif obj.type == "signal":
|
|
100
|
+
lc_signal_layers.append(obj.id)
|
|
101
|
+
for l in removal_list:
|
|
102
|
+
self._pedb.stackup.remove_layer(l)
|
|
103
|
+
|
|
104
|
+
# update all signal layers
|
|
105
|
+
id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
|
|
106
|
+
signal_idx = 0
|
|
107
|
+
for l in layers:
|
|
108
|
+
if l["type"] == "signal":
|
|
109
|
+
layer_id = lc_signal_layers[signal_idx]
|
|
110
|
+
layer_name = id_name[layer_id]
|
|
111
|
+
self._pedb.stackup.layers[layer_name].update(**l)
|
|
112
|
+
signal_idx = signal_idx + 1
|
|
113
|
+
|
|
114
|
+
# add all dielectric layers. Dielectric layers must be added last. Otherwise,
|
|
115
|
+
# dielectric layer will occupy signal and document layer id.
|
|
116
|
+
prev_layer_clone = None
|
|
117
|
+
l = layers.pop(0)
|
|
118
|
+
if l["type"] == "signal":
|
|
119
|
+
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
120
|
+
else:
|
|
121
|
+
prev_layer_clone = self._pedb.stackup.add_layer_top(**l)
|
|
122
|
+
for idx, l in enumerate(layers):
|
|
123
|
+
if l["type"] == "dielectric":
|
|
124
|
+
prev_layer_clone = self._pedb.stackup.add_layer_below(
|
|
125
|
+
base_layer_name=prev_layer_clone.name, **l
|
|
126
|
+
)
|
|
127
|
+
elif l["type"] == "signal":
|
|
128
|
+
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
129
|
+
|
|
130
|
+
def _load_package_def(self):
|
|
131
|
+
"""Imports package definition information from JSON."""
|
|
132
|
+
comps = self._pedb.components.instances
|
|
133
|
+
for pkgd in self.parent.data["package_definitions"]:
|
|
134
|
+
name = pkgd["name"]
|
|
135
|
+
if name in self._pedb.definitions.package:
|
|
136
|
+
self._pedb.definitions.package[name].delete()
|
|
137
|
+
extent_bounding_box = pkgd.get("extent_bounding_box", None)
|
|
138
|
+
if extent_bounding_box:
|
|
139
|
+
package_def = PackageDef(self._pedb, name=name, extent_bounding_box=extent_bounding_box)
|
|
140
|
+
else:
|
|
141
|
+
package_def = PackageDef(self._pedb, name=name, component_part_name=pkgd["component_definition"])
|
|
142
|
+
package_def.maximum_power = pkgd["maximum_power"]
|
|
143
|
+
package_def.therm_cond = pkgd["therm_cond"]
|
|
144
|
+
package_def.theta_jb = pkgd["theta_jb"]
|
|
145
|
+
package_def.theta_jc = pkgd["theta_jc"]
|
|
146
|
+
package_def.height = pkgd["height"]
|
|
147
|
+
|
|
148
|
+
heatsink = pkgd.get("heatsink", None)
|
|
149
|
+
if heatsink:
|
|
150
|
+
package_def.set_heatsink(
|
|
151
|
+
heatsink["fin_base_height"],
|
|
152
|
+
heatsink["fin_height"],
|
|
153
|
+
heatsink["fin_orientation"],
|
|
154
|
+
heatsink["fin_spacing"],
|
|
155
|
+
heatsink["fin_thickness"],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
comp_def_name = pkgd["component_definition"]
|
|
159
|
+
comp_def = self._pedb.definitions.component[comp_def_name]
|
|
160
|
+
|
|
161
|
+
comp_list = dict()
|
|
162
|
+
if pkgd["apply_to_all"]:
|
|
163
|
+
comp_list.update(
|
|
164
|
+
{
|
|
165
|
+
refdes: comp
|
|
166
|
+
for refdes, comp in comp_def.components.items()
|
|
167
|
+
if refdes not in pkgd["components"]
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
comp_list.update(
|
|
172
|
+
{refdes: comp for refdes, comp in comp_def.components.items() if refdes in pkgd["components"]}
|
|
173
|
+
)
|
|
174
|
+
for _, i in comp_list.items():
|
|
175
|
+
i.package_def = name
|
|
176
|
+
|
|
177
|
+
def get_data_from_db(self, **kwargs):
|
|
178
|
+
"""Get configuration data from layout.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
stackup
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
self._pedb.logger.info("Getting data from layout database.")
|
|
189
|
+
data = {}
|
|
190
|
+
if kwargs.get("general", False):
|
|
191
|
+
data["general"] = self.parent.cfg_data.general.get_data_from_db()
|
|
192
|
+
if kwargs.get("stackup", False):
|
|
193
|
+
data["stackup"] = self.parent.cfg_data.stackup.get_data_from_db()
|
|
194
|
+
if kwargs.get("package_definitions", False):
|
|
195
|
+
data["package_definitions"] = self.parent.cfg_data.package_definitions.get_data_from_db()
|
|
196
|
+
if kwargs.get("setups", False):
|
|
197
|
+
setups = self.parent.cfg_data.setups
|
|
198
|
+
setups.retrieve_parameters_from_edb()
|
|
199
|
+
data["setups"] = setups.to_dict()
|
|
200
|
+
if kwargs.get("sources", False):
|
|
201
|
+
data["sources"] = self.parent.cfg_data.sources.get_data_from_db()
|
|
202
|
+
if kwargs.get("ports", False):
|
|
203
|
+
data["ports"] = self.parent.cfg_data.ports.get_data_from_db()
|
|
204
|
+
if kwargs.get("components", False) or kwargs.get("s_parameters", False):
|
|
205
|
+
self.parent.cfg_data.components.retrieve_parameters_from_edb()
|
|
206
|
+
components = []
|
|
207
|
+
for i in self.parent.cfg_data.components.components:
|
|
208
|
+
components.append(i.get_attributes())
|
|
209
|
+
|
|
210
|
+
if kwargs.get("components", False):
|
|
211
|
+
data["components"] = components
|
|
212
|
+
elif kwargs.get("s_parameters", False):
|
|
213
|
+
data["s_parameters"] = self.parent.cfg_data.s_parameters.get_data_from_db(components)
|
|
214
|
+
if kwargs.get("nets", False):
|
|
215
|
+
data["nets"] = self.parent.cfg_data.nets.get_data_from_db()
|
|
216
|
+
if kwargs.get("pin_groups", False):
|
|
217
|
+
data["pin_groups"] = self.parent.cfg_data.pin_groups.get_data_from_db()
|
|
218
|
+
if kwargs.get("operations", False):
|
|
219
|
+
data["operations"] = self.parent.cfg_data.operations.get_data_from_db()
|
|
220
|
+
if kwargs.get("padstacks", False):
|
|
221
|
+
self.parent.cfg_data.padstacks.retrieve_parameters_from_edb()
|
|
222
|
+
definitions = []
|
|
223
|
+
for i in self.parent.cfg_data.padstacks.definitions:
|
|
224
|
+
definitions.append(i.get_attributes())
|
|
225
|
+
instances = []
|
|
226
|
+
for i in self.parent.cfg_data.padstacks.instances:
|
|
227
|
+
instances.append(i.get_attributes())
|
|
228
|
+
data["padstacks"] = dict()
|
|
229
|
+
data["padstacks"]["definitions"] = definitions
|
|
230
|
+
data["padstacks"]["instances"] = instances
|
|
231
|
+
|
|
232
|
+
if kwargs.get("boundaries", False):
|
|
233
|
+
data["boundaries"] = self.parent.cfg_data.boundaries.get_data_from_db()
|
|
234
|
+
|
|
235
|
+
return data
|
|
236
|
+
|
|
237
|
+
def export(
|
|
238
|
+
self,
|
|
239
|
+
file_path,
|
|
240
|
+
stackup=True,
|
|
241
|
+
package_definitions=False,
|
|
242
|
+
setups=True,
|
|
243
|
+
sources=True,
|
|
244
|
+
ports=True,
|
|
245
|
+
nets=True,
|
|
246
|
+
pin_groups=True,
|
|
247
|
+
operations=True,
|
|
248
|
+
components=True,
|
|
249
|
+
boundaries=True,
|
|
250
|
+
s_parameters=True,
|
|
251
|
+
padstacks=True,
|
|
252
|
+
general=True,
|
|
253
|
+
):
|
|
254
|
+
"""Export the configuration data from layout to a file.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
file_path : str, Path
|
|
259
|
+
File path to export the configuration data.
|
|
260
|
+
stackup : bool
|
|
261
|
+
Whether to export stackup or not.
|
|
262
|
+
package_definitions : bool
|
|
263
|
+
Whether to export package definitions or not.
|
|
264
|
+
setups : bool
|
|
265
|
+
Whether to export setups or not.
|
|
266
|
+
sources : bool
|
|
267
|
+
Whether to export sources or not.
|
|
268
|
+
ports : bool
|
|
269
|
+
Whether to export ports or not.
|
|
270
|
+
nets : bool
|
|
271
|
+
Whether to export nets.
|
|
272
|
+
pin_groups : bool
|
|
273
|
+
Whether to export pin groups.
|
|
274
|
+
operations : bool
|
|
275
|
+
Whether to export operations.
|
|
276
|
+
components : bool
|
|
277
|
+
Whether to export component.
|
|
278
|
+
boundaries : bool
|
|
279
|
+
Whether to export boundaries.
|
|
280
|
+
s_parameters : bool
|
|
281
|
+
Whether to export s_parameters.
|
|
282
|
+
padstacks : bool
|
|
283
|
+
Whether to export padstacks.
|
|
284
|
+
general : bool
|
|
285
|
+
Whether to export general information.
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
bool
|
|
289
|
+
"""
|
|
290
|
+
data = self.get_data_from_db(
|
|
291
|
+
stackup=stackup,
|
|
292
|
+
package_definitions=package_definitions,
|
|
293
|
+
setups=setups,
|
|
294
|
+
sources=sources,
|
|
295
|
+
ports=ports,
|
|
296
|
+
nets=nets,
|
|
297
|
+
pin_groups=pin_groups,
|
|
298
|
+
operations=operations,
|
|
299
|
+
components=components,
|
|
300
|
+
boundaries=boundaries,
|
|
301
|
+
s_parameters=s_parameters,
|
|
302
|
+
padstacks=padstacks,
|
|
303
|
+
general=general,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
file_path = file_path if isinstance(file_path, Path) else Path(file_path)
|
|
307
|
+
file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
|
|
308
|
+
|
|
309
|
+
with open(file_path, "w") as f:
|
|
310
|
+
if file_path.suffix == ".json":
|
|
311
|
+
json.dump(data, f, ensure_ascii=False, indent=4)
|
|
312
|
+
else:
|
|
313
|
+
toml.dump(data, f)
|
|
314
|
+
return True if os.path.isfile(file_path) else False
|
|
315
|
+
|
|
316
|
+
class DotNet(Grpc):
|
|
317
|
+
def __init__(self, parent):
|
|
318
|
+
super().__init__(parent)
|
|
319
|
+
|
|
320
|
+
def configuration_stackup(self, kwargs):
|
|
321
|
+
if kwargs.get("fix_padstack_def"):
|
|
322
|
+
from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
|
|
323
|
+
|
|
324
|
+
pedb_defs = self._pedb.padstacks.definitions
|
|
325
|
+
temp = []
|
|
326
|
+
for _, pdef in pedb_defs.items():
|
|
327
|
+
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
328
|
+
cfg_def.api.retrieve_parameters_from_edb()
|
|
329
|
+
temp.append(cfg_def)
|
|
330
|
+
self.parent.cfg_data.stackup.apply()
|
|
331
|
+
for cfg_pdef in temp:
|
|
332
|
+
cfg_pdef.api.set_parameters_to_edb()
|
|
333
|
+
else:
|
|
334
|
+
temp_pdef_data = {}
|
|
335
|
+
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
336
|
+
pdef_data = pdef._padstack_def_data
|
|
337
|
+
for lyr_name in list(pdef_data.GetLayerNames()):
|
|
338
|
+
result = pdef_data.GetPadParametersValue(
|
|
339
|
+
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
340
|
+
)
|
|
341
|
+
flag, pad_shape, params, offset_x, offset_y, rotation = result
|
|
342
|
+
if flag is False:
|
|
343
|
+
result = pdef_data.GetPolygonalPadParameters(
|
|
344
|
+
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
345
|
+
)
|
|
346
|
+
flag, polygon_data, offset_x, offset_y, rotation = result
|
|
347
|
+
if flag:
|
|
348
|
+
temp_pdef_data[pdef_name] = pdef_data
|
|
349
|
+
break
|
|
350
|
+
self.parent.cfg_data.stackup.apply()
|
|
351
|
+
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
352
|
+
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
353
|
+
pdef._padstack_def_data = pdef_data
|
|
354
|
+
|
|
36
355
|
def __init__(self, pedb):
|
|
37
356
|
self._pedb = pedb
|
|
357
|
+
if self._pedb.grpc:
|
|
358
|
+
self.api = self.Grpc(self)
|
|
359
|
+
else:
|
|
360
|
+
self.api = self.DotNet(self)
|
|
38
361
|
self._components = self._pedb.components.instances
|
|
39
362
|
self.data = {}
|
|
40
363
|
self._s_parameter_library = ""
|
|
@@ -133,52 +456,7 @@ class Configuration:
|
|
|
133
456
|
self.cfg_data.setups.apply()
|
|
134
457
|
|
|
135
458
|
# Configure stackup
|
|
136
|
-
|
|
137
|
-
from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
|
|
138
|
-
|
|
139
|
-
pedb_defs = self._pedb.padstacks.definitions
|
|
140
|
-
temp = []
|
|
141
|
-
for _, pdef in pedb_defs.items():
|
|
142
|
-
cfg_def = CfgPadstackDefinition(self._pedb, pdef)
|
|
143
|
-
cfg_def.api.retrieve_parameters_from_edb()
|
|
144
|
-
temp.append(cfg_def)
|
|
145
|
-
self.cfg_data.stackup.apply()
|
|
146
|
-
for cfg_pdef in temp:
|
|
147
|
-
cfg_pdef.api.set_parameters_to_edb()
|
|
148
|
-
else:
|
|
149
|
-
temp_pdef_data = {}
|
|
150
|
-
if self._pedb.grpc:
|
|
151
|
-
from ansys.edb.core.definition.padstack_def_data import (
|
|
152
|
-
PadType as GrpcPadType,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
156
|
-
for layer in pdef.data.layer_names:
|
|
157
|
-
result = pdef.data.get_pad_parameters(layer, GrpcPadType.REGULAR_PAD)
|
|
158
|
-
if len(result) == 4:
|
|
159
|
-
# polygon based
|
|
160
|
-
temp_pdef_data[pdef_name] = pdef.data
|
|
161
|
-
break
|
|
162
|
-
else:
|
|
163
|
-
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
164
|
-
pdef_data = pdef._padstack_def_data
|
|
165
|
-
for lyr_name in list(pdef_data.GetLayerNames()):
|
|
166
|
-
result = pdef_data.GetPadParametersValue(
|
|
167
|
-
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
168
|
-
)
|
|
169
|
-
flag, pad_shape, params, offset_x, offset_y, rotation = result
|
|
170
|
-
if flag is False:
|
|
171
|
-
result = pdef_data.GetPolygonalPadParameters(
|
|
172
|
-
lyr_name, self._pedb._edb.Definition.PadType.RegularPad
|
|
173
|
-
)
|
|
174
|
-
flag, polygon_data, offset_x, offset_y, rotation = result
|
|
175
|
-
if flag:
|
|
176
|
-
temp_pdef_data[pdef_name] = pdef_data
|
|
177
|
-
break
|
|
178
|
-
self.cfg_data.stackup.apply()
|
|
179
|
-
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
180
|
-
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
181
|
-
pdef._padstack_def_data = pdef_data
|
|
459
|
+
self.api.configuration_stackup(kwargs)
|
|
182
460
|
|
|
183
461
|
# Configure padstacks
|
|
184
462
|
if self.cfg_data.padstacks:
|
|
@@ -210,102 +488,11 @@ class Configuration:
|
|
|
210
488
|
|
|
211
489
|
def _load_stackup(self):
|
|
212
490
|
"""Imports stackup information from json."""
|
|
213
|
-
|
|
214
|
-
materials = data.get("materials")
|
|
215
|
-
|
|
216
|
-
if materials:
|
|
217
|
-
edb_materials = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
|
|
218
|
-
for mat in materials:
|
|
219
|
-
name = mat["name"].lower()
|
|
220
|
-
if name in edb_materials:
|
|
221
|
-
self._pedb.materials.delete_material(edb_materials[name])
|
|
222
|
-
for mat in materials:
|
|
223
|
-
self._pedb.materials.add_material(**mat)
|
|
224
|
-
|
|
225
|
-
layers = data.get("layers")
|
|
226
|
-
|
|
227
|
-
if layers:
|
|
228
|
-
input_signal_layers = [i for i in layers if i["type"].lower() == "signal"]
|
|
229
|
-
if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
|
|
230
|
-
self._pedb.logger.error("Input signal layer count do not match.")
|
|
231
|
-
return False
|
|
232
|
-
|
|
233
|
-
removal_list = []
|
|
234
|
-
lc_signal_layers = []
|
|
235
|
-
for name, obj in self._pedb.stackup.all_layers.items():
|
|
236
|
-
if obj.type == "dielectric":
|
|
237
|
-
removal_list.append(name)
|
|
238
|
-
elif obj.type == "signal":
|
|
239
|
-
lc_signal_layers.append(obj.id)
|
|
240
|
-
for l in removal_list:
|
|
241
|
-
self._pedb.stackup.remove_layer(l)
|
|
242
|
-
|
|
243
|
-
# update all signal layers
|
|
244
|
-
id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
|
|
245
|
-
signal_idx = 0
|
|
246
|
-
for l in layers:
|
|
247
|
-
if l["type"] == "signal":
|
|
248
|
-
layer_id = lc_signal_layers[signal_idx]
|
|
249
|
-
layer_name = id_name[layer_id]
|
|
250
|
-
self._pedb.stackup.layers[layer_name].update(**l)
|
|
251
|
-
signal_idx = signal_idx + 1
|
|
252
|
-
|
|
253
|
-
# add all dielectric layers. Dielectric layers must be added last. Otherwise,
|
|
254
|
-
# dielectric layer will occupy signal and document layer id.
|
|
255
|
-
prev_layer_clone = None
|
|
256
|
-
l = layers.pop(0)
|
|
257
|
-
if l["type"] == "signal":
|
|
258
|
-
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
259
|
-
else:
|
|
260
|
-
prev_layer_clone = self._pedb.stackup.add_layer_top(**l)
|
|
261
|
-
for idx, l in enumerate(layers):
|
|
262
|
-
if l["type"] == "dielectric":
|
|
263
|
-
prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **l)
|
|
264
|
-
elif l["type"] == "signal":
|
|
265
|
-
prev_layer_clone = self._pedb.stackup.layers[l["name"]]
|
|
491
|
+
self.api._load_stackup()
|
|
266
492
|
|
|
267
493
|
def _load_package_def(self):
|
|
268
494
|
"""Imports package definition information from JSON."""
|
|
269
|
-
|
|
270
|
-
for pkgd in self.data["package_definitions"]:
|
|
271
|
-
name = pkgd["name"]
|
|
272
|
-
if name in self._pedb.definitions.package:
|
|
273
|
-
self._pedb.definitions.package[name].delete()
|
|
274
|
-
extent_bounding_box = pkgd.get("extent_bounding_box", None)
|
|
275
|
-
if extent_bounding_box:
|
|
276
|
-
package_def = PackageDef(self._pedb, name=name, extent_bounding_box=extent_bounding_box)
|
|
277
|
-
else:
|
|
278
|
-
package_def = PackageDef(self._pedb, name=name, component_part_name=pkgd["component_definition"])
|
|
279
|
-
package_def.maximum_power = pkgd["maximum_power"]
|
|
280
|
-
package_def.therm_cond = pkgd["therm_cond"]
|
|
281
|
-
package_def.theta_jb = pkgd["theta_jb"]
|
|
282
|
-
package_def.theta_jc = pkgd["theta_jc"]
|
|
283
|
-
package_def.height = pkgd["height"]
|
|
284
|
-
|
|
285
|
-
heatsink = pkgd.get("heatsink", None)
|
|
286
|
-
if heatsink:
|
|
287
|
-
package_def.set_heatsink(
|
|
288
|
-
heatsink["fin_base_height"],
|
|
289
|
-
heatsink["fin_height"],
|
|
290
|
-
heatsink["fin_orientation"],
|
|
291
|
-
heatsink["fin_spacing"],
|
|
292
|
-
heatsink["fin_thickness"],
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
comp_def_name = pkgd["component_definition"]
|
|
296
|
-
comp_def = self._pedb.definitions.component[comp_def_name]
|
|
297
|
-
|
|
298
|
-
comp_list = dict()
|
|
299
|
-
if pkgd["apply_to_all"]:
|
|
300
|
-
comp_list.update(
|
|
301
|
-
{refdes: comp for refdes, comp in comp_def.components.items() if refdes not in pkgd["components"]}
|
|
302
|
-
)
|
|
303
|
-
else:
|
|
304
|
-
comp_list.update(
|
|
305
|
-
{refdes: comp for refdes, comp in comp_def.components.items() if refdes in pkgd["components"]}
|
|
306
|
-
)
|
|
307
|
-
for _, i in comp_list.items():
|
|
308
|
-
i.package_def = name
|
|
495
|
+
self.api._load_package_def()
|
|
309
496
|
|
|
310
497
|
def get_data_from_db(self, **kwargs):
|
|
311
498
|
"""Get configuration data from layout.
|
|
@@ -318,52 +505,7 @@ class Configuration:
|
|
|
318
505
|
-------
|
|
319
506
|
|
|
320
507
|
"""
|
|
321
|
-
self.
|
|
322
|
-
data = {}
|
|
323
|
-
if kwargs.get("general", False):
|
|
324
|
-
data["general"] = self.cfg_data.general.get_data_from_db()
|
|
325
|
-
if kwargs.get("stackup", False):
|
|
326
|
-
data["stackup"] = self.cfg_data.stackup.get_data_from_db()
|
|
327
|
-
if kwargs.get("package_definitions", False):
|
|
328
|
-
data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
|
|
329
|
-
if kwargs.get("setups", False):
|
|
330
|
-
data["setups"] = self.cfg_data.setups.get_data_from_db()
|
|
331
|
-
if kwargs.get("sources", False):
|
|
332
|
-
data["sources"] = self.cfg_data.sources.get_data_from_db()
|
|
333
|
-
if kwargs.get("ports", False):
|
|
334
|
-
data["ports"] = self.cfg_data.ports.get_data_from_db()
|
|
335
|
-
if kwargs.get("components", False) or kwargs.get("s_parameters", False):
|
|
336
|
-
self.cfg_data.components.retrieve_parameters_from_edb()
|
|
337
|
-
components = []
|
|
338
|
-
for i in self.cfg_data.components.components:
|
|
339
|
-
components.append(i.get_attributes())
|
|
340
|
-
|
|
341
|
-
if kwargs.get("components", False):
|
|
342
|
-
data["components"] = components
|
|
343
|
-
elif kwargs.get("s_parameters", False):
|
|
344
|
-
data["s_parameters"] = self.cfg_data.s_parameters.get_data_from_db(components)
|
|
345
|
-
if kwargs.get("nets", False):
|
|
346
|
-
data["nets"] = self.cfg_data.nets.get_data_from_db()
|
|
347
|
-
if kwargs.get("pin_groups", False):
|
|
348
|
-
data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db()
|
|
349
|
-
if kwargs.get("operations", False):
|
|
350
|
-
data["operations"] = self.cfg_data.operations.get_data_from_db()
|
|
351
|
-
if kwargs.get("padstacks", False):
|
|
352
|
-
self.cfg_data.padstacks.retrieve_parameters_from_edb()
|
|
353
|
-
definitions = []
|
|
354
|
-
for i in self.cfg_data.padstacks.definitions:
|
|
355
|
-
definitions.append(i.get_attributes())
|
|
356
|
-
instances = []
|
|
357
|
-
for i in self.cfg_data.padstacks.instances:
|
|
358
|
-
instances.append(i.get_attributes())
|
|
359
|
-
data["padstacks"] = dict()
|
|
360
|
-
data["padstacks"]["definitions"] = definitions
|
|
361
|
-
data["padstacks"]["instances"] = instances
|
|
362
|
-
|
|
363
|
-
if kwargs.get("boundaries", False):
|
|
364
|
-
data["boundaries"] = self.cfg_data.boundaries.get_data_from_db()
|
|
365
|
-
|
|
366
|
-
return data
|
|
508
|
+
return self.api.get_data_from_db(**kwargs)
|
|
367
509
|
|
|
368
510
|
def export(
|
|
369
511
|
self,
|
|
@@ -418,28 +560,19 @@ class Configuration:
|
|
|
418
560
|
-------
|
|
419
561
|
bool
|
|
420
562
|
"""
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
563
|
+
return self.api.export(
|
|
564
|
+
file_path,
|
|
565
|
+
stackup=True,
|
|
566
|
+
package_definitions=False,
|
|
567
|
+
setups=True,
|
|
568
|
+
sources=True,
|
|
569
|
+
ports=True,
|
|
570
|
+
nets=True,
|
|
571
|
+
pin_groups=True,
|
|
572
|
+
operations=True,
|
|
573
|
+
components=True,
|
|
574
|
+
boundaries=True,
|
|
575
|
+
s_parameters=True,
|
|
576
|
+
padstacks=True,
|
|
577
|
+
general=True,
|
|
435
578
|
)
|
|
436
|
-
|
|
437
|
-
file_path = file_path if isinstance(file_path, Path) else Path(file_path)
|
|
438
|
-
file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
|
|
439
|
-
|
|
440
|
-
with open(file_path, "w") as f:
|
|
441
|
-
if file_path.suffix == ".json":
|
|
442
|
-
json.dump(data, f, ensure_ascii=False, indent=4)
|
|
443
|
-
else:
|
|
444
|
-
toml.dump(data, f)
|
|
445
|
-
return True if os.path.isfile(file_path) else False
|
|
@@ -19,6 +19,7 @@
|
|
|
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
|
+
import warnings
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class EdbDesignOptions:
|
|
@@ -51,8 +52,25 @@ class EdbDesignOptions:
|
|
|
51
52
|
``True`` if antipad is always on, ``False`` otherwise.
|
|
52
53
|
|
|
53
54
|
"""
|
|
54
|
-
|
|
55
|
+
warnings.warn("Use new property :func:`anti_pads_always_on` instead.", DeprecationWarning)
|
|
56
|
+
return self.anti_pads_always_on
|
|
55
57
|
|
|
56
58
|
@antipads_always_on.setter
|
|
57
59
|
def antipads_always_on(self, value):
|
|
60
|
+
self.anti_pads_always_on = value
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def anti_pads_always_on(self):
|
|
64
|
+
"""Whether to always turn on antipad.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
bool
|
|
69
|
+
``True`` if antipad is always on, ``False`` otherwise.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
return self._active_cell.GetAntiPadsAlwaysOn()
|
|
73
|
+
|
|
74
|
+
@anti_pads_always_on.setter
|
|
75
|
+
def anti_pads_always_on(self, value):
|
|
58
76
|
self._active_cell.SetAntiPadsAlwaysOn(value)
|
|
@@ -35,6 +35,7 @@ from pyedb.dotnet.database.general import (
|
|
|
35
35
|
snake_to_pascal,
|
|
36
36
|
)
|
|
37
37
|
from pyedb.dotnet.database.geometry.polygon_data import PolygonData
|
|
38
|
+
from pyedb.generic.data_handlers import float_units
|
|
38
39
|
from pyedb.generic.general_methods import generate_unique_name
|
|
39
40
|
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
40
41
|
|
|
@@ -1958,15 +1959,18 @@ class EDBPadstackInstance(Primitive):
|
|
|
1958
1959
|
pad_shape = padstack_pad.geometry_type
|
|
1959
1960
|
params = padstack_pad.parameters_values
|
|
1960
1961
|
polygon_data = padstack_pad._polygon_data_dotnet
|
|
1962
|
+
pad_offset = [float_units(padstack_pad.offset_x), float_units(padstack_pad.offset_y)]
|
|
1961
1963
|
|
|
1962
1964
|
def _rotate(p):
|
|
1963
1965
|
x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
|
|
1964
1966
|
y = p[0] * math.sin(rotation) + p[1] * math.cos(rotation)
|
|
1965
1967
|
return [x, y]
|
|
1966
1968
|
|
|
1967
|
-
def _translate(p):
|
|
1968
|
-
|
|
1969
|
-
|
|
1969
|
+
def _translate(p, t=None):
|
|
1970
|
+
if t is None:
|
|
1971
|
+
t = padstack_center
|
|
1972
|
+
x = p[0] + t[0]
|
|
1973
|
+
y = p[1] + t[1]
|
|
1970
1974
|
return [x, y]
|
|
1971
1975
|
|
|
1972
1976
|
rect = None
|
|
@@ -2092,7 +2096,8 @@ class EDBPadstackInstance(Primitive):
|
|
|
2092
2096
|
|
|
2093
2097
|
if rect is None or len(rect) != 4:
|
|
2094
2098
|
return False
|
|
2095
|
-
|
|
2099
|
+
offset_rect = [_translate(p, _rotate(pad_offset)) for p in rect]
|
|
2100
|
+
path = self._pedb.modeler.Shape("polygon", points=offset_rect)
|
|
2096
2101
|
pdata = self._pedb.modeler.shape_to_polygon_data(path)
|
|
2097
2102
|
new_rect = []
|
|
2098
2103
|
for point in pdata.Points:
|
|
@@ -2142,7 +2147,7 @@ class EDBPadstackInstance(Primitive):
|
|
|
2142
2147
|
component_only=component_only,
|
|
2143
2148
|
)
|
|
2144
2149
|
|
|
2145
|
-
def split(self):
|
|
2150
|
+
def split(self) -> list:
|
|
2146
2151
|
"""Split padstack instance into multiple instances. The new instances only connect adjacent layers."""
|
|
2147
2152
|
pdef_name = self.padstack_definition
|
|
2148
2153
|
position = self.position
|
|
@@ -2151,11 +2156,16 @@ class EDBPadstackInstance(Primitive):
|
|
|
2151
2156
|
stackup_layer_range = list(self._pedb.stackup.signal_layers.keys())
|
|
2152
2157
|
start_idx = stackup_layer_range.index(self.start_layer)
|
|
2153
2158
|
stop_idx = stackup_layer_range.index(self.stop_layer)
|
|
2159
|
+
temp = []
|
|
2154
2160
|
for idx, (l1, l2) in enumerate(
|
|
2155
2161
|
list(zip(stackup_layer_range[start_idx:stop_idx], stackup_layer_range[start_idx + 1 : stop_idx + 1]))
|
|
2156
2162
|
):
|
|
2157
|
-
self._pedb.padstacks.place(
|
|
2163
|
+
pd_inst = self._pedb.padstacks.place(
|
|
2164
|
+
position, pdef_name, net_name, f"{name}_{idx}", fromlayer=l1, tolayer=l2
|
|
2165
|
+
)
|
|
2166
|
+
temp.append(pd_inst)
|
|
2158
2167
|
self.delete()
|
|
2168
|
+
return temp
|
|
2159
2169
|
|
|
2160
2170
|
def convert_hole_to_conical_shape(self, angle=75):
|
|
2161
2171
|
"""Convert actual padstack instance to microvias 3D Objects with a given aspect ratio.
|