pyedb 0.19.0__py3-none-any.whl → 0.21.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/dotnet/edb.py +311 -335
- pyedb/dotnet/edb_core/cell/connectable.py +64 -0
- pyedb/dotnet/edb_core/cell/hierarchy/component.py +12 -10
- pyedb/dotnet/edb_core/cell/hierarchy/hierarchy_obj.py +1 -1
- pyedb/dotnet/edb_core/cell/layout.py +253 -105
- pyedb/dotnet/edb_core/cell/layout_obj.py +4 -49
- pyedb/dotnet/edb_core/cell/primitive.py +14 -2
- pyedb/dotnet/edb_core/cell/terminal/padstack_instance_terminal.py +1 -1
- pyedb/dotnet/edb_core/cell/terminal/point_terminal.py +1 -1
- pyedb/dotnet/edb_core/cell/terminal/terminal.py +1 -19
- pyedb/dotnet/edb_core/cell/voltage_regulator.py +2 -16
- pyedb/dotnet/edb_core/components.py +14 -13
- pyedb/dotnet/edb_core/dotnet/database.py +5 -10
- pyedb/dotnet/edb_core/dotnet/primitive.py +11 -1
- pyedb/dotnet/edb_core/edb_data/control_file.py +2 -12
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +20 -33
- pyedb/dotnet/edb_core/general.py +6 -9
- pyedb/dotnet/edb_core/hfss.py +4 -8
- pyedb/dotnet/edb_core/layout_obj_instance.py +30 -0
- pyedb/dotnet/edb_core/materials.py +4 -11
- pyedb/dotnet/edb_core/{layout.py → modeler.py} +153 -7
- pyedb/dotnet/edb_core/net_class.py +7 -8
- pyedb/dotnet/edb_core/nets.py +3 -9
- pyedb/dotnet/edb_core/padstack.py +13 -9
- pyedb/dotnet/edb_core/sim_setup_data/data/sweep_data.py +5 -2
- pyedb/dotnet/edb_core/siwave.py +5 -6
- pyedb/dotnet/edb_core/stackup.py +18 -23
- pyedb/dotnet/edb_core/utilities/simulation_setup.py +1 -4
- pyedb/generic/filesystem.py +2 -8
- pyedb/generic/general_methods.py +4 -10
- pyedb/generic/plot.py +26 -29
- pyedb/generic/process.py +2 -6
- pyedb/misc/downloads.py +3 -40
- pyedb/siwave.py +2 -5
- {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/METADATA +2 -2
- {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/RECORD +39 -38
- pyedb/dotnet/edb_core/dotnet/layout.py +0 -260
- {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/LICENSE +0 -0
- {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/WHEEL +0 -0
pyedb/dotnet/edb.py
CHANGED
|
@@ -30,6 +30,7 @@ import os
|
|
|
30
30
|
from pathlib import Path
|
|
31
31
|
import re
|
|
32
32
|
import shutil
|
|
33
|
+
import subprocess
|
|
33
34
|
import sys
|
|
34
35
|
import tempfile
|
|
35
36
|
import time
|
|
@@ -37,21 +38,14 @@ import traceback
|
|
|
37
38
|
from typing import Union
|
|
38
39
|
import warnings
|
|
39
40
|
|
|
41
|
+
import rtree
|
|
42
|
+
|
|
40
43
|
from pyedb.configuration.configuration import Configuration
|
|
41
44
|
from pyedb.dotnet.application.Variables import decompose_variable_value
|
|
42
45
|
from pyedb.dotnet.edb_core.cell.layout import Layout
|
|
43
|
-
from pyedb.dotnet.edb_core.cell.terminal.bundle_terminal import BundleTerminal
|
|
44
|
-
from pyedb.dotnet.edb_core.cell.terminal.edge_terminal import EdgeTerminal
|
|
45
|
-
from pyedb.dotnet.edb_core.cell.terminal.padstack_instance_terminal import (
|
|
46
|
-
PadstackInstanceTerminal,
|
|
47
|
-
)
|
|
48
|
-
from pyedb.dotnet.edb_core.cell.terminal.pingroup_terminal import PinGroupTerminal
|
|
49
|
-
from pyedb.dotnet.edb_core.cell.terminal.point_terminal import PointTerminal
|
|
50
46
|
from pyedb.dotnet.edb_core.cell.terminal.terminal import Terminal
|
|
51
|
-
from pyedb.dotnet.edb_core.cell.voltage_regulator import VoltageRegulator
|
|
52
47
|
from pyedb.dotnet.edb_core.components import Components
|
|
53
48
|
from pyedb.dotnet.edb_core.dotnet.database import Database
|
|
54
|
-
from pyedb.dotnet.edb_core.dotnet.layout import LayoutDotNet
|
|
55
49
|
from pyedb.dotnet.edb_core.edb_data.control_file import (
|
|
56
50
|
ControlFile,
|
|
57
51
|
convert_technology_file,
|
|
@@ -82,6 +76,7 @@ from pyedb.dotnet.edb_core.general import (
|
|
|
82
76
|
from pyedb.dotnet.edb_core.hfss import EdbHfss
|
|
83
77
|
from pyedb.dotnet.edb_core.layout_validation import LayoutValidation
|
|
84
78
|
from pyedb.dotnet.edb_core.materials import Materials
|
|
79
|
+
from pyedb.dotnet.edb_core.modeler import Modeler
|
|
85
80
|
from pyedb.dotnet.edb_core.net_class import (
|
|
86
81
|
EdbDifferentialPairs,
|
|
87
82
|
EdbExtendedNets,
|
|
@@ -103,8 +98,6 @@ from pyedb.generic.constants import AEDT_UNITS, SolverType
|
|
|
103
98
|
from pyedb.generic.general_methods import (
|
|
104
99
|
generate_unique_name,
|
|
105
100
|
get_string_version,
|
|
106
|
-
inside_desktop,
|
|
107
|
-
is_ironpython,
|
|
108
101
|
is_linux,
|
|
109
102
|
is_windows,
|
|
110
103
|
)
|
|
@@ -114,13 +107,6 @@ from pyedb.ipc2581.ipc2581 import Ipc2581
|
|
|
114
107
|
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
115
108
|
from pyedb.workflow import Workflow
|
|
116
109
|
|
|
117
|
-
if is_linux and is_ironpython:
|
|
118
|
-
import subprocessdotnet as subprocess
|
|
119
|
-
else:
|
|
120
|
-
import subprocess
|
|
121
|
-
|
|
122
|
-
import rtree
|
|
123
|
-
|
|
124
110
|
|
|
125
111
|
class Edb(Database):
|
|
126
112
|
"""Provides the EDB application interface.
|
|
@@ -235,9 +221,7 @@ class Edb(Database):
|
|
|
235
221
|
if not isreadonly:
|
|
236
222
|
self._check_remove_project_files(edbpath, remove_existing_aedt)
|
|
237
223
|
|
|
238
|
-
if
|
|
239
|
-
self.open_edb_inside_aedt()
|
|
240
|
-
elif edbpath[-3:] in ["brd", "mcm", "sip", "gds", "xml", "dxf", "tgz", "anf"]:
|
|
224
|
+
if edbpath[-3:] in ["brd", "mcm", "sip", "gds", "xml", "dxf", "tgz", "anf"]:
|
|
241
225
|
self.edbpath = edbpath[:-4] + ".aedb"
|
|
242
226
|
working_dir = os.path.dirname(edbpath)
|
|
243
227
|
control_file = None
|
|
@@ -361,7 +345,7 @@ class Edb(Database):
|
|
|
361
345
|
self._siwave = EdbSiwave(self)
|
|
362
346
|
self._hfss = EdbHfss(self)
|
|
363
347
|
self._nets = EdbNets(self)
|
|
364
|
-
self._core_primitives =
|
|
348
|
+
self._core_primitives = Modeler(self)
|
|
365
349
|
self._stackup2 = self._stackup
|
|
366
350
|
self._materials = Materials(self)
|
|
367
351
|
|
|
@@ -437,23 +421,10 @@ class Edb(Database):
|
|
|
437
421
|
|
|
438
422
|
Returns
|
|
439
423
|
-------
|
|
440
|
-
|
|
424
|
+
Dict
|
|
441
425
|
"""
|
|
442
426
|
|
|
443
|
-
|
|
444
|
-
for i in self.layout.terminals:
|
|
445
|
-
terminal_type = i.ToString().split(".")[-1]
|
|
446
|
-
if terminal_type == "PinGroupTerminal":
|
|
447
|
-
temp[i.GetName()] = PinGroupTerminal(self, i)
|
|
448
|
-
elif terminal_type == "PadstackInstanceTerminal":
|
|
449
|
-
temp[i.GetName()] = PadstackInstanceTerminal(self, i)
|
|
450
|
-
elif terminal_type == "EdgeTerminal":
|
|
451
|
-
temp[i.GetName()] = EdgeTerminal(self, i)
|
|
452
|
-
elif terminal_type == "BundleTerminal":
|
|
453
|
-
temp[i.GetName()] = BundleTerminal(self, i)
|
|
454
|
-
elif terminal_type == "PointTerminal":
|
|
455
|
-
temp[i.GetName()] = PointTerminal(self, i)
|
|
456
|
-
return temp
|
|
427
|
+
return {i.name: i for i in self.layout.terminals}
|
|
457
428
|
|
|
458
429
|
@property
|
|
459
430
|
def excitations(self):
|
|
@@ -516,7 +487,7 @@ class Edb(Database):
|
|
|
516
487
|
@property
|
|
517
488
|
def voltage_regulator_modules(self):
|
|
518
489
|
"""Get all voltage regulator modules"""
|
|
519
|
-
vrms =
|
|
490
|
+
vrms = self.layout.voltage_regulators
|
|
520
491
|
_vrms = {}
|
|
521
492
|
for vrm in vrms:
|
|
522
493
|
_vrms[vrm.name] = vrm
|
|
@@ -574,40 +545,6 @@ class Edb(Database):
|
|
|
574
545
|
|
|
575
546
|
return True
|
|
576
547
|
|
|
577
|
-
def open_edb_inside_aedt(self):
|
|
578
|
-
"""Open EDB inside AEDT.
|
|
579
|
-
|
|
580
|
-
Returns
|
|
581
|
-
-------
|
|
582
|
-
``True`` when succeed ``False`` if failed : bool
|
|
583
|
-
|
|
584
|
-
"""
|
|
585
|
-
self.logger.info("Opening EDB from HDL")
|
|
586
|
-
self.run_as_standalone(False)
|
|
587
|
-
if self.oproject.GetEDBHandle():
|
|
588
|
-
self.attach(self.oproject.GetEDBHandle())
|
|
589
|
-
if not self.active_db:
|
|
590
|
-
self.logger.warning("Error getting the database.")
|
|
591
|
-
self._active_cell = None
|
|
592
|
-
return None
|
|
593
|
-
self._active_cell = self.edb_api.cell.cell.FindByName(
|
|
594
|
-
self.active_db,
|
|
595
|
-
self.edb_api.cell._cell.CellType.CircuitCell,
|
|
596
|
-
self.cellname,
|
|
597
|
-
)
|
|
598
|
-
if self._active_cell is None:
|
|
599
|
-
self._active_cell = list(self.top_circuit_cells)[0]
|
|
600
|
-
if self._active_cell:
|
|
601
|
-
if not os.path.exists(self.edbpath):
|
|
602
|
-
os.makedirs(self.edbpath)
|
|
603
|
-
self._init_objects()
|
|
604
|
-
return True
|
|
605
|
-
else:
|
|
606
|
-
return None
|
|
607
|
-
else:
|
|
608
|
-
self._active_cell = None
|
|
609
|
-
return None
|
|
610
|
-
|
|
611
548
|
def create_edb(self):
|
|
612
549
|
"""Create EDB.
|
|
613
550
|
|
|
@@ -1153,7 +1090,7 @@ class Edb(Database):
|
|
|
1153
1090
|
>>> top_prims = edbapp.modeler.primitives_by_layer["TOP"]
|
|
1154
1091
|
"""
|
|
1155
1092
|
if not self._core_primitives and self.active_db:
|
|
1156
|
-
self._core_primitives =
|
|
1093
|
+
self._core_primitives = Modeler(self)
|
|
1157
1094
|
return self._core_primitives
|
|
1158
1095
|
|
|
1159
1096
|
@property
|
|
@@ -1164,7 +1101,7 @@ class Edb(Database):
|
|
|
1164
1101
|
-------
|
|
1165
1102
|
:class:`legacy.edb_core.dotnet.layout.Layout`
|
|
1166
1103
|
"""
|
|
1167
|
-
return
|
|
1104
|
+
return Layout(self, self._active_cell.GetLayout())
|
|
1168
1105
|
|
|
1169
1106
|
@property
|
|
1170
1107
|
def active_layout(self):
|
|
@@ -1174,12 +1111,12 @@ class Edb(Database):
|
|
|
1174
1111
|
-------
|
|
1175
1112
|
Instance of EDB API Layout Class.
|
|
1176
1113
|
"""
|
|
1177
|
-
return self.layout.
|
|
1114
|
+
return self.layout._edb_object
|
|
1178
1115
|
|
|
1179
1116
|
@property
|
|
1180
1117
|
def layout_instance(self):
|
|
1181
1118
|
"""Edb Layout Instance."""
|
|
1182
|
-
return self.layout.
|
|
1119
|
+
return self.layout._edb_object.GetLayoutInstance()
|
|
1183
1120
|
|
|
1184
1121
|
def get_connected_objects(self, layout_object_instance):
|
|
1185
1122
|
"""Get connected objects.
|
|
@@ -2218,9 +2155,6 @@ class Edb(Database):
|
|
|
2218
2155
|
keep_lines_as_path=False,
|
|
2219
2156
|
inlcude_voids_in_extents=False,
|
|
2220
2157
|
):
|
|
2221
|
-
if is_ironpython: # pragma: no cover
|
|
2222
|
-
self.logger.error("Method working only in Cpython")
|
|
2223
|
-
return False
|
|
2224
2158
|
from concurrent.futures import ThreadPoolExecutor
|
|
2225
2159
|
|
|
2226
2160
|
if output_aedb_path:
|
|
@@ -2750,20 +2684,12 @@ class Edb(Database):
|
|
|
2750
2684
|
|
|
2751
2685
|
for void_circle in voids_to_add:
|
|
2752
2686
|
if void_circle.type == "Circle":
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
) = void_circle.primitive_object.GetParameters()
|
|
2760
|
-
else:
|
|
2761
|
-
(
|
|
2762
|
-
res,
|
|
2763
|
-
center_x,
|
|
2764
|
-
center_y,
|
|
2765
|
-
radius,
|
|
2766
|
-
) = void_circle.primitive_object.GetParameters(0.0, 0.0, 0.0)
|
|
2687
|
+
(
|
|
2688
|
+
res,
|
|
2689
|
+
center_x,
|
|
2690
|
+
center_y,
|
|
2691
|
+
radius,
|
|
2692
|
+
) = void_circle.primitive_object.GetParameters(0.0, 0.0, 0.0)
|
|
2767
2693
|
cloned_circle = self.edb_api.cell.primitive.circle.create(
|
|
2768
2694
|
layout,
|
|
2769
2695
|
void_circle.layer_name,
|
|
@@ -3344,163 +3270,156 @@ class Edb(Database):
|
|
|
3344
3270
|
legacy_name = self.edbpath
|
|
3345
3271
|
if simulation_setup.output_aedb:
|
|
3346
3272
|
self.save_edb_as(simulation_setup.output_aedb)
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
for
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
):
|
|
3382
|
-
self.logger.info("Cutout processed.")
|
|
3383
|
-
old_cell = self.active_cell.FindByName(
|
|
3384
|
-
self.db,
|
|
3385
|
-
self.edb_api.cell.CellType.CircuitCell,
|
|
3386
|
-
old_cell_name,
|
|
3387
|
-
)
|
|
3388
|
-
if old_cell:
|
|
3389
|
-
old_cell.Delete()
|
|
3390
|
-
else: # pragma: no cover
|
|
3391
|
-
self.logger.error("Cutout failed.")
|
|
3392
|
-
else:
|
|
3393
|
-
self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
|
|
3394
|
-
self.cutout(
|
|
3395
|
-
signal_list=simulation_setup.signal_nets,
|
|
3396
|
-
reference_list=simulation_setup.power_nets,
|
|
3397
|
-
expansion_size=simulation_setup.cutout_subdesign_expansion,
|
|
3398
|
-
use_round_corner=simulation_setup.cutout_subdesign_round_corner,
|
|
3399
|
-
extent_type=simulation_setup.cutout_subdesign_type,
|
|
3400
|
-
use_pyaedt_cutout=True,
|
|
3401
|
-
use_pyaedt_extent_computing=True,
|
|
3402
|
-
remove_single_pin_components=True,
|
|
3403
|
-
)
|
|
3273
|
+
if simulation_setup.signal_layer_etching_instances:
|
|
3274
|
+
for layer in simulation_setup.signal_layer_etching_instances:
|
|
3275
|
+
if layer in self.stackup.layers:
|
|
3276
|
+
idx = simulation_setup.signal_layer_etching_instances.index(layer)
|
|
3277
|
+
if len(simulation_setup.etching_factor_instances) > idx:
|
|
3278
|
+
self.stackup[layer].etch_factor = float(simulation_setup.etching_factor_instances[idx])
|
|
3279
|
+
|
|
3280
|
+
if not simulation_setup.signal_nets and simulation_setup.components:
|
|
3281
|
+
nets_to_include = []
|
|
3282
|
+
pnets = list(self.nets.power.keys())[:]
|
|
3283
|
+
for el in simulation_setup.components:
|
|
3284
|
+
nets_to_include.append([i for i in self.components[el].nets if i not in pnets])
|
|
3285
|
+
simulation_setup.signal_nets = [
|
|
3286
|
+
i
|
|
3287
|
+
for i in list(set.intersection(*map(set, nets_to_include)))
|
|
3288
|
+
if i not in simulation_setup.power_nets and i != ""
|
|
3289
|
+
]
|
|
3290
|
+
self.nets.classify_nets(simulation_setup.power_nets, simulation_setup.signal_nets)
|
|
3291
|
+
if not simulation_setup.power_nets or not simulation_setup.signal_nets:
|
|
3292
|
+
self.logger.info("Disabling cutout as no signals or power nets have been defined.")
|
|
3293
|
+
simulation_setup.do_cutout_subdesign = False
|
|
3294
|
+
if simulation_setup.do_cutout_subdesign:
|
|
3295
|
+
self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
|
|
3296
|
+
if simulation_setup.use_default_cutout:
|
|
3297
|
+
old_cell_name = self.active_cell.GetName()
|
|
3298
|
+
if self.cutout(
|
|
3299
|
+
signal_list=simulation_setup.signal_nets,
|
|
3300
|
+
reference_list=simulation_setup.power_nets,
|
|
3301
|
+
expansion_size=simulation_setup.cutout_subdesign_expansion,
|
|
3302
|
+
use_round_corner=simulation_setup.cutout_subdesign_round_corner,
|
|
3303
|
+
extent_type=simulation_setup.cutout_subdesign_type,
|
|
3304
|
+
use_pyaedt_cutout=False,
|
|
3305
|
+
use_pyaedt_extent_computing=False,
|
|
3306
|
+
):
|
|
3404
3307
|
self.logger.info("Cutout processed.")
|
|
3308
|
+
old_cell = self.active_cell.FindByName(
|
|
3309
|
+
self.db,
|
|
3310
|
+
self.edb_api.cell.CellType.CircuitCell,
|
|
3311
|
+
old_cell_name,
|
|
3312
|
+
)
|
|
3313
|
+
if old_cell:
|
|
3314
|
+
old_cell.Delete()
|
|
3315
|
+
else: # pragma: no cover
|
|
3316
|
+
self.logger.error("Cutout failed.")
|
|
3405
3317
|
else:
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3318
|
+
self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
|
|
3319
|
+
self.cutout(
|
|
3320
|
+
signal_list=simulation_setup.signal_nets,
|
|
3321
|
+
reference_list=simulation_setup.power_nets,
|
|
3322
|
+
expansion_size=simulation_setup.cutout_subdesign_expansion,
|
|
3323
|
+
use_round_corner=simulation_setup.cutout_subdesign_round_corner,
|
|
3324
|
+
extent_type=simulation_setup.cutout_subdesign_type,
|
|
3325
|
+
use_pyaedt_cutout=True,
|
|
3326
|
+
use_pyaedt_extent_computing=True,
|
|
3327
|
+
remove_single_pin_components=True,
|
|
3328
|
+
)
|
|
3329
|
+
self.logger.info("Cutout processed.")
|
|
3330
|
+
else:
|
|
3331
|
+
if simulation_setup.include_only_selected_nets:
|
|
3332
|
+
included_nets = simulation_setup.signal_nets + simulation_setup.power_nets
|
|
3333
|
+
nets_to_remove = [net.name for net in list(self.nets.nets.values()) if not net.name in included_nets]
|
|
3334
|
+
self.nets.delete(nets_to_remove)
|
|
3335
|
+
self.logger.info("Deleting existing ports.")
|
|
3336
|
+
map(lambda port: port.Delete(), self.layout.terminals)
|
|
3337
|
+
map(lambda pg: pg.Delete(), self.layout.pin_groups)
|
|
3338
|
+
if simulation_setup.solver_type == SolverType.Hfss3dLayout:
|
|
3339
|
+
if simulation_setup.generate_excitations:
|
|
3340
|
+
self.logger.info("Creating HFSS ports for signal nets.")
|
|
3341
|
+
source_type = SourceType.CoaxPort
|
|
3342
|
+
if not simulation_setup.generate_solder_balls:
|
|
3343
|
+
source_type = SourceType.CircPort
|
|
3344
|
+
for cmp in simulation_setup.components:
|
|
3345
|
+
if isinstance(cmp, str): # keep legacy component
|
|
3346
|
+
self.components.create_port_on_component(
|
|
3347
|
+
cmp,
|
|
3348
|
+
net_list=simulation_setup.signal_nets,
|
|
3349
|
+
do_pingroup=False,
|
|
3350
|
+
reference_net=simulation_setup.power_nets,
|
|
3351
|
+
port_type=source_type,
|
|
3352
|
+
)
|
|
3353
|
+
elif isinstance(cmp, dict):
|
|
3354
|
+
if "refdes" in cmp:
|
|
3355
|
+
if not "solder_balls_height" in cmp: # pragma no cover
|
|
3356
|
+
cmp["solder_balls_height"] = None
|
|
3357
|
+
if not "solder_balls_size" in cmp: # pragma no cover
|
|
3358
|
+
cmp["solder_balls_size"] = None
|
|
3359
|
+
cmp["solder_balls_mid_size"] = None
|
|
3360
|
+
if not "solder_balls_mid_size" in cmp: # pragma no cover
|
|
3361
|
+
cmp["solder_balls_mid_size"] = None
|
|
3423
3362
|
self.components.create_port_on_component(
|
|
3424
|
-
cmp,
|
|
3363
|
+
cmp["refdes"],
|
|
3425
3364
|
net_list=simulation_setup.signal_nets,
|
|
3426
3365
|
do_pingroup=False,
|
|
3427
3366
|
reference_net=simulation_setup.power_nets,
|
|
3428
3367
|
port_type=source_type,
|
|
3368
|
+
solder_balls_height=cmp["solder_balls_height"],
|
|
3369
|
+
solder_balls_size=cmp["solder_balls_size"],
|
|
3370
|
+
solder_balls_mid_size=cmp["solder_balls_mid_size"],
|
|
3429
3371
|
)
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
if
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
simulation_setup.generate_solder_balls and simulation_setup.trim_reference_size
|
|
3457
|
-
): # pragma: no cover
|
|
3458
|
-
self.logger.info(
|
|
3459
|
-
"Trimming the reference plane for coaxial ports: {0}".format(
|
|
3460
|
-
bool(simulation_setup.trim_reference_size)
|
|
3461
|
-
)
|
|
3372
|
+
if simulation_setup.generate_solder_balls and not self.hfss.set_coax_port_attributes(
|
|
3373
|
+
simulation_setup
|
|
3374
|
+
): # pragma: no cover
|
|
3375
|
+
self.logger.error("Failed to configure coaxial port attributes.")
|
|
3376
|
+
self.logger.info("Number of ports: {}".format(self.hfss.get_ports_number()))
|
|
3377
|
+
self.logger.info("Configure HFSS extents.")
|
|
3378
|
+
if simulation_setup.generate_solder_balls and simulation_setup.trim_reference_size: # pragma: no cover
|
|
3379
|
+
self.logger.info(
|
|
3380
|
+
"Trimming the reference plane for coaxial ports: {0}".format(
|
|
3381
|
+
bool(simulation_setup.trim_reference_size)
|
|
3382
|
+
)
|
|
3383
|
+
)
|
|
3384
|
+
self.hfss.trim_component_reference_size(simulation_setup) # pragma: no cover
|
|
3385
|
+
self.hfss.configure_hfss_extents(simulation_setup)
|
|
3386
|
+
if not self.hfss.configure_hfss_analysis_setup(simulation_setup):
|
|
3387
|
+
self.logger.error("Failed to configure HFSS simulation setup.")
|
|
3388
|
+
if simulation_setup.solver_type == SolverType.SiwaveSYZ:
|
|
3389
|
+
if simulation_setup.generate_excitations:
|
|
3390
|
+
for cmp in simulation_setup.components:
|
|
3391
|
+
if isinstance(cmp, str): # keep legacy
|
|
3392
|
+
self.components.create_port_on_component(
|
|
3393
|
+
cmp,
|
|
3394
|
+
net_list=simulation_setup.signal_nets,
|
|
3395
|
+
do_pingroup=simulation_setup.do_pingroup,
|
|
3396
|
+
reference_net=simulation_setup.power_nets,
|
|
3397
|
+
port_type=SourceType.CircPort,
|
|
3462
3398
|
)
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
if not self.hfss.configure_hfss_analysis_setup(simulation_setup):
|
|
3466
|
-
self.logger.error("Failed to configure HFSS simulation setup.")
|
|
3467
|
-
if simulation_setup.solver_type == SolverType.SiwaveSYZ:
|
|
3468
|
-
if simulation_setup.generate_excitations:
|
|
3469
|
-
for cmp in simulation_setup.components:
|
|
3470
|
-
if isinstance(cmp, str): # keep legacy
|
|
3399
|
+
elif isinstance(cmp, dict):
|
|
3400
|
+
if "refdes" in cmp: # pragma no cover
|
|
3471
3401
|
self.components.create_port_on_component(
|
|
3472
|
-
cmp,
|
|
3402
|
+
cmp["refdes"],
|
|
3473
3403
|
net_list=simulation_setup.signal_nets,
|
|
3474
3404
|
do_pingroup=simulation_setup.do_pingroup,
|
|
3475
3405
|
reference_net=simulation_setup.power_nets,
|
|
3476
3406
|
port_type=SourceType.CircPort,
|
|
3477
3407
|
)
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
|
|
3494
|
-
self.logger.error("Failed to configure Siwave simulation setup.")
|
|
3495
|
-
self.padstacks.check_and_fix_via_plating()
|
|
3496
|
-
self.save_edb()
|
|
3497
|
-
if not simulation_setup.open_edb_after_build and simulation_setup.output_aedb:
|
|
3498
|
-
self.close_edb()
|
|
3499
|
-
self.edbpath = legacy_name
|
|
3500
|
-
self.open_edb()
|
|
3501
|
-
return True
|
|
3502
|
-
except:
|
|
3503
|
-
return False
|
|
3408
|
+
self.logger.info("Configuring analysis setup.")
|
|
3409
|
+
if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
|
|
3410
|
+
self.logger.error("Failed to configure Siwave simulation setup.")
|
|
3411
|
+
if simulation_setup.solver_type == SolverType.SiwaveDC:
|
|
3412
|
+
if simulation_setup.generate_excitations:
|
|
3413
|
+
self.components.create_source_on_component(simulation_setup.sources)
|
|
3414
|
+
if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
|
|
3415
|
+
self.logger.error("Failed to configure Siwave simulation setup.")
|
|
3416
|
+
self.padstacks.check_and_fix_via_plating()
|
|
3417
|
+
self.save_edb()
|
|
3418
|
+
if not simulation_setup.open_edb_after_build and simulation_setup.output_aedb:
|
|
3419
|
+
self.close_edb()
|
|
3420
|
+
self.edbpath = legacy_name
|
|
3421
|
+
self.open_edb()
|
|
3422
|
+
return True
|
|
3504
3423
|
|
|
3505
3424
|
def get_statistics(self, compute_area=False):
|
|
3506
3425
|
"""Get the EDBStatistics object.
|
|
@@ -4178,6 +4097,12 @@ class Edb(Database):
|
|
|
4178
4097
|
material_filter=None,
|
|
4179
4098
|
padstack_definition_filter=None,
|
|
4180
4099
|
trace_net_filter=None,
|
|
4100
|
+
use_single_variable_for_padstack_definitions=True,
|
|
4101
|
+
use_relative_variables=True,
|
|
4102
|
+
output_aedb_path=None,
|
|
4103
|
+
open_aedb_at_end=True,
|
|
4104
|
+
expand_polygons_size=0,
|
|
4105
|
+
expand_voids_size=0,
|
|
4181
4106
|
):
|
|
4182
4107
|
"""Assign automatically design and project variables with current values.
|
|
4183
4108
|
|
|
@@ -4203,27 +4128,56 @@ class Edb(Database):
|
|
|
4203
4128
|
Enable padstack definition filter. Default value is ``None``, all padsatcks are parametrized.
|
|
4204
4129
|
trace_net_filter : str, List(str), optional
|
|
4205
4130
|
Enable nets filter for trace width parametrization. Default value is ``None``, all layers are parametrized.
|
|
4131
|
+
use_single_variable_for_padstack_definitions : bool, optional
|
|
4132
|
+
Whether to use a single design variable for each padstack definition or a variable per pad layer.
|
|
4133
|
+
Default value is ``True``.
|
|
4134
|
+
use_relative_variables : bool, optional
|
|
4135
|
+
Whether if use an absolute variable for each trace, padstacks and layers or a delta variable instead.
|
|
4136
|
+
Default value is ``True``.
|
|
4137
|
+
output_aedb_path : str, optional
|
|
4138
|
+
Full path and name for the new AEDB file. If None, then current aedb will be cutout.
|
|
4139
|
+
open_aedb_at_end : bool, optional
|
|
4140
|
+
Whether to open the cutout at the end. The default is ``True``.
|
|
4206
4141
|
|
|
4207
4142
|
Returns
|
|
4208
4143
|
-------
|
|
4209
4144
|
List(str)
|
|
4210
4145
|
List of all parameters name created.
|
|
4211
4146
|
"""
|
|
4147
|
+
edb_original_path = self.edbpath
|
|
4148
|
+
if output_aedb_path:
|
|
4149
|
+
self.save_edb_as(output_aedb_path)
|
|
4150
|
+
if isinstance(trace_net_filter, str):
|
|
4151
|
+
trace_net_filter = [trace_net_filter]
|
|
4212
4152
|
parameters = []
|
|
4153
|
+
|
|
4154
|
+
def _apply_variable(orig_name, orig_value):
|
|
4155
|
+
if use_relative_variables:
|
|
4156
|
+
var = f"{orig_name}_delta"
|
|
4157
|
+
else:
|
|
4158
|
+
var = f"{orig_name}_value"
|
|
4159
|
+
var = self._clean_string_for_variable_name(var)
|
|
4160
|
+
if var not in self.variables:
|
|
4161
|
+
if use_relative_variables:
|
|
4162
|
+
self.add_design_variable(var, 0.0)
|
|
4163
|
+
else:
|
|
4164
|
+
self.add_design_variable(var, orig_value)
|
|
4165
|
+
if use_relative_variables:
|
|
4166
|
+
return f"{orig_value}+{var}", var
|
|
4167
|
+
else:
|
|
4168
|
+
return var, var
|
|
4169
|
+
|
|
4213
4170
|
if layers:
|
|
4214
4171
|
if not layer_filter:
|
|
4215
|
-
_layers = self.stackup.
|
|
4172
|
+
_layers = self.stackup.layers
|
|
4216
4173
|
else:
|
|
4217
4174
|
if isinstance(layer_filter, str):
|
|
4218
4175
|
layer_filter = [layer_filter]
|
|
4219
|
-
_layers = {k: v for k, v in self.stackup.
|
|
4176
|
+
_layers = {k: v for k, v in self.stackup.layers.items() if k in layer_filter}
|
|
4220
4177
|
for layer_name, layer in _layers.items():
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
self.add_design_variable(thickness_variable, layer.thickness)
|
|
4225
|
-
layer.thickness = thickness_variable
|
|
4226
|
-
parameters.append(thickness_variable)
|
|
4178
|
+
var, val = _apply_variable(f"${layer_name}", layer.thickness)
|
|
4179
|
+
layer.thickness = var
|
|
4180
|
+
parameters.append(val)
|
|
4227
4181
|
if materials:
|
|
4228
4182
|
if not material_filter:
|
|
4229
4183
|
_materials = self.materials.materials
|
|
@@ -4231,117 +4185,139 @@ class Edb(Database):
|
|
|
4231
4185
|
_materials = {k: v for k, v in self.materials.materials.items() if k in material_filter}
|
|
4232
4186
|
for mat_name, material in _materials.items():
|
|
4233
4187
|
if material.conductivity < 1e4:
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
material.
|
|
4239
|
-
parameters.append(
|
|
4240
|
-
loss_tg_variable = "$loss_tangent_{}".format(mat_name)
|
|
4241
|
-
loss_tg_variable = self._clean_string_for_variable_name(loss_tg_variable)
|
|
4242
|
-
if not loss_tg_variable in self.variables:
|
|
4243
|
-
self.add_design_variable(loss_tg_variable, material.dielectric_loss_tangent)
|
|
4244
|
-
material.dielectric_loss_tangent = loss_tg_variable
|
|
4245
|
-
parameters.append(loss_tg_variable)
|
|
4188
|
+
var, val = _apply_variable(f"$epsr_{mat_name}", material.permittivity)
|
|
4189
|
+
material.permittivity = var
|
|
4190
|
+
parameters.append(val)
|
|
4191
|
+
var, val = _apply_variable(f"$loss_tangent_{mat_name}", material.dielectric_loss_tangent)
|
|
4192
|
+
material.dielectric_loss_tangent = var
|
|
4193
|
+
parameters.append(val)
|
|
4246
4194
|
else:
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
self.add_design_variable(sigma_variable, material.conductivity)
|
|
4251
|
-
material.conductivity = sigma_variable
|
|
4252
|
-
parameters.append(sigma_variable)
|
|
4195
|
+
var, val = _apply_variable(f"$sigma_{mat_name}", material.conductivity)
|
|
4196
|
+
material.conductivity = var
|
|
4197
|
+
parameters.append(val)
|
|
4253
4198
|
if traces:
|
|
4254
4199
|
if not trace_net_filter:
|
|
4255
4200
|
paths = self.modeler.paths
|
|
4256
4201
|
else:
|
|
4257
4202
|
paths = [path for path in self.modeler.paths if path.net_name in trace_net_filter]
|
|
4258
4203
|
for path in paths:
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4204
|
+
net_name = path.net_name
|
|
4205
|
+
if use_relative_variables:
|
|
4206
|
+
trace_width_variable = "trace"
|
|
4207
|
+
elif net_name:
|
|
4208
|
+
trace_width_variable = f"{path.net_name}_{path.aedt_name}"
|
|
4209
|
+
else:
|
|
4210
|
+
trace_width_variable = f"{path.aedt_name}"
|
|
4211
|
+
var, val = _apply_variable(trace_width_variable, path.width)
|
|
4212
|
+
path.width = var
|
|
4213
|
+
parameters.append(val)
|
|
4265
4214
|
if not padstack_definition_filter:
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4215
|
+
if trace_net_filter:
|
|
4216
|
+
padstack_defs = {}
|
|
4217
|
+
for net in trace_net_filter:
|
|
4218
|
+
for via in self.nets[net].padstack_instances:
|
|
4219
|
+
padstack_defs[via.padstack_definition] = self.padstacks.definitions[via.padstack_definition]
|
|
4220
|
+
else:
|
|
4221
|
+
used_padsatck_defs = list(
|
|
4222
|
+
set(
|
|
4223
|
+
[padstack_inst.padstack_definition for padstack_inst in list(self.padstacks.instances.values())]
|
|
4224
|
+
)
|
|
4225
|
+
)
|
|
4226
|
+
padstack_defs = {k: v for k, v in self.padstacks.definitions.items() if k in used_padsatck_defs}
|
|
4270
4227
|
else:
|
|
4271
4228
|
padstack_defs = {k: v for k, v in self.padstacks.definitions.items() if k in padstack_definition_filter}
|
|
4229
|
+
|
|
4272
4230
|
for def_name, padstack_def in padstack_defs.items():
|
|
4273
4231
|
if not padstack_def.via_start_layer == padstack_def.via_stop_layer:
|
|
4274
4232
|
if via_holes: # pragma no cover
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4233
|
+
if use_relative_variables:
|
|
4234
|
+
hole_variable = "$hole_diameter"
|
|
4235
|
+
else:
|
|
4236
|
+
hole_variable = f"${def_name}_hole_diameter"
|
|
4237
|
+
var, val = _apply_variable(hole_variable, padstack_def.hole_diameter_string)
|
|
4238
|
+
padstack_def.hole_properties = var
|
|
4239
|
+
parameters.append(val)
|
|
4280
4240
|
if pads:
|
|
4281
4241
|
for layer, pad in padstack_def.pad_by_layer.items():
|
|
4282
|
-
if
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
pad.parameters = {"Size": pad_size_variable}
|
|
4297
|
-
parameters.append(pad_size_variable)
|
|
4242
|
+
if use_relative_variables:
|
|
4243
|
+
pad_name = "$pad"
|
|
4244
|
+
elif use_single_variable_for_padstack_definitions:
|
|
4245
|
+
pad_name = f"${def_name}_pad"
|
|
4246
|
+
else:
|
|
4247
|
+
pad_name = f"${def_name}_{layer}_pad"
|
|
4248
|
+
|
|
4249
|
+
if pad.geometry_type in [1, 2]:
|
|
4250
|
+
var, val = _apply_variable(pad_name, pad.parameters_values_string[0])
|
|
4251
|
+
if pad.geometry_type == 1:
|
|
4252
|
+
pad.parameters = {"Diameter": var}
|
|
4253
|
+
else:
|
|
4254
|
+
pad.parameters = {"Size": var}
|
|
4255
|
+
parameters.append(val)
|
|
4298
4256
|
elif pad.geometry_type == 3: # pragma no cover
|
|
4299
|
-
|
|
4300
|
-
"$
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
"$
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4257
|
+
if use_relative_variables:
|
|
4258
|
+
pad_name_x = "$pad_x"
|
|
4259
|
+
pad_name_y = "$pad_y"
|
|
4260
|
+
elif use_single_variable_for_padstack_definitions:
|
|
4261
|
+
pad_name_x = f"${def_name}_pad_x"
|
|
4262
|
+
pad_name_y = f"${def_name}_pad_y"
|
|
4263
|
+
else:
|
|
4264
|
+
pad_name_x = f"${def_name}_{layer}_pad_x"
|
|
4265
|
+
pad_name_y = f"${def_name}_pad_y"
|
|
4266
|
+
var, val = _apply_variable(pad_name_x, pad.parameters_values_string[0])
|
|
4267
|
+
var2, val2 = _apply_variable(pad_name_y, pad.parameters_values_string[1])
|
|
4268
|
+
|
|
4269
|
+
pad.parameters = {"XSize": var, "YSize": var2}
|
|
4270
|
+
parameters.append(val)
|
|
4271
|
+
parameters.append(val2)
|
|
4311
4272
|
if antipads:
|
|
4312
4273
|
for layer, antipad in padstack_def.antipad_by_layer.items():
|
|
4313
|
-
if
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
antipad.parameters = {"Size": antipad_size_variable}
|
|
4328
|
-
parameters.append(antipad_size_variable)
|
|
4274
|
+
if use_relative_variables:
|
|
4275
|
+
pad_name = "$antipad"
|
|
4276
|
+
elif use_single_variable_for_padstack_definitions:
|
|
4277
|
+
pad_name = f"${def_name}_antipad"
|
|
4278
|
+
else:
|
|
4279
|
+
pad_name = f"${def_name}_{layer}_antipad"
|
|
4280
|
+
|
|
4281
|
+
if antipad.geometry_type in [1, 2]:
|
|
4282
|
+
var, val = _apply_variable(pad_name, antipad.parameters_values_string[0])
|
|
4283
|
+
if antipad.geometry_type == 1: # pragma no cover
|
|
4284
|
+
antipad.parameters = {"Diameter": var}
|
|
4285
|
+
else:
|
|
4286
|
+
antipad.parameters = {"Size": var}
|
|
4287
|
+
parameters.append(val)
|
|
4329
4288
|
elif antipad.geometry_type == 3: # pragma no cover
|
|
4330
|
-
|
|
4331
|
-
"$
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
"$
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
antipad.parameters = {"XSize":
|
|
4343
|
-
parameters.append(
|
|
4344
|
-
parameters.append(
|
|
4289
|
+
if use_relative_variables:
|
|
4290
|
+
pad_name_x = "$antipad_x"
|
|
4291
|
+
pad_name_y = "$antipad_y"
|
|
4292
|
+
elif use_single_variable_for_padstack_definitions:
|
|
4293
|
+
pad_name_x = f"${def_name}_antipad_x"
|
|
4294
|
+
pad_name_y = f"${def_name}_antipad_y"
|
|
4295
|
+
else:
|
|
4296
|
+
pad_name_x = f"${def_name}_{layer}_antipad_x"
|
|
4297
|
+
pad_name_y = f"${def_name}_antipad_y"
|
|
4298
|
+
|
|
4299
|
+
var, val = _apply_variable(pad_name_x, antipad.parameters_values_string[0])
|
|
4300
|
+
var2, val2 = _apply_variable(pad_name_y, antipad.parameters_values_string[1])
|
|
4301
|
+
antipad.parameters = {"XSize": var, "YSize": var2}
|
|
4302
|
+
parameters.append(val)
|
|
4303
|
+
parameters.append(val2)
|
|
4304
|
+
if expand_polygons_size:
|
|
4305
|
+
for poly in self.modeler.polygons:
|
|
4306
|
+
if not poly.is_void:
|
|
4307
|
+
poly.expand(expand_polygons_size)
|
|
4308
|
+
if expand_voids_size:
|
|
4309
|
+
for poly in self.modeler.polygons:
|
|
4310
|
+
if poly.is_void:
|
|
4311
|
+
poly.expand(expand_voids_size, round_corners=False)
|
|
4312
|
+
elif poly.has_voids:
|
|
4313
|
+
for void in poly.voids:
|
|
4314
|
+
void.expand(expand_voids_size, round_corners=False)
|
|
4315
|
+
|
|
4316
|
+
if not open_aedb_at_end and self.edbpath != edb_original_path:
|
|
4317
|
+
self.save_edb()
|
|
4318
|
+
self.close_edb()
|
|
4319
|
+
self.edbpath = edb_original_path
|
|
4320
|
+
self.open_edb()
|
|
4345
4321
|
return parameters
|
|
4346
4322
|
|
|
4347
4323
|
def _clean_string_for_variable_name(self, variable_name):
|