pyedb 0.2.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 +17 -0
- pyedb/dotnet/__init__.py +0 -0
- pyedb/dotnet/application/Variables.py +2261 -0
- pyedb/dotnet/application/__init__.py +0 -0
- pyedb/dotnet/clr_module.py +103 -0
- pyedb/dotnet/edb.py +4237 -0
- pyedb/dotnet/edb_core/__init__.py +1 -0
- pyedb/dotnet/edb_core/cell/__init__.py +0 -0
- pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
- pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
- pyedb/dotnet/edb_core/components.py +2669 -0
- pyedb/dotnet/edb_core/configuration.py +423 -0
- pyedb/dotnet/edb_core/definition/__init__.py +0 -0
- pyedb/dotnet/edb_core/definition/component_def.py +166 -0
- pyedb/dotnet/edb_core/definition/component_model.py +30 -0
- pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
- pyedb/dotnet/edb_core/definition/definitions.py +12 -0
- pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
- pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
- pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
- pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
- pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
- pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
- pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
- pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
- pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
- pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
- pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
- pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
- pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
- pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
- pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
- pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
- pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
- pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
- pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
- pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
- pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
- pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
- pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
- pyedb/dotnet/edb_core/general.py +181 -0
- pyedb/dotnet/edb_core/hfss.py +1646 -0
- pyedb/dotnet/edb_core/layout.py +1244 -0
- pyedb/dotnet/edb_core/layout_validation.py +272 -0
- pyedb/dotnet/edb_core/materials.py +939 -0
- pyedb/dotnet/edb_core/net_class.py +335 -0
- pyedb/dotnet/edb_core/nets.py +1215 -0
- pyedb/dotnet/edb_core/padstack.py +1389 -0
- pyedb/dotnet/edb_core/siwave.py +1427 -0
- pyedb/dotnet/edb_core/stackup.py +2703 -0
- pyedb/edb_logger.py +396 -0
- pyedb/generic/__init__.py +0 -0
- pyedb/generic/constants.py +1063 -0
- pyedb/generic/data_handlers.py +320 -0
- pyedb/generic/design_types.py +104 -0
- pyedb/generic/filesystem.py +150 -0
- pyedb/generic/general_methods.py +1535 -0
- pyedb/generic/plot.py +1840 -0
- pyedb/generic/process.py +285 -0
- pyedb/generic/settings.py +224 -0
- pyedb/ipc2581/__init__.py +0 -0
- pyedb/ipc2581/bom/__init__.py +0 -0
- pyedb/ipc2581/bom/bom.py +21 -0
- pyedb/ipc2581/bom/bom_item.py +32 -0
- pyedb/ipc2581/bom/characteristics.py +37 -0
- pyedb/ipc2581/bom/refdes.py +16 -0
- pyedb/ipc2581/content/__init__.py +0 -0
- pyedb/ipc2581/content/color.py +38 -0
- pyedb/ipc2581/content/content.py +55 -0
- pyedb/ipc2581/content/dictionary_color.py +29 -0
- pyedb/ipc2581/content/dictionary_fill.py +28 -0
- pyedb/ipc2581/content/dictionary_line.py +30 -0
- pyedb/ipc2581/content/entry_color.py +13 -0
- pyedb/ipc2581/content/entry_line.py +14 -0
- pyedb/ipc2581/content/fill.py +15 -0
- pyedb/ipc2581/content/layer_ref.py +10 -0
- pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
- pyedb/ipc2581/ecad/__init__.py +0 -0
- pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
- pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
- pyedb/ipc2581/ecad/cad_data/component.py +41 -0
- pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
- pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
- pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
- pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
- pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
- pyedb/ipc2581/ecad/cad_data/package.py +104 -0
- pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
- pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
- pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
- pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
- pyedb/ipc2581/ecad/cad_data/path.py +89 -0
- pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
- pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
- pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
- pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
- pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
- pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
- pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
- pyedb/ipc2581/ecad/cad_data/step.py +275 -0
- pyedb/ipc2581/ecad/cad_header.py +33 -0
- pyedb/ipc2581/ecad/ecad.py +19 -0
- pyedb/ipc2581/ecad/spec.py +46 -0
- pyedb/ipc2581/history_record.py +37 -0
- pyedb/ipc2581/ipc2581.py +387 -0
- pyedb/ipc2581/logistic_header.py +25 -0
- pyedb/misc/__init__.py +0 -0
- pyedb/misc/aedtlib_personalib_install.py +14 -0
- pyedb/misc/downloads.py +322 -0
- pyedb/misc/misc.py +67 -0
- pyedb/misc/pyedb.runtimeconfig.json +13 -0
- pyedb/misc/siw_feature_config/__init__.py +0 -0
- pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
- pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
- pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
- pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
- pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
- pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
- pyedb/misc/utilities.py +27 -0
- pyedb/modeler/geometry_operators.py +2082 -0
- pyedb-0.2.0.dist-info/LICENSE +21 -0
- pyedb-0.2.0.dist-info/METADATA +208 -0
- pyedb-0.2.0.dist-info/RECORD +128 -0
- pyedb-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,2669 @@
|
|
|
1
|
+
"""This module contains the `Components` class.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
import codecs
|
|
5
|
+
import json
|
|
6
|
+
import math
|
|
7
|
+
import re
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
from pyedb.dotnet.clr_module import String
|
|
11
|
+
from pyedb.dotnet.edb_core.edb_data.components_data import EDBComponent
|
|
12
|
+
from pyedb.dotnet.edb_core.edb_data.padstacks_data import EDBPadstackInstance
|
|
13
|
+
from pyedb.dotnet.edb_core.edb_data.sources import Source, SourceType
|
|
14
|
+
from pyedb.dotnet.edb_core.definition.component_def import EDBComponentDef
|
|
15
|
+
from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
|
|
16
|
+
from pyedb.dotnet.edb_core.padstack import EdbPadstacks
|
|
17
|
+
from pyedb.generic.general_methods import (
|
|
18
|
+
_retry_ntimes,
|
|
19
|
+
get_filename_without_extension,
|
|
20
|
+
pyedb_function_handler,
|
|
21
|
+
)
|
|
22
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def resistor_value_parser(RValue):
|
|
26
|
+
"""Convert a resistor value.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
RValue : float
|
|
31
|
+
Resistor value.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
float
|
|
36
|
+
Resistor value.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
if isinstance(RValue, str):
|
|
40
|
+
RValue = RValue.replace(" ", "")
|
|
41
|
+
RValue = RValue.replace("meg", "m")
|
|
42
|
+
RValue = RValue.replace("Ohm", "")
|
|
43
|
+
RValue = RValue.replace("ohm", "")
|
|
44
|
+
RValue = RValue.replace("k", "e3")
|
|
45
|
+
RValue = RValue.replace("m", "e-3")
|
|
46
|
+
RValue = RValue.replace("M", "e6")
|
|
47
|
+
RValue = float(RValue)
|
|
48
|
+
return RValue
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Components(object):
|
|
52
|
+
"""Manages EDB components and related method accessible from `Edb.components` property.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
edb_class : :class:`pyedb.dotnet.edb.Edb`
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
>>> from pyedb import Edb
|
|
61
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
62
|
+
>>> edbapp.components
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
@pyedb_function_handler()
|
|
66
|
+
def __getitem__(self, name):
|
|
67
|
+
"""Get a component or component definition from the Edb project.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
name : str
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
:class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
if name in self.instances:
|
|
79
|
+
return self.instances[name]
|
|
80
|
+
elif name in self.definitions:
|
|
81
|
+
return self.definitions[name]
|
|
82
|
+
self._pedb.logger.error("Component or definition not found.")
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
def __init__(self, p_edb):
|
|
86
|
+
self._pedb = p_edb
|
|
87
|
+
self._cmp = {}
|
|
88
|
+
self._res = {}
|
|
89
|
+
self._cap = {}
|
|
90
|
+
self._ind = {}
|
|
91
|
+
self._ios = {}
|
|
92
|
+
self._ics = {}
|
|
93
|
+
self._others = {}
|
|
94
|
+
self._pins = {}
|
|
95
|
+
self._comps_by_part = {}
|
|
96
|
+
self._init_parts()
|
|
97
|
+
self._padstack = EdbPadstacks(self._pedb)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def _logger(self):
|
|
101
|
+
"""Logger."""
|
|
102
|
+
return self._pedb.logger
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def _edb(self):
|
|
106
|
+
return self._pedb.edb_api
|
|
107
|
+
|
|
108
|
+
@pyedb_function_handler()
|
|
109
|
+
def _init_parts(self):
|
|
110
|
+
a = self.components
|
|
111
|
+
a = self.resistors
|
|
112
|
+
a = self.ICs
|
|
113
|
+
a = self.Others
|
|
114
|
+
a = self.inductors
|
|
115
|
+
a = self.IOs
|
|
116
|
+
a = self.components_by_partname
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
def _get_edb_value(self, value):
|
|
120
|
+
return self._pedb.edb_value(value)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def _edbutils(self):
|
|
124
|
+
return self._pedb.edbutils
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def _active_layout(self):
|
|
128
|
+
return self._pedb.active_layout
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def _layout(self):
|
|
132
|
+
return self._pedb.layout
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def _cell(self):
|
|
136
|
+
return self._pedb.cell
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def _db(self):
|
|
140
|
+
return self._pedb.active_db
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def components(self):
|
|
144
|
+
"""Component setup information.
|
|
145
|
+
|
|
146
|
+
.. deprecated:: 0.6.62
|
|
147
|
+
Use new property :func:`instances` instead.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
152
|
+
Default dictionary for the EDB component.
|
|
153
|
+
|
|
154
|
+
Examples
|
|
155
|
+
--------
|
|
156
|
+
|
|
157
|
+
>>> from pyedb.dotnet.edb import Edb
|
|
158
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
159
|
+
>>> edbapp.components.components
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
warnings.warn("Use new property :func:`instances` instead.", DeprecationWarning)
|
|
163
|
+
return self.instances
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def instances(self):
|
|
167
|
+
"""All Cell components objects.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
Dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
172
|
+
Default dictionary for the EDB component.
|
|
173
|
+
|
|
174
|
+
Examples
|
|
175
|
+
--------
|
|
176
|
+
|
|
177
|
+
>>> from pyedb.dotnet.edb import Edb
|
|
178
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
179
|
+
>>> edbapp.components.components
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
if not self._cmp:
|
|
183
|
+
self.refresh_components()
|
|
184
|
+
return self._cmp
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def definitions(self):
|
|
188
|
+
"""Retrieve component definition list.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
dict of :class:`EDBComponentDef`"""
|
|
193
|
+
return {l.GetName(): EDBComponentDef(self._pedb, l) for l in list(self._pedb.component_defs)}
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def nport_comp_definition(self):
|
|
197
|
+
"""Retrieve Nport component definition list."""
|
|
198
|
+
m = "Ansys.Ansoft.Edb.Definition.NPortComponentModel"
|
|
199
|
+
return {name: l for name, l in self.definitions.items() if m in [i.ToString() for i in l._comp_model]}
|
|
200
|
+
|
|
201
|
+
@pyedb_function_handler()
|
|
202
|
+
def import_definition(self, file_path):
|
|
203
|
+
"""Import component definition from json file.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
file_path : str
|
|
208
|
+
File path of json file.
|
|
209
|
+
"""
|
|
210
|
+
with codecs.open(file_path, "r", encoding="utf-8") as f:
|
|
211
|
+
data = json.load(f)
|
|
212
|
+
for part_name, p in data["Definitions"].items():
|
|
213
|
+
model_type = p["Model_type"]
|
|
214
|
+
if part_name not in self.definitions:
|
|
215
|
+
continue
|
|
216
|
+
comp_definition = self.definitions[part_name]
|
|
217
|
+
comp_definition.type = p["Component_type"]
|
|
218
|
+
|
|
219
|
+
if model_type == "RLC":
|
|
220
|
+
comp_definition.assign_rlc_model(p["Res"], p["Ind"], p["Cap"], p["Is_parallel"])
|
|
221
|
+
else:
|
|
222
|
+
model_name = p["Model_name"]
|
|
223
|
+
file_path = data[model_type][model_name]
|
|
224
|
+
if model_type == "SParameterModel":
|
|
225
|
+
if "Reference_net" in p:
|
|
226
|
+
reference_net = p["Reference_net"]
|
|
227
|
+
else:
|
|
228
|
+
reference_net = None
|
|
229
|
+
comp_definition.assign_s_param_model(file_path, model_name, reference_net)
|
|
230
|
+
elif model_type == "SPICEModel":
|
|
231
|
+
comp_definition.assign_spice_model(file_path, model_name)
|
|
232
|
+
else:
|
|
233
|
+
pass
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
@pyedb_function_handler()
|
|
237
|
+
def export_definition(self, file_path):
|
|
238
|
+
"""Export component definitions to json file.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
file_path : str
|
|
243
|
+
File path of json file.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
|
|
248
|
+
"""
|
|
249
|
+
data = {
|
|
250
|
+
"SParameterModel": {},
|
|
251
|
+
"SPICEModel": {},
|
|
252
|
+
"Definitions": {},
|
|
253
|
+
}
|
|
254
|
+
for part_name, props in self.definitions.items():
|
|
255
|
+
comp_list = list(props.components.values())
|
|
256
|
+
if comp_list:
|
|
257
|
+
data["Definitions"][part_name] = {}
|
|
258
|
+
data["Definitions"][part_name]["Component_type"] = props.type
|
|
259
|
+
comp = comp_list[0]
|
|
260
|
+
data["Definitions"][part_name]["Model_type"] = comp.model_type
|
|
261
|
+
if comp.model_type == "RLC":
|
|
262
|
+
rlc_values = [i if i else 0 for i in comp.rlc_values]
|
|
263
|
+
data["Definitions"][part_name]["Res"] = rlc_values[0]
|
|
264
|
+
data["Definitions"][part_name]["Ind"] = rlc_values[1]
|
|
265
|
+
data["Definitions"][part_name]["Cap"] = rlc_values[2]
|
|
266
|
+
data["Definitions"][part_name]["Is_parallel"] = True if comp.is_parallel_rlc else False
|
|
267
|
+
else:
|
|
268
|
+
if comp.model_type == "SParameterModel":
|
|
269
|
+
model = comp.s_param_model
|
|
270
|
+
data["Definitions"][part_name]["Model_name"] = model.name
|
|
271
|
+
data["Definitions"][part_name]["Reference_net"] = model.reference_net
|
|
272
|
+
if not model.name in data["SParameterModel"]:
|
|
273
|
+
data["SParameterModel"][model.name] = model.file_path
|
|
274
|
+
elif comp.model_type == "SPICEModel":
|
|
275
|
+
model = comp.spice_model
|
|
276
|
+
data["Definitions"][part_name]["Model_name"] = model.name
|
|
277
|
+
if not model.name in data["SPICEModel"]:
|
|
278
|
+
data["SPICEModel"][model.name] = model.file_path
|
|
279
|
+
else:
|
|
280
|
+
model = comp.netlist_model
|
|
281
|
+
data["Definitions"][part_name]["Model_name"] = model.netlist
|
|
282
|
+
|
|
283
|
+
with codecs.open(file_path, "w", encoding="utf-8") as f:
|
|
284
|
+
json.dump(data, f, ensure_ascii=False, indent=4)
|
|
285
|
+
return file_path
|
|
286
|
+
|
|
287
|
+
@pyedb_function_handler()
|
|
288
|
+
def refresh_components(self):
|
|
289
|
+
"""Refresh the component dictionary."""
|
|
290
|
+
# self._logger.info("Refreshing the Components dictionary.")
|
|
291
|
+
self._cmp = {
|
|
292
|
+
l.GetName(): EDBComponent(self._pedb, l)
|
|
293
|
+
for l in self._layout.groups
|
|
294
|
+
if l.ToString() == "Ansys.Ansoft.Edb.Cell.Hierarchy.Component"
|
|
295
|
+
}
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def resistors(self):
|
|
300
|
+
"""Resistors.
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
305
|
+
Dictionary of resistors.
|
|
306
|
+
|
|
307
|
+
Examples
|
|
308
|
+
--------
|
|
309
|
+
|
|
310
|
+
>>> from pyedb import Edb
|
|
311
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
312
|
+
>>> edbapp.components.resistors
|
|
313
|
+
"""
|
|
314
|
+
self._res = {}
|
|
315
|
+
for el, val in self.instances.items():
|
|
316
|
+
if val.type == "Resistor":
|
|
317
|
+
self._res[el] = val
|
|
318
|
+
return self._res
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def capacitors(self):
|
|
322
|
+
"""Capacitors.
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
327
|
+
Dictionary of capacitors.
|
|
328
|
+
|
|
329
|
+
Examples
|
|
330
|
+
--------
|
|
331
|
+
|
|
332
|
+
>>> from pyedb import Edb
|
|
333
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
334
|
+
>>> edbapp.components.capacitors
|
|
335
|
+
"""
|
|
336
|
+
self._cap = {}
|
|
337
|
+
for el, val in self.instances.items():
|
|
338
|
+
if val.type == "Capacitor":
|
|
339
|
+
self._cap[el] = val
|
|
340
|
+
return self._cap
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def inductors(self):
|
|
344
|
+
"""Inductors.
|
|
345
|
+
|
|
346
|
+
Returns
|
|
347
|
+
-------
|
|
348
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
349
|
+
Dictionary of inductors.
|
|
350
|
+
|
|
351
|
+
Examples
|
|
352
|
+
--------
|
|
353
|
+
|
|
354
|
+
>>> from pyedb import Edb
|
|
355
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
356
|
+
>>> edbapp.components.inductors
|
|
357
|
+
|
|
358
|
+
"""
|
|
359
|
+
self._ind = {}
|
|
360
|
+
for el, val in self.instances.items():
|
|
361
|
+
if val.type == "Inductor":
|
|
362
|
+
self._ind[el] = val
|
|
363
|
+
return self._ind
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def ICs(self):
|
|
367
|
+
"""Integrated circuits.
|
|
368
|
+
|
|
369
|
+
Returns
|
|
370
|
+
-------
|
|
371
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
372
|
+
Dictionary of integrated circuits.
|
|
373
|
+
|
|
374
|
+
Examples
|
|
375
|
+
--------
|
|
376
|
+
|
|
377
|
+
>>> from pyedb import Edb
|
|
378
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
379
|
+
>>> edbapp.components.ICs
|
|
380
|
+
|
|
381
|
+
"""
|
|
382
|
+
self._ics = {}
|
|
383
|
+
for el, val in self.instances.items():
|
|
384
|
+
if val.type == "IC":
|
|
385
|
+
self._ics[el] = val
|
|
386
|
+
return self._ics
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def IOs(self):
|
|
390
|
+
"""Circuit inupts and outputs.
|
|
391
|
+
|
|
392
|
+
Returns
|
|
393
|
+
-------
|
|
394
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
395
|
+
Dictionary of circuit inputs and outputs.
|
|
396
|
+
|
|
397
|
+
Examples
|
|
398
|
+
--------
|
|
399
|
+
|
|
400
|
+
>>> from pyedb import Edb
|
|
401
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
402
|
+
>>> edbapp.components.IOs
|
|
403
|
+
|
|
404
|
+
"""
|
|
405
|
+
self._ios = {}
|
|
406
|
+
for el, val in self.instances.items():
|
|
407
|
+
if val.type == "IO":
|
|
408
|
+
self._ios[el] = val
|
|
409
|
+
return self._ios
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def Others(self):
|
|
413
|
+
"""Other core components.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
|
|
418
|
+
Dictionary of other core components.
|
|
419
|
+
|
|
420
|
+
Examples
|
|
421
|
+
--------
|
|
422
|
+
|
|
423
|
+
>>> from pyedb import Edb
|
|
424
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
425
|
+
>>> edbapp.components.others
|
|
426
|
+
|
|
427
|
+
"""
|
|
428
|
+
self._others = {}
|
|
429
|
+
for el, val in self.instances.items():
|
|
430
|
+
if val.type == "Other":
|
|
431
|
+
self._others[el] = val
|
|
432
|
+
return self._others
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def components_by_partname(self):
|
|
436
|
+
"""Components by part name.
|
|
437
|
+
|
|
438
|
+
Returns
|
|
439
|
+
-------
|
|
440
|
+
dict
|
|
441
|
+
Dictionary of components by part name.
|
|
442
|
+
|
|
443
|
+
Examples
|
|
444
|
+
--------
|
|
445
|
+
|
|
446
|
+
>>> from pyedb import Edb
|
|
447
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
448
|
+
>>> edbapp.components.components_by_partname
|
|
449
|
+
|
|
450
|
+
"""
|
|
451
|
+
self._comps_by_part = {}
|
|
452
|
+
for el, val in self.instances.items():
|
|
453
|
+
if val.partname in self._comps_by_part.keys():
|
|
454
|
+
self._comps_by_part[val.partname].append(val)
|
|
455
|
+
else:
|
|
456
|
+
self._comps_by_part[val.partname] = [val]
|
|
457
|
+
return self._comps_by_part
|
|
458
|
+
|
|
459
|
+
@pyedb_function_handler()
|
|
460
|
+
def get_component_by_name(self, name):
|
|
461
|
+
"""Retrieve a component by name.
|
|
462
|
+
|
|
463
|
+
Parameters
|
|
464
|
+
----------
|
|
465
|
+
name : str
|
|
466
|
+
Name of the component.
|
|
467
|
+
|
|
468
|
+
Returns
|
|
469
|
+
-------
|
|
470
|
+
bool
|
|
471
|
+
``True`` when successful, ``False`` when failed.
|
|
472
|
+
|
|
473
|
+
"""
|
|
474
|
+
edbcmp = self._pedb.edb_api.cell.hierarchy.component.FindByName(self._active_layout, name)
|
|
475
|
+
if edbcmp is not None:
|
|
476
|
+
return edbcmp
|
|
477
|
+
else:
|
|
478
|
+
pass
|
|
479
|
+
|
|
480
|
+
@pyedb_function_handler()
|
|
481
|
+
def get_components_from_nets(self, netlist=None):
|
|
482
|
+
"""Retrieve components from a net list.
|
|
483
|
+
|
|
484
|
+
Parameters
|
|
485
|
+
----------
|
|
486
|
+
netlist : str, optional
|
|
487
|
+
Name of the net list. The default is ``None``.
|
|
488
|
+
|
|
489
|
+
Returns
|
|
490
|
+
-------
|
|
491
|
+
list
|
|
492
|
+
List of components that belong to the signal nets.
|
|
493
|
+
|
|
494
|
+
"""
|
|
495
|
+
cmp_list = []
|
|
496
|
+
if isinstance(netlist, str):
|
|
497
|
+
netlist = [netlist]
|
|
498
|
+
components = list(self.instances.keys())
|
|
499
|
+
for refdes in components:
|
|
500
|
+
cmpnets = self._cmp[refdes].nets
|
|
501
|
+
if set(cmpnets).intersection(set(netlist)):
|
|
502
|
+
cmp_list.append(refdes)
|
|
503
|
+
return cmp_list
|
|
504
|
+
|
|
505
|
+
@pyedb_function_handler()
|
|
506
|
+
def _get_edb_pin_from_pin_name(self, cmp, pin):
|
|
507
|
+
if not isinstance(cmp, self._pedb.edb_api.cell.hierarchy.component):
|
|
508
|
+
return False
|
|
509
|
+
if not isinstance(pin, str):
|
|
510
|
+
pin = pin.GetName()
|
|
511
|
+
pins = self.get_pin_from_component(component=cmp, pinName=pin)
|
|
512
|
+
if pins:
|
|
513
|
+
return pins[0]
|
|
514
|
+
return False
|
|
515
|
+
|
|
516
|
+
@pyedb_function_handler()
|
|
517
|
+
def get_component_placement_vector(
|
|
518
|
+
self,
|
|
519
|
+
mounted_component,
|
|
520
|
+
hosting_component,
|
|
521
|
+
mounted_component_pin1,
|
|
522
|
+
mounted_component_pin2,
|
|
523
|
+
hosting_component_pin1,
|
|
524
|
+
hosting_component_pin2,
|
|
525
|
+
flipped=False,
|
|
526
|
+
):
|
|
527
|
+
"""Get the placement vector between 2 components.
|
|
528
|
+
|
|
529
|
+
Parameters
|
|
530
|
+
----------
|
|
531
|
+
mounted_component : `edb.cell.hierarchy._hierarchy.Component`
|
|
532
|
+
Mounted component name.
|
|
533
|
+
hosting_component : `edb.cell.hierarchy._hierarchy.Component`
|
|
534
|
+
Hosting component name.
|
|
535
|
+
mounted_component_pin1 : str
|
|
536
|
+
Mounted component Pin 1 name.
|
|
537
|
+
mounted_component_pin2 : str
|
|
538
|
+
Mounted component Pin 2 name.
|
|
539
|
+
hosting_component_pin1 : str
|
|
540
|
+
Hosted component Pin 1 name.
|
|
541
|
+
hosting_component_pin2 : str
|
|
542
|
+
Hosted component Pin 2 name.
|
|
543
|
+
flipped : bool, optional
|
|
544
|
+
Either if the mounted component will be flipped or not.
|
|
545
|
+
|
|
546
|
+
Returns
|
|
547
|
+
-------
|
|
548
|
+
tuple
|
|
549
|
+
Tuple of Vector offset, rotation and solder height.
|
|
550
|
+
|
|
551
|
+
Examples
|
|
552
|
+
--------
|
|
553
|
+
>>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
|
|
554
|
+
>>> hosting_cmp = edb1.components.get_component_by_name("U100")
|
|
555
|
+
>>> mounted_cmp = edb2.components.get_component_by_name("BGA")
|
|
556
|
+
>>> vector, rotation, solder_ball_height = edb1.components.get_component_placement_vector(
|
|
557
|
+
... mounted_component=mounted_cmp,
|
|
558
|
+
... hosting_component=hosting_cmp,
|
|
559
|
+
... mounted_component_pin1="A12",
|
|
560
|
+
... mounted_component_pin2="A14",
|
|
561
|
+
... hosting_component_pin1="A12",
|
|
562
|
+
... hosting_component_pin2="A14")
|
|
563
|
+
"""
|
|
564
|
+
m_pin1_pos = [0.0, 0.0]
|
|
565
|
+
m_pin2_pos = [0.0, 0.0]
|
|
566
|
+
h_pin1_pos = [0.0, 0.0]
|
|
567
|
+
h_pin2_pos = [0.0, 0.0]
|
|
568
|
+
if not isinstance(mounted_component, self._pedb.edb_api.cell.hierarchy.component):
|
|
569
|
+
return False
|
|
570
|
+
if not isinstance(hosting_component, self._pedb.edb_api.cell.hierarchy.component):
|
|
571
|
+
return False
|
|
572
|
+
|
|
573
|
+
if mounted_component_pin1:
|
|
574
|
+
m_pin1 = self._get_edb_pin_from_pin_name(mounted_component, mounted_component_pin1)
|
|
575
|
+
m_pin1_pos = self.get_pin_position(m_pin1)
|
|
576
|
+
if mounted_component_pin2:
|
|
577
|
+
m_pin2 = self._get_edb_pin_from_pin_name(mounted_component, mounted_component_pin2)
|
|
578
|
+
m_pin2_pos = self.get_pin_position(m_pin2)
|
|
579
|
+
|
|
580
|
+
if hosting_component_pin1:
|
|
581
|
+
h_pin1 = self._get_edb_pin_from_pin_name(hosting_component, hosting_component_pin1)
|
|
582
|
+
h_pin1_pos = self.get_pin_position(h_pin1)
|
|
583
|
+
|
|
584
|
+
if hosting_component_pin2:
|
|
585
|
+
h_pin2 = self._get_edb_pin_from_pin_name(hosting_component, hosting_component_pin2)
|
|
586
|
+
h_pin2_pos = self.get_pin_position(h_pin2)
|
|
587
|
+
#
|
|
588
|
+
vector = [h_pin1_pos[0] - m_pin1_pos[0], h_pin1_pos[1] - m_pin1_pos[1]]
|
|
589
|
+
vector1 = GeometryOperators.v_points(m_pin1_pos, m_pin2_pos)
|
|
590
|
+
vector2 = GeometryOperators.v_points(h_pin1_pos, h_pin2_pos)
|
|
591
|
+
multiplier = 1
|
|
592
|
+
if flipped:
|
|
593
|
+
multiplier = -1
|
|
594
|
+
vector1[1] = multiplier * vector1[1]
|
|
595
|
+
|
|
596
|
+
rotation = GeometryOperators.v_angle_sign_2D(vector1, vector2, False)
|
|
597
|
+
if rotation != 0.0:
|
|
598
|
+
layinst = mounted_component.GetLayout().GetLayoutInstance()
|
|
599
|
+
cmpinst = layinst.GetLayoutObjInstance(mounted_component, None)
|
|
600
|
+
center = cmpinst.GetCenter()
|
|
601
|
+
center_double = [center.X.ToDouble(), center.Y.ToDouble()]
|
|
602
|
+
vector_center = GeometryOperators.v_points(center_double, m_pin1_pos)
|
|
603
|
+
x_v2 = vector_center[0] * math.cos(rotation) + multiplier * vector_center[1] * math.sin(rotation)
|
|
604
|
+
y_v2 = -1 * vector_center[0] * math.sin(rotation) + multiplier * vector_center[1] * math.cos(rotation)
|
|
605
|
+
new_vector = [x_v2 + center_double[0], y_v2 + center_double[1]]
|
|
606
|
+
vector = [h_pin1_pos[0] - new_vector[0], h_pin1_pos[1] - new_vector[1]]
|
|
607
|
+
|
|
608
|
+
if vector:
|
|
609
|
+
solder_ball_height = self.get_solder_ball_height(mounted_component)
|
|
610
|
+
return True, vector, rotation, solder_ball_height
|
|
611
|
+
self._logger.warning("Failed to compute vector.")
|
|
612
|
+
return False, [0, 0], 0, 0
|
|
613
|
+
|
|
614
|
+
@pyedb_function_handler()
|
|
615
|
+
def get_solder_ball_height(self, cmp):
|
|
616
|
+
"""Get component solder ball height.
|
|
617
|
+
|
|
618
|
+
Parameters
|
|
619
|
+
----------
|
|
620
|
+
cmp : str or self._pedb.component
|
|
621
|
+
EDB component or str component name.
|
|
622
|
+
|
|
623
|
+
Returns
|
|
624
|
+
-------
|
|
625
|
+
double, bool
|
|
626
|
+
Salder ball height vale, ``False`` when failed.
|
|
627
|
+
|
|
628
|
+
"""
|
|
629
|
+
if cmp is not None:
|
|
630
|
+
if not (isinstance(cmp, self._pedb.edb_api.cell.hierarchy.component)):
|
|
631
|
+
cmp = self.get_component_by_name(cmp)
|
|
632
|
+
cmp_prop = cmp.GetComponentProperty().Clone()
|
|
633
|
+
return cmp_prop.GetSolderBallProperty().GetHeight()
|
|
634
|
+
return False
|
|
635
|
+
|
|
636
|
+
@pyedb_function_handler()
|
|
637
|
+
def create_source_on_component(self, sources=None):
|
|
638
|
+
"""Create voltage, current source, or resistor on component.
|
|
639
|
+
|
|
640
|
+
Parameters
|
|
641
|
+
----------
|
|
642
|
+
sources : list[Source]
|
|
643
|
+
List of ``edb_data.sources.Source`` objects.
|
|
644
|
+
|
|
645
|
+
Returns
|
|
646
|
+
-------
|
|
647
|
+
bool
|
|
648
|
+
``True`` when successful, ``False`` when failed.
|
|
649
|
+
|
|
650
|
+
"""
|
|
651
|
+
|
|
652
|
+
if not sources: # pragma: no cover
|
|
653
|
+
return False
|
|
654
|
+
if isinstance(sources, Source): # pragma: no cover
|
|
655
|
+
sources = [sources]
|
|
656
|
+
if isinstance(sources, list): # pragma: no cover
|
|
657
|
+
for src in sources:
|
|
658
|
+
if not isinstance(src, Source): # pragma: no cover
|
|
659
|
+
self._logger.error("List of source objects must be passed as an argument.")
|
|
660
|
+
return False
|
|
661
|
+
for source in sources:
|
|
662
|
+
positive_pins = self.get_pin_from_component(source.positive_node.component, source.positive_node.net)
|
|
663
|
+
negative_pins = self.get_pin_from_component(source.negative_node.component, source.negative_node.net)
|
|
664
|
+
positive_pin_group = self.create_pingroup_from_pins(positive_pins)
|
|
665
|
+
if not positive_pin_group: # pragma: no cover
|
|
666
|
+
return False
|
|
667
|
+
negative_pin_group = self.create_pingroup_from_pins(negative_pins)
|
|
668
|
+
if not negative_pin_group: # pragma: no cover
|
|
669
|
+
return False
|
|
670
|
+
if source.source_type == SourceType.Vsource: # pragma: no cover
|
|
671
|
+
positive_pin_group_term = self._create_pin_group_terminal(
|
|
672
|
+
positive_pin_group,
|
|
673
|
+
)
|
|
674
|
+
negative_pin_group_term = self._create_pin_group_terminal(negative_pin_group, isref=True)
|
|
675
|
+
positive_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kVoltageSource)
|
|
676
|
+
negative_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kVoltageSource)
|
|
677
|
+
term_name = source.name
|
|
678
|
+
positive_pin_group_term.SetName(term_name)
|
|
679
|
+
negative_pin_group_term.SetName("{}_ref".format(term_name))
|
|
680
|
+
positive_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
|
|
681
|
+
negative_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
|
|
682
|
+
positive_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
|
|
683
|
+
negative_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
|
|
684
|
+
positive_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
|
|
685
|
+
negative_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
|
|
686
|
+
positive_pin_group_term.SetReferenceTerminal(negative_pin_group_term)
|
|
687
|
+
elif source.source_type == SourceType.Isource: # pragma: no cover
|
|
688
|
+
positive_pin_group_term = self._create_pin_group_terminal(
|
|
689
|
+
positive_pin_group,
|
|
690
|
+
)
|
|
691
|
+
negative_pin_group_term = self._create_pin_group_terminal(negative_pin_group, isref=True)
|
|
692
|
+
positive_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kCurrentSource)
|
|
693
|
+
negative_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kCurrentSource)
|
|
694
|
+
term_name = source.name
|
|
695
|
+
positive_pin_group_term.SetName(term_name)
|
|
696
|
+
negative_pin_group_term.SetName("{}_ref".format(term_name))
|
|
697
|
+
positive_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
|
|
698
|
+
negative_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
|
|
699
|
+
positive_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
|
|
700
|
+
negative_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
|
|
701
|
+
positive_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
|
|
702
|
+
negative_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
|
|
703
|
+
positive_pin_group_term.SetReferenceTerminal(negative_pin_group_term)
|
|
704
|
+
elif source.source_type == SourceType.Rlc: # pragma: no cover
|
|
705
|
+
self.create(
|
|
706
|
+
pins=[positive_pins[0], negative_pins[0]],
|
|
707
|
+
component_name=source.name,
|
|
708
|
+
is_rlc=True,
|
|
709
|
+
r_value=source.r_value,
|
|
710
|
+
l_value=source.l_value,
|
|
711
|
+
c_value=source.c_value,
|
|
712
|
+
)
|
|
713
|
+
return True
|
|
714
|
+
|
|
715
|
+
@pyedb_function_handler()
|
|
716
|
+
def create_port_on_pins(self, refdes, pins, reference_pins, impedance=50.0, port_name=None, pec_boundary=False):
|
|
717
|
+
"""Create circuit port between pins and reference ones.
|
|
718
|
+
|
|
719
|
+
Parameters
|
|
720
|
+
----------
|
|
721
|
+
refdes : Component reference designator
|
|
722
|
+
str or EDBComponent object.
|
|
723
|
+
pins : pin name where the terminal has to be created. Single pin or several ones can be provided.If several
|
|
724
|
+
pins are provided a pin group will is created. Pin names can be the EDB name or the EDBPadstackInstance one.
|
|
725
|
+
For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1`` or ``Pin1`` can be provided and
|
|
726
|
+
will be handled.
|
|
727
|
+
str, [str], EDBPadstackInstance, [EDBPadstackInstance]
|
|
728
|
+
reference_pins : reference pin name used for terminal reference. Single pin or several ones can be provided.
|
|
729
|
+
If several pins are provided a pin group will is created. Pin names can be the EDB name or the
|
|
730
|
+
EDBPadstackInstance one. For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1``
|
|
731
|
+
or ``Pin1`` can be provided and will be handled.
|
|
732
|
+
str, [str], EDBPadstackInstance, [EDBPadstackInstance]
|
|
733
|
+
impedance : Port impedance
|
|
734
|
+
str, float
|
|
735
|
+
port_name : str, optional
|
|
736
|
+
Port name. The default is ``None``, in which case a name is automatically assigned.
|
|
737
|
+
pec_boundary : bool, optional
|
|
738
|
+
Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
|
|
739
|
+
a perfect short is created between the pin and impedance is ignored. This
|
|
740
|
+
parameter is only supported on a port created between two pins, such as
|
|
741
|
+
when there is no pin group.
|
|
742
|
+
|
|
743
|
+
Returns
|
|
744
|
+
-------
|
|
745
|
+
EDB terminal created, or False if failed to create.
|
|
746
|
+
|
|
747
|
+
Example:
|
|
748
|
+
>>> from pyedb import Edb
|
|
749
|
+
>>> edb = Edb(path_to_edb_file)
|
|
750
|
+
>>> pin = "AJ6"
|
|
751
|
+
>>> ref_pins = ["AM7", "AM4"]
|
|
752
|
+
Or to take all reference pins
|
|
753
|
+
>>> ref_pins = [pin for pin in list(edb.components["U2A5"].pins.values()) if pin.net_name == "GND"]
|
|
754
|
+
>>> edb.components.create_port_on_pins(refdes="U2A5", pins=pin, reference_pins=ref_pins)
|
|
755
|
+
>>> edb.save_edb()
|
|
756
|
+
>>> edb.close_edb()
|
|
757
|
+
"""
|
|
758
|
+
|
|
759
|
+
if isinstance(pins, str) or isinstance(pins, EDBPadstackInstance):
|
|
760
|
+
pins = [pins]
|
|
761
|
+
if isinstance(reference_pins, str):
|
|
762
|
+
reference_pins = [reference_pins]
|
|
763
|
+
if isinstance(refdes, str) or isinstance(refdes, EDBComponent):
|
|
764
|
+
refdes = self.instances[refdes]
|
|
765
|
+
if len([pin for pin in pins if isinstance(pin, str)]) == len(pins):
|
|
766
|
+
cmp_pins = []
|
|
767
|
+
for pin_name in pins:
|
|
768
|
+
cmp_pin = [pin for pin in list(refdes.pins.values()) if pin_name == pin.name]
|
|
769
|
+
if not cmp_pin:
|
|
770
|
+
cmp_pin = [pin for pin in list(refdes.pins.values()) if pin_name == pin.name.split("-")[1]]
|
|
771
|
+
if cmp_pin:
|
|
772
|
+
cmp_pins.append(cmp_pin[0])
|
|
773
|
+
if not cmp_pins:
|
|
774
|
+
return
|
|
775
|
+
pins = cmp_pins
|
|
776
|
+
if not len([pin for pin in pins if isinstance(pin, EDBPadstackInstance)]) == len(pins):
|
|
777
|
+
self._logger.error("Pin list must contain only pins instances")
|
|
778
|
+
return
|
|
779
|
+
if not port_name:
|
|
780
|
+
port_name = "Port_{}_{}".format(pins[0].net_name, pins[0].name)
|
|
781
|
+
if len([pin for pin in reference_pins if isinstance(pin, str)]) == len(reference_pins):
|
|
782
|
+
ref_cmp_pins = []
|
|
783
|
+
for ref_pin_name in reference_pins:
|
|
784
|
+
cmp_ref_pin = [pin for pin in list(refdes.pins.values()) if ref_pin_name == pin.name]
|
|
785
|
+
if not cmp_ref_pin:
|
|
786
|
+
cmp_ref_pin = [pin for pin in list(refdes.pins.values()) if ref_pin_name == pin.name.split("-")[1]]
|
|
787
|
+
if cmp_ref_pin:
|
|
788
|
+
ref_cmp_pins.append(cmp_ref_pin[0])
|
|
789
|
+
if not ref_cmp_pins:
|
|
790
|
+
return
|
|
791
|
+
reference_pins = ref_cmp_pins
|
|
792
|
+
if not len([pin for pin in reference_pins if isinstance(pin, EDBPadstackInstance)]) == len(reference_pins):
|
|
793
|
+
return
|
|
794
|
+
if len(pins) > 1:
|
|
795
|
+
pec_boundary = False
|
|
796
|
+
self._logger.info(
|
|
797
|
+
"Disabling PEC boundary creation, this feature is supported on single pin "
|
|
798
|
+
"ports only, {} pins found".format(len(pins))
|
|
799
|
+
)
|
|
800
|
+
group_name = "group_{}".format(port_name)
|
|
801
|
+
pin_group = self.create_pingroup_from_pins(pins, group_name)
|
|
802
|
+
term = self._create_pin_group_terminal(pingroup=pin_group, term_name=port_name)
|
|
803
|
+
|
|
804
|
+
else:
|
|
805
|
+
term = self._create_terminal(pins[0].primitive_object, term_name=port_name)
|
|
806
|
+
term.SetIsCircuitPort(True)
|
|
807
|
+
if len(reference_pins) > 1:
|
|
808
|
+
pec_boundary = False
|
|
809
|
+
self._logger.info(
|
|
810
|
+
"Disabling PEC boundary creation. This feature is supported on single pin"
|
|
811
|
+
"ports only {} reference pins found.".format(len(reference_pins))
|
|
812
|
+
)
|
|
813
|
+
ref_group_name = "group_{}_ref".format(port_name)
|
|
814
|
+
ref_pin_group = self.create_pingroup_from_pins(reference_pins, ref_group_name)
|
|
815
|
+
ref_term = self._create_pin_group_terminal(pingroup=ref_pin_group, term_name=port_name + "_ref")
|
|
816
|
+
else:
|
|
817
|
+
ref_term = self._create_terminal(reference_pins[0].primitive_object, term_name=port_name + "_ref")
|
|
818
|
+
ref_term.SetIsCircuitPort(True)
|
|
819
|
+
term.SetImpedance(self._edb.utility.value(impedance))
|
|
820
|
+
term.SetReferenceTerminal(ref_term)
|
|
821
|
+
if pec_boundary:
|
|
822
|
+
term.SetIsCircuitPort(False)
|
|
823
|
+
ref_term.SetIsCircuitPort(False)
|
|
824
|
+
term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.PecBoundary)
|
|
825
|
+
ref_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.PecBoundary)
|
|
826
|
+
self._logger.info(
|
|
827
|
+
"PEC boundary created between pin {} and reference pin {}".format(pins[0].name, reference_pins[0].name)
|
|
828
|
+
)
|
|
829
|
+
if term:
|
|
830
|
+
return term
|
|
831
|
+
return False
|
|
832
|
+
|
|
833
|
+
@pyedb_function_handler()
|
|
834
|
+
def create_port_on_component(
|
|
835
|
+
self,
|
|
836
|
+
component,
|
|
837
|
+
net_list,
|
|
838
|
+
port_type=SourceType.CoaxPort,
|
|
839
|
+
do_pingroup=True,
|
|
840
|
+
reference_net="gnd",
|
|
841
|
+
port_name=None,
|
|
842
|
+
solder_balls_height=None,
|
|
843
|
+
solder_balls_size=None,
|
|
844
|
+
solder_balls_mid_size=None,
|
|
845
|
+
):
|
|
846
|
+
"""Create ports on a component.
|
|
847
|
+
|
|
848
|
+
Parameters
|
|
849
|
+
----------
|
|
850
|
+
component : str or self._pedb.component
|
|
851
|
+
EDB component or str component name.
|
|
852
|
+
net_list : str or list of string.
|
|
853
|
+
List of nets where ports must be created on the component.
|
|
854
|
+
If the net is not part of the component, this parameter is skipped.
|
|
855
|
+
port_type : SourceType enumerator, CoaxPort or CircuitPort
|
|
856
|
+
Type of port to create. ``CoaxPort`` generates solder balls.
|
|
857
|
+
``CircuitPort`` generates circuit ports on pins belonging to the net list.
|
|
858
|
+
do_pingroup : bool
|
|
859
|
+
True activate pingroup during port creation (only used with combination of CoaxPort),
|
|
860
|
+
False will take the closest reference pin and generate one port per signal pin.
|
|
861
|
+
refnet : string or list of string.
|
|
862
|
+
list of the reference net.
|
|
863
|
+
port_name : str
|
|
864
|
+
Port name for overwriting the default port-naming convention,
|
|
865
|
+
which is ``[component][net][pin]``. The port name must be unique.
|
|
866
|
+
If a port with the specified name already exists, the
|
|
867
|
+
default naming convention is used so that port creation does
|
|
868
|
+
not fail.
|
|
869
|
+
solder_balls_height : float, optional
|
|
870
|
+
Solder balls height used for the component. When provided default value is overwritten and must be
|
|
871
|
+
provided in meter.
|
|
872
|
+
solder_balls_size : float, optional
|
|
873
|
+
Solder balls diameter. When provided auto evaluation based on padstack size will be disabled.
|
|
874
|
+
solder_balls_mid_size : float, optional
|
|
875
|
+
Solder balls mid diameter. When provided if value is different than solder balls size, spheroid shape will
|
|
876
|
+
be switched.
|
|
877
|
+
|
|
878
|
+
Returns
|
|
879
|
+
-------
|
|
880
|
+
double, bool
|
|
881
|
+
Salder ball height vale, ``False`` when failed.
|
|
882
|
+
|
|
883
|
+
Examples
|
|
884
|
+
--------
|
|
885
|
+
|
|
886
|
+
>>> from pyedb import Edb
|
|
887
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
888
|
+
>>> net_list = ["M_DQ<1>", "M_DQ<2>", "M_DQ<3>", "M_DQ<4>", "M_DQ<5>"]
|
|
889
|
+
>>> edbapp.components.create_port_on_component(cmp="U2A5", net_list=net_list,
|
|
890
|
+
>>> port_type=SourceType.CoaxPort, do_pingroup=False, refnet="GND")
|
|
891
|
+
|
|
892
|
+
"""
|
|
893
|
+
if isinstance(component, str):
|
|
894
|
+
component = self.instances[component].edbcomponent
|
|
895
|
+
if not isinstance(net_list, list):
|
|
896
|
+
net_list = [net_list]
|
|
897
|
+
for net in net_list:
|
|
898
|
+
if not isinstance(net, str):
|
|
899
|
+
try:
|
|
900
|
+
net_name = net.name
|
|
901
|
+
if net_name != "":
|
|
902
|
+
net_list.append(net_name)
|
|
903
|
+
except:
|
|
904
|
+
pass
|
|
905
|
+
if reference_net in net_list:
|
|
906
|
+
net_list.remove(reference_net)
|
|
907
|
+
cmp_pins = [
|
|
908
|
+
p for p in list(component.LayoutObjs) if int(p.GetObjType()) == 1 and p.GetNet().GetName() in net_list
|
|
909
|
+
]
|
|
910
|
+
for p in cmp_pins: # pragma no cover
|
|
911
|
+
if not p.IsLayoutPin():
|
|
912
|
+
p.SetIsLayoutPin(True)
|
|
913
|
+
if len(cmp_pins) == 0:
|
|
914
|
+
self._logger.info(
|
|
915
|
+
"No pins found on component {}, searching padstack instances instead".format(component.GetName())
|
|
916
|
+
)
|
|
917
|
+
return False
|
|
918
|
+
pin_layers = cmp_pins[0].GetPadstackDef().GetData().GetLayerNames()
|
|
919
|
+
if port_type == SourceType.CoaxPort:
|
|
920
|
+
pad_params = self._padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
|
|
921
|
+
if not pad_params[0] == 7:
|
|
922
|
+
if not solder_balls_size: # pragma no cover
|
|
923
|
+
sball_diam = min([self._pedb.edb_value(val).ToDouble() for val in pad_params[1]])
|
|
924
|
+
sball_mid_diam = sball_diam
|
|
925
|
+
else: # pragma no cover
|
|
926
|
+
sball_diam = solder_balls_size
|
|
927
|
+
if solder_balls_mid_size:
|
|
928
|
+
sball_mid_diam = solder_balls_mid_size
|
|
929
|
+
else:
|
|
930
|
+
sball_mid_diam = solder_balls_size
|
|
931
|
+
if not solder_balls_height: # pragma no cover
|
|
932
|
+
solder_balls_height = 2 * sball_diam / 3
|
|
933
|
+
else: # pragma no cover
|
|
934
|
+
if not solder_balls_size:
|
|
935
|
+
bbox = pad_params[1]
|
|
936
|
+
sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
|
|
937
|
+
else:
|
|
938
|
+
sball_diam = solder_balls_size
|
|
939
|
+
if not solder_balls_height:
|
|
940
|
+
solder_balls_height = 2 * sball_diam / 3
|
|
941
|
+
if solder_balls_mid_size:
|
|
942
|
+
sball_mid_diam = solder_balls_mid_size
|
|
943
|
+
else:
|
|
944
|
+
sball_mid_diam = sball_diam
|
|
945
|
+
sball_shape = "Cylinder"
|
|
946
|
+
if not sball_diam == sball_mid_diam:
|
|
947
|
+
sball_shape = "Spheroid"
|
|
948
|
+
self.set_solder_ball(
|
|
949
|
+
component=component,
|
|
950
|
+
sball_height=solder_balls_height,
|
|
951
|
+
sball_diam=sball_diam,
|
|
952
|
+
sball_mid_diam=sball_mid_diam,
|
|
953
|
+
shape=sball_shape,
|
|
954
|
+
)
|
|
955
|
+
for pin in cmp_pins:
|
|
956
|
+
self._padstack.create_coax_port(padstackinstance=pin, name=port_name)
|
|
957
|
+
|
|
958
|
+
elif port_type == SourceType.CircPort: # pragma no cover
|
|
959
|
+
ref_pins = [
|
|
960
|
+
p
|
|
961
|
+
for p in list(component.LayoutObjs)
|
|
962
|
+
if int(p.GetObjType()) == 1 and p.GetNet().GetName() in reference_net
|
|
963
|
+
]
|
|
964
|
+
for p in ref_pins:
|
|
965
|
+
if not p.IsLayoutPin():
|
|
966
|
+
p.SetIsLayoutPin(True)
|
|
967
|
+
if len(ref_pins) == 0:
|
|
968
|
+
self._logger.info("No reference pin found on component {}.".format(component.GetName()))
|
|
969
|
+
if do_pingroup:
|
|
970
|
+
if len(ref_pins) == 1:
|
|
971
|
+
ref_pin_group_term = self._create_terminal(ref_pins[0])
|
|
972
|
+
else:
|
|
973
|
+
ref_pin_group = self.create_pingroup_from_pins(ref_pins)
|
|
974
|
+
if not ref_pin_group:
|
|
975
|
+
return False
|
|
976
|
+
ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=True)
|
|
977
|
+
if not ref_pin_group_term:
|
|
978
|
+
return False
|
|
979
|
+
for net in net_list:
|
|
980
|
+
pins = [pin for pin in cmp_pins if pin.GetNet().GetName() == net]
|
|
981
|
+
if pins:
|
|
982
|
+
if len(pins) == 1:
|
|
983
|
+
pin_term = self._create_terminal(pins[0])
|
|
984
|
+
if pin_term:
|
|
985
|
+
pin_term.SetReferenceTerminal(ref_pin_group_term)
|
|
986
|
+
else:
|
|
987
|
+
pin_group = self.create_pingroup_from_pins(pins)
|
|
988
|
+
if not pin_group:
|
|
989
|
+
return False
|
|
990
|
+
pin_group_term = self._create_pin_group_terminal(pin_group)
|
|
991
|
+
if pin_group_term:
|
|
992
|
+
pin_group_term.SetReferenceTerminal(ref_pin_group_term)
|
|
993
|
+
else:
|
|
994
|
+
self._logger.info("No pins found on component {} for the net {}".format(component, net))
|
|
995
|
+
else:
|
|
996
|
+
ref_pin_group = self.create_pingroup_from_pins(ref_pins)
|
|
997
|
+
if not ref_pin_group:
|
|
998
|
+
self._logger.warning("failed to create reference pin group")
|
|
999
|
+
return False
|
|
1000
|
+
ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=True)
|
|
1001
|
+
for net in net_list:
|
|
1002
|
+
pins = [pin for pin in cmp_pins if pin.GetNet().GetName() == net]
|
|
1003
|
+
for pin in pins:
|
|
1004
|
+
pin_group = self.create_pingroup_from_pins([pin])
|
|
1005
|
+
pin_group_term = self._create_pin_group_terminal(pin_group, isref=False)
|
|
1006
|
+
pin_group_term.SetReferenceTerminal(ref_pin_group_term)
|
|
1007
|
+
return True
|
|
1008
|
+
|
|
1009
|
+
@pyedb_function_handler()
|
|
1010
|
+
def _create_terminal(self, pin, term_name=None):
|
|
1011
|
+
"""Create terminal on component pin.
|
|
1012
|
+
|
|
1013
|
+
Parameters
|
|
1014
|
+
----------
|
|
1015
|
+
pin : Edb padstack instance.
|
|
1016
|
+
|
|
1017
|
+
term_name : Terminal name (Optional).
|
|
1018
|
+
str.
|
|
1019
|
+
|
|
1020
|
+
Returns
|
|
1021
|
+
-------
|
|
1022
|
+
EDB terminal.
|
|
1023
|
+
"""
|
|
1024
|
+
|
|
1025
|
+
res, from_layer, _ = pin.GetLayerRange()
|
|
1026
|
+
cmp_name = pin.GetComponent().GetName()
|
|
1027
|
+
net_name = pin.GetNet().GetName()
|
|
1028
|
+
pin_name = pin.GetName()
|
|
1029
|
+
if term_name is None:
|
|
1030
|
+
term_name = "{}.{}.{}".format(cmp_name, pin_name, net_name)
|
|
1031
|
+
for term in list(self._pedb.active_layout.Terminals):
|
|
1032
|
+
if term.GetName() == term_name:
|
|
1033
|
+
return term
|
|
1034
|
+
term = self._edb.cell.terminal.PadstackInstanceTerminal.Create(
|
|
1035
|
+
pin.GetLayout(), pin.GetNet(), term_name, pin, from_layer
|
|
1036
|
+
)
|
|
1037
|
+
return term
|
|
1038
|
+
|
|
1039
|
+
@pyedb_function_handler()
|
|
1040
|
+
def _get_closest_pin_from(self, pin, ref_pinlist):
|
|
1041
|
+
"""Returns the closest pin from given pin among the list of reference pins.
|
|
1042
|
+
|
|
1043
|
+
Parameters
|
|
1044
|
+
----------
|
|
1045
|
+
pin : Edb padstack instance.
|
|
1046
|
+
|
|
1047
|
+
ref_pinlist : list of reference edb pins.
|
|
1048
|
+
|
|
1049
|
+
Returns
|
|
1050
|
+
-------
|
|
1051
|
+
Edb pin.
|
|
1052
|
+
|
|
1053
|
+
"""
|
|
1054
|
+
res, pin_position, pin_rot = pin.GetPositionAndRotation(
|
|
1055
|
+
self._pedb.point_data(0.0, 0.0),
|
|
1056
|
+
0.0,
|
|
1057
|
+
)
|
|
1058
|
+
distance = 1e3
|
|
1059
|
+
closest_pin = ref_pinlist[0]
|
|
1060
|
+
for ref_pin in ref_pinlist:
|
|
1061
|
+
res, ref_pin_position, ref_pin_rot = ref_pin.GetPositionAndRotation(
|
|
1062
|
+
self._pedb.point_data(0.0, 0.0),
|
|
1063
|
+
0.0,
|
|
1064
|
+
)
|
|
1065
|
+
temp_distance = pin_position.Distance(ref_pin_position)
|
|
1066
|
+
if temp_distance < distance:
|
|
1067
|
+
distance = temp_distance
|
|
1068
|
+
closest_pin = ref_pin
|
|
1069
|
+
return closest_pin
|
|
1070
|
+
|
|
1071
|
+
@pyedb_function_handler()
|
|
1072
|
+
def replace_rlc_by_gap_boundaries(self, component=None):
|
|
1073
|
+
"""Replace RLC component by RLC gap boundaries. These boundary types are compatible with 3D modeler export.
|
|
1074
|
+
Only 2 pins RLC components are supported in this command.
|
|
1075
|
+
|
|
1076
|
+
Parameters
|
|
1077
|
+
----------
|
|
1078
|
+
component : str
|
|
1079
|
+
Reference designator of the RLC component.
|
|
1080
|
+
|
|
1081
|
+
Returns
|
|
1082
|
+
-------
|
|
1083
|
+
bool
|
|
1084
|
+
``True`` when succeed, ``False`` if it failed.
|
|
1085
|
+
|
|
1086
|
+
Examples
|
|
1087
|
+
--------
|
|
1088
|
+
>>> from pyedb import Edb
|
|
1089
|
+
>>> edb = Edb(edb_file)
|
|
1090
|
+
>>> for refdes, cmp in edb.components.capacitors.items():
|
|
1091
|
+
>>> edb.components.replace_rlc_by_gap_boundaries(refdes)
|
|
1092
|
+
>>> edb.save_edb()
|
|
1093
|
+
>>> edb.close_edb()
|
|
1094
|
+
"""
|
|
1095
|
+
if not component: # pragma no cover
|
|
1096
|
+
return False
|
|
1097
|
+
if isinstance(component, str):
|
|
1098
|
+
component = self.instances[component]
|
|
1099
|
+
if not component: # pragma no cover
|
|
1100
|
+
self._logger.error("component %s not found.", component)
|
|
1101
|
+
return False
|
|
1102
|
+
component_type = component.edbcomponent.GetComponentType()
|
|
1103
|
+
if (
|
|
1104
|
+
component_type == self._edb.definition.ComponentType.Other
|
|
1105
|
+
or component_type == self._edb.definition.ComponentType.IC
|
|
1106
|
+
or component_type == self._edb.definition.ComponentType.IO
|
|
1107
|
+
):
|
|
1108
|
+
self._logger.info("Component %s passed to deactivate is not an RLC.", component.refdes)
|
|
1109
|
+
return False
|
|
1110
|
+
component.is_enabled = False
|
|
1111
|
+
return self.add_rlc_boundary(component.refdes, False)
|
|
1112
|
+
|
|
1113
|
+
@pyedb_function_handler()
|
|
1114
|
+
def deactivate_rlc_component(self, component=None, create_circuit_port=False, pec_boundary=False):
|
|
1115
|
+
"""Deactivate RLC component with a possibility to convert it to a circuit port.
|
|
1116
|
+
|
|
1117
|
+
Parameters
|
|
1118
|
+
----------
|
|
1119
|
+
component : str
|
|
1120
|
+
Reference designator of the RLC component.
|
|
1121
|
+
|
|
1122
|
+
create_circuit_port : bool, optional
|
|
1123
|
+
Whether to replace the deactivated RLC component with a circuit port. The default
|
|
1124
|
+
is ``False``.
|
|
1125
|
+
pec_boundary : bool, optional
|
|
1126
|
+
Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
|
|
1127
|
+
a perfect short is created between the pin and impedance is ignored. This
|
|
1128
|
+
parameter is only supported on a port created between two pins, such as
|
|
1129
|
+
when there is no pin group.
|
|
1130
|
+
|
|
1131
|
+
Returns
|
|
1132
|
+
-------
|
|
1133
|
+
bool
|
|
1134
|
+
``True`` when successful, ``False`` when failed.
|
|
1135
|
+
|
|
1136
|
+
Examples
|
|
1137
|
+
--------
|
|
1138
|
+
>>> from pyedb import Edb
|
|
1139
|
+
>>> edb_file = r'C:\my_edb_file.aedb'
|
|
1140
|
+
>>> edb = Edb(edb_file)
|
|
1141
|
+
>>> for cmp in list(edb.components.instances.keys()):
|
|
1142
|
+
>>> edb.components.deactivate_rlc_component(component=cmp, create_circuit_port=False)
|
|
1143
|
+
>>> edb.save_edb()
|
|
1144
|
+
>>> edb.close_edb()
|
|
1145
|
+
"""
|
|
1146
|
+
if not component:
|
|
1147
|
+
return False
|
|
1148
|
+
if isinstance(component, str):
|
|
1149
|
+
component = self.instances[component]
|
|
1150
|
+
if not component:
|
|
1151
|
+
self._logger.error("component %s not found.", component)
|
|
1152
|
+
return False
|
|
1153
|
+
component_type = component.edbcomponent.GetComponentType()
|
|
1154
|
+
if (
|
|
1155
|
+
component_type == self._edb.definition.ComponentType.Other
|
|
1156
|
+
or component_type == self._edb.definition.ComponentType.IC
|
|
1157
|
+
or component_type == self._edb.definition.ComponentType.IO
|
|
1158
|
+
):
|
|
1159
|
+
self._logger.info("Component %s passed to deactivate is not an RLC.", component.refdes)
|
|
1160
|
+
return False
|
|
1161
|
+
component.is_enabled = False
|
|
1162
|
+
return self.add_port_on_rlc_component(
|
|
1163
|
+
component=component.refdes, circuit_ports=create_circuit_port, pec_boundary=pec_boundary
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1166
|
+
@pyedb_function_handler()
|
|
1167
|
+
def add_port_on_rlc_component(self, component=None, circuit_ports=True, pec_boundary=False):
|
|
1168
|
+
"""Deactivate RLC component and replace it with a circuit port.
|
|
1169
|
+
The circuit port supports only two-pin components.
|
|
1170
|
+
|
|
1171
|
+
Parameters
|
|
1172
|
+
----------
|
|
1173
|
+
component : str
|
|
1174
|
+
Reference designator of the RLC component.
|
|
1175
|
+
|
|
1176
|
+
circuit_ports : bool
|
|
1177
|
+
``True`` will replace RLC component by circuit ports, ``False`` gap ports compatible with HFSS 3D modeler
|
|
1178
|
+
export.
|
|
1179
|
+
|
|
1180
|
+
pec_boundary : bool, optional
|
|
1181
|
+
Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
|
|
1182
|
+
a perfect short is created between the pin and impedance is ignored. This
|
|
1183
|
+
parameter is only supported on a port created between two pins, such as
|
|
1184
|
+
when there is no pin group.
|
|
1185
|
+
|
|
1186
|
+
Returns
|
|
1187
|
+
-------
|
|
1188
|
+
bool
|
|
1189
|
+
``True`` when successful, ``False`` when failed.
|
|
1190
|
+
"""
|
|
1191
|
+
if isinstance(component, str): # pragma: no cover
|
|
1192
|
+
component = self.instances[component]
|
|
1193
|
+
if not isinstance(component, EDBComponent): # pragma: no cover
|
|
1194
|
+
return False
|
|
1195
|
+
self.set_component_rlc(component.refdes)
|
|
1196
|
+
pins = self.get_pin_from_component(component.refdes)
|
|
1197
|
+
if len(pins) == 2: # pragma: no cover
|
|
1198
|
+
pin_layers = self._padstack._get_pin_layer_range(pins[0])
|
|
1199
|
+
pos_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
|
|
1200
|
+
self._active_layout,
|
|
1201
|
+
pins[0].GetNet(),
|
|
1202
|
+
"{}_{}".format(component.refdes, pins[0].GetName()),
|
|
1203
|
+
pins[0],
|
|
1204
|
+
pin_layers[0],
|
|
1205
|
+
False,
|
|
1206
|
+
)
|
|
1207
|
+
if not pos_pin_term: # pragma: no cover
|
|
1208
|
+
return False
|
|
1209
|
+
neg_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
|
|
1210
|
+
self._active_layout,
|
|
1211
|
+
pins[1].GetNet(),
|
|
1212
|
+
"{}_{}_ref".format(component.refdes, pins[1].GetName()),
|
|
1213
|
+
pins[1],
|
|
1214
|
+
pin_layers[0],
|
|
1215
|
+
False,
|
|
1216
|
+
)
|
|
1217
|
+
if not neg_pin_term: # pragma: no cover
|
|
1218
|
+
return False
|
|
1219
|
+
if pec_boundary:
|
|
1220
|
+
pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PecBoundary)
|
|
1221
|
+
neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PecBoundary)
|
|
1222
|
+
else:
|
|
1223
|
+
pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PortBoundary)
|
|
1224
|
+
neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PortBoundary)
|
|
1225
|
+
pos_pin_term.SetName(component.refdes)
|
|
1226
|
+
pos_pin_term.SetReferenceTerminal(neg_pin_term)
|
|
1227
|
+
if circuit_ports and not pec_boundary:
|
|
1228
|
+
pos_pin_term.SetIsCircuitPort(True)
|
|
1229
|
+
neg_pin_term.SetIsCircuitPort(True)
|
|
1230
|
+
elif pec_boundary:
|
|
1231
|
+
pos_pin_term.SetIsCircuitPort(False)
|
|
1232
|
+
neg_pin_term.SetIsCircuitPort(False)
|
|
1233
|
+
else:
|
|
1234
|
+
pos_pin_term.SetIsCircuitPort(False)
|
|
1235
|
+
neg_pin_term.SetIsCircuitPort(False)
|
|
1236
|
+
self._logger.info("Component {} has been replaced by port".format(component.refdes))
|
|
1237
|
+
return True
|
|
1238
|
+
return False
|
|
1239
|
+
|
|
1240
|
+
@pyedb_function_handler()
|
|
1241
|
+
def add_rlc_boundary(self, component=None, circuit_type=True):
|
|
1242
|
+
"""Add RLC gap boundary on component and replace it with a circuit port.
|
|
1243
|
+
The circuit port supports only 2-pin components.
|
|
1244
|
+
|
|
1245
|
+
Parameters
|
|
1246
|
+
----------
|
|
1247
|
+
component : str
|
|
1248
|
+
Reference designator of the RLC component.
|
|
1249
|
+
circuit_type : bool
|
|
1250
|
+
When ``True`` circuit type are defined, if ``False`` gap type will be used instead (compatible with HFSS 3D
|
|
1251
|
+
modeler). Default value is ``True``.
|
|
1252
|
+
|
|
1253
|
+
Returns
|
|
1254
|
+
-------
|
|
1255
|
+
bool
|
|
1256
|
+
``True`` when successful, ``False`` when failed.
|
|
1257
|
+
"""
|
|
1258
|
+
if isinstance(component, str): # pragma: no cover
|
|
1259
|
+
component = self.instances[component]
|
|
1260
|
+
if not isinstance(component, EDBComponent): # pragma: no cover
|
|
1261
|
+
return False
|
|
1262
|
+
self.set_component_rlc(component.refdes)
|
|
1263
|
+
pins = self.get_pin_from_component(component.refdes)
|
|
1264
|
+
if len(pins) == 2: # pragma: no cover
|
|
1265
|
+
pin_layer = self._padstack._get_pin_layer_range(pins[0])[0]
|
|
1266
|
+
pos_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
|
|
1267
|
+
self._active_layout,
|
|
1268
|
+
pins[0].GetNet(),
|
|
1269
|
+
"{}_{}".format(component.refdes, pins[0].GetName()),
|
|
1270
|
+
pins[0],
|
|
1271
|
+
pin_layer,
|
|
1272
|
+
False,
|
|
1273
|
+
)
|
|
1274
|
+
if not pos_pin_term: # pragma: no cover
|
|
1275
|
+
return False
|
|
1276
|
+
neg_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
|
|
1277
|
+
self._active_layout,
|
|
1278
|
+
pins[1].GetNet(),
|
|
1279
|
+
"{}_{}_ref".format(component.refdes, pins[1].GetName()),
|
|
1280
|
+
pins[1],
|
|
1281
|
+
pin_layer,
|
|
1282
|
+
True,
|
|
1283
|
+
)
|
|
1284
|
+
if not neg_pin_term: # pragma: no cover
|
|
1285
|
+
return False
|
|
1286
|
+
pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.RlcBoundary)
|
|
1287
|
+
if not circuit_type:
|
|
1288
|
+
pos_pin_term.SetIsCircuitPort(False)
|
|
1289
|
+
else:
|
|
1290
|
+
pos_pin_term.SetIsCircuitPort(True)
|
|
1291
|
+
pos_pin_term.SetName(component.refdes)
|
|
1292
|
+
neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.RlcBoundary)
|
|
1293
|
+
if not circuit_type:
|
|
1294
|
+
neg_pin_term.SetIsCircuitPort(False)
|
|
1295
|
+
else:
|
|
1296
|
+
neg_pin_term.SetIsCircuitPort(True)
|
|
1297
|
+
pos_pin_term.SetReferenceTerminal(neg_pin_term)
|
|
1298
|
+
rlc_values = component.rlc_values
|
|
1299
|
+
rlc = self._edb.utility.Rlc()
|
|
1300
|
+
if rlc_values[0]:
|
|
1301
|
+
rlc.REnabled = True
|
|
1302
|
+
rlc.R = self._edb.utility.value(rlc_values[0])
|
|
1303
|
+
if rlc_values[1]:
|
|
1304
|
+
rlc.LEnabled = True
|
|
1305
|
+
rlc.L = self._edb.utility.value(rlc_values[1])
|
|
1306
|
+
if rlc_values[2]:
|
|
1307
|
+
rlc.CEnabled = True
|
|
1308
|
+
rlc.C = self._edb.utility.value(rlc_values[2])
|
|
1309
|
+
rlc.is_parallel = component.is_parallel_rlc
|
|
1310
|
+
pos_pin_term.SetRlcBoundaryParameters(rlc)
|
|
1311
|
+
self._logger.info("Component {} has been replaced by port".format(component.refdes))
|
|
1312
|
+
return True
|
|
1313
|
+
|
|
1314
|
+
@pyedb_function_handler()
|
|
1315
|
+
def _create_pin_group_terminal(self, pingroup, isref=False, term_name=None):
|
|
1316
|
+
"""Creates an EDB pin group terminal from a given EDB pin group.
|
|
1317
|
+
|
|
1318
|
+
Parameters
|
|
1319
|
+
----------
|
|
1320
|
+
pingroup : Edb pin group.
|
|
1321
|
+
|
|
1322
|
+
isref : bool
|
|
1323
|
+
|
|
1324
|
+
term_name : Terminal name (Optional). If not provided default name is Component name, Pin name, Net name.
|
|
1325
|
+
str.
|
|
1326
|
+
|
|
1327
|
+
Returns
|
|
1328
|
+
-------
|
|
1329
|
+
Edb pin group terminal.
|
|
1330
|
+
"""
|
|
1331
|
+
pin = list(pingroup.GetPins())[0]
|
|
1332
|
+
if term_name is None:
|
|
1333
|
+
term_name = "{}.{}.{}".format(pin.GetComponent().GetName(), pin.GetName(), pin.GetNet().GetName())
|
|
1334
|
+
for t in list(self._pedb.active_layout.Terminals):
|
|
1335
|
+
if t.GetName() == term_name:
|
|
1336
|
+
return t
|
|
1337
|
+
pingroup_term = self._edb.cell.terminal.PinGroupTerminal.Create(
|
|
1338
|
+
self._active_layout, pingroup.GetNet(), term_name, pingroup, isref
|
|
1339
|
+
)
|
|
1340
|
+
return pingroup_term
|
|
1341
|
+
|
|
1342
|
+
@pyedb_function_handler()
|
|
1343
|
+
def _is_top_component(self, cmp):
|
|
1344
|
+
"""Test the component placement layer.
|
|
1345
|
+
|
|
1346
|
+
Parameters
|
|
1347
|
+
----------
|
|
1348
|
+
cmp : self._pedb.component
|
|
1349
|
+
Edb component.
|
|
1350
|
+
|
|
1351
|
+
Returns
|
|
1352
|
+
-------
|
|
1353
|
+
bool
|
|
1354
|
+
``True`` when component placed on top layer, ``False`` on bottom layer.
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
"""
|
|
1358
|
+
signal_layers = cmp.GetLayout().GetLayerCollection().Layers(self._edb.cell.layer_type_set.SignalLayerSet)
|
|
1359
|
+
if cmp.GetPlacementLayer() == signal_layers[0]:
|
|
1360
|
+
return True
|
|
1361
|
+
else:
|
|
1362
|
+
return False
|
|
1363
|
+
|
|
1364
|
+
@pyedb_function_handler()
|
|
1365
|
+
def _getComponentDefinition(self, name, pins):
|
|
1366
|
+
componentDefinition = self._pedb.edb_api.definition.ComponentDef.FindByName(self._db, name)
|
|
1367
|
+
if componentDefinition.IsNull():
|
|
1368
|
+
componentDefinition = self._pedb.edb_api.definition.ComponentDef.Create(self._db, name, None)
|
|
1369
|
+
if componentDefinition.IsNull():
|
|
1370
|
+
self._logger.error("Failed to create component definition {}".format(name))
|
|
1371
|
+
return None
|
|
1372
|
+
ind = 1
|
|
1373
|
+
for pin in pins:
|
|
1374
|
+
if not pin.GetName():
|
|
1375
|
+
pin.SetName(str(ind))
|
|
1376
|
+
ind += 1
|
|
1377
|
+
componentDefinitionPin = self._pedb.edb_api.definition.ComponentDefPin.Create(
|
|
1378
|
+
componentDefinition, pin.GetName()
|
|
1379
|
+
)
|
|
1380
|
+
if componentDefinitionPin.IsNull():
|
|
1381
|
+
self._logger.error("Failed to create component definition pin {}-{}".format(name, pin.GetName()))
|
|
1382
|
+
return None
|
|
1383
|
+
else:
|
|
1384
|
+
self._logger.warning("Found existing component definition for footprint {}".format(name))
|
|
1385
|
+
return componentDefinition
|
|
1386
|
+
|
|
1387
|
+
@pyedb_function_handler()
|
|
1388
|
+
def create_rlc_component(
|
|
1389
|
+
self, pins, component_name="", r_value=1.0, c_value=1e-9, l_value=1e-9, is_parallel=False
|
|
1390
|
+
): # pragma: no cover
|
|
1391
|
+
"""Create physical Rlc component.
|
|
1392
|
+
|
|
1393
|
+
Parameters
|
|
1394
|
+
----------
|
|
1395
|
+
pins : list
|
|
1396
|
+
List of EDB pins, length must be 2, since only 2 pins component are currently supported.
|
|
1397
|
+
It can be an `dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance` object or
|
|
1398
|
+
an Edb Padstack Instance object.
|
|
1399
|
+
component_name : str
|
|
1400
|
+
Component definition name.
|
|
1401
|
+
r_value : float
|
|
1402
|
+
Resistor value.
|
|
1403
|
+
c_value : float
|
|
1404
|
+
Capacitance value.
|
|
1405
|
+
l_value : float
|
|
1406
|
+
Inductor value.
|
|
1407
|
+
is_parallel : bool
|
|
1408
|
+
Using parallel model when ``True``, series when ``False``.
|
|
1409
|
+
|
|
1410
|
+
Returns
|
|
1411
|
+
-------
|
|
1412
|
+
Component
|
|
1413
|
+
Created EDB component.
|
|
1414
|
+
|
|
1415
|
+
"""
|
|
1416
|
+
warnings.warn("`create_rlc_component` is deprecated. Use `create` method instead.", DeprecationWarning)
|
|
1417
|
+
return self.create(
|
|
1418
|
+
pins=pins,
|
|
1419
|
+
component_name=component_name,
|
|
1420
|
+
is_rlc=True,
|
|
1421
|
+
r_value=r_value,
|
|
1422
|
+
l_value=l_value,
|
|
1423
|
+
c_value=c_value,
|
|
1424
|
+
is_parallel=is_parallel,
|
|
1425
|
+
)
|
|
1426
|
+
|
|
1427
|
+
@pyedb_function_handler()
|
|
1428
|
+
def create(
|
|
1429
|
+
self,
|
|
1430
|
+
pins,
|
|
1431
|
+
component_name,
|
|
1432
|
+
placement_layer=None,
|
|
1433
|
+
component_part_name=None,
|
|
1434
|
+
is_rlc=False,
|
|
1435
|
+
r_value=0,
|
|
1436
|
+
c_value=0,
|
|
1437
|
+
l_value=0,
|
|
1438
|
+
is_parallel=False,
|
|
1439
|
+
):
|
|
1440
|
+
"""Create a component from pins.
|
|
1441
|
+
|
|
1442
|
+
Parameters
|
|
1443
|
+
----------
|
|
1444
|
+
pins : list
|
|
1445
|
+
List of EDB core pins.
|
|
1446
|
+
component_name : str
|
|
1447
|
+
Name of the reference designator for the component.
|
|
1448
|
+
placement_layer : str, optional
|
|
1449
|
+
Name of the layer used for placing the component.
|
|
1450
|
+
component_part_name : str, optional
|
|
1451
|
+
Part name of the component.
|
|
1452
|
+
is_rlc : bool, optional
|
|
1453
|
+
Whether if the new component will be an RLC or not.
|
|
1454
|
+
r_value : float
|
|
1455
|
+
Resistor value.
|
|
1456
|
+
c_value : float
|
|
1457
|
+
Capacitance value.
|
|
1458
|
+
l_value : float
|
|
1459
|
+
Inductor value.
|
|
1460
|
+
is_parallel : bool
|
|
1461
|
+
Using parallel model when ``True``, series when ``False``.
|
|
1462
|
+
|
|
1463
|
+
Returns
|
|
1464
|
+
-------
|
|
1465
|
+
bool
|
|
1466
|
+
``True`` when successful, ``False`` when failed.
|
|
1467
|
+
|
|
1468
|
+
Examples
|
|
1469
|
+
--------
|
|
1470
|
+
|
|
1471
|
+
>>> from pyedb import Edb
|
|
1472
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1473
|
+
>>> pins = edbapp.components.get_pin_from_component("A1")
|
|
1474
|
+
>>> edbapp.components.create(pins, "A1New")
|
|
1475
|
+
|
|
1476
|
+
"""
|
|
1477
|
+
if component_part_name:
|
|
1478
|
+
compdef = self._getComponentDefinition(component_part_name, pins)
|
|
1479
|
+
else:
|
|
1480
|
+
compdef = self._getComponentDefinition(component_name, pins)
|
|
1481
|
+
if not compdef:
|
|
1482
|
+
return False
|
|
1483
|
+
new_cmp = self._pedb.edb_api.cell.hierarchy.component.Create(
|
|
1484
|
+
self._active_layout, component_name, compdef.GetName()
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
if isinstance(pins[0], EDBPadstackInstance):
|
|
1488
|
+
pins = [i._edb_padstackinstance for i in pins]
|
|
1489
|
+
hosting_component_location = pins[0].GetComponent().GetTransform()
|
|
1490
|
+
for pin in pins:
|
|
1491
|
+
pin.SetIsLayoutPin(True)
|
|
1492
|
+
new_cmp.AddMember(pin)
|
|
1493
|
+
new_cmp.SetComponentType(self._edb.definition.ComponentType.Other)
|
|
1494
|
+
if not placement_layer:
|
|
1495
|
+
new_cmp_layer_name = pins[0].GetPadstackDef().GetData().GetLayerNames()[0]
|
|
1496
|
+
else:
|
|
1497
|
+
new_cmp_layer_name = placement_layer
|
|
1498
|
+
new_cmp_placement_layer = self._edb.cell.layer.FindByName(self._layout.layer_collection, new_cmp_layer_name)
|
|
1499
|
+
new_cmp.SetPlacementLayer(new_cmp_placement_layer)
|
|
1500
|
+
|
|
1501
|
+
if is_rlc and len(pins) == 2:
|
|
1502
|
+
rlc = self._edb.utility.utility.Rlc()
|
|
1503
|
+
rlc.IsParallel = is_parallel
|
|
1504
|
+
if r_value:
|
|
1505
|
+
rlc.REnabled = True
|
|
1506
|
+
rlc.R = self._get_edb_value(r_value)
|
|
1507
|
+
else:
|
|
1508
|
+
rlc.REnabled = False
|
|
1509
|
+
if l_value:
|
|
1510
|
+
rlc.LEnabled = True
|
|
1511
|
+
rlc.L = self._get_edb_value(l_value)
|
|
1512
|
+
else:
|
|
1513
|
+
rlc.LEnabled = False
|
|
1514
|
+
if c_value:
|
|
1515
|
+
rlc.CEnabled = True
|
|
1516
|
+
rlc.C = self._get_edb_value(c_value)
|
|
1517
|
+
else:
|
|
1518
|
+
rlc.CEnabled = False
|
|
1519
|
+
if rlc.REnabled and not rlc.CEnabled and not rlc.CEnabled:
|
|
1520
|
+
new_cmp.SetComponentType(self._edb.definition.ComponentType.Resistor)
|
|
1521
|
+
elif rlc.CEnabled and not rlc.REnabled and not rlc.LEnabled:
|
|
1522
|
+
new_cmp.SetComponentType(self._edb.definition.ComponentType.Capacitor)
|
|
1523
|
+
elif rlc.LEnabled and not rlc.REnabled and not rlc.CEnabled:
|
|
1524
|
+
new_cmp.SetComponentType(self._edb.definition.ComponentType.Inductor)
|
|
1525
|
+
else:
|
|
1526
|
+
new_cmp.SetComponentType(self._edb.definition.ComponentType.Resistor)
|
|
1527
|
+
|
|
1528
|
+
pin_pair = self._edb.utility.utility.PinPair(pins[0].GetName(), pins[1].GetName())
|
|
1529
|
+
rlc_model = self._edb.cell.hierarchy._hierarchy.PinPairModel()
|
|
1530
|
+
rlc_model.SetPinPairRlc(pin_pair, rlc)
|
|
1531
|
+
edb_rlc_component_property = self._edb.cell.hierarchy._hierarchy.RLCComponentProperty()
|
|
1532
|
+
if not edb_rlc_component_property.SetModel(rlc_model) or not new_cmp.SetComponentProperty(
|
|
1533
|
+
edb_rlc_component_property
|
|
1534
|
+
):
|
|
1535
|
+
return False # pragma no cover
|
|
1536
|
+
new_cmp.SetTransform(hosting_component_location)
|
|
1537
|
+
new_edb_comp = EDBComponent(self._pedb, new_cmp)
|
|
1538
|
+
self._cmp[new_cmp.GetName()] = new_edb_comp
|
|
1539
|
+
return new_edb_comp
|
|
1540
|
+
|
|
1541
|
+
@pyedb_function_handler()
|
|
1542
|
+
def create_component_from_pins(
|
|
1543
|
+
self, pins, component_name, placement_layer=None, component_part_name=None
|
|
1544
|
+
): # pragma: no cover
|
|
1545
|
+
"""Create a component from pins.
|
|
1546
|
+
|
|
1547
|
+
.. deprecated:: 0.6.62
|
|
1548
|
+
Use :func:`create` method instead.
|
|
1549
|
+
|
|
1550
|
+
Parameters
|
|
1551
|
+
----------
|
|
1552
|
+
pins : list
|
|
1553
|
+
List of EDB core pins.
|
|
1554
|
+
component_name : str
|
|
1555
|
+
Name of the reference designator for the component.
|
|
1556
|
+
placement_layer : str, optional
|
|
1557
|
+
Name of the layer used for placing the component.
|
|
1558
|
+
component_part_name : str, optional
|
|
1559
|
+
Part name of the component. It's created a new definition if doesn't exists.
|
|
1560
|
+
|
|
1561
|
+
Returns
|
|
1562
|
+
-------
|
|
1563
|
+
bool
|
|
1564
|
+
``True`` when successful, ``False`` when failed.
|
|
1565
|
+
|
|
1566
|
+
Examples
|
|
1567
|
+
--------
|
|
1568
|
+
|
|
1569
|
+
>>> from pyedb import Edb
|
|
1570
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1571
|
+
>>> pins = edbapp.components.get_pin_from_component("A1")
|
|
1572
|
+
>>> edbapp.components.create(pins, "A1New")
|
|
1573
|
+
|
|
1574
|
+
"""
|
|
1575
|
+
warnings.warn("`create_component_from_pins` is deprecated. Use `create` method instead.", DeprecationWarning)
|
|
1576
|
+
return self.create(
|
|
1577
|
+
pins=pins,
|
|
1578
|
+
component_name=component_name,
|
|
1579
|
+
placement_layer=placement_layer,
|
|
1580
|
+
component_part_name=component_part_name,
|
|
1581
|
+
is_rlc=False,
|
|
1582
|
+
)
|
|
1583
|
+
|
|
1584
|
+
@pyedb_function_handler()
|
|
1585
|
+
def set_component_model(self, componentname, model_type="Spice", modelpath=None, modelname=None):
|
|
1586
|
+
"""Assign a Spice or Touchstone model to a component.
|
|
1587
|
+
|
|
1588
|
+
Parameters
|
|
1589
|
+
----------
|
|
1590
|
+
componentname : str
|
|
1591
|
+
Name of the component.
|
|
1592
|
+
model_type : str, optional
|
|
1593
|
+
Type of the model. Options are ``"Spice"`` and
|
|
1594
|
+
``"Touchstone"``. The default is ``"Spice"``.
|
|
1595
|
+
modelpath : str, optional
|
|
1596
|
+
Full path to the model file. The default is ``None``.
|
|
1597
|
+
modelname : str, optional
|
|
1598
|
+
Name of the model. The default is ``None``.
|
|
1599
|
+
|
|
1600
|
+
Returns
|
|
1601
|
+
-------
|
|
1602
|
+
bool
|
|
1603
|
+
``True`` when successful, ``False`` when failed.
|
|
1604
|
+
|
|
1605
|
+
Examples
|
|
1606
|
+
--------
|
|
1607
|
+
|
|
1608
|
+
>>> from pyedb import Edb
|
|
1609
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1610
|
+
>>> edbapp.components.set_component_model("A1", model_type="Spice",
|
|
1611
|
+
... modelpath="pathtospfile",
|
|
1612
|
+
... modelname="spicemodelname")
|
|
1613
|
+
|
|
1614
|
+
"""
|
|
1615
|
+
if not modelname:
|
|
1616
|
+
modelname = get_filename_without_extension(modelpath)
|
|
1617
|
+
edbComponent = self.get_component_by_name(componentname)
|
|
1618
|
+
if str(edbComponent.EDBHandle) == "0":
|
|
1619
|
+
return False
|
|
1620
|
+
edbRlcComponentProperty = edbComponent.GetComponentProperty().Clone()
|
|
1621
|
+
|
|
1622
|
+
componentPins = self.get_pin_from_component(componentname)
|
|
1623
|
+
componentNets = self.get_nets_from_pin_list(componentPins)
|
|
1624
|
+
pinNumber = len(componentPins)
|
|
1625
|
+
if model_type == "Spice":
|
|
1626
|
+
with open(modelpath, "r") as f:
|
|
1627
|
+
for line in f:
|
|
1628
|
+
if "subckt" in line.lower():
|
|
1629
|
+
pinNames = [i.strip() for i in re.split(" |\t", line) if i]
|
|
1630
|
+
pinNames.remove(pinNames[0])
|
|
1631
|
+
pinNames.remove(pinNames[0])
|
|
1632
|
+
break
|
|
1633
|
+
if len(pinNames) == pinNumber:
|
|
1634
|
+
spiceMod = self._edb.cell.hierarchy._hierarchy.SPICEModel()
|
|
1635
|
+
spiceMod.SetModelPath(modelpath)
|
|
1636
|
+
spiceMod.SetModelName(modelname)
|
|
1637
|
+
terminal = 1
|
|
1638
|
+
for pn in pinNames:
|
|
1639
|
+
spiceMod.AddTerminalPinPair(pn, str(terminal))
|
|
1640
|
+
terminal += 1
|
|
1641
|
+
|
|
1642
|
+
edbRlcComponentProperty.SetModel(spiceMod)
|
|
1643
|
+
if not edbComponent.SetComponentProperty(edbRlcComponentProperty):
|
|
1644
|
+
self._logger.error("Error assigning the `Spice` model.")
|
|
1645
|
+
return False
|
|
1646
|
+
else:
|
|
1647
|
+
self._logger.error("Wrong number of Pins")
|
|
1648
|
+
return False
|
|
1649
|
+
|
|
1650
|
+
elif model_type == "Touchstone": # pragma: no cover
|
|
1651
|
+
nPortModelName = modelname
|
|
1652
|
+
edbComponentDef = edbComponent.GetComponentDef()
|
|
1653
|
+
nPortModel = self._edb.definition.NPortComponentModel.FindByName(edbComponentDef, nPortModelName)
|
|
1654
|
+
if nPortModel.IsNull():
|
|
1655
|
+
nPortModel = self._edb.definition.NPortComponentModel.Create(nPortModelName)
|
|
1656
|
+
nPortModel.SetReferenceFile(modelpath)
|
|
1657
|
+
edbComponentDef.AddComponentModel(nPortModel)
|
|
1658
|
+
|
|
1659
|
+
sParameterMod = self._edb.cell.hierarchy._hierarchy.SParameterModel()
|
|
1660
|
+
sParameterMod.SetComponentModelName(nPortModelName)
|
|
1661
|
+
gndnets = filter(lambda x: "gnd" in x.lower(), componentNets)
|
|
1662
|
+
if len(list(gndnets)) > 0: # pragma: no cover
|
|
1663
|
+
net = gndnets[0]
|
|
1664
|
+
else: # pragma: no cover
|
|
1665
|
+
net = componentNets[len(componentNets) - 1]
|
|
1666
|
+
sParameterMod.SetReferenceNet(net)
|
|
1667
|
+
edbRlcComponentProperty.SetModel(sParameterMod)
|
|
1668
|
+
if not edbComponent.SetComponentProperty(edbRlcComponentProperty):
|
|
1669
|
+
self._logger.error("Error assigning the `Touchstone` model")
|
|
1670
|
+
return False
|
|
1671
|
+
return True
|
|
1672
|
+
|
|
1673
|
+
@pyedb_function_handler()
|
|
1674
|
+
def create_pingroup_from_pins(self, pins, group_name=None):
|
|
1675
|
+
"""Create a pin group on a component.
|
|
1676
|
+
|
|
1677
|
+
Parameters
|
|
1678
|
+
----------
|
|
1679
|
+
pins : list
|
|
1680
|
+
List of EDB pins.
|
|
1681
|
+
group_name : str, optional
|
|
1682
|
+
Name for the group. The default is ``None``, in which case
|
|
1683
|
+
a default name is assigned as follows: ``[component Name] [NetName]``.
|
|
1684
|
+
|
|
1685
|
+
Returns
|
|
1686
|
+
-------
|
|
1687
|
+
tuple
|
|
1688
|
+
The tuple is structured as: (bool, pingroup).
|
|
1689
|
+
|
|
1690
|
+
Examples
|
|
1691
|
+
--------
|
|
1692
|
+
>>> from pyedb import Edb
|
|
1693
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1694
|
+
>>> edbapp.components.create_pingroup_from_pins(gndpinlist, "MyGNDPingroup")
|
|
1695
|
+
|
|
1696
|
+
"""
|
|
1697
|
+
if len(pins) < 1:
|
|
1698
|
+
self._logger.error("No pins specified for pin group %s", group_name)
|
|
1699
|
+
return (False, None)
|
|
1700
|
+
if len([pin for pin in pins if isinstance(pin, EDBPadstackInstance)]):
|
|
1701
|
+
_pins = [pin._edb_padstackinstance for pin in pins]
|
|
1702
|
+
if _pins:
|
|
1703
|
+
pins = _pins
|
|
1704
|
+
if group_name is None:
|
|
1705
|
+
group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(self._active_layout)
|
|
1706
|
+
for pin in pins:
|
|
1707
|
+
pin.SetIsLayoutPin(True)
|
|
1708
|
+
forbiden_car = "-><"
|
|
1709
|
+
group_name = group_name.translate({ord(i): "_" for i in forbiden_car})
|
|
1710
|
+
for pgroup in list(self._pedb.active_layout.PinGroups):
|
|
1711
|
+
if pgroup.GetName() == group_name:
|
|
1712
|
+
pin_group_exists = True
|
|
1713
|
+
if len(pgroup.GetPins()) == len(pins):
|
|
1714
|
+
pnames = [i.GetName() for i in pins]
|
|
1715
|
+
for p in pgroup.GetPins():
|
|
1716
|
+
if p.GetName() in pnames:
|
|
1717
|
+
continue
|
|
1718
|
+
else:
|
|
1719
|
+
group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(
|
|
1720
|
+
self._active_layout, group_name
|
|
1721
|
+
)
|
|
1722
|
+
pin_group_exists = False
|
|
1723
|
+
else:
|
|
1724
|
+
group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(self._active_layout, group_name)
|
|
1725
|
+
pin_group_exists = False
|
|
1726
|
+
if pin_group_exists:
|
|
1727
|
+
return pgroup
|
|
1728
|
+
pingroup = _retry_ntimes(
|
|
1729
|
+
10,
|
|
1730
|
+
self._edb.cell.hierarchy.pin_group.Create,
|
|
1731
|
+
self._active_layout,
|
|
1732
|
+
group_name,
|
|
1733
|
+
convert_py_list_to_net_list(pins),
|
|
1734
|
+
)
|
|
1735
|
+
if pingroup.IsNull():
|
|
1736
|
+
return False
|
|
1737
|
+
else:
|
|
1738
|
+
pingroup.SetNet(pins[0].GetNet())
|
|
1739
|
+
return pingroup
|
|
1740
|
+
|
|
1741
|
+
@pyedb_function_handler()
|
|
1742
|
+
def delete_single_pin_rlc(self, deactivate_only=False):
|
|
1743
|
+
# type: (bool) -> list
|
|
1744
|
+
"""Delete all RLC components with a single pin.
|
|
1745
|
+
Single pin component model type will be reverted to ``"RLC"``.
|
|
1746
|
+
|
|
1747
|
+
Parameters
|
|
1748
|
+
----------
|
|
1749
|
+
deactivate_only : bool, optional
|
|
1750
|
+
Whether to only deactivate RLC components with a single point rather than
|
|
1751
|
+
delete them. The default is ``False``, in which case they are deleted.
|
|
1752
|
+
|
|
1753
|
+
Returns
|
|
1754
|
+
-------
|
|
1755
|
+
list
|
|
1756
|
+
List of deleted RLC components.
|
|
1757
|
+
|
|
1758
|
+
|
|
1759
|
+
Examples
|
|
1760
|
+
--------
|
|
1761
|
+
|
|
1762
|
+
>>> from pyedb import Edb
|
|
1763
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1764
|
+
>>> list_of_deleted_rlcs = edbapp.components.delete_single_pin_rlc()
|
|
1765
|
+
>>> print(list_of_deleted_rlcs)
|
|
1766
|
+
|
|
1767
|
+
"""
|
|
1768
|
+
deleted_comps = []
|
|
1769
|
+
for comp, val in self.instances.items():
|
|
1770
|
+
if val.numpins < 2 and val.type in ["Resistor", "Capacitor", "Inductor"]:
|
|
1771
|
+
if deactivate_only:
|
|
1772
|
+
val.is_enabled = False
|
|
1773
|
+
val.model_type = "RLC"
|
|
1774
|
+
else:
|
|
1775
|
+
val.edbcomponent.Delete()
|
|
1776
|
+
deleted_comps.append(comp)
|
|
1777
|
+
if not deactivate_only:
|
|
1778
|
+
self.refresh_components()
|
|
1779
|
+
self._pedb._logger.info("Deleted {} components".format(len(deleted_comps)))
|
|
1780
|
+
|
|
1781
|
+
return deleted_comps
|
|
1782
|
+
|
|
1783
|
+
@pyedb_function_handler()
|
|
1784
|
+
def delete_component(self, component_name): # pragma: no cover
|
|
1785
|
+
"""Delete a component.
|
|
1786
|
+
|
|
1787
|
+
.. deprecated:: 0.6.62
|
|
1788
|
+
Use :func:`delete` method instead.
|
|
1789
|
+
|
|
1790
|
+
Parameters
|
|
1791
|
+
----------
|
|
1792
|
+
component_name : str
|
|
1793
|
+
Name of the component.
|
|
1794
|
+
|
|
1795
|
+
Returns
|
|
1796
|
+
-------
|
|
1797
|
+
bool
|
|
1798
|
+
``True`` when successful, ``False`` when failed.
|
|
1799
|
+
|
|
1800
|
+
Examples
|
|
1801
|
+
--------
|
|
1802
|
+
|
|
1803
|
+
>>> from pyedb import Edb
|
|
1804
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1805
|
+
>>> edbapp.components.delete("A1")
|
|
1806
|
+
|
|
1807
|
+
"""
|
|
1808
|
+
warnings.warn("`delete_component` is deprecated. Use `delete` property instead.", DeprecationWarning)
|
|
1809
|
+
return self.delete(component_name=component_name)
|
|
1810
|
+
|
|
1811
|
+
@pyedb_function_handler()
|
|
1812
|
+
def delete(self, component_name):
|
|
1813
|
+
"""Delete a component.
|
|
1814
|
+
|
|
1815
|
+
Parameters
|
|
1816
|
+
----------
|
|
1817
|
+
component_name : str
|
|
1818
|
+
Name of the component.
|
|
1819
|
+
|
|
1820
|
+
Returns
|
|
1821
|
+
-------
|
|
1822
|
+
bool
|
|
1823
|
+
``True`` when successful, ``False`` when failed.
|
|
1824
|
+
|
|
1825
|
+
Examples
|
|
1826
|
+
--------
|
|
1827
|
+
|
|
1828
|
+
>>> from pyedb import Edb
|
|
1829
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1830
|
+
>>> edbapp.components.delete("A1")
|
|
1831
|
+
|
|
1832
|
+
"""
|
|
1833
|
+
edb_cmp = self.get_component_by_name(component_name)
|
|
1834
|
+
if edb_cmp is not None:
|
|
1835
|
+
edb_cmp.Delete()
|
|
1836
|
+
if edb_cmp in list(self.instances.keys()):
|
|
1837
|
+
del self.components[edb_cmp]
|
|
1838
|
+
return True
|
|
1839
|
+
return False
|
|
1840
|
+
|
|
1841
|
+
@pyedb_function_handler()
|
|
1842
|
+
def disable_rlc_component(self, component_name):
|
|
1843
|
+
"""Disable a RLC component.
|
|
1844
|
+
|
|
1845
|
+
Parameters
|
|
1846
|
+
----------
|
|
1847
|
+
component_name : str
|
|
1848
|
+
Name of the RLC component.
|
|
1849
|
+
|
|
1850
|
+
Returns
|
|
1851
|
+
-------
|
|
1852
|
+
bool
|
|
1853
|
+
``True`` when successful, ``False`` when failed.
|
|
1854
|
+
|
|
1855
|
+
Examples
|
|
1856
|
+
--------
|
|
1857
|
+
|
|
1858
|
+
>>> from pyedb import Edb
|
|
1859
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1860
|
+
>>> edbapp.components.disable_rlc_component("A1")
|
|
1861
|
+
|
|
1862
|
+
"""
|
|
1863
|
+
edb_cmp = self.get_component_by_name(component_name)
|
|
1864
|
+
if edb_cmp is not None:
|
|
1865
|
+
rlc_property = edb_cmp.GetComponentProperty().Clone()
|
|
1866
|
+
pin_pair_model = rlc_property.GetModel().Clone()
|
|
1867
|
+
pprlc = pin_pair_model.GetPinPairRlc(list(pin_pair_model.PinPairs)[0])
|
|
1868
|
+
pprlc.CEnabled = False
|
|
1869
|
+
pprlc.LEnabled = False
|
|
1870
|
+
pprlc.REnabled = False
|
|
1871
|
+
pin_pair_model.SetPinPairRlc(list(pin_pair_model.PinPairs)[0], pprlc)
|
|
1872
|
+
rlc_property.SetModel(pin_pair_model)
|
|
1873
|
+
edb_cmp.SetComponentProperty(rlc_property)
|
|
1874
|
+
return True
|
|
1875
|
+
return False
|
|
1876
|
+
|
|
1877
|
+
@pyedb_function_handler()
|
|
1878
|
+
def set_solder_ball(
|
|
1879
|
+
self,
|
|
1880
|
+
component="",
|
|
1881
|
+
sball_diam=None,
|
|
1882
|
+
sball_height=None,
|
|
1883
|
+
shape="Cylinder",
|
|
1884
|
+
sball_mid_diam=None,
|
|
1885
|
+
chip_orientation="chip_down",
|
|
1886
|
+
auto_reference_size=True,
|
|
1887
|
+
reference_size_x=0,
|
|
1888
|
+
reference_size_y=0,
|
|
1889
|
+
reference_height=0,
|
|
1890
|
+
):
|
|
1891
|
+
"""Set cylindrical solder balls on a given component.
|
|
1892
|
+
|
|
1893
|
+
Parameters
|
|
1894
|
+
----------
|
|
1895
|
+
component : str or EDB component, optional
|
|
1896
|
+
Name of the discrete component.
|
|
1897
|
+
sball_diam : str, float, optional
|
|
1898
|
+
Diameter of the solder ball.
|
|
1899
|
+
sball_height : str, float, optional
|
|
1900
|
+
Height of the solder ball.
|
|
1901
|
+
shape : str, optional
|
|
1902
|
+
Shape of solder ball. Options are ``"Cylinder"``,
|
|
1903
|
+
``"Spheroid"``. The default is ``"Cylinder"``.
|
|
1904
|
+
sball_mid_diam : str, float, optional
|
|
1905
|
+
Mid diameter of the solder ball.
|
|
1906
|
+
chip_orientation : str, optional
|
|
1907
|
+
Give the chip orientation, ``"chip_down"`` or ``"chip_up"``. Default is ``"chip_down"``. Only applicable on
|
|
1908
|
+
IC model.
|
|
1909
|
+
auto_reference_size : bool, optional
|
|
1910
|
+
Whether to automatically set reference size.
|
|
1911
|
+
reference_size_x : int, str, float, optional
|
|
1912
|
+
X size of the reference. Applicable when auto_reference_size is False.
|
|
1913
|
+
reference_size_y : int, str, float, optional
|
|
1914
|
+
Y size of the reference. Applicable when auto_reference_size is False.
|
|
1915
|
+
reference_height : int, str, float, optional
|
|
1916
|
+
Height of the reference. Applicable when auto_reference_size is False.
|
|
1917
|
+
|
|
1918
|
+
Returns
|
|
1919
|
+
-------
|
|
1920
|
+
bool
|
|
1921
|
+
``True`` when successful, ``False`` when failed.
|
|
1922
|
+
|
|
1923
|
+
Examples
|
|
1924
|
+
--------
|
|
1925
|
+
|
|
1926
|
+
>>> from pyedb import Edb
|
|
1927
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
1928
|
+
>>> edbapp.components.set_solder_ball("A1")
|
|
1929
|
+
|
|
1930
|
+
"""
|
|
1931
|
+
if not isinstance(component, self._pedb.edb_api.cell.hierarchy.component):
|
|
1932
|
+
edb_cmp = self.get_component_by_name(component)
|
|
1933
|
+
cmp = self.instances[component]
|
|
1934
|
+
else: # pragma: no cover
|
|
1935
|
+
edb_cmp = component
|
|
1936
|
+
cmp = self.instances[edb_cmp.GetName()]
|
|
1937
|
+
|
|
1938
|
+
cmp_type = edb_cmp.GetComponentType()
|
|
1939
|
+
if not sball_diam:
|
|
1940
|
+
pin1 = list(cmp.pins.values())[0].pin
|
|
1941
|
+
pin_layers = pin1.GetPadstackDef().GetData().GetLayerNames()
|
|
1942
|
+
pad_params = self._padstack.get_pad_parameters(pin=pin1, layername=pin_layers[0], pad_type=0)
|
|
1943
|
+
_sb_diam = min([self._get_edb_value(val).ToDouble() for val in pad_params[1]])
|
|
1944
|
+
sball_diam = _sb_diam
|
|
1945
|
+
if sball_height:
|
|
1946
|
+
sball_height = round(self._edb.utility.Value(sball_height).ToDouble(), 9)
|
|
1947
|
+
else:
|
|
1948
|
+
sball_height = round(self._edb.utility.Value(sball_diam).ToDouble(), 9) / 2
|
|
1949
|
+
|
|
1950
|
+
if not sball_mid_diam:
|
|
1951
|
+
sball_mid_diam = sball_diam
|
|
1952
|
+
|
|
1953
|
+
if shape == "Cylinder":
|
|
1954
|
+
sball_shape = self._edb.definition.SolderballShape.Cylinder
|
|
1955
|
+
else:
|
|
1956
|
+
sball_shape = self._edb.definition.SolderballShape.Spheroid
|
|
1957
|
+
|
|
1958
|
+
cmp_property = edb_cmp.GetComponentProperty().Clone()
|
|
1959
|
+
if cmp_type == self._edb.definition.ComponentType.IC:
|
|
1960
|
+
ic_die_prop = cmp_property.GetDieProperty().Clone()
|
|
1961
|
+
ic_die_prop.SetType(self._edb.definition.DieType.FlipChip)
|
|
1962
|
+
if chip_orientation.lower() == "chip_up":
|
|
1963
|
+
ic_die_prop.SetOrientation(self._edb.definition.DieOrientation.ChipUp)
|
|
1964
|
+
else:
|
|
1965
|
+
ic_die_prop.SetOrientation(self._edb.definition.DieOrientation.ChipDown)
|
|
1966
|
+
cmp_property.SetDieProperty(ic_die_prop)
|
|
1967
|
+
|
|
1968
|
+
solder_ball_prop = cmp_property.GetSolderBallProperty().Clone()
|
|
1969
|
+
solder_ball_prop.SetDiameter(self._get_edb_value(sball_diam), self._get_edb_value(sball_mid_diam))
|
|
1970
|
+
solder_ball_prop.SetHeight(self._get_edb_value(sball_height))
|
|
1971
|
+
|
|
1972
|
+
solder_ball_prop.SetShape(sball_shape)
|
|
1973
|
+
cmp_property.SetSolderBallProperty(solder_ball_prop)
|
|
1974
|
+
|
|
1975
|
+
port_prop = cmp_property.GetPortProperty().Clone()
|
|
1976
|
+
port_prop.SetReferenceHeight(self._pedb.edb_value(reference_height))
|
|
1977
|
+
port_prop.SetReferenceSizeAuto(auto_reference_size)
|
|
1978
|
+
if not auto_reference_size:
|
|
1979
|
+
port_prop.SetReferenceSize(self._pedb.edb_value(reference_size_x), self._pedb.edb_value(reference_size_y))
|
|
1980
|
+
cmp_property.SetPortProperty(port_prop)
|
|
1981
|
+
edb_cmp.SetComponentProperty(cmp_property)
|
|
1982
|
+
return True
|
|
1983
|
+
|
|
1984
|
+
@pyedb_function_handler()
|
|
1985
|
+
def set_component_rlc(
|
|
1986
|
+
self,
|
|
1987
|
+
componentname,
|
|
1988
|
+
res_value=None,
|
|
1989
|
+
ind_value=None,
|
|
1990
|
+
cap_value=None,
|
|
1991
|
+
isparallel=False,
|
|
1992
|
+
):
|
|
1993
|
+
"""Update values for an RLC component.
|
|
1994
|
+
|
|
1995
|
+
Parameters
|
|
1996
|
+
----------
|
|
1997
|
+
componentname :
|
|
1998
|
+
Name of the RLC component.
|
|
1999
|
+
res_value : float, optional
|
|
2000
|
+
Resistance value. The default is ``None``.
|
|
2001
|
+
ind_value : float, optional
|
|
2002
|
+
Inductor value. The default is ``None``.
|
|
2003
|
+
cap_value : float optional
|
|
2004
|
+
Capacitor value. The default is ``None``.
|
|
2005
|
+
isparallel : bool, optional
|
|
2006
|
+
Whether the RLC component is parallel. The default is ``False``.
|
|
2007
|
+
|
|
2008
|
+
Returns
|
|
2009
|
+
-------
|
|
2010
|
+
bool
|
|
2011
|
+
``True`` when successful, ``False`` when failed.
|
|
2012
|
+
|
|
2013
|
+
Examples
|
|
2014
|
+
--------
|
|
2015
|
+
|
|
2016
|
+
>>> from pyedb import Edb
|
|
2017
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
2018
|
+
>>> edbapp.components.set_component_rlc(
|
|
2019
|
+
... "R1", res_value=50, ind_value=1e-9, cap_value=1e-12, isparallel=False
|
|
2020
|
+
... )
|
|
2021
|
+
|
|
2022
|
+
"""
|
|
2023
|
+
if res_value is None and ind_value is None and cap_value is None:
|
|
2024
|
+
self.instances[componentname].is_enabled = False
|
|
2025
|
+
self._logger.info("No parameters passed, component %s is disabled.", componentname)
|
|
2026
|
+
return True
|
|
2027
|
+
edb_component = self.get_component_by_name(componentname)
|
|
2028
|
+
edb_rlc_component_property = self._edb.cell.hierarchy._hierarchy.RLCComponentProperty()
|
|
2029
|
+
component_pins = self.get_pin_from_component(componentname)
|
|
2030
|
+
pin_number = len(component_pins)
|
|
2031
|
+
if pin_number == 2:
|
|
2032
|
+
from_pin = component_pins[0]
|
|
2033
|
+
to_pin = component_pins[1]
|
|
2034
|
+
rlc = self._edb.utility.utility.Rlc()
|
|
2035
|
+
rlc.IsParallel = isparallel
|
|
2036
|
+
if res_value is not None:
|
|
2037
|
+
rlc.REnabled = True
|
|
2038
|
+
rlc.R = self._get_edb_value(res_value)
|
|
2039
|
+
else:
|
|
2040
|
+
rlc.REnabled = False
|
|
2041
|
+
if ind_value is not None:
|
|
2042
|
+
rlc.LEnabled = True
|
|
2043
|
+
rlc.L = self._get_edb_value(ind_value)
|
|
2044
|
+
else:
|
|
2045
|
+
rlc.LEnabled = False
|
|
2046
|
+
if cap_value is not None:
|
|
2047
|
+
rlc.CEnabled = True
|
|
2048
|
+
rlc.C = self._get_edb_value(cap_value)
|
|
2049
|
+
else:
|
|
2050
|
+
rlc.CEnabled = False
|
|
2051
|
+
pin_pair = self._edb.utility.utility.PinPair(from_pin.GetName(), to_pin.GetName())
|
|
2052
|
+
rlc_model = self._edb.cell.hierarchy._hierarchy.PinPairModel()
|
|
2053
|
+
rlc_model.SetPinPairRlc(pin_pair, rlc)
|
|
2054
|
+
if not edb_rlc_component_property.SetModel(rlc_model) or not edb_component.SetComponentProperty(
|
|
2055
|
+
edb_rlc_component_property
|
|
2056
|
+
):
|
|
2057
|
+
self._logger.error("Failed to set RLC model on component")
|
|
2058
|
+
return False
|
|
2059
|
+
else:
|
|
2060
|
+
self._logger.warning(
|
|
2061
|
+
"Component %s has not been assigned because either it is not present in the layout "
|
|
2062
|
+
"or it contains a number of pins not equal to 2",
|
|
2063
|
+
componentname,
|
|
2064
|
+
)
|
|
2065
|
+
return False
|
|
2066
|
+
self._logger.info("RLC properties for Component %s has been assigned.", componentname)
|
|
2067
|
+
return True
|
|
2068
|
+
|
|
2069
|
+
@pyedb_function_handler()
|
|
2070
|
+
def update_rlc_from_bom(
|
|
2071
|
+
self,
|
|
2072
|
+
bom_file,
|
|
2073
|
+
delimiter=";",
|
|
2074
|
+
valuefield="Func des",
|
|
2075
|
+
comptype="Prod name",
|
|
2076
|
+
refdes="Pos / Place",
|
|
2077
|
+
):
|
|
2078
|
+
"""Update the EDC core component values (RLCs) with values coming from a BOM file.
|
|
2079
|
+
|
|
2080
|
+
Parameters
|
|
2081
|
+
----------
|
|
2082
|
+
bom_file : str
|
|
2083
|
+
Full path to the BOM file, which is a delimited text file.
|
|
2084
|
+
Header values needed inside the BOM reader must
|
|
2085
|
+
be explicitly set if different from the defaults.
|
|
2086
|
+
delimiter : str, optional
|
|
2087
|
+
Value to use for the delimiter. The default is ``";"``.
|
|
2088
|
+
valuefield : str, optional
|
|
2089
|
+
Field header containing the value of the component. The default is ``"Func des"``.
|
|
2090
|
+
The value for this parameter must being with the value of the component
|
|
2091
|
+
followed by a space and then the rest of the value. For example, ``"22pF"``.
|
|
2092
|
+
comptype : str, optional
|
|
2093
|
+
Field header containing the type of component. The default is ``"Prod name"``. For
|
|
2094
|
+
example, you might enter ``"Inductor"``.
|
|
2095
|
+
refdes : str, optional
|
|
2096
|
+
Field header containing the reference designator of the component. The default is
|
|
2097
|
+
``"Pos / Place"``. For example, you might enter ``"C100"``.
|
|
2098
|
+
|
|
2099
|
+
Returns
|
|
2100
|
+
-------
|
|
2101
|
+
bool
|
|
2102
|
+
``True`` if the file contains the header and it is correctly parsed. ``True`` is
|
|
2103
|
+
returned even if no values are assigned.
|
|
2104
|
+
|
|
2105
|
+
"""
|
|
2106
|
+
with open(bom_file, "r") as f:
|
|
2107
|
+
Lines = f.readlines()
|
|
2108
|
+
found = False
|
|
2109
|
+
refdescolumn = None
|
|
2110
|
+
comptypecolumn = None
|
|
2111
|
+
valuecolumn = None
|
|
2112
|
+
unmount_comp_list = list(self.instances.keys())
|
|
2113
|
+
for line in Lines:
|
|
2114
|
+
content_line = [i.strip() for i in line.split(delimiter)]
|
|
2115
|
+
if valuefield in content_line:
|
|
2116
|
+
valuecolumn = content_line.index(valuefield)
|
|
2117
|
+
if comptype in content_line:
|
|
2118
|
+
comptypecolumn = content_line.index(comptype)
|
|
2119
|
+
if refdes in content_line:
|
|
2120
|
+
refdescolumn = content_line.index(refdes)
|
|
2121
|
+
elif refdescolumn:
|
|
2122
|
+
found = True
|
|
2123
|
+
new_refdes = content_line[refdescolumn].split(" ")[0]
|
|
2124
|
+
new_value = content_line[valuecolumn].split(" ")[0]
|
|
2125
|
+
new_type = content_line[comptypecolumn]
|
|
2126
|
+
if "resistor" in new_type.lower():
|
|
2127
|
+
self.set_component_rlc(new_refdes, res_value=new_value)
|
|
2128
|
+
unmount_comp_list.remove(new_refdes)
|
|
2129
|
+
elif "capacitor" in new_type.lower():
|
|
2130
|
+
self.set_component_rlc(new_refdes, cap_value=new_value)
|
|
2131
|
+
unmount_comp_list.remove(new_refdes)
|
|
2132
|
+
elif "inductor" in new_type.lower():
|
|
2133
|
+
self.set_component_rlc(new_refdes, ind_value=new_value)
|
|
2134
|
+
unmount_comp_list.remove(new_refdes)
|
|
2135
|
+
for comp in unmount_comp_list:
|
|
2136
|
+
self.components[comp].is_enabled = False
|
|
2137
|
+
return found
|
|
2138
|
+
|
|
2139
|
+
@pyedb_function_handler()
|
|
2140
|
+
def import_bom(
|
|
2141
|
+
self,
|
|
2142
|
+
bom_file,
|
|
2143
|
+
delimiter=",",
|
|
2144
|
+
refdes_col=0,
|
|
2145
|
+
part_name_col=1,
|
|
2146
|
+
comp_type_col=2,
|
|
2147
|
+
value_col=3,
|
|
2148
|
+
):
|
|
2149
|
+
"""Load external BOM file.
|
|
2150
|
+
|
|
2151
|
+
Parameters
|
|
2152
|
+
----------
|
|
2153
|
+
bom_file : str
|
|
2154
|
+
Full path to the BOM file, which is a delimited text file.
|
|
2155
|
+
delimiter : str, optional
|
|
2156
|
+
Value to use for the delimiter. The default is ``","``.
|
|
2157
|
+
refdes_col : int, optional
|
|
2158
|
+
Column index of reference designator. The default is ``"0"``.
|
|
2159
|
+
part_name_col : int, optional
|
|
2160
|
+
Column index of part name. The default is ``"1"``. Set to ``None`` if
|
|
2161
|
+
the column does not exist.
|
|
2162
|
+
comp_type_col : int, optional
|
|
2163
|
+
Column index of component type. The default is ``"2"``.
|
|
2164
|
+
value_col : int, optional
|
|
2165
|
+
Column index of value. The default is ``"3"``. Set to ``None``
|
|
2166
|
+
if the column does not exist.
|
|
2167
|
+
|
|
2168
|
+
Returns
|
|
2169
|
+
-------
|
|
2170
|
+
bool
|
|
2171
|
+
"""
|
|
2172
|
+
with open(bom_file, "r") as f:
|
|
2173
|
+
lines = f.readlines()
|
|
2174
|
+
unmount_comp_list = list(self.instances.keys())
|
|
2175
|
+
for l in lines[1:]:
|
|
2176
|
+
l = l.replace(" ", "").replace("\n", "")
|
|
2177
|
+
if not l:
|
|
2178
|
+
continue
|
|
2179
|
+
l = l.split(delimiter)
|
|
2180
|
+
|
|
2181
|
+
refdes = l[refdes_col]
|
|
2182
|
+
comp = self.components[refdes]
|
|
2183
|
+
if not part_name_col == None:
|
|
2184
|
+
part_name = l[part_name_col]
|
|
2185
|
+
if comp.partname == part_name:
|
|
2186
|
+
pass
|
|
2187
|
+
else:
|
|
2188
|
+
pinlist = self.get_pin_from_component(refdes)
|
|
2189
|
+
if not part_name in self.definitions:
|
|
2190
|
+
footprint_cell = self.definitions[comp.partname]._edb_object.GetFootprintCell()
|
|
2191
|
+
comp_def = self._edb.definition.ComponentDef.Create(self._db, part_name, footprint_cell)
|
|
2192
|
+
for pin in pinlist:
|
|
2193
|
+
self._edb.definition.ComponentDefPin.Create(comp_def, pin.GetName())
|
|
2194
|
+
|
|
2195
|
+
p_layer = comp.placement_layer
|
|
2196
|
+
refdes_temp = comp.refdes + "_temp"
|
|
2197
|
+
comp.refdes = refdes_temp
|
|
2198
|
+
|
|
2199
|
+
unmount_comp_list.remove(refdes)
|
|
2200
|
+
comp.edbcomponent.Ungroup(True)
|
|
2201
|
+
|
|
2202
|
+
self.create(pinlist, refdes, p_layer, part_name)
|
|
2203
|
+
self.refresh_components()
|
|
2204
|
+
comp = self.components[refdes]
|
|
2205
|
+
|
|
2206
|
+
comp_type = l[comp_type_col]
|
|
2207
|
+
if comp_type.capitalize() in ["Resistor", "Capacitor", "Inductor", "Other"]:
|
|
2208
|
+
comp.type = comp_type.capitalize()
|
|
2209
|
+
else:
|
|
2210
|
+
comp.type = comp_type.upper()
|
|
2211
|
+
|
|
2212
|
+
if comp_type.capitalize() in ["Resistor", "Capacitor", "Inductor"] and refdes in unmount_comp_list:
|
|
2213
|
+
unmount_comp_list.remove(refdes)
|
|
2214
|
+
if not value_col == None:
|
|
2215
|
+
try:
|
|
2216
|
+
value = l[value_col]
|
|
2217
|
+
except:
|
|
2218
|
+
value = None
|
|
2219
|
+
if value:
|
|
2220
|
+
if comp_type == "Resistor":
|
|
2221
|
+
self.set_component_rlc(refdes, res_value=value)
|
|
2222
|
+
elif comp_type == "Capacitor":
|
|
2223
|
+
self.set_component_rlc(refdes, cap_value=value)
|
|
2224
|
+
elif comp_type == "Inductor":
|
|
2225
|
+
self.set_component_rlc(refdes, ind_value=value)
|
|
2226
|
+
for comp in unmount_comp_list:
|
|
2227
|
+
self.components[comp].is_enabled = False
|
|
2228
|
+
return True
|
|
2229
|
+
|
|
2230
|
+
@pyedb_function_handler()
|
|
2231
|
+
def export_bom(self, bom_file, delimiter=","):
|
|
2232
|
+
"""Export Bom file from layout.
|
|
2233
|
+
|
|
2234
|
+
Parameters
|
|
2235
|
+
----------
|
|
2236
|
+
bom_file : str
|
|
2237
|
+
Full path to the BOM file, which is a delimited text file.
|
|
2238
|
+
delimiter : str, optional
|
|
2239
|
+
Value to use for the delimiter. The default is ``","``.
|
|
2240
|
+
"""
|
|
2241
|
+
with open(bom_file, "w") as f:
|
|
2242
|
+
f.writelines([delimiter.join(["RefDes", "Part name", "Type", "Value\n"])])
|
|
2243
|
+
for refdes, comp in self.instances.items():
|
|
2244
|
+
if not comp.is_enabled and comp.type in ["Resistor", "Capacitor", "Inductor"]:
|
|
2245
|
+
continue
|
|
2246
|
+
part_name = comp.partname
|
|
2247
|
+
comp_type = comp.type
|
|
2248
|
+
if comp_type == "Resistor":
|
|
2249
|
+
value = comp.res_value
|
|
2250
|
+
elif comp_type == "Capacitor":
|
|
2251
|
+
value = comp.cap_value
|
|
2252
|
+
elif comp_type == "Inductor":
|
|
2253
|
+
value = comp.ind_value
|
|
2254
|
+
else:
|
|
2255
|
+
value = ""
|
|
2256
|
+
if not value:
|
|
2257
|
+
value = ""
|
|
2258
|
+
f.writelines([delimiter.join([refdes, part_name, comp_type, value + "\n"])])
|
|
2259
|
+
return True
|
|
2260
|
+
|
|
2261
|
+
@pyedb_function_handler()
|
|
2262
|
+
def get_pin_from_component(self, component, netName=None, pinName=None):
|
|
2263
|
+
"""Retrieve the pins of a component.
|
|
2264
|
+
|
|
2265
|
+
Parameters
|
|
2266
|
+
----------
|
|
2267
|
+
component : str or EDB component
|
|
2268
|
+
Name of the component or the EDB component object.
|
|
2269
|
+
netName : str, optional
|
|
2270
|
+
Filter on the net name as an alternative to
|
|
2271
|
+
``pinName``. The default is ``None``.
|
|
2272
|
+
pinName : str, optional
|
|
2273
|
+
Filter on the pin name an an alternative to
|
|
2274
|
+
``netName``. The default is ``None``.
|
|
2275
|
+
|
|
2276
|
+
Returns
|
|
2277
|
+
-------
|
|
2278
|
+
list
|
|
2279
|
+
List of pins when the component is found or ``[]`` otherwise.
|
|
2280
|
+
|
|
2281
|
+
Examples
|
|
2282
|
+
--------
|
|
2283
|
+
|
|
2284
|
+
>>> from pyedb import Edb
|
|
2285
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2286
|
+
>>> edbapp.components.get_pin_from_component("R1", refdes)
|
|
2287
|
+
|
|
2288
|
+
"""
|
|
2289
|
+
if not isinstance(component, self._pedb.edb_api.cell.hierarchy.component):
|
|
2290
|
+
component = self._pedb.edb_api.cell.hierarchy.component.FindByName(self._active_layout, component)
|
|
2291
|
+
if netName:
|
|
2292
|
+
if not isinstance(netName, list):
|
|
2293
|
+
netName = [netName]
|
|
2294
|
+
pins = [
|
|
2295
|
+
p
|
|
2296
|
+
for p in list(component.LayoutObjs)
|
|
2297
|
+
if int(p.GetObjType()) == 1 and p.IsLayoutPin() and p.GetNet().GetName() in netName
|
|
2298
|
+
]
|
|
2299
|
+
elif pinName:
|
|
2300
|
+
if not isinstance(pinName, list):
|
|
2301
|
+
pinName = [pinName]
|
|
2302
|
+
pins = [
|
|
2303
|
+
p
|
|
2304
|
+
for p in list(component.LayoutObjs)
|
|
2305
|
+
if int(p.GetObjType()) == 1
|
|
2306
|
+
and p.IsLayoutPin()
|
|
2307
|
+
and (self.get_aedt_pin_name(p) in pinName or p.GetName() in pinName)
|
|
2308
|
+
]
|
|
2309
|
+
else:
|
|
2310
|
+
pins = [p for p in list(component.LayoutObjs) if int(p.GetObjType()) == 1 and p.IsLayoutPin()]
|
|
2311
|
+
return pins
|
|
2312
|
+
|
|
2313
|
+
@pyedb_function_handler()
|
|
2314
|
+
def get_aedt_pin_name(self, pin):
|
|
2315
|
+
"""Retrieve the pin name that is shown in AEDT.
|
|
2316
|
+
|
|
2317
|
+
.. note::
|
|
2318
|
+
To obtain the EDB core pin name, use `pin.GetName()`.
|
|
2319
|
+
|
|
2320
|
+
Parameters
|
|
2321
|
+
----------
|
|
2322
|
+
pin : str
|
|
2323
|
+
Name of the pin in EDB core.
|
|
2324
|
+
|
|
2325
|
+
Returns
|
|
2326
|
+
-------
|
|
2327
|
+
str
|
|
2328
|
+
Name of the pin in AEDT.
|
|
2329
|
+
|
|
2330
|
+
Examples
|
|
2331
|
+
--------
|
|
2332
|
+
|
|
2333
|
+
>>> from pyedb import Edb
|
|
2334
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2335
|
+
>>> edbapp.components.get_aedt_pin_name(pin)
|
|
2336
|
+
|
|
2337
|
+
"""
|
|
2338
|
+
if isinstance(pin, EDBPadstackInstance):
|
|
2339
|
+
pin = pin._edb_padstackinstance
|
|
2340
|
+
val = String("")
|
|
2341
|
+
_, name = pin.GetProductProperty(self._edb.edb_api.ProductId.Designer, 11, val)
|
|
2342
|
+
name = str(name).strip("'")
|
|
2343
|
+
return name
|
|
2344
|
+
|
|
2345
|
+
@pyedb_function_handler()
|
|
2346
|
+
def get_pin_position(self, pin):
|
|
2347
|
+
"""Retrieve the pin position in meters.
|
|
2348
|
+
|
|
2349
|
+
Parameters
|
|
2350
|
+
----------
|
|
2351
|
+
pin : str
|
|
2352
|
+
Name of the pin.
|
|
2353
|
+
|
|
2354
|
+
Returns
|
|
2355
|
+
-------
|
|
2356
|
+
list
|
|
2357
|
+
Pin position as a list of float values in the form ``[x, y]``.
|
|
2358
|
+
|
|
2359
|
+
Examples
|
|
2360
|
+
--------
|
|
2361
|
+
|
|
2362
|
+
>>> from pyedb import Edb
|
|
2363
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2364
|
+
>>> edbapp.components.get_pin_position(pin)
|
|
2365
|
+
|
|
2366
|
+
"""
|
|
2367
|
+
res, pt_pos, rot_pos = pin.GetPositionAndRotation()
|
|
2368
|
+
|
|
2369
|
+
if pin.GetComponent().IsNull():
|
|
2370
|
+
transformed_pt_pos = pt_pos
|
|
2371
|
+
else:
|
|
2372
|
+
transformed_pt_pos = pin.GetComponent().GetTransform().TransformPoint(pt_pos)
|
|
2373
|
+
pin_xy = self._edb.geometry.point_data(
|
|
2374
|
+
self._get_edb_value(str(transformed_pt_pos.X.ToDouble())),
|
|
2375
|
+
self._get_edb_value(str(transformed_pt_pos.Y.ToDouble())),
|
|
2376
|
+
)
|
|
2377
|
+
return [pin_xy.X.ToDouble(), pin_xy.Y.ToDouble()]
|
|
2378
|
+
|
|
2379
|
+
@pyedb_function_handler()
|
|
2380
|
+
def get_pins_name_from_net(self, pin_list, net_name):
|
|
2381
|
+
"""Retrieve pins belonging to a net.
|
|
2382
|
+
|
|
2383
|
+
Parameters
|
|
2384
|
+
----------
|
|
2385
|
+
pin_list : list
|
|
2386
|
+
List of pins to check.
|
|
2387
|
+
net_name : str
|
|
2388
|
+
Name of the net.
|
|
2389
|
+
|
|
2390
|
+
Returns
|
|
2391
|
+
-------
|
|
2392
|
+
list
|
|
2393
|
+
List of pins belong to the net.
|
|
2394
|
+
|
|
2395
|
+
Examples
|
|
2396
|
+
--------
|
|
2397
|
+
|
|
2398
|
+
>>> from pyedb import Edb
|
|
2399
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2400
|
+
>>> edbapp.components.get_pins_name_from_net(pin_list, net_name)
|
|
2401
|
+
|
|
2402
|
+
"""
|
|
2403
|
+
pinlist = []
|
|
2404
|
+
for pin in pin_list:
|
|
2405
|
+
if pin.GetNet().GetName() == net_name:
|
|
2406
|
+
pinlist.append(pin.GetName())
|
|
2407
|
+
return pinlist
|
|
2408
|
+
|
|
2409
|
+
@pyedb_function_handler()
|
|
2410
|
+
def get_nets_from_pin_list(self, PinList):
|
|
2411
|
+
"""Retrieve nets with one or more pins.
|
|
2412
|
+
|
|
2413
|
+
Parameters
|
|
2414
|
+
----------
|
|
2415
|
+
PinList : list
|
|
2416
|
+
List of pins.
|
|
2417
|
+
|
|
2418
|
+
Returns
|
|
2419
|
+
-------
|
|
2420
|
+
list
|
|
2421
|
+
List of nets with one or more pins.
|
|
2422
|
+
|
|
2423
|
+
Examples
|
|
2424
|
+
--------
|
|
2425
|
+
|
|
2426
|
+
>>> from pyedb import Edb
|
|
2427
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2428
|
+
>>> edbapp.components.get_nets_from_pin_list(pinlist)
|
|
2429
|
+
|
|
2430
|
+
"""
|
|
2431
|
+
netlist = []
|
|
2432
|
+
for pin in PinList:
|
|
2433
|
+
netlist.append(pin.GetNet().GetName())
|
|
2434
|
+
return list(set(netlist))
|
|
2435
|
+
|
|
2436
|
+
@pyedb_function_handler()
|
|
2437
|
+
def get_component_net_connection_info(self, refdes):
|
|
2438
|
+
"""Retrieve net connection information.
|
|
2439
|
+
|
|
2440
|
+
Parameters
|
|
2441
|
+
----------
|
|
2442
|
+
refdes :
|
|
2443
|
+
Reference designator for the net.
|
|
2444
|
+
|
|
2445
|
+
Returns
|
|
2446
|
+
-------
|
|
2447
|
+
dict
|
|
2448
|
+
Dictionary of the net connection information for the reference designator.
|
|
2449
|
+
|
|
2450
|
+
Examples
|
|
2451
|
+
--------
|
|
2452
|
+
|
|
2453
|
+
>>> from pyedb import Edb
|
|
2454
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2455
|
+
>>> edbapp.components.get_component_net_connection_info(refdes)
|
|
2456
|
+
|
|
2457
|
+
"""
|
|
2458
|
+
component_pins = self.get_pin_from_component(refdes)
|
|
2459
|
+
data = {"refdes": [], "pin_name": [], "net_name": []}
|
|
2460
|
+
for pin_obj in component_pins:
|
|
2461
|
+
pin_name = pin_obj.GetName()
|
|
2462
|
+
net_name = pin_obj.GetNet().GetName()
|
|
2463
|
+
if pin_name is not None:
|
|
2464
|
+
data["refdes"].append(refdes)
|
|
2465
|
+
data["pin_name"].append(pin_name)
|
|
2466
|
+
data["net_name"].append(net_name)
|
|
2467
|
+
return data
|
|
2468
|
+
|
|
2469
|
+
def get_rats(self):
|
|
2470
|
+
"""Retrieve a list of dictionaries of the reference designator, pin names, and net names.
|
|
2471
|
+
|
|
2472
|
+
Returns
|
|
2473
|
+
-------
|
|
2474
|
+
list
|
|
2475
|
+
List of dictionaries of the reference designator, pin names,
|
|
2476
|
+
and net names.
|
|
2477
|
+
|
|
2478
|
+
Examples
|
|
2479
|
+
--------
|
|
2480
|
+
|
|
2481
|
+
>>> from pyedb import Edb
|
|
2482
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2483
|
+
>>> edbapp.components.get_rats()
|
|
2484
|
+
|
|
2485
|
+
"""
|
|
2486
|
+
df_list = []
|
|
2487
|
+
for refdes in self.instances.keys():
|
|
2488
|
+
df = self.get_component_net_connection_info(refdes)
|
|
2489
|
+
df_list.append(df)
|
|
2490
|
+
return df_list
|
|
2491
|
+
|
|
2492
|
+
def get_through_resistor_list(self, threshold=1):
|
|
2493
|
+
"""Retrieve through resistors.
|
|
2494
|
+
|
|
2495
|
+
Parameters
|
|
2496
|
+
----------
|
|
2497
|
+
threshold : int, optional
|
|
2498
|
+
Threshold value. The default is ``1``.
|
|
2499
|
+
|
|
2500
|
+
Returns
|
|
2501
|
+
-------
|
|
2502
|
+
list
|
|
2503
|
+
List of through resistors.
|
|
2504
|
+
|
|
2505
|
+
Examples
|
|
2506
|
+
--------
|
|
2507
|
+
|
|
2508
|
+
>>> from pyedb import Edb
|
|
2509
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2510
|
+
>>> edbapp.components.get_through_resistor_list()
|
|
2511
|
+
|
|
2512
|
+
"""
|
|
2513
|
+
through_comp_list = []
|
|
2514
|
+
for refdes, comp_obj in self.resistors.items():
|
|
2515
|
+
numpins = comp_obj.numpins
|
|
2516
|
+
|
|
2517
|
+
if numpins == 2:
|
|
2518
|
+
value = comp_obj.res_value
|
|
2519
|
+
value = resistor_value_parser(value)
|
|
2520
|
+
|
|
2521
|
+
if value <= threshold:
|
|
2522
|
+
through_comp_list.append(refdes)
|
|
2523
|
+
|
|
2524
|
+
return through_comp_list
|
|
2525
|
+
|
|
2526
|
+
@pyedb_function_handler()
|
|
2527
|
+
def short_component_pins(self, component_name, pins_to_short=None, width=1e-3):
|
|
2528
|
+
"""Short pins of component with a trace.
|
|
2529
|
+
|
|
2530
|
+
Parameters
|
|
2531
|
+
----------
|
|
2532
|
+
component_name : str
|
|
2533
|
+
Name of the component.
|
|
2534
|
+
pins_to_short : list, optional
|
|
2535
|
+
List of pins to short. If `None`, all pins will be shorted.
|
|
2536
|
+
width : float, optional
|
|
2537
|
+
Short Trace width. It will be used in trace computation algorithm
|
|
2538
|
+
|
|
2539
|
+
Returns
|
|
2540
|
+
-------
|
|
2541
|
+
bool
|
|
2542
|
+
``True`` when successful, ``False`` when failed.
|
|
2543
|
+
|
|
2544
|
+
Examples
|
|
2545
|
+
--------
|
|
2546
|
+
|
|
2547
|
+
>>> from pyedb import Edb
|
|
2548
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
2549
|
+
>>> edbapp.components.short_component_pins("J4A2", ["G4", "9", "3"])
|
|
2550
|
+
|
|
2551
|
+
"""
|
|
2552
|
+
component = self.components[component_name]
|
|
2553
|
+
pins = component.pins
|
|
2554
|
+
pins_list = []
|
|
2555
|
+
|
|
2556
|
+
component.center
|
|
2557
|
+
for pin_name, pin in pins.items():
|
|
2558
|
+
if pins_to_short:
|
|
2559
|
+
if pin_name in pins_to_short:
|
|
2560
|
+
pins_list.append(pin)
|
|
2561
|
+
else:
|
|
2562
|
+
pins_list.append(pin)
|
|
2563
|
+
positions_to_short = []
|
|
2564
|
+
center = component.center
|
|
2565
|
+
c = [center[0], center[1], 0]
|
|
2566
|
+
delta_pins = []
|
|
2567
|
+
w = width
|
|
2568
|
+
for pin in pins_list:
|
|
2569
|
+
placement_layer = pin.placement_layer
|
|
2570
|
+
positions_to_short.append(pin.position)
|
|
2571
|
+
if placement_layer in self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer:
|
|
2572
|
+
pad = self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer[placement_layer]
|
|
2573
|
+
else:
|
|
2574
|
+
layer = list(self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer.keys())[
|
|
2575
|
+
0
|
|
2576
|
+
]
|
|
2577
|
+
pad = self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer[layer]
|
|
2578
|
+
pars = pad.parameters_values
|
|
2579
|
+
geom = pad.geometry_type
|
|
2580
|
+
if geom < 6 and pars:
|
|
2581
|
+
delta_pins.append(max(pars) + min(pars) / 2)
|
|
2582
|
+
w = min(min(pars), w)
|
|
2583
|
+
elif pars:
|
|
2584
|
+
delta_pins.append(1.5 * pars[0])
|
|
2585
|
+
w = min(pars[0], w)
|
|
2586
|
+
elif pad.polygon_data.edb_api: # pragma: no cover
|
|
2587
|
+
bbox = pad.polygon_data.edb_api.GetBBox()
|
|
2588
|
+
lower = [bbox.Item1.X.ToDouble(), bbox.Item1.Y.ToDouble()]
|
|
2589
|
+
upper = [bbox.Item2.X.ToDouble(), bbox.Item2.Y.ToDouble()]
|
|
2590
|
+
pars = [abs(lower[0] - upper[0]), abs(lower[1] - upper[1])]
|
|
2591
|
+
delta_pins.append(max(pars) + min(pars) / 2)
|
|
2592
|
+
w = min(min(pars), w)
|
|
2593
|
+
else:
|
|
2594
|
+
delta_pins.append(1.5 * width)
|
|
2595
|
+
i = 0
|
|
2596
|
+
|
|
2597
|
+
while i < len(positions_to_short) - 1:
|
|
2598
|
+
p0 = []
|
|
2599
|
+
p0.append([positions_to_short[i][0] - delta_pins[i], positions_to_short[i][1], 0])
|
|
2600
|
+
p0.append([positions_to_short[i][0] + delta_pins[i], positions_to_short[i][1], 0])
|
|
2601
|
+
p0.append([positions_to_short[i][0], positions_to_short[i][1] - delta_pins[i], 0])
|
|
2602
|
+
p0.append([positions_to_short[i][0], positions_to_short[i][1] + delta_pins[i], 0])
|
|
2603
|
+
p0.append([positions_to_short[i][0], positions_to_short[i][1], 0])
|
|
2604
|
+
l0 = [
|
|
2605
|
+
GeometryOperators.points_distance(p0[0], c),
|
|
2606
|
+
GeometryOperators.points_distance(p0[1], c),
|
|
2607
|
+
GeometryOperators.points_distance(p0[2], c),
|
|
2608
|
+
GeometryOperators.points_distance(p0[3], c),
|
|
2609
|
+
GeometryOperators.points_distance(p0[4], c),
|
|
2610
|
+
]
|
|
2611
|
+
l0_min = l0.index(min(l0))
|
|
2612
|
+
p1 = []
|
|
2613
|
+
p1.append(
|
|
2614
|
+
[
|
|
2615
|
+
positions_to_short[i + 1][0] - delta_pins[i + 1],
|
|
2616
|
+
positions_to_short[i + 1][1],
|
|
2617
|
+
0,
|
|
2618
|
+
]
|
|
2619
|
+
)
|
|
2620
|
+
p1.append(
|
|
2621
|
+
[
|
|
2622
|
+
positions_to_short[i + 1][0] + delta_pins[i + 1],
|
|
2623
|
+
positions_to_short[i + 1][1],
|
|
2624
|
+
0,
|
|
2625
|
+
]
|
|
2626
|
+
)
|
|
2627
|
+
p1.append(
|
|
2628
|
+
[
|
|
2629
|
+
positions_to_short[i + 1][0],
|
|
2630
|
+
positions_to_short[i + 1][1] - delta_pins[i + 1],
|
|
2631
|
+
0,
|
|
2632
|
+
]
|
|
2633
|
+
)
|
|
2634
|
+
p1.append(
|
|
2635
|
+
[
|
|
2636
|
+
positions_to_short[i + 1][0],
|
|
2637
|
+
positions_to_short[i + 1][1] + delta_pins[i + 1],
|
|
2638
|
+
0,
|
|
2639
|
+
]
|
|
2640
|
+
)
|
|
2641
|
+
p1.append([positions_to_short[i + 1][0], positions_to_short[i + 1][1], 0])
|
|
2642
|
+
|
|
2643
|
+
l1 = [
|
|
2644
|
+
GeometryOperators.points_distance(p1[0], c),
|
|
2645
|
+
GeometryOperators.points_distance(p1[1], c),
|
|
2646
|
+
GeometryOperators.points_distance(p1[2], c),
|
|
2647
|
+
GeometryOperators.points_distance(p1[3], c),
|
|
2648
|
+
GeometryOperators.points_distance(p1[4], c),
|
|
2649
|
+
]
|
|
2650
|
+
l1_min = l1.index(min(l1))
|
|
2651
|
+
|
|
2652
|
+
trace_points = [positions_to_short[i]]
|
|
2653
|
+
|
|
2654
|
+
trace_points.append(p0[l0_min][:2])
|
|
2655
|
+
trace_points.append(c[:2])
|
|
2656
|
+
trace_points.append(p1[l1_min][:2])
|
|
2657
|
+
|
|
2658
|
+
trace_points.append(positions_to_short[i + 1])
|
|
2659
|
+
|
|
2660
|
+
self._pedb.modeler.create_trace(
|
|
2661
|
+
trace_points,
|
|
2662
|
+
layer_name=placement_layer,
|
|
2663
|
+
net_name="short",
|
|
2664
|
+
width=w,
|
|
2665
|
+
start_cap_style="Flat",
|
|
2666
|
+
end_cap_style="Flat",
|
|
2667
|
+
)
|
|
2668
|
+
i += 1
|
|
2669
|
+
return True
|