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,2080 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
import math
|
|
3
|
+
import re
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from pyedb.dotnet.clr_module import String, _clr
|
|
7
|
+
from pyedb.dotnet.edb_core.dotnet.database import PolygonDataDotNet
|
|
8
|
+
from pyedb.dotnet.edb_core.edb_data.edbvalue import EdbValue
|
|
9
|
+
from pyedb.dotnet.edb_core.edb_data.primitives_data import EDBPrimitivesMain
|
|
10
|
+
from pyedb.dotnet.edb_core.general import PadGeometryTpe, convert_py_list_to_net_list
|
|
11
|
+
from pyedb.generic.general_methods import (
|
|
12
|
+
generate_unique_name,
|
|
13
|
+
is_ironpython,
|
|
14
|
+
pyedb_function_handler,
|
|
15
|
+
)
|
|
16
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EDBPadProperties(object):
|
|
20
|
+
"""Manages EDB functionalities for pad properties.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
edb_padstack :
|
|
25
|
+
|
|
26
|
+
layer_name : str
|
|
27
|
+
Name of the layer.
|
|
28
|
+
pad_type :
|
|
29
|
+
Type of the pad.
|
|
30
|
+
pedbpadstack : str
|
|
31
|
+
Inherited AEDT object.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
>>> from pyedb import Edb
|
|
36
|
+
>>> edb = Edb(myedb, edbversion="2021.2")
|
|
37
|
+
>>> edb_pad_properties = edb.padstacks.definitions["MyPad"].pad_by_layer["TOP"]
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, edb_padstack, layer_name, pad_type, p_edb_padstack):
|
|
41
|
+
self._edb_padstack = edb_padstack
|
|
42
|
+
self._pedbpadstack = p_edb_padstack
|
|
43
|
+
self.layer_name = layer_name
|
|
44
|
+
self.pad_type = pad_type
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def _padstack_methods(self):
|
|
48
|
+
return self._pedbpadstack._padstack_methods
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def _stackup_layers(self):
|
|
52
|
+
return self._pedbpadstack._stackup_layers
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def _edb(self):
|
|
56
|
+
return self._pedbpadstack._edb
|
|
57
|
+
|
|
58
|
+
def _get_edb_value(self, value):
|
|
59
|
+
return self._pedbpadstack._get_edb_value(value)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def _pad_parameter_value(self):
|
|
63
|
+
pad_params = self._edb_padstack.GetData().GetPadParametersValue(
|
|
64
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
65
|
+
)
|
|
66
|
+
return pad_params
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def geometry_type(self):
|
|
70
|
+
"""Geometry type.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
int
|
|
75
|
+
Type of the geometry.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
padparams = self._edb_padstack.GetData().GetPadParametersValue(
|
|
79
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
80
|
+
)
|
|
81
|
+
return int(padparams[1])
|
|
82
|
+
|
|
83
|
+
@geometry_type.setter
|
|
84
|
+
def geometry_type(self, geom_type):
|
|
85
|
+
"""0, NoGeometry. 1, Circle. 2 Square. 3, Rectangle. 4, Oval. 5, Bullet. 6, N-sided polygon. 7, Polygonal
|
|
86
|
+
shape.8, Round gap with 45 degree thermal ties. 9, Round gap with 90 degree thermal ties.10, Square gap
|
|
87
|
+
with 45 degree thermal ties. 11, Square gap with 90 degree thermal ties.
|
|
88
|
+
"""
|
|
89
|
+
val = self._get_edb_value(0)
|
|
90
|
+
params = []
|
|
91
|
+
if geom_type == 0:
|
|
92
|
+
pass
|
|
93
|
+
elif geom_type == 1:
|
|
94
|
+
params = [val]
|
|
95
|
+
elif geom_type == 2:
|
|
96
|
+
params = [val]
|
|
97
|
+
elif geom_type == 3:
|
|
98
|
+
params = [val, val]
|
|
99
|
+
elif geom_type == 4:
|
|
100
|
+
params = [val, val, val]
|
|
101
|
+
elif geom_type == 5:
|
|
102
|
+
params = [val, val, val]
|
|
103
|
+
self._update_pad_parameters_parameters(geom_type=geom_type, params=params)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def shape(self):
|
|
107
|
+
"""Get the shape of the pad."""
|
|
108
|
+
return self._pad_parameter_value[1].ToString()
|
|
109
|
+
|
|
110
|
+
@shape.setter
|
|
111
|
+
def shape(self, value):
|
|
112
|
+
self._update_pad_parameters_parameters(geom_type=PadGeometryTpe[value].value)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def parameters_values(self):
|
|
116
|
+
"""Parameters.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
list
|
|
121
|
+
List of parameters.
|
|
122
|
+
"""
|
|
123
|
+
return [i.tofloat for i in self.parameters.values()]
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def polygon_data(self):
|
|
127
|
+
"""Parameters.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
list
|
|
132
|
+
List of parameters.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
pad_values = self._edb_padstack.GetData().GetPolygonalPadParameters(
|
|
136
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
137
|
+
)
|
|
138
|
+
if pad_values[1]:
|
|
139
|
+
return PolygonDataDotNet(self._edb._app, pad_values[1])
|
|
140
|
+
else:
|
|
141
|
+
return
|
|
142
|
+
except:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def parameters(self):
|
|
147
|
+
"""Get parameters.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
dict
|
|
152
|
+
"""
|
|
153
|
+
value = list(self._pad_parameter_value[2])
|
|
154
|
+
if self.shape == PadGeometryTpe.Circle.name:
|
|
155
|
+
return OrderedDict({"Diameter": EdbValue(value[0])})
|
|
156
|
+
elif self.shape == PadGeometryTpe.Square.name:
|
|
157
|
+
return OrderedDict({"Size": EdbValue(value[0])})
|
|
158
|
+
elif self.shape == PadGeometryTpe.Rectangle.name:
|
|
159
|
+
return OrderedDict({"XSize": EdbValue(value[0]), "YSize": EdbValue(value[1])})
|
|
160
|
+
elif self.shape in [PadGeometryTpe.Oval.name, PadGeometryTpe.Bullet.name]:
|
|
161
|
+
return OrderedDict(
|
|
162
|
+
{"XSize": EdbValue(value[0]), "YSize": EdbValue(value[1]), "CornerRadius": EdbValue(value[2])}
|
|
163
|
+
)
|
|
164
|
+
elif self.shape == PadGeometryTpe.NSidedPolygon.name:
|
|
165
|
+
return OrderedDict({"Size": EdbValue(value[0]), "NumSides": EdbValue(value[1])})
|
|
166
|
+
elif self.shape in [PadGeometryTpe.Round45.name, PadGeometryTpe.Round90.name]: # pragma: no cover
|
|
167
|
+
return OrderedDict(
|
|
168
|
+
{"Inner": EdbValue(value[0]), "ChannelWidth": EdbValue(value[1]), "IsolationGap": EdbValue(value[2])}
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
return OrderedDict() # pragma: no cover
|
|
172
|
+
|
|
173
|
+
@parameters.setter
|
|
174
|
+
def parameters(self, value):
|
|
175
|
+
"""Set parameters.
|
|
176
|
+
"Circle", {"Diameter": "0.5mm"}
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
value : dict
|
|
181
|
+
Pad parameters in dictionary.
|
|
182
|
+
>>> pad = Edb.padstacks["PlanarEMVia"]["TOP"]
|
|
183
|
+
>>> pad.shape = "Circle"
|
|
184
|
+
>>> pad.pad_parameters{"Diameter": "0.5mm"}
|
|
185
|
+
>>> pad.shape = "Bullet"
|
|
186
|
+
>>> pad.pad_parameters{"XSize": "0.5mm", "YSize": "0.5mm"}
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
if isinstance(value, dict):
|
|
190
|
+
value = {k: v.tostring if isinstance(v, EdbValue) else v for k, v in value.items()}
|
|
191
|
+
if self.shape == PadGeometryTpe.Circle.name:
|
|
192
|
+
params = [self._get_edb_value(value["Diameter"])]
|
|
193
|
+
elif self.shape == PadGeometryTpe.Square.name:
|
|
194
|
+
params = [self._get_edb_value(value["Size"])]
|
|
195
|
+
elif self.shape == PadGeometryTpe.Rectangle.name:
|
|
196
|
+
params = [self._get_edb_value(value["XSize"]), self._get_edb_value(value["YSize"])]
|
|
197
|
+
elif self.shape == [PadGeometryTpe.Oval.name, PadGeometryTpe.Bullet.name]:
|
|
198
|
+
params = [
|
|
199
|
+
self._get_edb_value(value["XSize"]),
|
|
200
|
+
self._get_edb_value(value["YSize"]),
|
|
201
|
+
self._get_edb_value(value["CornerRadius"]),
|
|
202
|
+
]
|
|
203
|
+
elif self.shape in [PadGeometryTpe.Round45.name, PadGeometryTpe.Round90.name]: # pragma: no cover
|
|
204
|
+
params = [
|
|
205
|
+
self._get_edb_value(value["Inner"]),
|
|
206
|
+
self._get_edb_value(value["ChannelWidth"]),
|
|
207
|
+
self._get_edb_value(value["IsolationGap"]),
|
|
208
|
+
]
|
|
209
|
+
else: # pragma: no cover
|
|
210
|
+
params = None
|
|
211
|
+
elif isinstance(value, list):
|
|
212
|
+
params = [self._get_edb_value(i) for i in value]
|
|
213
|
+
else:
|
|
214
|
+
params = [self._get_edb_value(value)]
|
|
215
|
+
self._update_pad_parameters_parameters(params=params)
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def offset_x(self):
|
|
219
|
+
"""Offset for the X axis.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
str
|
|
224
|
+
Offset for the X axis.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
pad_values = self._edb_padstack.GetData().GetPadParametersValue(
|
|
228
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
229
|
+
)
|
|
230
|
+
return pad_values[3].ToString()
|
|
231
|
+
|
|
232
|
+
@offset_x.setter
|
|
233
|
+
def offset_x(self, offset_value):
|
|
234
|
+
self._update_pad_parameters_parameters(offsetx=offset_value)
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def offset_y(self):
|
|
238
|
+
"""Offset for the Y axis.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
str
|
|
243
|
+
Offset for the Y axis.
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
pad_values = self._edb_padstack.GetData().GetPadParametersValue(
|
|
247
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
248
|
+
)
|
|
249
|
+
return pad_values[4].ToString()
|
|
250
|
+
|
|
251
|
+
@offset_y.setter
|
|
252
|
+
def offset_y(self, offset_value):
|
|
253
|
+
self._update_pad_parameters_parameters(offsety=offset_value)
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def rotation(self):
|
|
257
|
+
"""Rotation.
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
str
|
|
262
|
+
Value for the rotation.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
pad_values = self._edb_padstack.GetData().GetPadParametersValue(
|
|
266
|
+
self.layer_name, self.int_to_pad_type(self.pad_type)
|
|
267
|
+
)
|
|
268
|
+
return pad_values[5].ToString()
|
|
269
|
+
|
|
270
|
+
@rotation.setter
|
|
271
|
+
def rotation(self, rotation_value):
|
|
272
|
+
self._update_pad_parameters_parameters(rotation=rotation_value)
|
|
273
|
+
|
|
274
|
+
@pyedb_function_handler()
|
|
275
|
+
def int_to_pad_type(self, val=0):
|
|
276
|
+
"""Convert an integer to an EDB.PadGeometryType.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
val : int
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
object
|
|
285
|
+
EDB.PadType enumerator value.
|
|
286
|
+
"""
|
|
287
|
+
return self._pedbpadstack._ppadstack.int_to_pad_type(val)
|
|
288
|
+
|
|
289
|
+
@pyedb_function_handler()
|
|
290
|
+
def int_to_geometry_type(self, val=0):
|
|
291
|
+
"""Convert an integer to an EDB.PadGeometryType.
|
|
292
|
+
|
|
293
|
+
Parameters
|
|
294
|
+
----------
|
|
295
|
+
val : int
|
|
296
|
+
|
|
297
|
+
Returns
|
|
298
|
+
-------
|
|
299
|
+
object
|
|
300
|
+
EDB.PadGeometryType enumerator value.
|
|
301
|
+
"""
|
|
302
|
+
return self._pedbpadstack._ppadstack.int_to_geometry_type(val)
|
|
303
|
+
|
|
304
|
+
@pyedb_function_handler()
|
|
305
|
+
def _update_pad_parameters_parameters(
|
|
306
|
+
self,
|
|
307
|
+
layer_name=None,
|
|
308
|
+
pad_type=None,
|
|
309
|
+
geom_type=None,
|
|
310
|
+
params=None,
|
|
311
|
+
offsetx=None,
|
|
312
|
+
offsety=None,
|
|
313
|
+
rotation=None,
|
|
314
|
+
):
|
|
315
|
+
"""Update padstack parameters.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
layer_name : str, optional
|
|
320
|
+
Name of the layer. The default is ``None``.
|
|
321
|
+
pad_type : int, optional
|
|
322
|
+
Type of the pad. The default is ``None``.
|
|
323
|
+
geom_type : int, optional
|
|
324
|
+
Type of the geometry. The default is ``None``.
|
|
325
|
+
params : list, optional
|
|
326
|
+
The default is ``None``.
|
|
327
|
+
offsetx : float, optional
|
|
328
|
+
Offset value for the X axis. The default is ``None``.
|
|
329
|
+
offsety : float, optional
|
|
330
|
+
Offset value for the Y axis. The default is ``None``.
|
|
331
|
+
rotation : float, optional
|
|
332
|
+
Rotation value. The default is ``None``.
|
|
333
|
+
|
|
334
|
+
Returns
|
|
335
|
+
-------
|
|
336
|
+
bool
|
|
337
|
+
``True`` when successful, ``False`` when failed.
|
|
338
|
+
"""
|
|
339
|
+
originalPadstackDefinitionData = self._edb_padstack.GetData()
|
|
340
|
+
newPadstackDefinitionData = self._edb.definition.PadstackDefData(originalPadstackDefinitionData)
|
|
341
|
+
if not pad_type:
|
|
342
|
+
pad_type = self.pad_type
|
|
343
|
+
if not geom_type:
|
|
344
|
+
geom_type = self.geometry_type
|
|
345
|
+
if params:
|
|
346
|
+
params = convert_py_list_to_net_list(params)
|
|
347
|
+
else:
|
|
348
|
+
params = self._pad_parameter_value[2]
|
|
349
|
+
if not offsetx:
|
|
350
|
+
offsetx = self.offset_x
|
|
351
|
+
if not offsety:
|
|
352
|
+
offsety = self.offset_y
|
|
353
|
+
if not rotation:
|
|
354
|
+
rotation = self.rotation
|
|
355
|
+
if not layer_name:
|
|
356
|
+
layer_name = self.layer_name
|
|
357
|
+
|
|
358
|
+
newPadstackDefinitionData.SetPadParameters(
|
|
359
|
+
layer_name,
|
|
360
|
+
self.int_to_pad_type(pad_type),
|
|
361
|
+
self.int_to_geometry_type(geom_type),
|
|
362
|
+
params,
|
|
363
|
+
self._get_edb_value(offsetx),
|
|
364
|
+
self._get_edb_value(offsety),
|
|
365
|
+
self._get_edb_value(rotation),
|
|
366
|
+
)
|
|
367
|
+
self._edb_padstack.SetData(newPadstackDefinitionData)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class EDBPadstack(object):
|
|
371
|
+
"""Manages EDB functionalities for a padstack.
|
|
372
|
+
|
|
373
|
+
Parameters
|
|
374
|
+
----------
|
|
375
|
+
edb_padstack :
|
|
376
|
+
|
|
377
|
+
ppadstack : str
|
|
378
|
+
Inherited AEDT object.
|
|
379
|
+
|
|
380
|
+
Examples
|
|
381
|
+
--------
|
|
382
|
+
>>> from pyedb import Edb
|
|
383
|
+
>>> edb = Edb(myedb, edbversion="2021.2")
|
|
384
|
+
>>> edb_padstack = edb.padstacks.definitions["MyPad"]
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
def __init__(self, edb_padstack, ppadstack):
|
|
388
|
+
self.edb_padstack = edb_padstack
|
|
389
|
+
self._ppadstack = ppadstack
|
|
390
|
+
self.pad_by_layer = {}
|
|
391
|
+
self.antipad_by_layer = {}
|
|
392
|
+
self.thermalpad_by_layer = {}
|
|
393
|
+
self._bounding_box = []
|
|
394
|
+
self._hole_params = None
|
|
395
|
+
for layer in self.via_layers:
|
|
396
|
+
self.pad_by_layer[layer] = EDBPadProperties(edb_padstack, layer, 0, self)
|
|
397
|
+
self.antipad_by_layer[layer] = EDBPadProperties(edb_padstack, layer, 1, self)
|
|
398
|
+
self.thermalpad_by_layer[layer] = EDBPadProperties(edb_padstack, layer, 2, self)
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def instances(self):
|
|
403
|
+
"""Definitions Instances."""
|
|
404
|
+
name = self.name
|
|
405
|
+
return [i for i in self._ppadstack.instances.values() if i.padstack_definition == name]
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def name(self):
|
|
409
|
+
"""Padstack Definition Name."""
|
|
410
|
+
return self.edb_padstack.GetName()
|
|
411
|
+
|
|
412
|
+
@property
|
|
413
|
+
def _padstack_methods(self):
|
|
414
|
+
return self._ppadstack._padstack_methods
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def _stackup_layers(self):
|
|
418
|
+
return self._ppadstack._stackup_layers
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def _edb(self):
|
|
422
|
+
return self._ppadstack._edb
|
|
423
|
+
|
|
424
|
+
def _get_edb_value(self, value):
|
|
425
|
+
return self._ppadstack._get_edb_value(value)
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def via_layers(self):
|
|
429
|
+
"""Layers.
|
|
430
|
+
|
|
431
|
+
Returns
|
|
432
|
+
-------
|
|
433
|
+
list
|
|
434
|
+
List of layers.
|
|
435
|
+
"""
|
|
436
|
+
return self.edb_padstack.GetData().GetLayerNames()
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
def via_start_layer(self):
|
|
440
|
+
"""Starting layer.
|
|
441
|
+
|
|
442
|
+
Returns
|
|
443
|
+
-------
|
|
444
|
+
str
|
|
445
|
+
Name of the starting layer.
|
|
446
|
+
"""
|
|
447
|
+
return list(self.via_layers)[0]
|
|
448
|
+
|
|
449
|
+
@property
|
|
450
|
+
def via_stop_layer(self):
|
|
451
|
+
"""Stopping layer.
|
|
452
|
+
|
|
453
|
+
Returns
|
|
454
|
+
-------
|
|
455
|
+
str
|
|
456
|
+
Name of the stopping layer.
|
|
457
|
+
"""
|
|
458
|
+
return list(self.via_layers)[-1]
|
|
459
|
+
|
|
460
|
+
@property
|
|
461
|
+
def hole_params(self):
|
|
462
|
+
"""Via Hole parameters values."""
|
|
463
|
+
|
|
464
|
+
viaData = self.edb_padstack.GetData()
|
|
465
|
+
self._hole_params = viaData.GetHoleParametersValue()
|
|
466
|
+
return self._hole_params
|
|
467
|
+
|
|
468
|
+
@property
|
|
469
|
+
def hole_parameters(self):
|
|
470
|
+
"""Hole parameters.
|
|
471
|
+
|
|
472
|
+
Returns
|
|
473
|
+
-------
|
|
474
|
+
list
|
|
475
|
+
List of the hole parameters.
|
|
476
|
+
"""
|
|
477
|
+
self._hole_parameters = self.hole_params[2]
|
|
478
|
+
return self._hole_parameters
|
|
479
|
+
|
|
480
|
+
@pyedb_function_handler()
|
|
481
|
+
def _update_hole_parameters(self, hole_type=None, params=None, offsetx=None, offsety=None, rotation=None):
|
|
482
|
+
"""Update hole parameters.
|
|
483
|
+
|
|
484
|
+
Parameters
|
|
485
|
+
----------
|
|
486
|
+
hole_type : optional
|
|
487
|
+
Type of the hole. The default is ``None``.
|
|
488
|
+
params : optional
|
|
489
|
+
The default is ``None``.
|
|
490
|
+
offsetx : float, optional
|
|
491
|
+
Offset value for the X axis. The default is ``None``.
|
|
492
|
+
offsety : float, optional
|
|
493
|
+
Offset value for the Y axis. The default is ``None``.
|
|
494
|
+
rotation : float, optional
|
|
495
|
+
Rotation value in degrees. The default is ``None``.
|
|
496
|
+
|
|
497
|
+
Returns
|
|
498
|
+
-------
|
|
499
|
+
bool
|
|
500
|
+
``True`` when successful, ``False`` when failed.
|
|
501
|
+
"""
|
|
502
|
+
originalPadstackDefinitionData = self.edb_padstack.GetData()
|
|
503
|
+
newPadstackDefinitionData = self._edb.definition.PadstackDefData(originalPadstackDefinitionData)
|
|
504
|
+
if not hole_type:
|
|
505
|
+
hole_type = self.hole_type
|
|
506
|
+
if not params:
|
|
507
|
+
params = self.hole_parameters
|
|
508
|
+
if isinstance(params, list):
|
|
509
|
+
params = convert_py_list_to_net_list(params)
|
|
510
|
+
if not offsetx:
|
|
511
|
+
offsetx = self.hole_offset_x
|
|
512
|
+
if not offsety:
|
|
513
|
+
offsety = self.hole_offset_y
|
|
514
|
+
if not rotation:
|
|
515
|
+
rotation = self.hole_rotation
|
|
516
|
+
newPadstackDefinitionData.SetHoleParameters(
|
|
517
|
+
hole_type,
|
|
518
|
+
params,
|
|
519
|
+
self._get_edb_value(offsetx),
|
|
520
|
+
self._get_edb_value(offsety),
|
|
521
|
+
self._get_edb_value(rotation),
|
|
522
|
+
)
|
|
523
|
+
self.edb_padstack.SetData(newPadstackDefinitionData)
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def hole_properties(self):
|
|
527
|
+
"""Hole properties.
|
|
528
|
+
|
|
529
|
+
Returns
|
|
530
|
+
-------
|
|
531
|
+
list
|
|
532
|
+
List of float values for hole properties.
|
|
533
|
+
"""
|
|
534
|
+
self._hole_properties = [i.ToDouble() for i in self.hole_params[2]]
|
|
535
|
+
return self._hole_properties
|
|
536
|
+
|
|
537
|
+
@hole_properties.setter
|
|
538
|
+
def hole_properties(self, propertylist):
|
|
539
|
+
if not isinstance(propertylist, list):
|
|
540
|
+
propertylist = [self._get_edb_value(propertylist)]
|
|
541
|
+
else:
|
|
542
|
+
propertylist = [self._get_edb_value(i) for i in propertylist]
|
|
543
|
+
self._update_hole_parameters(params=propertylist)
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def hole_type(self):
|
|
547
|
+
"""Hole type.
|
|
548
|
+
|
|
549
|
+
Returns
|
|
550
|
+
-------
|
|
551
|
+
int
|
|
552
|
+
Type of the hole.
|
|
553
|
+
"""
|
|
554
|
+
self._hole_type = self.hole_params[1]
|
|
555
|
+
return self._hole_type
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
def hole_offset_x(self):
|
|
559
|
+
"""Hole offset for the X axis.
|
|
560
|
+
|
|
561
|
+
Returns
|
|
562
|
+
-------
|
|
563
|
+
str
|
|
564
|
+
Hole offset value for the X axis.
|
|
565
|
+
"""
|
|
566
|
+
self._hole_offset_x = self.hole_params[3].ToString()
|
|
567
|
+
return self._hole_offset_x
|
|
568
|
+
|
|
569
|
+
@hole_offset_x.setter
|
|
570
|
+
def hole_offset_x(self, offset):
|
|
571
|
+
self._hole_offset_x = offset
|
|
572
|
+
self._update_hole_parameters(offsetx=offset)
|
|
573
|
+
|
|
574
|
+
@property
|
|
575
|
+
def hole_offset_y(self):
|
|
576
|
+
"""Hole offset for the Y axis.
|
|
577
|
+
|
|
578
|
+
Returns
|
|
579
|
+
-------
|
|
580
|
+
str
|
|
581
|
+
Hole offset value for the Y axis.
|
|
582
|
+
"""
|
|
583
|
+
self._hole_offset_y = self.hole_params[4].ToString()
|
|
584
|
+
return self._hole_offset_y
|
|
585
|
+
|
|
586
|
+
@hole_offset_y.setter
|
|
587
|
+
def hole_offset_y(self, offset):
|
|
588
|
+
self._hole_offset_y = offset
|
|
589
|
+
self._update_hole_parameters(offsety=offset)
|
|
590
|
+
|
|
591
|
+
@property
|
|
592
|
+
def hole_rotation(self):
|
|
593
|
+
"""Hole rotation.
|
|
594
|
+
|
|
595
|
+
Returns
|
|
596
|
+
-------
|
|
597
|
+
str
|
|
598
|
+
Value for the hole rotation.
|
|
599
|
+
"""
|
|
600
|
+
self._hole_rotation = self.hole_params[5].ToString()
|
|
601
|
+
return self._hole_rotation
|
|
602
|
+
|
|
603
|
+
@hole_rotation.setter
|
|
604
|
+
def hole_rotation(self, rotation):
|
|
605
|
+
self._hole_rotation = rotation
|
|
606
|
+
self._update_hole_parameters(rotation=rotation)
|
|
607
|
+
|
|
608
|
+
@property
|
|
609
|
+
def hole_plating_ratio(self):
|
|
610
|
+
"""Hole plating ratio.
|
|
611
|
+
|
|
612
|
+
Returns
|
|
613
|
+
-------
|
|
614
|
+
float
|
|
615
|
+
Percentage for the hole plating.
|
|
616
|
+
"""
|
|
617
|
+
return self._edb.definition.PadstackDefData(self.edb_padstack.GetData()).GetHolePlatingPercentage()
|
|
618
|
+
|
|
619
|
+
@hole_plating_ratio.setter
|
|
620
|
+
def hole_plating_ratio(self, ratio):
|
|
621
|
+
originalPadstackDefinitionData = self.edb_padstack.GetData()
|
|
622
|
+
newPadstackDefinitionData = self._edb.definition.PadstackDefData(originalPadstackDefinitionData)
|
|
623
|
+
newPadstackDefinitionData.SetHolePlatingPercentage(self._get_edb_value(ratio))
|
|
624
|
+
self.edb_padstack.SetData(newPadstackDefinitionData)
|
|
625
|
+
|
|
626
|
+
@property
|
|
627
|
+
def hole_plating_thickness(self):
|
|
628
|
+
"""Hole plating thickness.
|
|
629
|
+
|
|
630
|
+
Returns
|
|
631
|
+
-------
|
|
632
|
+
float
|
|
633
|
+
Thickness of the hole plating if present.
|
|
634
|
+
"""
|
|
635
|
+
if len(self.hole_properties) > 0:
|
|
636
|
+
return (float(self.hole_properties[0]) * self.hole_plating_ratio / 100) / 2
|
|
637
|
+
else:
|
|
638
|
+
return 0
|
|
639
|
+
|
|
640
|
+
@hole_plating_thickness.setter
|
|
641
|
+
def hole_plating_thickness(self, value):
|
|
642
|
+
"""Hole plating thickness.
|
|
643
|
+
|
|
644
|
+
Returns
|
|
645
|
+
-------
|
|
646
|
+
float
|
|
647
|
+
Thickness of the hole plating if present.
|
|
648
|
+
"""
|
|
649
|
+
hr = 200 * float(value) / float(self.hole_properties[0])
|
|
650
|
+
self.hole_plating_ratio = hr
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def hole_finished_size(self):
|
|
654
|
+
"""Finished hole size.
|
|
655
|
+
|
|
656
|
+
Returns
|
|
657
|
+
-------
|
|
658
|
+
float
|
|
659
|
+
Finished size of the hole (Total Size + PlatingThickess*2).
|
|
660
|
+
"""
|
|
661
|
+
if len(self.hole_properties) > 0:
|
|
662
|
+
return float(self.hole_properties[0]) - (self.hole_plating_thickness * 2)
|
|
663
|
+
else:
|
|
664
|
+
return 0
|
|
665
|
+
|
|
666
|
+
@property
|
|
667
|
+
def material(self):
|
|
668
|
+
"""Hole material.
|
|
669
|
+
|
|
670
|
+
Returns
|
|
671
|
+
-------
|
|
672
|
+
str
|
|
673
|
+
Material of the hole.
|
|
674
|
+
"""
|
|
675
|
+
return self.edb_padstack.GetData().GetMaterial()
|
|
676
|
+
|
|
677
|
+
@material.setter
|
|
678
|
+
def material(self, materialname):
|
|
679
|
+
originalPadstackDefinitionData = self.edb_padstack.GetData()
|
|
680
|
+
newPadstackDefinitionData = self._edb.definition.PadstackDefData(originalPadstackDefinitionData)
|
|
681
|
+
newPadstackDefinitionData.SetMaterial(materialname)
|
|
682
|
+
self.edb_padstack.SetData(newPadstackDefinitionData)
|
|
683
|
+
|
|
684
|
+
@property
|
|
685
|
+
def padstack_instances(self):
|
|
686
|
+
"""Get all the vias that belongs to active Padstack definition.
|
|
687
|
+
|
|
688
|
+
Returns
|
|
689
|
+
-------
|
|
690
|
+
dict
|
|
691
|
+
"""
|
|
692
|
+
return {
|
|
693
|
+
id: via for id, via in self._ppadstack.padstack_instances.items() if via.padstack_definition == self.name
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@property
|
|
697
|
+
def hole_range(self):
|
|
698
|
+
"""Get hole range value from padstack definition.
|
|
699
|
+
|
|
700
|
+
Returns
|
|
701
|
+
-------
|
|
702
|
+
str
|
|
703
|
+
Possible returned values are ``"through"``, ``"begin_on_upper_pad"``,
|
|
704
|
+
``"end_on_lower_pad"``, ``"upper_pad_to_lower_pad"``, and ``"undefined"``.
|
|
705
|
+
"""
|
|
706
|
+
cloned_padstackdef_data = self._edb.definition.PadstackDefData(self.edb_padstack.GetData())
|
|
707
|
+
hole_ange_type = int(cloned_padstackdef_data.GetHoleRange())
|
|
708
|
+
if hole_ange_type == 0: # pragma no cover
|
|
709
|
+
return "through"
|
|
710
|
+
elif hole_ange_type == 1: # pragma no cover
|
|
711
|
+
return "begin_on_upper_pad"
|
|
712
|
+
elif hole_ange_type == 2: # pragma no cover
|
|
713
|
+
return "end_on_lower_pad"
|
|
714
|
+
elif hole_ange_type == 3: # pragma no cover
|
|
715
|
+
return "upper_pad_to_lower_pad"
|
|
716
|
+
else: # pragma no cover
|
|
717
|
+
return "undefined"
|
|
718
|
+
|
|
719
|
+
@hole_range.setter
|
|
720
|
+
def hole_range(self, value):
|
|
721
|
+
if isinstance(value, str): # pragma no cover
|
|
722
|
+
cloned_padstackdef_data = self._edb.definition.PadstackDefData(self.edb_padstack.GetData())
|
|
723
|
+
if value == "through": # pragma no cover
|
|
724
|
+
cloned_padstackdef_data.SetHoleRange(self._edb.definition.PadstackHoleRange.Through)
|
|
725
|
+
elif value == "begin_on_upper_pad": # pragma no cover
|
|
726
|
+
cloned_padstackdef_data.SetHoleRange(self._edb.definition.PadstackHoleRange.BeginOnUpperPad)
|
|
727
|
+
elif value == "end_on_lower_pad": # pragma no cover
|
|
728
|
+
cloned_padstackdef_data.SetHoleRange(self._edb.definition.PadstackHoleRange.EndOnLowerPad)
|
|
729
|
+
elif value == "upper_pad_to_lower_pad": # pragma no cover
|
|
730
|
+
cloned_padstackdef_data.SetHoleRange(self._edb.definition.PadstackHoleRange.UpperPadToLowerPad)
|
|
731
|
+
else: # pragma no cover
|
|
732
|
+
return
|
|
733
|
+
self.edb_padstack.SetData(cloned_padstackdef_data)
|
|
734
|
+
|
|
735
|
+
@pyedb_function_handler()
|
|
736
|
+
def convert_to_3d_microvias(self, convert_only_signal_vias=True, hole_wall_angle=15, delete_padstack_def=True):
|
|
737
|
+
"""Convert actual padstack instance to microvias 3D Objects with a given aspect ratio.
|
|
738
|
+
|
|
739
|
+
Parameters
|
|
740
|
+
----------
|
|
741
|
+
convert_only_signal_vias : bool, optional
|
|
742
|
+
Either to convert only vias belonging to signal nets or all vias. Defaults is ``True``.
|
|
743
|
+
hole_wall_angle : float, optional
|
|
744
|
+
Angle of laser penetration in degrees. The angle defines the bottom hole diameter with this formula:
|
|
745
|
+
HoleDiameter -2*tan(laser_angle* Hole depth). Hole depth is the height of the via (dielectric thickness).
|
|
746
|
+
The default is ``15``.
|
|
747
|
+
The bottom hole is ``0.75*HoleDepth/HoleDiam``.
|
|
748
|
+
delete_padstack_def : bool, optional
|
|
749
|
+
Whether to delete the padstack definition. The default is ``True``.
|
|
750
|
+
If ``False``, the padstack definition is not deleted and the hole size is set to zero.
|
|
751
|
+
|
|
752
|
+
Returns
|
|
753
|
+
-------
|
|
754
|
+
``True`` when successful, ``False`` when failed.
|
|
755
|
+
"""
|
|
756
|
+
|
|
757
|
+
if len(self.hole_properties) == 0:
|
|
758
|
+
self._ppadstack._pedb.logger.error("Microvias cannot be applied on vias using hole shape polygon")
|
|
759
|
+
return False
|
|
760
|
+
|
|
761
|
+
if self.via_start_layer == self.via_stop_layer:
|
|
762
|
+
self._ppadstack._pedb.logger.error("Microvias cannot be applied when Start and Stop Layers are the same.")
|
|
763
|
+
layout = self._ppadstack._pedb.active_layout
|
|
764
|
+
layers = self._ppadstack._pedb.stackup.signal_layers
|
|
765
|
+
layer_names = [i for i in list(layers.keys())]
|
|
766
|
+
if convert_only_signal_vias:
|
|
767
|
+
signal_nets = [i for i in list(self._ppadstack._pedb.nets.signal_nets.keys())]
|
|
768
|
+
topl, topz, bottoml, bottomz = self._ppadstack._pedb.stackup.stackup_limits(True)
|
|
769
|
+
try:
|
|
770
|
+
start_elevation = layers[self.via_start_layer].lower_elevation
|
|
771
|
+
except KeyError: # pragma: no cover
|
|
772
|
+
start_elevation = layers[self.instances[0].start_layer].lower_elevation
|
|
773
|
+
try:
|
|
774
|
+
stop_elevation = layers[self.via_start_layer].upper_elevation
|
|
775
|
+
except KeyError: # pragma: no cover
|
|
776
|
+
stop_elevation = layers[self.instances[0].stop_layer].upper_elevation
|
|
777
|
+
|
|
778
|
+
diel_thick = abs(start_elevation - stop_elevation)
|
|
779
|
+
rad1 = self.hole_properties[0] / 2
|
|
780
|
+
rad2 = self.hole_properties[0] / 2 - math.tan(hole_wall_angle * diel_thick * math.pi / 180)
|
|
781
|
+
|
|
782
|
+
if start_elevation < (topz + bottomz) / 2:
|
|
783
|
+
rad1, rad2 = rad2, rad1
|
|
784
|
+
i = 0
|
|
785
|
+
for via in list(self.padstack_instances.values()):
|
|
786
|
+
if convert_only_signal_vias and via.net_name in signal_nets or not convert_only_signal_vias:
|
|
787
|
+
pos = via.position
|
|
788
|
+
started = False
|
|
789
|
+
if len(self.pad_by_layer[self.via_start_layer].parameters) == 0:
|
|
790
|
+
self._edb.cell.primitive.polygon.create(
|
|
791
|
+
layout,
|
|
792
|
+
self.via_start_layer,
|
|
793
|
+
via._edb_padstackinstance.GetNet(),
|
|
794
|
+
self.pad_by_layer[self.via_start_layer].polygon_data.edb_api,
|
|
795
|
+
)
|
|
796
|
+
else:
|
|
797
|
+
self._edb.cell.primitive.circle.create(
|
|
798
|
+
layout,
|
|
799
|
+
self.via_start_layer,
|
|
800
|
+
via._edb_padstackinstance.GetNet(),
|
|
801
|
+
self._get_edb_value(pos[0]),
|
|
802
|
+
self._get_edb_value(pos[1]),
|
|
803
|
+
self._get_edb_value(self.pad_by_layer[self.via_start_layer].parameters_values[0] / 2),
|
|
804
|
+
)
|
|
805
|
+
if len(self.pad_by_layer[self.via_stop_layer].parameters) == 0:
|
|
806
|
+
self._edb.cell.primitive.polygon.create(
|
|
807
|
+
layout,
|
|
808
|
+
self.via_stop_layer,
|
|
809
|
+
via._edb_padstackinstance.GetNet(),
|
|
810
|
+
self.pad_by_layer[self.via_stop_layer].polygon_data.edb_api,
|
|
811
|
+
)
|
|
812
|
+
else:
|
|
813
|
+
self._edb.cell.primitive.circle.create(
|
|
814
|
+
layout,
|
|
815
|
+
self.via_stop_layer,
|
|
816
|
+
via._edb_padstackinstance.GetNet(),
|
|
817
|
+
self._get_edb_value(pos[0]),
|
|
818
|
+
self._get_edb_value(pos[1]),
|
|
819
|
+
self._get_edb_value(self.pad_by_layer[self.via_stop_layer].parameters_values[0] / 2),
|
|
820
|
+
)
|
|
821
|
+
for layer_name in layer_names:
|
|
822
|
+
stop = ""
|
|
823
|
+
if layer_name == via.start_layer or started:
|
|
824
|
+
start = layer_name
|
|
825
|
+
stop = layer_names[layer_names.index(layer_name) + 1]
|
|
826
|
+
cloned_circle = self._edb.cell.primitive.circle.create(
|
|
827
|
+
layout,
|
|
828
|
+
start,
|
|
829
|
+
via._edb_padstackinstance.GetNet(),
|
|
830
|
+
self._get_edb_value(pos[0]),
|
|
831
|
+
self._get_edb_value(pos[1]),
|
|
832
|
+
self._get_edb_value(rad1),
|
|
833
|
+
)
|
|
834
|
+
cloned_circle2 = self._edb.cell.primitive.circle.create(
|
|
835
|
+
layout,
|
|
836
|
+
stop,
|
|
837
|
+
via._edb_padstackinstance.GetNet(),
|
|
838
|
+
self._get_edb_value(pos[0]),
|
|
839
|
+
self._get_edb_value(pos[1]),
|
|
840
|
+
self._get_edb_value(rad2),
|
|
841
|
+
)
|
|
842
|
+
s3d = self._edb.cell.hierarchy._hierarchy.Structure3D.Create(
|
|
843
|
+
layout, generate_unique_name("via3d_" + via.aedt_name.replace("via_", ""), n=3)
|
|
844
|
+
)
|
|
845
|
+
s3d.AddMember(cloned_circle.prim_obj)
|
|
846
|
+
s3d.AddMember(cloned_circle2.prim_obj)
|
|
847
|
+
s3d.SetMaterial(self.material)
|
|
848
|
+
s3d.SetMeshClosureProp(self._edb.cell.hierarchy._hierarchy.Structure3D.TClosure.EndsClosed)
|
|
849
|
+
started = True
|
|
850
|
+
i += 1
|
|
851
|
+
if stop == via.stop_layer:
|
|
852
|
+
break
|
|
853
|
+
if delete_padstack_def: # pragma no cover
|
|
854
|
+
via.delete()
|
|
855
|
+
else: # pragma no cover
|
|
856
|
+
padstack_def = self._ppadstack.definitions[via.padstack_definition]
|
|
857
|
+
padstack_def.hole_properties = 0
|
|
858
|
+
self._ppadstack._pedb.logger.info("Padstack definition kept, hole size set to 0.")
|
|
859
|
+
|
|
860
|
+
self._ppadstack._pedb.logger.info("{} Converted successfully to 3D Objects.".format(i))
|
|
861
|
+
return True
|
|
862
|
+
|
|
863
|
+
@pyedb_function_handler()
|
|
864
|
+
def split_to_microvias(self):
|
|
865
|
+
"""Convert actual padstack definition to multiple microvias definitions.
|
|
866
|
+
|
|
867
|
+
Returns
|
|
868
|
+
-------
|
|
869
|
+
List of :class:`pyedb.dotnet.edb_core.padstackEDBPadstack`
|
|
870
|
+
"""
|
|
871
|
+
if self.via_start_layer == self.via_stop_layer:
|
|
872
|
+
self._ppadstack._pedb.logger.error("Microvias cannot be applied when Start and Stop Layers are the same.")
|
|
873
|
+
layout = self._ppadstack._pedb.active_layout
|
|
874
|
+
layers = self._ppadstack._pedb.stackup.signal_layers
|
|
875
|
+
layer_names = [i for i in list(layers.keys())]
|
|
876
|
+
if abs(layer_names.index(self.via_start_layer) - layer_names.index(self.via_stop_layer)) < 2:
|
|
877
|
+
self._ppadstack._pedb.logger.error(
|
|
878
|
+
"Conversion can be applied only if Padstack definition is composed by more than 2 layers."
|
|
879
|
+
)
|
|
880
|
+
return False
|
|
881
|
+
started = False
|
|
882
|
+
p1 = self.edb_padstack.GetData()
|
|
883
|
+
new_instances = []
|
|
884
|
+
for layer_name in layer_names:
|
|
885
|
+
stop = ""
|
|
886
|
+
if layer_name == self.via_start_layer or started:
|
|
887
|
+
start = layer_name
|
|
888
|
+
stop = layer_names[layer_names.index(layer_name) + 1]
|
|
889
|
+
new_padstack_name = "MV_{}_{}_{}".format(self.name, start, stop)
|
|
890
|
+
included = [start, stop]
|
|
891
|
+
new_padstack_definition_data = self._ppadstack._pedb.edb_api.definition.PadstackDefData.Create()
|
|
892
|
+
new_padstack_definition_data.AddLayers(convert_py_list_to_net_list(included))
|
|
893
|
+
for layer in included:
|
|
894
|
+
pl = self.pad_by_layer[layer]
|
|
895
|
+
new_padstack_definition_data.SetPadParameters(
|
|
896
|
+
layer,
|
|
897
|
+
self._ppadstack._pedb.edb_api.definition.PadType.RegularPad,
|
|
898
|
+
pl.int_to_geometry_type(pl.geometry_type),
|
|
899
|
+
list(
|
|
900
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
901
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
902
|
+
)
|
|
903
|
+
)[2],
|
|
904
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
905
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
906
|
+
)[3],
|
|
907
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
908
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
909
|
+
)[4],
|
|
910
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
911
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
912
|
+
)[5],
|
|
913
|
+
)
|
|
914
|
+
pl = self.antipad_by_layer[layer]
|
|
915
|
+
new_padstack_definition_data.SetPadParameters(
|
|
916
|
+
layer,
|
|
917
|
+
self._ppadstack._pedb.edb_api.definition.PadType.AntiPad,
|
|
918
|
+
pl.int_to_geometry_type(pl.geometry_type),
|
|
919
|
+
list(
|
|
920
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
921
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
922
|
+
)
|
|
923
|
+
)[2],
|
|
924
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
925
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
926
|
+
)[3],
|
|
927
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
928
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
929
|
+
)[4],
|
|
930
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
931
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
932
|
+
)[5],
|
|
933
|
+
)
|
|
934
|
+
pl = self.thermalpad_by_layer[layer]
|
|
935
|
+
new_padstack_definition_data.SetPadParameters(
|
|
936
|
+
layer,
|
|
937
|
+
self._ppadstack._pedb.edb_api.definition.PadType.ThermalPad,
|
|
938
|
+
pl.int_to_geometry_type(pl.geometry_type),
|
|
939
|
+
list(
|
|
940
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
941
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
942
|
+
)
|
|
943
|
+
)[2],
|
|
944
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
945
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
946
|
+
)[3],
|
|
947
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
948
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
949
|
+
)[4],
|
|
950
|
+
pl._edb_padstack.GetData().GetPadParametersValue(
|
|
951
|
+
pl.layer_name, pl.int_to_pad_type(pl.pad_type)
|
|
952
|
+
)[5],
|
|
953
|
+
)
|
|
954
|
+
new_padstack_definition_data.SetHoleParameters(
|
|
955
|
+
self.hole_type,
|
|
956
|
+
self.hole_parameters,
|
|
957
|
+
self._get_edb_value(self.hole_offset_x),
|
|
958
|
+
self._get_edb_value(self.hole_offset_y),
|
|
959
|
+
self._get_edb_value(self.hole_rotation),
|
|
960
|
+
)
|
|
961
|
+
new_padstack_definition_data.SetMaterial(self.material)
|
|
962
|
+
new_padstack_definition_data.SetHolePlatingPercentage(self._get_edb_value(self.hole_plating_ratio))
|
|
963
|
+
padstack_definition = self._edb.definition.PadstackDef.Create(
|
|
964
|
+
self._ppadstack._pedb.active_db, new_padstack_name
|
|
965
|
+
)
|
|
966
|
+
padstack_definition.SetData(new_padstack_definition_data)
|
|
967
|
+
new_instances.append(EDBPadstack(padstack_definition, self._ppadstack))
|
|
968
|
+
started = True
|
|
969
|
+
if self.via_stop_layer == stop:
|
|
970
|
+
break
|
|
971
|
+
i = 0
|
|
972
|
+
for via in list(self.padstack_instances.values()):
|
|
973
|
+
for inst in new_instances:
|
|
974
|
+
instance = inst.edb_padstack
|
|
975
|
+
from_layer = [
|
|
976
|
+
l
|
|
977
|
+
for l in self._ppadstack._pedb.stackup._edb_layer_list
|
|
978
|
+
if l.GetName() == list(instance.GetData().GetLayerNames())[0]
|
|
979
|
+
][0]
|
|
980
|
+
to_layer = [
|
|
981
|
+
l
|
|
982
|
+
for l in self._ppadstack._pedb.stackup._edb_layer_list
|
|
983
|
+
if l.GetName() == list(instance.GetData().GetLayerNames())[-1]
|
|
984
|
+
][0]
|
|
985
|
+
padstack_instance = self._edb.cell.primitive.padstack_instance.create(
|
|
986
|
+
layout,
|
|
987
|
+
via._edb_padstackinstance.GetNet(),
|
|
988
|
+
generate_unique_name(instance.GetName()),
|
|
989
|
+
instance,
|
|
990
|
+
via._edb_padstackinstance.GetPositionAndRotationValue()[1],
|
|
991
|
+
via._edb_padstackinstance.GetPositionAndRotationValue()[2],
|
|
992
|
+
from_layer,
|
|
993
|
+
to_layer,
|
|
994
|
+
None,
|
|
995
|
+
None,
|
|
996
|
+
)
|
|
997
|
+
padstack_instance.SetIsLayoutPin(via.is_pin)
|
|
998
|
+
i += 1
|
|
999
|
+
via.delete()
|
|
1000
|
+
self._ppadstack._pedb.logger.info("Created {} new microvias.".format(i))
|
|
1001
|
+
return new_instances
|
|
1002
|
+
|
|
1003
|
+
@pyedb_function_handler()
|
|
1004
|
+
def _update_layer_names(self, old_name, updated_name):
|
|
1005
|
+
"""Update padstack definition layer name when layer name is edited with the layer name setter.
|
|
1006
|
+
Parameters
|
|
1007
|
+
----------
|
|
1008
|
+
old_name
|
|
1009
|
+
old name : str
|
|
1010
|
+
updated_name
|
|
1011
|
+
new name : str
|
|
1012
|
+
Returns
|
|
1013
|
+
-------
|
|
1014
|
+
bool
|
|
1015
|
+
``True`` when succeed ``False`` when failed.
|
|
1016
|
+
"""
|
|
1017
|
+
cloned_padstack_data = self._edb.definition.PadstackDefData(self.edb_padstack.GetData())
|
|
1018
|
+
new_padstack_data = self._edb.definition.PadstackDefData.Create()
|
|
1019
|
+
layers_name = cloned_padstack_data.GetLayerNames()
|
|
1020
|
+
layers_to_add = []
|
|
1021
|
+
for layer in layers_name:
|
|
1022
|
+
if layer == old_name:
|
|
1023
|
+
layers_to_add.append(updated_name)
|
|
1024
|
+
else:
|
|
1025
|
+
layers_to_add.append(layer)
|
|
1026
|
+
new_padstack_data.AddLayers(convert_py_list_to_net_list(layers_to_add))
|
|
1027
|
+
for layer in layers_name:
|
|
1028
|
+
updated_pad = self.pad_by_layer[layer]
|
|
1029
|
+
if not updated_pad.geometry_type == 0: # pragma no cover
|
|
1030
|
+
pad_type = self._edb.definition.PadType.RegularPad
|
|
1031
|
+
geom_type = self.pad_by_layer[layer]._pad_parameter_value[1]
|
|
1032
|
+
parameters = self.pad_by_layer[layer]._pad_parameter_value[2]
|
|
1033
|
+
offset_x = self.pad_by_layer[layer]._pad_parameter_value[3]
|
|
1034
|
+
offset_y = self.pad_by_layer[layer]._pad_parameter_value[4]
|
|
1035
|
+
rot = self.pad_by_layer[layer]._pad_parameter_value[5]
|
|
1036
|
+
if layer == old_name: # pragma no cover
|
|
1037
|
+
new_padstack_data.SetPadParameters(
|
|
1038
|
+
updated_name, pad_type, geom_type, parameters, offset_x, offset_y, rot
|
|
1039
|
+
)
|
|
1040
|
+
else:
|
|
1041
|
+
new_padstack_data.SetPadParameters(layer, pad_type, geom_type, parameters, offset_x, offset_y, rot)
|
|
1042
|
+
|
|
1043
|
+
updated_anti_pad = self.antipad_by_layer[layer]
|
|
1044
|
+
if not updated_anti_pad.geometry_type == 0: # pragma no cover
|
|
1045
|
+
pad_type = self._edb.definition.PadType.AntiPad
|
|
1046
|
+
geom_type = self.pad_by_layer[layer]._pad_parameter_value[1]
|
|
1047
|
+
parameters = self.pad_by_layer[layer]._pad_parameter_value[2]
|
|
1048
|
+
offset_x = self.pad_by_layer[layer]._pad_parameter_value[3]
|
|
1049
|
+
offset_y = self.pad_by_layer[layer]._pad_parameter_value[4]
|
|
1050
|
+
rotation = self.pad_by_layer[layer]._pad_parameter_value[5]
|
|
1051
|
+
if layer == old_name: # pragma no cover
|
|
1052
|
+
new_padstack_data.SetPadParameters(
|
|
1053
|
+
updated_name, pad_type, geom_type, parameters, offset_x, offset_y, rotation
|
|
1054
|
+
)
|
|
1055
|
+
else:
|
|
1056
|
+
new_padstack_data.SetPadParameters(
|
|
1057
|
+
layer, pad_type, geom_type, parameters, offset_x, offset_y, rotation
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
updated_thermal_pad = self.thermalpad_by_layer[layer]
|
|
1061
|
+
if not updated_thermal_pad.geometry_type == 0: # pragma no cover
|
|
1062
|
+
pad_type = self._edb.definition.PadType.ThermalPad
|
|
1063
|
+
geom_type = self.pad_by_layer[layer]._pad_parameter_value[1]
|
|
1064
|
+
parameters = self.pad_by_layer[layer]._pad_parameter_value[2]
|
|
1065
|
+
offset_x = self.pad_by_layer[layer]._pad_parameter_value[3]
|
|
1066
|
+
offset_y = self.pad_by_layer[layer]._pad_parameter_value[4]
|
|
1067
|
+
rotation = self.pad_by_layer[layer]._pad_parameter_value[5]
|
|
1068
|
+
if layer == old_name: # pragma no cover
|
|
1069
|
+
new_padstack_data.SetPadParameters(
|
|
1070
|
+
updated_name, pad_type, geom_type, parameters, offset_x, offset_y, rotation
|
|
1071
|
+
)
|
|
1072
|
+
else:
|
|
1073
|
+
new_padstack_data.SetPadParameters(
|
|
1074
|
+
layer, pad_type, geom_type, parameters, offset_x, offset_y, rotation
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
hole_param = cloned_padstack_data.GetHoleParameters()
|
|
1078
|
+
if hole_param[0]:
|
|
1079
|
+
hole_geom = hole_param[1]
|
|
1080
|
+
hole_params = convert_py_list_to_net_list([self._get_edb_value(i) for i in hole_param[2]])
|
|
1081
|
+
hole_off_x = self._get_edb_value(hole_param[3])
|
|
1082
|
+
hole_off_y = self._get_edb_value(hole_param[4])
|
|
1083
|
+
hole_rot = self._get_edb_value(hole_param[5])
|
|
1084
|
+
new_padstack_data.SetHoleParameters(hole_geom, hole_params, hole_off_x, hole_off_y, hole_rot)
|
|
1085
|
+
|
|
1086
|
+
new_padstack_data.SetHolePlatingPercentage(self._get_edb_value(cloned_padstack_data.GetHolePlatingPercentage()))
|
|
1087
|
+
|
|
1088
|
+
new_padstack_data.SetHoleRange(cloned_padstack_data.GetHoleRange())
|
|
1089
|
+
new_padstack_data.SetMaterial(cloned_padstack_data.GetMaterial())
|
|
1090
|
+
new_padstack_data.SetSolderBallMaterial(cloned_padstack_data.GetSolderBallMaterial())
|
|
1091
|
+
solder_ball_param = cloned_padstack_data.GetSolderBallParameter()
|
|
1092
|
+
if solder_ball_param[0]:
|
|
1093
|
+
new_padstack_data.SetSolderBallParameter(
|
|
1094
|
+
self._get_edb_value(solder_ball_param[1]), self._get_edb_value(solder_ball_param[2])
|
|
1095
|
+
)
|
|
1096
|
+
new_padstack_data.SetSolderBallPlacement(cloned_padstack_data.GetSolderBallPlacement())
|
|
1097
|
+
new_padstack_data.SetSolderBallShape(cloned_padstack_data.GetSolderBallShape())
|
|
1098
|
+
self.edb_padstack.SetData(new_padstack_data)
|
|
1099
|
+
return True
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
class EDBPadstackInstance(EDBPrimitivesMain):
|
|
1103
|
+
"""Manages EDB functionalities for a padstack.
|
|
1104
|
+
|
|
1105
|
+
Parameters
|
|
1106
|
+
----------
|
|
1107
|
+
edb_padstackinstance :
|
|
1108
|
+
|
|
1109
|
+
_pedb :
|
|
1110
|
+
Inherited AEDT object.
|
|
1111
|
+
|
|
1112
|
+
Examples
|
|
1113
|
+
--------
|
|
1114
|
+
>>> from pyedb import Edb
|
|
1115
|
+
>>> edb = Edb(myedb, edbversion="2021.2")
|
|
1116
|
+
>>> edb_padstack_instance = edb.padstacks.instances[0]
|
|
1117
|
+
"""
|
|
1118
|
+
|
|
1119
|
+
def __init__(self, edb_padstackinstance, _pedb):
|
|
1120
|
+
super().__init__(edb_padstackinstance, _pedb)
|
|
1121
|
+
self._edb_padstackinstance = self._edb_object
|
|
1122
|
+
self._bounding_box = []
|
|
1123
|
+
self._object_instance = None
|
|
1124
|
+
self._position = []
|
|
1125
|
+
self._pdef = None
|
|
1126
|
+
|
|
1127
|
+
def get_terminal(self, name=None, create_new_terminal=False):
|
|
1128
|
+
"""Get PadstackInstanceTerminal object.
|
|
1129
|
+
|
|
1130
|
+
Parameters
|
|
1131
|
+
----------
|
|
1132
|
+
name : str, optional
|
|
1133
|
+
Name of the terminal. Only applicable when create_new_terminal is True.
|
|
1134
|
+
create_new_terminal : bool, optional
|
|
1135
|
+
Whether to create a new terminal.
|
|
1136
|
+
|
|
1137
|
+
Returns
|
|
1138
|
+
-------
|
|
1139
|
+
:class:`pyedb.dotnet.edb_core.edb_data.terminals`
|
|
1140
|
+
"""
|
|
1141
|
+
|
|
1142
|
+
if create_new_terminal:
|
|
1143
|
+
term = self._create_terminal(name)
|
|
1144
|
+
else:
|
|
1145
|
+
from pyedb.dotnet.edb_core.edb_data.terminals import (
|
|
1146
|
+
PadstackInstanceTerminal,
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
term = PadstackInstanceTerminal(self._pedb, self._edb_object.GetPadstackInstanceTerminal())
|
|
1150
|
+
if not term.is_null:
|
|
1151
|
+
return term
|
|
1152
|
+
|
|
1153
|
+
@pyedb_function_handler()
|
|
1154
|
+
def _create_terminal(self, name=None):
|
|
1155
|
+
"""Create a padstack instance terminal"""
|
|
1156
|
+
from pyedb.dotnet.edb_core.edb_data.terminals import PadstackInstanceTerminal
|
|
1157
|
+
|
|
1158
|
+
term = PadstackInstanceTerminal(self._pedb, self._edb_object.GetPadstackInstanceTerminal())
|
|
1159
|
+
return term.create(self, name)
|
|
1160
|
+
|
|
1161
|
+
@pyedb_function_handler()
|
|
1162
|
+
def create_coax_port(self, name=None, radial_extent_factor=0):
|
|
1163
|
+
"""Create a coax port."""
|
|
1164
|
+
port = self.create_port(name)
|
|
1165
|
+
port.radial_extent_factor = radial_extent_factor
|
|
1166
|
+
return port
|
|
1167
|
+
|
|
1168
|
+
@pyedb_function_handler
|
|
1169
|
+
def create_port(self, name=None, reference=None, is_circuit_port=False):
|
|
1170
|
+
"""Create a port on the padstack.
|
|
1171
|
+
|
|
1172
|
+
Parameters
|
|
1173
|
+
----------
|
|
1174
|
+
name : str, optional
|
|
1175
|
+
Name of the port. The default is ``None``, in which case a name is automatically assigned.
|
|
1176
|
+
reference : class:`pyedb.dotnet.edb_core.edb_data.nets_data.EDBNetsData`, \
|
|
1177
|
+
class:`pyedb.dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`, \
|
|
1178
|
+
class:`pyedb.dotnet.edb_core.edb_data.sources.PinGroup`, optional
|
|
1179
|
+
Negative terminal of the port.
|
|
1180
|
+
is_circuit_port : bool, optional
|
|
1181
|
+
Whether it is a circuit port.
|
|
1182
|
+
"""
|
|
1183
|
+
terminal = self._create_terminal(name)
|
|
1184
|
+
if reference:
|
|
1185
|
+
ref_terminal = reference._create_terminal(terminal.name + "_ref")
|
|
1186
|
+
if reference._edb_object.ToString() == "PinGroup":
|
|
1187
|
+
is_circuit_port = True
|
|
1188
|
+
else:
|
|
1189
|
+
ref_terminal = None
|
|
1190
|
+
|
|
1191
|
+
return self._pedb.create_port(terminal, ref_terminal, is_circuit_port)
|
|
1192
|
+
|
|
1193
|
+
@property
|
|
1194
|
+
def _em_properties(self):
|
|
1195
|
+
"""Get EM properties."""
|
|
1196
|
+
default = (
|
|
1197
|
+
r"$begin 'EM properties'\n"
|
|
1198
|
+
r"\tType('Mesh')\n"
|
|
1199
|
+
r"\tDataId='EM properties1'\n"
|
|
1200
|
+
r"\t$begin 'Properties'\n"
|
|
1201
|
+
r"\t\tGeneral=''\n"
|
|
1202
|
+
r"\t\tModeled='true'\n"
|
|
1203
|
+
r"\t\tUnion='true'\n"
|
|
1204
|
+
r"\t\t'Use Precedence'='false'\n"
|
|
1205
|
+
r"\t\t'Precedence Value'='1'\n"
|
|
1206
|
+
r"\t\tPlanarEM=''\n"
|
|
1207
|
+
r"\t\tRefined='true'\n"
|
|
1208
|
+
r"\t\tRefineFactor='1'\n"
|
|
1209
|
+
r"\t\tNoEdgeMesh='false'\n"
|
|
1210
|
+
r"\t\tHFSS=''\n"
|
|
1211
|
+
r"\t\t'Solve Inside'='false'\n"
|
|
1212
|
+
r"\t\tSIwave=''\n"
|
|
1213
|
+
r"\t\t'DCIR Equipotential Region'='false'\n"
|
|
1214
|
+
r"\t$end 'Properties'\n"
|
|
1215
|
+
r"$end 'EM properties'\n"
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
pid = self._pedb.edb_api.ProductId.Designer
|
|
1219
|
+
_, p = self._edb_padstackinstance.GetProductProperty(pid, 18, "")
|
|
1220
|
+
if p:
|
|
1221
|
+
return p
|
|
1222
|
+
else:
|
|
1223
|
+
return default
|
|
1224
|
+
|
|
1225
|
+
@_em_properties.setter
|
|
1226
|
+
def _em_properties(self, em_prop):
|
|
1227
|
+
"""Set EM properties"""
|
|
1228
|
+
pid = self._pedb.edb_api.ProductId.Designer
|
|
1229
|
+
self._edb_padstackinstance.SetProductProperty(pid, 18, em_prop)
|
|
1230
|
+
|
|
1231
|
+
@property
|
|
1232
|
+
def dcir_equipotential_region(self):
|
|
1233
|
+
"""Check whether dcir equipotential region is enabled.
|
|
1234
|
+
|
|
1235
|
+
Returns
|
|
1236
|
+
-------
|
|
1237
|
+
bool
|
|
1238
|
+
"""
|
|
1239
|
+
pattern = r"'DCIR Equipotential Region'='([^']+)'"
|
|
1240
|
+
em_pp = self._em_properties
|
|
1241
|
+
result = re.search(pattern, em_pp).group(1)
|
|
1242
|
+
if result == "true":
|
|
1243
|
+
return True
|
|
1244
|
+
else:
|
|
1245
|
+
return False
|
|
1246
|
+
|
|
1247
|
+
@dcir_equipotential_region.setter
|
|
1248
|
+
def dcir_equipotential_region(self, value):
|
|
1249
|
+
"""Set dcir equipotential region."""
|
|
1250
|
+
pp = r"'DCIR Equipotential Region'='true'" if value else r"'DCIR Equipotential Region'='false'"
|
|
1251
|
+
em_pp = self._em_properties
|
|
1252
|
+
pattern = r"'DCIR Equipotential Region'='([^']+)'"
|
|
1253
|
+
new_em_pp = re.sub(pattern, pp, em_pp)
|
|
1254
|
+
self._em_properties = new_em_pp
|
|
1255
|
+
|
|
1256
|
+
@property
|
|
1257
|
+
def object_instance(self):
|
|
1258
|
+
"""Return Ansys.Ansoft.Edb.LayoutInstance.LayoutObjInstance object."""
|
|
1259
|
+
if not self._object_instance:
|
|
1260
|
+
self._object_instance = (
|
|
1261
|
+
self._edb_padstackinstance.GetLayout()
|
|
1262
|
+
.GetLayoutInstance()
|
|
1263
|
+
.GetLayoutObjInstance(self._edb_padstackinstance, None)
|
|
1264
|
+
)
|
|
1265
|
+
return self._object_instance
|
|
1266
|
+
|
|
1267
|
+
@property
|
|
1268
|
+
def bounding_box(self):
|
|
1269
|
+
"""Get bounding box of the padstack instance.
|
|
1270
|
+
Because this method is slow, the bounding box is stored in a variable and reused.
|
|
1271
|
+
|
|
1272
|
+
Returns
|
|
1273
|
+
-------
|
|
1274
|
+
list of float
|
|
1275
|
+
"""
|
|
1276
|
+
if self._bounding_box:
|
|
1277
|
+
return self._bounding_box
|
|
1278
|
+
bbox = self.object_instance.GetBBox()
|
|
1279
|
+
self._bounding_box = [
|
|
1280
|
+
[bbox.Item1.X.ToDouble(), bbox.Item1.Y.ToDouble()],
|
|
1281
|
+
[bbox.Item2.X.ToDouble(), bbox.Item2.Y.ToDouble()],
|
|
1282
|
+
]
|
|
1283
|
+
return self._bounding_box
|
|
1284
|
+
|
|
1285
|
+
@pyedb_function_handler()
|
|
1286
|
+
def in_polygon(self, polygon_data, include_partial=True, simple_check=False):
|
|
1287
|
+
"""Check if padstack Instance is in given polygon data.
|
|
1288
|
+
|
|
1289
|
+
Parameters
|
|
1290
|
+
----------
|
|
1291
|
+
polygon_data : PolygonData Object
|
|
1292
|
+
include_partial : bool, optional
|
|
1293
|
+
Whether to include partial intersecting instances. The default is ``True``.
|
|
1294
|
+
simple_check : bool, optional
|
|
1295
|
+
Whether to perform a single check based on the padstack center or check the padstack bounding box.
|
|
1296
|
+
|
|
1297
|
+
Returns
|
|
1298
|
+
-------
|
|
1299
|
+
bool
|
|
1300
|
+
``True`` when successful, ``False`` when failed.
|
|
1301
|
+
"""
|
|
1302
|
+
pos = [i for i in self.position]
|
|
1303
|
+
int_val = 1 if polygon_data.PointInPolygon(self._pedb.point_data(*pos)) else 0
|
|
1304
|
+
if int_val == 0:
|
|
1305
|
+
return False
|
|
1306
|
+
|
|
1307
|
+
if simple_check:
|
|
1308
|
+
# pos = [i for i in self.position]
|
|
1309
|
+
# int_val = 1 if polygon_data.PointInPolygon(self._pedb.point_data(*pos)) else 0
|
|
1310
|
+
return True
|
|
1311
|
+
else:
|
|
1312
|
+
plane = self._pedb.modeler.Shape("rectangle", pointA=self.bounding_box[0], pointB=self.bounding_box[1])
|
|
1313
|
+
rectangle_data = self._pedb.modeler.shape_to_polygon_data(plane)
|
|
1314
|
+
int_val = polygon_data.GetIntersectionType(rectangle_data)
|
|
1315
|
+
# Intersection type:
|
|
1316
|
+
# 0 = objects do not intersect
|
|
1317
|
+
# 1 = this object fully inside other (no common contour points)
|
|
1318
|
+
# 2 = other object fully inside this
|
|
1319
|
+
# 3 = common contour points 4 = undefined intersection
|
|
1320
|
+
if int_val == 0:
|
|
1321
|
+
return False
|
|
1322
|
+
elif include_partial:
|
|
1323
|
+
return True
|
|
1324
|
+
elif int_val < 3:
|
|
1325
|
+
return True
|
|
1326
|
+
else:
|
|
1327
|
+
return False
|
|
1328
|
+
|
|
1329
|
+
@property
|
|
1330
|
+
def pin(self):
|
|
1331
|
+
"""EDB padstack object."""
|
|
1332
|
+
warnings.warn("`pin` is deprecated.", DeprecationWarning)
|
|
1333
|
+
return self._edb_padstackinstance
|
|
1334
|
+
|
|
1335
|
+
@property
|
|
1336
|
+
def padstack_definition(self):
|
|
1337
|
+
"""Padstack definition.
|
|
1338
|
+
|
|
1339
|
+
Returns
|
|
1340
|
+
-------
|
|
1341
|
+
str
|
|
1342
|
+
Name of the padstack definition.
|
|
1343
|
+
"""
|
|
1344
|
+
self._pdef = self._edb_padstackinstance.GetPadstackDef().GetName()
|
|
1345
|
+
return self._pdef
|
|
1346
|
+
|
|
1347
|
+
@property
|
|
1348
|
+
def backdrill_top(self):
|
|
1349
|
+
"""Backdrill layer from top.
|
|
1350
|
+
|
|
1351
|
+
Returns
|
|
1352
|
+
-------
|
|
1353
|
+
tuple
|
|
1354
|
+
Tuple of the layer name, drill diameter, and offset if it exists.
|
|
1355
|
+
"""
|
|
1356
|
+
layer = self._pedb.edb_api.cell.layer("", self._pedb.edb_api.cell.layer_type.SignalLayer)
|
|
1357
|
+
val = self._pedb.edb_value(0)
|
|
1358
|
+
offset = self._pedb.edb_value(0.0)
|
|
1359
|
+
if is_ironpython: # pragma: no cover
|
|
1360
|
+
diameter = _clr.StrongBox[type(val)]()
|
|
1361
|
+
drill_to_layer = _clr.StrongBox[self._pedb.edb_api.Cell.ILayerReadOnly]()
|
|
1362
|
+
flag = self._edb_padstackinstance.GetBackDrillParametersLayerValue(drill_to_layer, offset, diameter, False)
|
|
1363
|
+
else:
|
|
1364
|
+
(
|
|
1365
|
+
flag,
|
|
1366
|
+
drill_to_layer,
|
|
1367
|
+
offset,
|
|
1368
|
+
diameter,
|
|
1369
|
+
) = self._edb_padstackinstance.GetBackDrillParametersLayerValue(layer, offset, val, False)
|
|
1370
|
+
if flag:
|
|
1371
|
+
if offset.ToDouble():
|
|
1372
|
+
return drill_to_layer.GetName(), diameter.ToString(), offset.ToString()
|
|
1373
|
+
else:
|
|
1374
|
+
return drill_to_layer.GetName(), diameter.ToString()
|
|
1375
|
+
else:
|
|
1376
|
+
return
|
|
1377
|
+
|
|
1378
|
+
def set_backdrill_top(self, drill_depth, drill_diameter, offset=0.0):
|
|
1379
|
+
"""Set backdrill from top.
|
|
1380
|
+
|
|
1381
|
+
Parameters
|
|
1382
|
+
----------
|
|
1383
|
+
drill_depth : str
|
|
1384
|
+
Name of the drill to layer.
|
|
1385
|
+
drill_diameter : float, str
|
|
1386
|
+
Diameter of backdrill size.
|
|
1387
|
+
offset : float, str
|
|
1388
|
+
Offset for the backdrill. The default is ``0.0``. If the value is other than the
|
|
1389
|
+
default, the stub does not stop at the layer. In AEDT, this parameter is called
|
|
1390
|
+
"Mfg stub length".
|
|
1391
|
+
|
|
1392
|
+
Returns
|
|
1393
|
+
-------
|
|
1394
|
+
bool
|
|
1395
|
+
True if success, False otherwise.
|
|
1396
|
+
"""
|
|
1397
|
+
layer = self._pedb.stackup.layers[drill_depth]._edb_layer
|
|
1398
|
+
val = self._pedb.edb_value(drill_diameter)
|
|
1399
|
+
offset = self._pedb.edb_value(offset)
|
|
1400
|
+
if offset.ToDouble():
|
|
1401
|
+
return self._edb_padstackinstance.SetBackDrillParameters(layer, offset, val, False)
|
|
1402
|
+
else:
|
|
1403
|
+
return self._edb_padstackinstance.SetBackDrillParameters(layer, val, False)
|
|
1404
|
+
|
|
1405
|
+
@property
|
|
1406
|
+
def backdrill_bottom(self):
|
|
1407
|
+
"""Backdrill layer from bottom.
|
|
1408
|
+
|
|
1409
|
+
Returns
|
|
1410
|
+
-------
|
|
1411
|
+
tuple
|
|
1412
|
+
Tuple of the layer name, drill diameter, and drill offset if it exists.
|
|
1413
|
+
"""
|
|
1414
|
+
layer = self._pedb.edb_api.cell.layer("", self._pedb.edb_api.cell.layer_type.SignalLayer)
|
|
1415
|
+
val = self._pedb.edb_value(0)
|
|
1416
|
+
offset = self._pedb.edb_value(0.0)
|
|
1417
|
+
if is_ironpython: # pragma: no cover
|
|
1418
|
+
diameter = _clr.StrongBox[type(val)]()
|
|
1419
|
+
drill_to_layer = _clr.StrongBox[self._pedb.edb_api.Cell.ILayerReadOnly]()
|
|
1420
|
+
flag = self._edb_padstackinstance.GetBackDrillParametersLayerValue(drill_to_layer, offset, diameter, True)
|
|
1421
|
+
else:
|
|
1422
|
+
(
|
|
1423
|
+
flag,
|
|
1424
|
+
drill_to_layer,
|
|
1425
|
+
offset,
|
|
1426
|
+
diameter,
|
|
1427
|
+
) = self._edb_padstackinstance.GetBackDrillParametersLayerValue(layer, offset, val, True)
|
|
1428
|
+
if flag:
|
|
1429
|
+
if offset.ToDouble():
|
|
1430
|
+
return drill_to_layer.GetName(), diameter.ToString(), offset.ToString()
|
|
1431
|
+
else:
|
|
1432
|
+
return drill_to_layer.GetName(), diameter.ToString()
|
|
1433
|
+
else:
|
|
1434
|
+
return
|
|
1435
|
+
|
|
1436
|
+
def set_backdrill_bottom(self, drill_depth, drill_diameter, offset=0.0):
|
|
1437
|
+
"""Set backdrill from bottom.
|
|
1438
|
+
|
|
1439
|
+
Parameters
|
|
1440
|
+
----------
|
|
1441
|
+
drill_depth : str
|
|
1442
|
+
Name of the drill to layer.
|
|
1443
|
+
drill_diameter : float, str
|
|
1444
|
+
Diameter of the backdrill size.
|
|
1445
|
+
offset : float, str, optional
|
|
1446
|
+
Offset for the backdrill. The default is ``0.0``. If the value is other than the
|
|
1447
|
+
default, the stub does not stop at the layer. In AEDT, this parameter is called
|
|
1448
|
+
"Mfg stub length".
|
|
1449
|
+
|
|
1450
|
+
Returns
|
|
1451
|
+
-------
|
|
1452
|
+
bool
|
|
1453
|
+
True if success, False otherwise.
|
|
1454
|
+
"""
|
|
1455
|
+
layer = self._pedb.stackup.layers[drill_depth]._edb_layer
|
|
1456
|
+
val = self._pedb.edb_value(drill_diameter)
|
|
1457
|
+
offset = self._pedb.edb_value(offset)
|
|
1458
|
+
if offset.ToDouble():
|
|
1459
|
+
return self._edb_padstackinstance.SetBackDrillParameters(layer, offset, val, True)
|
|
1460
|
+
else:
|
|
1461
|
+
return self._edb_padstackinstance.SetBackDrillParameters(layer, val, True)
|
|
1462
|
+
|
|
1463
|
+
@property
|
|
1464
|
+
def start_layer(self):
|
|
1465
|
+
"""Starting layer.
|
|
1466
|
+
|
|
1467
|
+
Returns
|
|
1468
|
+
-------
|
|
1469
|
+
str
|
|
1470
|
+
Name of the starting layer.
|
|
1471
|
+
"""
|
|
1472
|
+
layer = self._pedb.edb_api.cell.layer("", self._pedb.edb_api.cell.layer_type.SignalLayer)
|
|
1473
|
+
_, start_layer, stop_layer = self._edb_padstackinstance.GetLayerRange()
|
|
1474
|
+
|
|
1475
|
+
if start_layer:
|
|
1476
|
+
return start_layer.GetName()
|
|
1477
|
+
return None
|
|
1478
|
+
|
|
1479
|
+
@start_layer.setter
|
|
1480
|
+
def start_layer(self, layer_name):
|
|
1481
|
+
stop_layer = self._pedb.stackup.signal_layers[self.stop_layer]._edb_layer
|
|
1482
|
+
layer = self._pedb.stackup.signal_layers[layer_name]._edb_layer
|
|
1483
|
+
self._edb_padstackinstance.SetLayerRange(layer, stop_layer)
|
|
1484
|
+
|
|
1485
|
+
@property
|
|
1486
|
+
def stop_layer(self):
|
|
1487
|
+
"""Stopping layer.
|
|
1488
|
+
|
|
1489
|
+
Returns
|
|
1490
|
+
-------
|
|
1491
|
+
str
|
|
1492
|
+
Name of the stopping layer.
|
|
1493
|
+
"""
|
|
1494
|
+
layer = self._pedb.edb_api.cell.layer("", self._pedb.edb_api.cell.layer_type.SignalLayer)
|
|
1495
|
+
_, start_layer, stop_layer = self._edb_padstackinstance.GetLayerRange()
|
|
1496
|
+
|
|
1497
|
+
if stop_layer:
|
|
1498
|
+
return stop_layer.GetName()
|
|
1499
|
+
return None
|
|
1500
|
+
|
|
1501
|
+
@stop_layer.setter
|
|
1502
|
+
def stop_layer(self, layer_name):
|
|
1503
|
+
start_layer = self._pedb.stackup.signal_layers[self.start_layer]._edb_layer
|
|
1504
|
+
layer = self._pedb.stackup.signal_layers[layer_name]._edb_layer
|
|
1505
|
+
self._edb_padstackinstance.SetLayerRange(start_layer, layer)
|
|
1506
|
+
|
|
1507
|
+
@property
|
|
1508
|
+
def layer_range_names(self):
|
|
1509
|
+
"""List of all layers to which the padstack instance belongs."""
|
|
1510
|
+
_, start_layer, stop_layer = self._edb_padstackinstance.GetLayerRange()
|
|
1511
|
+
started = False
|
|
1512
|
+
layer_list = []
|
|
1513
|
+
start_layer_name = start_layer.GetName()
|
|
1514
|
+
stop_layer_name = stop_layer.GetName()
|
|
1515
|
+
for layer_name in list(self._pedb.stackup.layers.keys()):
|
|
1516
|
+
if started:
|
|
1517
|
+
layer_list.append(layer_name)
|
|
1518
|
+
if layer_name == stop_layer_name or layer_name == start_layer_name:
|
|
1519
|
+
break
|
|
1520
|
+
elif layer_name == start_layer_name:
|
|
1521
|
+
started = True
|
|
1522
|
+
layer_list.append(layer_name)
|
|
1523
|
+
if layer_name == stop_layer_name:
|
|
1524
|
+
break
|
|
1525
|
+
elif layer_name == stop_layer_name:
|
|
1526
|
+
started = True
|
|
1527
|
+
layer_list.append(layer_name)
|
|
1528
|
+
if layer_name == start_layer_name:
|
|
1529
|
+
break
|
|
1530
|
+
return layer_list
|
|
1531
|
+
|
|
1532
|
+
@property
|
|
1533
|
+
def net_name(self):
|
|
1534
|
+
"""Net name.
|
|
1535
|
+
|
|
1536
|
+
Returns
|
|
1537
|
+
-------
|
|
1538
|
+
str
|
|
1539
|
+
Name of the net.
|
|
1540
|
+
"""
|
|
1541
|
+
return self._edb_padstackinstance.GetNet().GetName()
|
|
1542
|
+
|
|
1543
|
+
@net_name.setter
|
|
1544
|
+
def net_name(self, val):
|
|
1545
|
+
if not isinstance(val, str):
|
|
1546
|
+
try:
|
|
1547
|
+
self._edb_padstackinstance.SetNet(val.net_obj)
|
|
1548
|
+
except:
|
|
1549
|
+
raise AttributeError("Value inserted not found. Input has to be net name or net object.")
|
|
1550
|
+
elif val in self._pedb.nets.netlist:
|
|
1551
|
+
net = self._pedb.nets.nets[val].net_object
|
|
1552
|
+
self._edb_padstackinstance.SetNet(net)
|
|
1553
|
+
else:
|
|
1554
|
+
raise AttributeError("Value inserted not found. Input has to be net name or net object.")
|
|
1555
|
+
|
|
1556
|
+
@property
|
|
1557
|
+
def is_pin(self):
|
|
1558
|
+
"""Determines whether this padstack instance is a layout pin.
|
|
1559
|
+
|
|
1560
|
+
Returns
|
|
1561
|
+
-------
|
|
1562
|
+
bool
|
|
1563
|
+
True if this padstack type is a layout pin, False otherwise.
|
|
1564
|
+
"""
|
|
1565
|
+
return self._edb_padstackinstance.IsLayoutPin()
|
|
1566
|
+
|
|
1567
|
+
@is_pin.setter
|
|
1568
|
+
def is_pin(self, pin):
|
|
1569
|
+
"""Set padstack type
|
|
1570
|
+
|
|
1571
|
+
Parameters
|
|
1572
|
+
----------
|
|
1573
|
+
pin : bool
|
|
1574
|
+
True if set this padstack instance as pin, False otherwise
|
|
1575
|
+
"""
|
|
1576
|
+
self._edb_padstackinstance.SetIsLayoutPin(pin)
|
|
1577
|
+
|
|
1578
|
+
@property
|
|
1579
|
+
def position(self):
|
|
1580
|
+
"""Padstack instance position.
|
|
1581
|
+
|
|
1582
|
+
Returns
|
|
1583
|
+
-------
|
|
1584
|
+
list
|
|
1585
|
+
List of ``[x, y]`` coordinates for the padstack instance position.
|
|
1586
|
+
"""
|
|
1587
|
+
self._position = []
|
|
1588
|
+
out = self._edb_padstackinstance.GetPositionAndRotationValue()
|
|
1589
|
+
if self._edb_padstackinstance.GetComponent():
|
|
1590
|
+
out2 = self._edb_padstackinstance.GetComponent().GetTransform().TransformPoint(out[1])
|
|
1591
|
+
self._position = [out2.X.ToDouble(), out2.Y.ToDouble()]
|
|
1592
|
+
elif out[0]:
|
|
1593
|
+
self._position = [out[1].X.ToDouble(), out[1].Y.ToDouble()]
|
|
1594
|
+
return self._position
|
|
1595
|
+
|
|
1596
|
+
@position.setter
|
|
1597
|
+
def position(self, value):
|
|
1598
|
+
pos = []
|
|
1599
|
+
for v in value:
|
|
1600
|
+
if isinstance(v, (float, int, str)):
|
|
1601
|
+
pos.append(self._pedb.edb_value(v))
|
|
1602
|
+
else:
|
|
1603
|
+
pos.append(v)
|
|
1604
|
+
point_data = self._pedb.edb_api.geometry.point_data(pos[0], pos[1])
|
|
1605
|
+
self._edb_padstackinstance.SetPositionAndRotation(point_data, self._pedb.edb_value(self.rotation))
|
|
1606
|
+
|
|
1607
|
+
@property
|
|
1608
|
+
def rotation(self):
|
|
1609
|
+
"""Padstack instance rotation.
|
|
1610
|
+
|
|
1611
|
+
Returns
|
|
1612
|
+
-------
|
|
1613
|
+
float
|
|
1614
|
+
Rotatation value for the padstack instance.
|
|
1615
|
+
"""
|
|
1616
|
+
point_data = self._pedb.edb_api.geometry.point_data(self._pedb.edb_value(0.0), self._pedb.edb_value(0.0))
|
|
1617
|
+
out = self._edb_padstackinstance.GetPositionAndRotationValue()
|
|
1618
|
+
|
|
1619
|
+
if out[0]:
|
|
1620
|
+
return out[2].ToDouble()
|
|
1621
|
+
|
|
1622
|
+
@property
|
|
1623
|
+
def name(self):
|
|
1624
|
+
"""Padstack Instance Name. If it is a pin, the syntax will be like in AEDT ComponentName-PinName."""
|
|
1625
|
+
if self.is_pin:
|
|
1626
|
+
comp_name = self._edb_padstackinstance.GetComponent().GetName()
|
|
1627
|
+
pin_name = self._edb_padstackinstance.GetName()
|
|
1628
|
+
return "-".join([comp_name, pin_name])
|
|
1629
|
+
else:
|
|
1630
|
+
return self._edb_padstackinstance.GetName()
|
|
1631
|
+
|
|
1632
|
+
@name.setter
|
|
1633
|
+
def name(self, value):
|
|
1634
|
+
self._edb_padstackinstance.SetName(value)
|
|
1635
|
+
self._edb_padstackinstance.SetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, value)
|
|
1636
|
+
|
|
1637
|
+
@property
|
|
1638
|
+
def metal_volume(self):
|
|
1639
|
+
"""Metal volume of the via hole instance in cubic units (m3). Metal plating ratio is accounted.
|
|
1640
|
+
|
|
1641
|
+
Returns
|
|
1642
|
+
-------
|
|
1643
|
+
float
|
|
1644
|
+
Metal volume of the via hole instance.
|
|
1645
|
+
|
|
1646
|
+
"""
|
|
1647
|
+
volume = 0
|
|
1648
|
+
if not self.start_layer == self.stop_layer:
|
|
1649
|
+
start_layer = self.start_layer
|
|
1650
|
+
stop_layer = self.stop_layer
|
|
1651
|
+
if self.backdrill_top: # pragma no cover
|
|
1652
|
+
start_layer = self.backdrill_top[0]
|
|
1653
|
+
if self.backdrill_bottom: # pragma no cover
|
|
1654
|
+
stop_layer = self.backdrill_bottom[0]
|
|
1655
|
+
padstack_def = self._pedb.padstacks.definitions[self.padstack_definition]
|
|
1656
|
+
hole_diam = 0
|
|
1657
|
+
try: # pragma no cover
|
|
1658
|
+
hole_diam = padstack_def.hole_properties[0]
|
|
1659
|
+
except: # pragma no cover
|
|
1660
|
+
pass
|
|
1661
|
+
if hole_diam: # pragma no cover
|
|
1662
|
+
hole_finished_size = padstack_def.hole_finished_size
|
|
1663
|
+
via_length = (
|
|
1664
|
+
self._pedb.stackup.signal_layers[start_layer].upper_elevation
|
|
1665
|
+
- self._pedb.stackup.signal_layers[stop_layer].lower_elevation
|
|
1666
|
+
)
|
|
1667
|
+
volume = (math.pi * (hole_diam / 2) ** 2 - math.pi * (hole_finished_size / 2) ** 2) * via_length
|
|
1668
|
+
return volume
|
|
1669
|
+
|
|
1670
|
+
@property
|
|
1671
|
+
def pin_number(self):
|
|
1672
|
+
"""Get pin number."""
|
|
1673
|
+
return self._edb_padstackinstance.GetName()
|
|
1674
|
+
|
|
1675
|
+
@property
|
|
1676
|
+
def aedt_name(self):
|
|
1677
|
+
"""Retrieve the pin name that is shown in AEDT.
|
|
1678
|
+
|
|
1679
|
+
.. note::
|
|
1680
|
+
To obtain the EDB core pin name, use `pin.GetName()`.
|
|
1681
|
+
|
|
1682
|
+
Returns
|
|
1683
|
+
-------
|
|
1684
|
+
str
|
|
1685
|
+
Name of the pin in AEDT.
|
|
1686
|
+
|
|
1687
|
+
Examples
|
|
1688
|
+
--------
|
|
1689
|
+
|
|
1690
|
+
>>> from pyedb import Edb
|
|
1691
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1692
|
+
>>> edbapp.padstacks.instances[111].get_aedt_pin_name()
|
|
1693
|
+
|
|
1694
|
+
"""
|
|
1695
|
+
if is_ironpython:
|
|
1696
|
+
name = _clr.Reference[String]()
|
|
1697
|
+
self._edb_padstackinstance.GetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, name)
|
|
1698
|
+
else:
|
|
1699
|
+
val = String("")
|
|
1700
|
+
_, name = self._edb_padstackinstance.GetProductProperty(self._pedb.edb_api.ProductId.Designer, 11, val)
|
|
1701
|
+
name = str(name).strip("'")
|
|
1702
|
+
return name
|
|
1703
|
+
|
|
1704
|
+
@pyedb_function_handler()
|
|
1705
|
+
def parametrize_position(self, prefix=None):
|
|
1706
|
+
"""Parametrize the instance position.
|
|
1707
|
+
|
|
1708
|
+
Parameters
|
|
1709
|
+
----------
|
|
1710
|
+
prefix : str, optional
|
|
1711
|
+
Prefix for the variable name. Default is ``None``.
|
|
1712
|
+
Example `"MyVariableName"` will create 2 Project variables $MyVariableNamesX and $MyVariableNamesY.
|
|
1713
|
+
|
|
1714
|
+
Returns
|
|
1715
|
+
-------
|
|
1716
|
+
List
|
|
1717
|
+
List of variables created.
|
|
1718
|
+
"""
|
|
1719
|
+
p = self.position
|
|
1720
|
+
if not prefix:
|
|
1721
|
+
var_name = "${}_pos".format(self.name)
|
|
1722
|
+
else:
|
|
1723
|
+
var_name = "${}".format(prefix)
|
|
1724
|
+
self._pedb.add_project_variable(var_name + "X", p[0])
|
|
1725
|
+
self._pedb.add_project_variable(var_name + "Y", p[1])
|
|
1726
|
+
self.position = [var_name + "X", var_name + "Y"]
|
|
1727
|
+
return [var_name + "X", var_name + "Y"]
|
|
1728
|
+
|
|
1729
|
+
@pyedb_function_handler()
|
|
1730
|
+
def delete_padstack_instance(self):
|
|
1731
|
+
"""Delete this padstack instance.
|
|
1732
|
+
|
|
1733
|
+
.. deprecated:: 0.6.28
|
|
1734
|
+
Use :func:`delete` property instead.
|
|
1735
|
+
"""
|
|
1736
|
+
warnings.warn("`delete_padstack_instance` is deprecated. Use `delete` instead.", DeprecationWarning)
|
|
1737
|
+
self._edb_padstackinstance.Delete()
|
|
1738
|
+
return True
|
|
1739
|
+
|
|
1740
|
+
@pyedb_function_handler()
|
|
1741
|
+
def in_voids(self, net_name=None, layer_name=None):
|
|
1742
|
+
"""Check if this padstack instance is in any void.
|
|
1743
|
+
|
|
1744
|
+
Parameters
|
|
1745
|
+
----------
|
|
1746
|
+
net_name : str
|
|
1747
|
+
Net name of the voids to be checked. Default is ``None``.
|
|
1748
|
+
layer_name : str
|
|
1749
|
+
Layer name of the voids to be checked. Default is ``None``.
|
|
1750
|
+
|
|
1751
|
+
Returns
|
|
1752
|
+
-------
|
|
1753
|
+
list
|
|
1754
|
+
List of the voids that include this padstack instance.
|
|
1755
|
+
"""
|
|
1756
|
+
x_pos = self._pedb.edb_value(self.position[0])
|
|
1757
|
+
y_pos = self._pedb.edb_value(self.position[1])
|
|
1758
|
+
point_data = self._pedb.modeler._edb.geometry.point_data(x_pos, y_pos)
|
|
1759
|
+
|
|
1760
|
+
voids = []
|
|
1761
|
+
for prim in self._pedb.modeler.get_primitives(net_name, layer_name, is_void=True):
|
|
1762
|
+
if prim.primitive_object.GetPolygonData().PointInPolygon(point_data):
|
|
1763
|
+
voids.append(prim)
|
|
1764
|
+
return voids
|
|
1765
|
+
|
|
1766
|
+
@property
|
|
1767
|
+
def pingroups(self):
|
|
1768
|
+
"""Pin groups that the pin belongs to.
|
|
1769
|
+
|
|
1770
|
+
Returns
|
|
1771
|
+
-------
|
|
1772
|
+
list
|
|
1773
|
+
List of pin groups that the pin belongs to.
|
|
1774
|
+
"""
|
|
1775
|
+
return self._edb_padstackinstance.GetPinGroups()
|
|
1776
|
+
|
|
1777
|
+
@property
|
|
1778
|
+
def placement_layer(self):
|
|
1779
|
+
"""Placement layer.
|
|
1780
|
+
|
|
1781
|
+
Returns
|
|
1782
|
+
-------
|
|
1783
|
+
str
|
|
1784
|
+
Name of the placement layer.
|
|
1785
|
+
"""
|
|
1786
|
+
return self._edb_padstackinstance.GetGroup().GetPlacementLayer().Clone().GetName()
|
|
1787
|
+
|
|
1788
|
+
@property
|
|
1789
|
+
def lower_elevation(self):
|
|
1790
|
+
"""Lower elevation of the placement layer.
|
|
1791
|
+
|
|
1792
|
+
Returns
|
|
1793
|
+
-------
|
|
1794
|
+
float
|
|
1795
|
+
Lower elavation of the placement layer.
|
|
1796
|
+
"""
|
|
1797
|
+
try:
|
|
1798
|
+
return self._edb_padstackinstance.GetGroup().GetPlacementLayer().Clone().GetLowerElevation()
|
|
1799
|
+
except AttributeError: # pragma: no cover
|
|
1800
|
+
return None
|
|
1801
|
+
|
|
1802
|
+
@property
|
|
1803
|
+
def upper_elevation(self):
|
|
1804
|
+
"""Upper elevation of the placement layer.
|
|
1805
|
+
|
|
1806
|
+
Returns
|
|
1807
|
+
-------
|
|
1808
|
+
float
|
|
1809
|
+
Upper elevation of the placement layer.
|
|
1810
|
+
"""
|
|
1811
|
+
try:
|
|
1812
|
+
return self._edb_padstackinstance.GetGroup().GetPlacementLayer().Clone().GetUpperElevation()
|
|
1813
|
+
except AttributeError: # pragma: no cover
|
|
1814
|
+
return None
|
|
1815
|
+
|
|
1816
|
+
@property
|
|
1817
|
+
def top_bottom_association(self):
|
|
1818
|
+
"""Top/bottom association of the placement layer.
|
|
1819
|
+
|
|
1820
|
+
Returns
|
|
1821
|
+
-------
|
|
1822
|
+
int
|
|
1823
|
+
Top/bottom association of the placement layer.
|
|
1824
|
+
|
|
1825
|
+
* 0 Top associated.
|
|
1826
|
+
* 1 No association.
|
|
1827
|
+
* 2 Bottom associated.
|
|
1828
|
+
* 4 Number of top/bottom association type.
|
|
1829
|
+
* -1 Undefined.
|
|
1830
|
+
"""
|
|
1831
|
+
return int(self._edb_padstackinstance.GetGroup().GetPlacementLayer().GetTopBottomAssociation())
|
|
1832
|
+
|
|
1833
|
+
@pyedb_function_handler()
|
|
1834
|
+
def create_rectangle_in_pad(self, layer_name, return_points=False, partition_max_order=16):
|
|
1835
|
+
"""Create a rectangle inscribed inside a padstack instance pad.
|
|
1836
|
+
|
|
1837
|
+
The rectangle is fully inscribed in the pad and has the maximum area.
|
|
1838
|
+
It is necessary to specify the layer on which the rectangle will be created.
|
|
1839
|
+
|
|
1840
|
+
Parameters
|
|
1841
|
+
----------
|
|
1842
|
+
layer_name : str
|
|
1843
|
+
Name of the layer on which to create the polygon.
|
|
1844
|
+
return_points : bool, optional
|
|
1845
|
+
If `True` does not create the rectangle and just returns a list containing the rectangle vertices.
|
|
1846
|
+
Default is `False`.
|
|
1847
|
+
partition_max_order : float, optional
|
|
1848
|
+
Order of the lattice partition used to find the quasi-lattice polygon that approximates ``polygon``.
|
|
1849
|
+
Default is ``16``.
|
|
1850
|
+
|
|
1851
|
+
Returns
|
|
1852
|
+
-------
|
|
1853
|
+
bool, List, :class:`pyedb.dotnet.edb_core.edb_data.primitives.EDBPrimitives`
|
|
1854
|
+
Polygon when successful, ``False`` when failed, list of list if `return_points=True`.
|
|
1855
|
+
|
|
1856
|
+
Examples
|
|
1857
|
+
--------
|
|
1858
|
+
>>> from pyedb import Edb
|
|
1859
|
+
>>> edbapp = Edb("myaedbfolder", edbversion="2021.2")
|
|
1860
|
+
>>> edb_layout = edbapp.modeler
|
|
1861
|
+
>>> list_of_padstack_instances = list(edbapp.padstacks.instances.values())
|
|
1862
|
+
>>> padstack_inst = list_of_padstack_instances[0]
|
|
1863
|
+
>>> padstack_inst.create_rectangle_in_pad("TOP")
|
|
1864
|
+
"""
|
|
1865
|
+
|
|
1866
|
+
padstack_center = self.position
|
|
1867
|
+
rotation = self.rotation # in radians
|
|
1868
|
+
padstack_name = self.padstack_definition
|
|
1869
|
+
try:
|
|
1870
|
+
padstack = self._pedb.padstacks.definitions[padstack_name]
|
|
1871
|
+
except KeyError: # pragma: no cover
|
|
1872
|
+
return False
|
|
1873
|
+
try:
|
|
1874
|
+
padstack_pad = padstack.pad_by_layer[layer_name]
|
|
1875
|
+
except KeyError: # pragma: no cover
|
|
1876
|
+
try:
|
|
1877
|
+
padstack_pad = padstack.pad_by_layer[padstack.via_start_layer]
|
|
1878
|
+
except KeyError: # pragma: no cover
|
|
1879
|
+
return False
|
|
1880
|
+
|
|
1881
|
+
pad_shape = padstack_pad.geometry_type
|
|
1882
|
+
params = padstack_pad.parameters_values
|
|
1883
|
+
polygon_data = padstack_pad.polygon_data
|
|
1884
|
+
|
|
1885
|
+
def _rotate(p):
|
|
1886
|
+
x = p[0] * math.cos(rotation) - p[1] * math.sin(rotation)
|
|
1887
|
+
y = p[0] * math.sin(rotation) + p[1] * math.cos(rotation)
|
|
1888
|
+
return [x, y]
|
|
1889
|
+
|
|
1890
|
+
def _translate(p):
|
|
1891
|
+
x = p[0] + padstack_center[0]
|
|
1892
|
+
y = p[1] + padstack_center[1]
|
|
1893
|
+
return [x, y]
|
|
1894
|
+
|
|
1895
|
+
rect = None
|
|
1896
|
+
|
|
1897
|
+
if pad_shape == 1:
|
|
1898
|
+
# Circle
|
|
1899
|
+
diameter = params[0]
|
|
1900
|
+
r = diameter * 0.5
|
|
1901
|
+
p1 = [r, 0.0]
|
|
1902
|
+
p2 = [0.0, r]
|
|
1903
|
+
p3 = [-r, 0.0]
|
|
1904
|
+
p4 = [0.0, -r]
|
|
1905
|
+
rect = [_translate(p1), _translate(p2), _translate(p3), _translate(p4)]
|
|
1906
|
+
elif pad_shape == 2:
|
|
1907
|
+
# Square
|
|
1908
|
+
square_size = params[0]
|
|
1909
|
+
s2 = square_size * 0.5
|
|
1910
|
+
p1 = [s2, s2]
|
|
1911
|
+
p2 = [-s2, s2]
|
|
1912
|
+
p3 = [-s2, -s2]
|
|
1913
|
+
p4 = [s2, -s2]
|
|
1914
|
+
rect = [
|
|
1915
|
+
_translate(_rotate(p1)),
|
|
1916
|
+
_translate(_rotate(p2)),
|
|
1917
|
+
_translate(_rotate(p3)),
|
|
1918
|
+
_translate(_rotate(p4)),
|
|
1919
|
+
]
|
|
1920
|
+
elif pad_shape == 3:
|
|
1921
|
+
# Rectangle
|
|
1922
|
+
x_size = float(params[0])
|
|
1923
|
+
y_size = float(params[1])
|
|
1924
|
+
sx2 = x_size * 0.5
|
|
1925
|
+
sy2 = y_size * 0.5
|
|
1926
|
+
p1 = [sx2, sy2]
|
|
1927
|
+
p2 = [-sx2, sy2]
|
|
1928
|
+
p3 = [-sx2, -sy2]
|
|
1929
|
+
p4 = [sx2, -sy2]
|
|
1930
|
+
rect = [
|
|
1931
|
+
_translate(_rotate(p1)),
|
|
1932
|
+
_translate(_rotate(p2)),
|
|
1933
|
+
_translate(_rotate(p3)),
|
|
1934
|
+
_translate(_rotate(p4)),
|
|
1935
|
+
]
|
|
1936
|
+
elif pad_shape == 4:
|
|
1937
|
+
# Oval
|
|
1938
|
+
x_size = params[0]
|
|
1939
|
+
y_size = params[1]
|
|
1940
|
+
corner_radius = float(params[2])
|
|
1941
|
+
if corner_radius >= min(x_size, y_size):
|
|
1942
|
+
r = min(x_size, y_size)
|
|
1943
|
+
else:
|
|
1944
|
+
r = corner_radius
|
|
1945
|
+
sx = x_size * 0.5 - r
|
|
1946
|
+
sy = y_size * 0.5 - r
|
|
1947
|
+
k = r / math.sqrt(2)
|
|
1948
|
+
p1 = [sx + k, sy + k]
|
|
1949
|
+
p2 = [-sx - k, sy + k]
|
|
1950
|
+
p3 = [-sx - k, -sy - k]
|
|
1951
|
+
p4 = [sx + k, -sy - k]
|
|
1952
|
+
rect = [
|
|
1953
|
+
_translate(_rotate(p1)),
|
|
1954
|
+
_translate(_rotate(p2)),
|
|
1955
|
+
_translate(_rotate(p3)),
|
|
1956
|
+
_translate(_rotate(p4)),
|
|
1957
|
+
]
|
|
1958
|
+
elif pad_shape == 5:
|
|
1959
|
+
# Bullet
|
|
1960
|
+
x_size = params[0]
|
|
1961
|
+
y_size = params[1]
|
|
1962
|
+
corner_radius = params[2]
|
|
1963
|
+
if corner_radius >= min(x_size, y_size):
|
|
1964
|
+
r = min(x_size, y_size)
|
|
1965
|
+
else:
|
|
1966
|
+
r = corner_radius
|
|
1967
|
+
sx = x_size * 0.5 - r
|
|
1968
|
+
sy = y_size * 0.5 - r
|
|
1969
|
+
k = r / math.sqrt(2)
|
|
1970
|
+
p1 = [sx + k, sy + k]
|
|
1971
|
+
p2 = [-x_size * 0.5, sy + k]
|
|
1972
|
+
p3 = [-x_size * 0.5, -sy - k]
|
|
1973
|
+
p4 = [sx + k, -sy - k]
|
|
1974
|
+
rect = [
|
|
1975
|
+
_translate(_rotate(p1)),
|
|
1976
|
+
_translate(_rotate(p2)),
|
|
1977
|
+
_translate(_rotate(p3)),
|
|
1978
|
+
_translate(_rotate(p4)),
|
|
1979
|
+
]
|
|
1980
|
+
elif pad_shape == 6:
|
|
1981
|
+
# N-Sided Polygon
|
|
1982
|
+
size = params[0]
|
|
1983
|
+
num_sides = params[1]
|
|
1984
|
+
ext_radius = size * 0.5
|
|
1985
|
+
apothem = ext_radius * math.cos(math.pi / num_sides)
|
|
1986
|
+
p1 = [apothem, 0.0]
|
|
1987
|
+
p2 = [0.0, apothem]
|
|
1988
|
+
p3 = [-apothem, 0.0]
|
|
1989
|
+
p4 = [0.0, -apothem]
|
|
1990
|
+
rect = [
|
|
1991
|
+
_translate(_rotate(p1)),
|
|
1992
|
+
_translate(_rotate(p2)),
|
|
1993
|
+
_translate(_rotate(p3)),
|
|
1994
|
+
_translate(_rotate(p4)),
|
|
1995
|
+
]
|
|
1996
|
+
elif pad_shape == 0 and polygon_data is not None:
|
|
1997
|
+
# Polygon
|
|
1998
|
+
points = []
|
|
1999
|
+
i = 0
|
|
2000
|
+
while i < polygon_data.edb_api.Count:
|
|
2001
|
+
point = polygon_data.edb_api.GetPoint(i)
|
|
2002
|
+
i += 1
|
|
2003
|
+
if point.IsArc():
|
|
2004
|
+
continue
|
|
2005
|
+
else:
|
|
2006
|
+
points.append([point.X.ToDouble(), point.Y.ToDouble()])
|
|
2007
|
+
xpoly, ypoly = zip(*points)
|
|
2008
|
+
polygon = [list(xpoly), list(ypoly)]
|
|
2009
|
+
rectangles = GeometryOperators.find_largest_rectangle_inside_polygon(
|
|
2010
|
+
polygon, partition_max_order=partition_max_order
|
|
2011
|
+
)
|
|
2012
|
+
rect = rectangles[0]
|
|
2013
|
+
for i in range(4):
|
|
2014
|
+
rect[i] = _translate(_rotate(rect[i]))
|
|
2015
|
+
|
|
2016
|
+
if rect is None or len(rect) != 4:
|
|
2017
|
+
return False
|
|
2018
|
+
path = self._pedb.modeler.Shape("polygon", points=rect)
|
|
2019
|
+
pdata = self._pedb.modeler.shape_to_polygon_data(path)
|
|
2020
|
+
new_rect = []
|
|
2021
|
+
for point in pdata.Points:
|
|
2022
|
+
p_transf = self._edb_padstackinstance.GetComponent().GetTransform().TransformPoint(point)
|
|
2023
|
+
new_rect.append([p_transf.X.ToDouble(), p_transf.Y.ToDouble()])
|
|
2024
|
+
if return_points:
|
|
2025
|
+
return new_rect
|
|
2026
|
+
else:
|
|
2027
|
+
path = self._pedb.modeler.Shape("polygon", points=new_rect)
|
|
2028
|
+
created_polygon = self._pedb.modeler.create_polygon(path, layer_name)
|
|
2029
|
+
return created_polygon
|
|
2030
|
+
|
|
2031
|
+
@pyedb_function_handler()
|
|
2032
|
+
def get_connected_object_id_set(self):
|
|
2033
|
+
"""Produce a list of all geometries physically connected to a given layout object.
|
|
2034
|
+
|
|
2035
|
+
Returns
|
|
2036
|
+
-------
|
|
2037
|
+
list
|
|
2038
|
+
Found connected objects IDs with Layout object.
|
|
2039
|
+
"""
|
|
2040
|
+
layoutInst = self._edb_padstackinstance.GetLayout().GetLayoutInstance()
|
|
2041
|
+
layoutObjInst = self.object_instance
|
|
2042
|
+
return [loi.GetLayoutObj().GetId() for loi in layoutInst.GetConnectedObjects(layoutObjInst).Items]
|
|
2043
|
+
|
|
2044
|
+
@pyedb_function_handler()
|
|
2045
|
+
def get_reference_pins(self, reference_net="GND", search_radius=5e-3, max_limit=0, component_only=True):
|
|
2046
|
+
"""Search for reference pins using given criteria.
|
|
2047
|
+
|
|
2048
|
+
Parameters
|
|
2049
|
+
----------
|
|
2050
|
+
reference_net : str, optional
|
|
2051
|
+
Reference net. The default is ``"GND"``.
|
|
2052
|
+
search_radius : float, optional
|
|
2053
|
+
Search radius for finding padstack instances. The default is ``5e-3``.
|
|
2054
|
+
max_limit : int, optional
|
|
2055
|
+
Maximum limit for the padstack instances found. The default is ``0``, in which
|
|
2056
|
+
case no limit is applied. The maximum limit value occurs on the nearest
|
|
2057
|
+
reference pins from the positive one that is found.
|
|
2058
|
+
component_only : bool, optional
|
|
2059
|
+
Whether to limit the search to component padstack instances only. The
|
|
2060
|
+
default is ``True``. When ``False``, the search is extended to the entire layout.
|
|
2061
|
+
|
|
2062
|
+
Returns
|
|
2063
|
+
-------
|
|
2064
|
+
list
|
|
2065
|
+
List of :class:`dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance`.
|
|
2066
|
+
|
|
2067
|
+
Examples
|
|
2068
|
+
--------
|
|
2069
|
+
>>> edbapp = Edb("target_path")
|
|
2070
|
+
>>> pin = edbapp.components.instances["J5"].pins["19"]
|
|
2071
|
+
>>> reference_pins = pin.get_reference_pins(reference_net="GND", search_radius=5e-3, max_limit=0,
|
|
2072
|
+
>>> component_only=True)
|
|
2073
|
+
"""
|
|
2074
|
+
return self._pedb.padstacks.get_reference_pins(
|
|
2075
|
+
positive_pin=self,
|
|
2076
|
+
reference_net=reference_net,
|
|
2077
|
+
search_radius=search_radius,
|
|
2078
|
+
max_limit=max_limit,
|
|
2079
|
+
component_only=component_only,
|
|
2080
|
+
)
|