pyedb 0.52.0__py3-none-any.whl → 0.53.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_common.py +12 -15
- pyedb/configuration/cfg_data.py +2 -2
- pyedb/configuration/cfg_modeler.py +163 -234
- pyedb/configuration/cfg_stackup.py +62 -249
- pyedb/configuration/configuration.py +271 -170
- pyedb/dotnet/database/components.py +9 -3
- pyedb/dotnet/database/dotnet/database.py +4 -0
- pyedb/dotnet/database/edb_data/layer_data.py +3 -1
- pyedb/dotnet/database/siwave.py +14 -0
- pyedb/dotnet/database/stackup.py +6 -60
- pyedb/dotnet/database/utilities/simulation_setup.py +1 -1
- pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +894 -0
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +15 -0
- pyedb/dotnet/edb.py +15 -2
- pyedb/generic/design_types.py +29 -0
- pyedb/generic/grpc_warnings.py +5 -0
- pyedb/grpc/database/components.py +102 -81
- pyedb/grpc/database/control_file.py +240 -193
- pyedb/grpc/database/definitions.py +7 -5
- pyedb/grpc/database/modeler.py +105 -77
- pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +961 -0
- pyedb/grpc/database/siwave.py +14 -0
- pyedb/grpc/edb.py +70 -7
- pyedb/siwave_core/cpa/simulation_setup_data_model.py +132 -0
- pyedb/siwave_core/product_properties.py +198 -0
- {pyedb-0.52.0.dist-info → pyedb-0.53.0.dist-info}/METADATA +13 -11
- {pyedb-0.52.0.dist-info → pyedb-0.53.0.dist-info}/RECORD +30 -25
- {pyedb-0.52.0.dist-info → pyedb-0.53.0.dist-info}/WHEEL +1 -1
- {pyedb-0.52.0.dist-info → pyedb-0.53.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -27,14 +27,14 @@ import warnings
|
|
|
27
27
|
|
|
28
28
|
import toml
|
|
29
29
|
|
|
30
|
+
from pyedb import Edb
|
|
30
31
|
from pyedb.configuration.cfg_data import CfgData
|
|
31
|
-
from pyedb.dotnet.database.definition.package_def import PackageDef
|
|
32
32
|
|
|
33
33
|
|
|
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
|
-
def __init__(self, pedb):
|
|
37
|
+
def __init__(self, pedb: Edb):
|
|
38
38
|
self._pedb = pedb
|
|
39
39
|
|
|
40
40
|
self._components = self._pedb.components.instances
|
|
@@ -43,6 +43,11 @@ class Configuration:
|
|
|
43
43
|
self._spice_model_library = ""
|
|
44
44
|
self.cfg_data = CfgData(self._pedb)
|
|
45
45
|
|
|
46
|
+
def __apply_with_logging(self, label: str, func):
|
|
47
|
+
start = datetime.now()
|
|
48
|
+
func()
|
|
49
|
+
self._pedb.logger.info(f"{label} finished. Time lapse {datetime.now() - start}")
|
|
50
|
+
|
|
46
51
|
def load(self, config_file, append=True, apply_file=False, output_file=None, open_at_the_end=True):
|
|
47
52
|
"""Import configuration settings from a configure file.
|
|
48
53
|
|
|
@@ -110,206 +115,292 @@ class Configuration:
|
|
|
110
115
|
if kwargs.get("fix_padstack_def"):
|
|
111
116
|
warnings.warn("fix_padstack_def is deprecated.", DeprecationWarning)
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
self.cfg_data.variables.apply()
|
|
118
|
+
self.apply_variables()
|
|
115
119
|
|
|
116
120
|
if self.cfg_data.general:
|
|
117
121
|
self.cfg_data.general.apply()
|
|
118
122
|
|
|
119
123
|
# Configure boundary settings
|
|
120
|
-
now = datetime.now()
|
|
121
124
|
if self.cfg_data.boundaries:
|
|
122
|
-
self.cfg_data.boundaries.apply
|
|
123
|
-
self._pedb.logger.info(f"Updating boundaries finished. Time lapse {datetime.now() - now}")
|
|
124
|
-
now = datetime.now()
|
|
125
|
+
self.__apply_with_logging("Updating boundaries", self.cfg_data.boundaries.apply)
|
|
125
126
|
|
|
126
|
-
# Configure nets
|
|
127
127
|
if self.cfg_data.nets:
|
|
128
|
-
self.cfg_data.nets.apply
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
self.cfg_data.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
self.cfg_data.pin_groups.apply()
|
|
139
|
-
self._pedb.logger.info(f"Creating pin groups finished. Time lapse {datetime.now() - now}")
|
|
140
|
-
now = datetime.now()
|
|
141
|
-
|
|
142
|
-
# Configure sources
|
|
143
|
-
self.cfg_data.sources.apply()
|
|
144
|
-
self._pedb.logger.info(f"Placing sources finished. Time lapse {datetime.now() - now}")
|
|
145
|
-
now = datetime.now()
|
|
146
|
-
|
|
147
|
-
# Configure setup
|
|
148
|
-
self.cfg_data.setups.apply()
|
|
149
|
-
self._pedb.logger.info(f"Creating setups finished. Time lapse {datetime.now() - now}")
|
|
150
|
-
now = datetime.now()
|
|
151
|
-
|
|
152
|
-
# Configure stackup
|
|
153
|
-
self.configuration_stackup()
|
|
154
|
-
self._pedb.logger.info(f"Updating stackup finished. Time lapse {datetime.now() - now}")
|
|
155
|
-
now = datetime.now()
|
|
156
|
-
|
|
157
|
-
# Configure padstacks
|
|
128
|
+
self.__apply_with_logging("Updating nets", self.cfg_data.nets.apply)
|
|
129
|
+
|
|
130
|
+
self.__apply_with_logging("Updating components", self.cfg_data.components.apply)
|
|
131
|
+
self.__apply_with_logging("Creating pin groups", self.cfg_data.pin_groups.apply)
|
|
132
|
+
self.__apply_with_logging("Placing sources", self.cfg_data.sources.apply)
|
|
133
|
+
self.__apply_with_logging("Creating setups", self.cfg_data.setups.apply)
|
|
134
|
+
|
|
135
|
+
self.__apply_with_logging("Applying materials", self.apply_materials)
|
|
136
|
+
self.__apply_with_logging("Updating stackup", self.apply_stackup)
|
|
137
|
+
|
|
158
138
|
if self.cfg_data.padstacks:
|
|
159
|
-
self.cfg_data.padstacks.apply
|
|
160
|
-
self._pedb.logger.info(f"Applying padstacks finished. Time lapse {datetime.now() - now}")
|
|
161
|
-
now = datetime.now()
|
|
139
|
+
self.__apply_with_logging("Applying padstacks", self.cfg_data.padstacks.apply)
|
|
162
140
|
|
|
163
|
-
|
|
164
|
-
self.cfg_data.s_parameters.apply()
|
|
165
|
-
self._pedb.logger.info(f"Applying S-parameters finished. Time lapse {datetime.now() - now}")
|
|
166
|
-
now = datetime.now()
|
|
141
|
+
self.__apply_with_logging("Applying S-parameters", self.cfg_data.s_parameters.apply)
|
|
167
142
|
|
|
168
|
-
# Configure SPICE models
|
|
169
143
|
for spice_model in self.cfg_data.spice_models:
|
|
170
|
-
spice_model.apply
|
|
171
|
-
|
|
172
|
-
|
|
144
|
+
self.__apply_with_logging(f"Assigning Spice model {spice_model}", spice_model.apply)
|
|
145
|
+
|
|
146
|
+
self.__apply_with_logging("Applying package definitions", self.cfg_data.package_definitions.apply)
|
|
147
|
+
self.__apply_with_logging("Applying modeler", self.apply_modeler)
|
|
148
|
+
self.__apply_with_logging("Placing ports", self.cfg_data.ports.apply)
|
|
149
|
+
self.__apply_with_logging("Placing probes", self.cfg_data.probes.apply)
|
|
150
|
+
self.__apply_with_logging("Applying operations", self.cfg_data.operations.apply)
|
|
151
|
+
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
def apply_modeler(self):
|
|
155
|
+
modeler = self.cfg_data.modeler
|
|
156
|
+
if modeler.traces:
|
|
157
|
+
for t in modeler.traces:
|
|
158
|
+
if t.path:
|
|
159
|
+
obj = self._pedb.modeler.create_trace(
|
|
160
|
+
path_list=t.path,
|
|
161
|
+
layer_name=t.layer,
|
|
162
|
+
net_name=t.net_name,
|
|
163
|
+
width=t.width,
|
|
164
|
+
start_cap_style=t.start_cap_style,
|
|
165
|
+
end_cap_style=t.end_cap_style,
|
|
166
|
+
corner_style=t.corner_style,
|
|
167
|
+
)
|
|
168
|
+
obj.aedt_name = t.name
|
|
169
|
+
else:
|
|
170
|
+
obj = self._pedb.modeler.create_trace(
|
|
171
|
+
path_list=[t.incremental_path[0]],
|
|
172
|
+
layer_name=t.layer,
|
|
173
|
+
net_name=t.net_name,
|
|
174
|
+
width=t.width,
|
|
175
|
+
start_cap_style=t.start_cap_style,
|
|
176
|
+
end_cap_style=t.end_cap_style,
|
|
177
|
+
corner_style=t.corner_style,
|
|
178
|
+
)
|
|
179
|
+
obj.aedt_name = t.name
|
|
180
|
+
for x, y in t.incremental_path[1:]:
|
|
181
|
+
obj.add_point(x, y, True)
|
|
182
|
+
|
|
183
|
+
if modeler.padstack_defs:
|
|
184
|
+
for p in modeler.padstack_defs:
|
|
185
|
+
pdata = self._pedb._edb.Definition.PadstackDefData.Create()
|
|
186
|
+
pdef = self._pedb._edb.Definition.PadstackDef.Create(self._pedb.active_db, p.name)
|
|
187
|
+
pdef.SetData(pdata)
|
|
188
|
+
pdef = self._pedb.pedb_class.database.edb_data.padstacks_data.EDBPadstack(pdef, self._pedb.padstacks)
|
|
189
|
+
p.pyedb_obj = pdef
|
|
190
|
+
p.api.set_parameters_to_edb()
|
|
191
|
+
|
|
192
|
+
if modeler.padstack_instances:
|
|
193
|
+
for p in modeler.padstack_instances:
|
|
194
|
+
p_inst = self._pedb.padstacks.place(
|
|
195
|
+
via_name=p.name,
|
|
196
|
+
net_name=p.net_name,
|
|
197
|
+
position=p.position,
|
|
198
|
+
definition_name=p.definition,
|
|
199
|
+
rotation=p.rotation if p.rotation is not None else 0,
|
|
200
|
+
)
|
|
201
|
+
p.pyedb_obj = p_inst
|
|
202
|
+
p.api.set_parameters_to_edb()
|
|
203
|
+
|
|
204
|
+
if modeler.planes:
|
|
205
|
+
for p in modeler.planes:
|
|
206
|
+
if p.type == "rectangle":
|
|
207
|
+
obj = self._pedb.modeler.create_rectangle(
|
|
208
|
+
layer_name=p.layer,
|
|
209
|
+
net_name=p.net_name,
|
|
210
|
+
lower_left_point=p.lower_left_point,
|
|
211
|
+
upper_right_point=p.upper_right_point,
|
|
212
|
+
corner_radius=p.corner_radius,
|
|
213
|
+
rotation=p.rotation,
|
|
214
|
+
)
|
|
215
|
+
obj.aedt_name = p.name
|
|
216
|
+
elif p.type == "polygon":
|
|
217
|
+
obj = self._pedb.modeler.create_polygon(
|
|
218
|
+
main_shape=p.points, layer_name=p.layer, net_name=p.net_name
|
|
219
|
+
)
|
|
220
|
+
obj.aedt_name = p.name
|
|
221
|
+
elif p.type == "circle":
|
|
222
|
+
obj = self._pedb.modeler.create_circle(
|
|
223
|
+
layer_name=p.layer,
|
|
224
|
+
net_name=p.net_name,
|
|
225
|
+
x=p.position[0],
|
|
226
|
+
y=p.position[1],
|
|
227
|
+
radius=p.radius,
|
|
228
|
+
)
|
|
229
|
+
obj.aedt_name = p.name
|
|
230
|
+
else:
|
|
231
|
+
raise RuntimeError(f"Plane type {p.type} not supported")
|
|
232
|
+
|
|
233
|
+
for v in p.voids:
|
|
234
|
+
for i in self._pedb.layout.primitives:
|
|
235
|
+
if i.aedt_name == v:
|
|
236
|
+
self._pedb.modeler.add_void(obj, i)
|
|
237
|
+
|
|
238
|
+
if modeler.components:
|
|
239
|
+
for c in modeler.components:
|
|
240
|
+
obj = self._pedb.components.create(
|
|
241
|
+
c.pins,
|
|
242
|
+
component_name=c.reference_designator,
|
|
243
|
+
placement_layer=c.placement_layer,
|
|
244
|
+
component_part_name=c.definition,
|
|
245
|
+
)
|
|
246
|
+
c.pyedb_obj = obj
|
|
247
|
+
c.api.set_parameters_to_edb()
|
|
248
|
+
|
|
249
|
+
primitives = self._pedb.layout.find_primitive(**modeler.primitives_to_delete)
|
|
250
|
+
for i in primitives:
|
|
251
|
+
i.delete()
|
|
252
|
+
|
|
253
|
+
def apply_variables(self):
|
|
254
|
+
"""Set variables into database."""
|
|
255
|
+
inst = self.cfg_data.variables
|
|
256
|
+
for i in inst.variables:
|
|
257
|
+
if i.name.startswith("$"):
|
|
258
|
+
self._pedb.add_project_variable(i.name, i.value, i.description)
|
|
259
|
+
else:
|
|
260
|
+
self._pedb.add_design_variable(i.name, i.value, description=i.description)
|
|
173
261
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
self._pedb.
|
|
177
|
-
|
|
262
|
+
def get_variables(self):
|
|
263
|
+
"""Retrieve variables from database."""
|
|
264
|
+
for name, obj in self._pedb.design_variables.items():
|
|
265
|
+
self.cfg_data.variables.add_variable(name, obj.value_string, obj.description)
|
|
266
|
+
for name, obj in self._pedb.project_variables.items():
|
|
267
|
+
self.cfg_data.variables.add_variable(name, obj.value_string, obj.description)
|
|
178
268
|
|
|
179
|
-
|
|
180
|
-
|
|
269
|
+
def apply_materials(self):
|
|
270
|
+
"""Apply material settings to the current design"""
|
|
271
|
+
cfg_stackup = self.cfg_data.stackup
|
|
272
|
+
if len(cfg_stackup.materials):
|
|
273
|
+
materials_in_db = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
|
|
274
|
+
for mat_in_cfg in cfg_stackup.materials:
|
|
275
|
+
if mat_in_cfg.name.lower() in materials_in_db:
|
|
276
|
+
self._pedb.materials.delete_material(materials_in_db[mat_in_cfg.name.lower()])
|
|
181
277
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
self._pedb.logger.info(f"Placing ports finished. Time lapse {datetime.now() - now}")
|
|
185
|
-
now = datetime.now()
|
|
278
|
+
attrs = mat_in_cfg.model_dump(exclude_none=True)
|
|
279
|
+
mat = self._pedb.materials.add_material(**attrs)
|
|
186
280
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
self._pedb.logger.info(f"Placing probes finished. Time lapse {datetime.now() - now}")
|
|
281
|
+
for i in attrs.get("thermal_modifiers", []):
|
|
282
|
+
mat.set_thermal_modifier(**i.to_dict())
|
|
190
283
|
|
|
191
|
-
|
|
192
|
-
|
|
284
|
+
def get_materials(self):
|
|
285
|
+
"""Retrieve materials from the current design.
|
|
193
286
|
|
|
194
|
-
|
|
287
|
+
Parameters
|
|
288
|
+
----------
|
|
289
|
+
append: bool, optional
|
|
290
|
+
If `True`, append materials to the current material list.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
self.cfg_data.stackup.materials = []
|
|
294
|
+
for name, mat in self._pedb.materials.materials.items():
|
|
295
|
+
self.cfg_data.stackup.add_material(**mat.to_dict())
|
|
296
|
+
|
|
297
|
+
def apply_stackup(self):
|
|
298
|
+
layers = self.cfg_data.stackup.layers
|
|
299
|
+
input_signal_layers = [i for i in layers if i.type.lower() == "signal"]
|
|
300
|
+
if len(input_signal_layers) == 0:
|
|
301
|
+
return
|
|
302
|
+
else: # Create materials with default properties used in stackup but not defined
|
|
303
|
+
materials = [m.name for m in self.cfg_data.stackup.materials]
|
|
304
|
+
for i in self.cfg_data.stackup.layers:
|
|
305
|
+
if i.type == "signal":
|
|
306
|
+
if i.material not in materials:
|
|
307
|
+
self.cfg_data.stackup.add_material(
|
|
308
|
+
name=i.material, **self._pedb.materials.default_conductor_property_values
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
if i.fill_material not in materials:
|
|
312
|
+
self.cfg_data.stackup.add_material(
|
|
313
|
+
name=i.material, **self._pedb.materials.default_dielectric_property_values
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
elif i.type == "dielectric":
|
|
317
|
+
if i.material not in materials:
|
|
318
|
+
self.cfg_data.stackup.add_material(
|
|
319
|
+
name=i.material, **self._pedb.materials.default_dielectric_property_values
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if len(self._pedb.stackup.signal_layers) == 0:
|
|
323
|
+
self.__create_stackup()
|
|
324
|
+
elif not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
|
|
325
|
+
raise Exception(f"Input signal layer count do not match.")
|
|
326
|
+
else:
|
|
327
|
+
self.__update_stackup()
|
|
328
|
+
|
|
329
|
+
def __create_stackup(self):
|
|
330
|
+
layers_ = list()
|
|
331
|
+
layers_.extend(self.cfg_data.stackup.layers)
|
|
332
|
+
for l_attrs in layers_:
|
|
333
|
+
attrs = l_attrs.model_dump(exclude_none=True)
|
|
334
|
+
self._pedb.stackup.add_layer_bottom(**attrs)
|
|
195
335
|
|
|
196
|
-
def
|
|
336
|
+
def __update_stackup(self):
|
|
337
|
+
"""Apply layer settings to the current design"""
|
|
338
|
+
|
|
339
|
+
# After import stackup, padstacks lose their definitions. They need to be fixed after loading stackup
|
|
340
|
+
# step 1, archive padstack definitions
|
|
197
341
|
temp_pdef_data = {}
|
|
198
342
|
for pdef_name, pdef in self._pedb.padstacks.definitions.items():
|
|
199
343
|
pdef_edb_object = pdef._padstack_def_data
|
|
200
344
|
temp_pdef_data[pdef_name] = pdef_edb_object
|
|
201
|
-
|
|
345
|
+
# step 2, archive padstack instance layer map
|
|
202
346
|
temp_p_inst_layer_map = {}
|
|
203
347
|
for p_inst in self._pedb.layout.padstack_instances:
|
|
204
348
|
temp_p_inst_layer_map[p_inst.id] = p_inst._edb_object.GetLayerMap()
|
|
205
349
|
|
|
206
|
-
|
|
207
|
-
|
|
350
|
+
# ----------------------------------------------------------------------
|
|
351
|
+
# Apply stackup
|
|
352
|
+
layers = list()
|
|
353
|
+
layers.extend(self.cfg_data.stackup.layers)
|
|
354
|
+
|
|
355
|
+
removal_list = []
|
|
356
|
+
lc_signal_layers = []
|
|
357
|
+
for name, obj in self._pedb.stackup.all_layers.items():
|
|
358
|
+
if obj.type == "dielectric":
|
|
359
|
+
removal_list.append(name)
|
|
360
|
+
elif obj.type == "signal":
|
|
361
|
+
lc_signal_layers.append(obj.id)
|
|
362
|
+
for l in removal_list:
|
|
363
|
+
self._pedb.stackup.remove_layer(l)
|
|
364
|
+
|
|
365
|
+
# update all signal layers
|
|
366
|
+
id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
|
|
367
|
+
signal_idx = 0
|
|
368
|
+
for l in layers:
|
|
369
|
+
if l.type == "signal":
|
|
370
|
+
layer_id = lc_signal_layers[signal_idx]
|
|
371
|
+
layer_name = id_name[layer_id]
|
|
372
|
+
attrs = l.model_dump(exclude_none=True)
|
|
373
|
+
self._pedb.stackup.layers[layer_name].update(**attrs)
|
|
374
|
+
signal_idx = signal_idx + 1
|
|
375
|
+
|
|
376
|
+
# add all dielectric layers. Dielectric layers must be added last. Otherwise,
|
|
377
|
+
# dielectric layer will occupy signal and document layer id.
|
|
378
|
+
l = layers.pop(0)
|
|
379
|
+
if l.type == "signal":
|
|
380
|
+
prev_layer_clone = self._pedb.stackup.layers[l.name]
|
|
381
|
+
else:
|
|
382
|
+
attrs = l.model_dump(exclude_none=True)
|
|
383
|
+
prev_layer_clone = self._pedb.stackup.add_layer_top(**attrs)
|
|
384
|
+
for idx, l in enumerate(layers):
|
|
385
|
+
if l.type == "dielectric":
|
|
386
|
+
attrs = l.model_dump(exclude_none=True)
|
|
387
|
+
prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **attrs)
|
|
388
|
+
elif l.type == "signal":
|
|
389
|
+
prev_layer_clone = self._pedb.stackup.layers[l.name]
|
|
390
|
+
|
|
391
|
+
# ----------------------------------------------------------------------
|
|
392
|
+
# restore padstack definitions
|
|
208
393
|
for pdef_name, pdef_data in temp_pdef_data.items():
|
|
209
394
|
pdef = self._pedb.padstacks.definitions[pdef_name]
|
|
210
395
|
pdef._padstack_def_data = pdef_data
|
|
211
|
-
|
|
396
|
+
# restore padstack instance layer map
|
|
212
397
|
for p_inst in self._pedb.layout.padstack_instances:
|
|
213
398
|
p_inst._edb_object.SetLayerMap(temp_p_inst_layer_map[p_inst.id])
|
|
214
399
|
|
|
215
|
-
def
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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"]]
|
|
270
|
-
|
|
271
|
-
def _load_package_def(self):
|
|
272
|
-
"""Imports package definition information from JSON."""
|
|
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
|
|
400
|
+
def get_stackup(self):
|
|
401
|
+
self.cfg_data.stackup.layers = []
|
|
402
|
+
for name, obj in self._pedb.stackup.all_layers.items():
|
|
403
|
+
self.cfg_data.stackup.add_layer_at_bottom(**obj.properties)
|
|
313
404
|
|
|
314
405
|
def get_data_from_db(self, **kwargs):
|
|
315
406
|
"""Get configuration data from layout.
|
|
@@ -323,11 +414,17 @@ class Configuration:
|
|
|
323
414
|
|
|
324
415
|
"""
|
|
325
416
|
self._pedb.logger.info("Getting data from layout database.")
|
|
417
|
+
self.get_variables()
|
|
418
|
+
self.get_materials()
|
|
419
|
+
self.get_stackup()
|
|
420
|
+
|
|
326
421
|
data = {}
|
|
327
422
|
if kwargs.get("general", False):
|
|
328
423
|
data["general"] = self.cfg_data.general.get_data_from_db()
|
|
424
|
+
if kwargs.get("variables", False):
|
|
425
|
+
data["variables"] = self.cfg_data.variables.model_dump(exclude_none=True)
|
|
329
426
|
if kwargs.get("stackup", False):
|
|
330
|
-
data["stackup"] = self.cfg_data.stackup.
|
|
427
|
+
data["stackup"] = self.cfg_data.stackup.model_dump(exclude_none=True)
|
|
331
428
|
if kwargs.get("package_definitions", False):
|
|
332
429
|
data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
|
|
333
430
|
if kwargs.get("setups", False):
|
|
@@ -389,6 +486,7 @@ class Configuration:
|
|
|
389
486
|
s_parameters=True,
|
|
390
487
|
padstacks=True,
|
|
391
488
|
general=True,
|
|
489
|
+
variables=True,
|
|
392
490
|
):
|
|
393
491
|
"""Export the configuration data from layout to a file.
|
|
394
492
|
|
|
@@ -422,6 +520,8 @@ class Configuration:
|
|
|
422
520
|
Whether to export padstacks.
|
|
423
521
|
general : bool
|
|
424
522
|
Whether to export general information.
|
|
523
|
+
variables : bool
|
|
524
|
+
Whether to export variable.
|
|
425
525
|
Returns
|
|
426
526
|
-------
|
|
427
527
|
bool
|
|
@@ -440,6 +540,7 @@ class Configuration:
|
|
|
440
540
|
s_parameters=s_parameters,
|
|
441
541
|
padstacks=padstacks,
|
|
442
542
|
general=general,
|
|
543
|
+
variables=variables,
|
|
443
544
|
)
|
|
444
545
|
|
|
445
546
|
file_path = file_path if isinstance(file_path, Path) else Path(file_path)
|
|
@@ -1597,9 +1597,15 @@ class Components(object):
|
|
|
1597
1597
|
>>> edbapp.components.create(pins, "A1New")
|
|
1598
1598
|
|
|
1599
1599
|
"""
|
|
1600
|
-
_pins = [
|
|
1601
|
-
|
|
1602
|
-
|
|
1600
|
+
_pins = []
|
|
1601
|
+
for p in pins:
|
|
1602
|
+
if isinstance(p, EDBPadstackInstance):
|
|
1603
|
+
_pins.append(p._edb_object)
|
|
1604
|
+
elif isinstance(p, str):
|
|
1605
|
+
_pins.append(self._pedb.padstacks.instances_by_name[p]._edb_object)
|
|
1606
|
+
else:
|
|
1607
|
+
_pins.append(p)
|
|
1608
|
+
pins = _pins
|
|
1603
1609
|
if not component_name:
|
|
1604
1610
|
component_name = generate_unique_name("Comp_")
|
|
1605
1611
|
if component_part_name:
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import os
|
|
25
25
|
import re
|
|
26
26
|
import sys
|
|
27
|
+
import warnings
|
|
27
28
|
|
|
28
29
|
from pyedb import __version__
|
|
29
30
|
from pyedb.dotnet.database.general import convert_py_list_to_net_list
|
|
@@ -34,6 +35,7 @@ from pyedb.generic.general_methods import (
|
|
|
34
35
|
is_linux,
|
|
35
36
|
settings,
|
|
36
37
|
)
|
|
38
|
+
from pyedb.generic.grpc_warnings import GRPC_GENERAL_WARNING
|
|
37
39
|
from pyedb.misc.misc import list_installed_ansysem
|
|
38
40
|
|
|
39
41
|
|
|
@@ -710,6 +712,8 @@ class EdbDotNet(object):
|
|
|
710
712
|
except IndexError:
|
|
711
713
|
raise Exception("No ANSYSEM_ROOTxxx is found.")
|
|
712
714
|
self.edbversion = edbversion
|
|
715
|
+
if float(self.edbversion) >= 2025.2:
|
|
716
|
+
warnings.warn(GRPC_GENERAL_WARNING, UserWarning)
|
|
713
717
|
self.student_version = student_version
|
|
714
718
|
"""Initialize DLLs."""
|
|
715
719
|
from pyedb.dotnet.clr_module import _clr, edb_initialized
|
|
@@ -76,7 +76,9 @@ class LayerEdbClass(object):
|
|
|
76
76
|
|
|
77
77
|
@fill_material.setter
|
|
78
78
|
def fill_material(self, value):
|
|
79
|
-
self.
|
|
79
|
+
layer_clone = self._edb_layer
|
|
80
|
+
layer_clone.SetFillMaterial(value)
|
|
81
|
+
self._pedb.stackup._set_layout_stackup(layer_clone, "change_attribute")
|
|
80
82
|
|
|
81
83
|
@property
|
|
82
84
|
def _stackup_layer_mapping(self):
|
pyedb/dotnet/database/siwave.py
CHANGED
|
@@ -40,6 +40,9 @@ from pyedb.dotnet.database.edb_data.sources import (
|
|
|
40
40
|
VoltageSource,
|
|
41
41
|
)
|
|
42
42
|
from pyedb.dotnet.database.general import convert_py_list_to_net_list
|
|
43
|
+
from pyedb.dotnet.database.utilities.siwave_cpa_simulation_setup import (
|
|
44
|
+
SIWaveCPASimulationSetup,
|
|
45
|
+
)
|
|
43
46
|
from pyedb.generic.constants import SolverType, SweepType
|
|
44
47
|
from pyedb.generic.general_methods import _retry_ntimes, generate_unique_name
|
|
45
48
|
from pyedb.misc.siw_feature_config.xtalk_scan.scan_config import SiwaveScanConfig
|
|
@@ -1507,6 +1510,17 @@ class EdbSiwave(object):
|
|
|
1507
1510
|
"""
|
|
1508
1511
|
return SiwaveScanConfig(self._pedb, scan_type)
|
|
1509
1512
|
|
|
1513
|
+
def add_cpa_analysis(self, name=None, siwave_cpa_setup_class=None):
|
|
1514
|
+
if not name:
|
|
1515
|
+
from pyedb.generic.general_methods import generate_unique_name
|
|
1516
|
+
|
|
1517
|
+
if not siwave_cpa_setup_class:
|
|
1518
|
+
name = generate_unique_name("cpa_setup")
|
|
1519
|
+
else:
|
|
1520
|
+
name = siwave_cpa_setup_class.name
|
|
1521
|
+
cpa_setup = SIWaveCPASimulationSetup(self._pedb, name=name, siwave_cpa_setup_class=siwave_cpa_setup_class)
|
|
1522
|
+
return cpa_setup
|
|
1523
|
+
|
|
1510
1524
|
@icepak_use_minimal_comp_defaults.setter
|
|
1511
1525
|
def icepak_use_minimal_comp_defaults(self, value):
|
|
1512
1526
|
value = "True" if bool(value) else ""
|