pyedb 0.38.0__py3-none-any.whl → 0.39.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -1
- pyedb/common/nets.py +53 -139
- pyedb/configuration/cfg_components.py +1 -1
- pyedb/configuration/cfg_general.py +4 -2
- pyedb/configuration/cfg_modeler.py +1 -1
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +1 -1
- pyedb/configuration/cfg_ports_sources.py +56 -23
- pyedb/configuration/configuration.py +18 -1
- pyedb/dotnet/{application → database}/Variables.py +21 -21
- pyedb/dotnet/{edb_core → database}/cell/connectable.py +5 -5
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/component.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/hierarchy_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/model.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/layout.py +17 -17
- pyedb/dotnet/{edb_core → database}/cell/layout_obj.py +3 -3
- pyedb/dotnet/{edb_core → database}/cell/primitive/bondwire.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/primitive/path.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/primitive/primitive.py +14 -14
- pyedb/dotnet/{edb_core → database}/cell/terminal/bundle_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/edge_terminal.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/terminal/padstack_instance_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/pingroup_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/point_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/terminal.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/voltage_regulator.py +2 -2
- pyedb/dotnet/{edb_core → database}/components.py +101 -124
- pyedb/dotnet/{edb_core → database}/definition/component_def.py +5 -5
- pyedb/dotnet/{edb_core → database}/definition/component_model.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definition_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definitions.py +2 -2
- pyedb/dotnet/{edb_core → database}/definition/package_def.py +4 -4
- pyedb/dotnet/{edb_core → database}/dotnet/database.py +8 -8
- pyedb/dotnet/{edb_core → database}/dotnet/primitive.py +9 -9
- pyedb/dotnet/{edb_core → database}/edb_data/control_file.py +12 -12
- pyedb/dotnet/{edb_core → database}/edb_data/hfss_extent_info.py +7 -7
- pyedb/dotnet/{edb_core → database}/edb_data/nets_data.py +10 -13
- pyedb/dotnet/{edb_core → database}/edb_data/padstacks_data.py +16 -16
- pyedb/dotnet/{edb_core → database}/edb_data/ports.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/primitives_data.py +5 -5
- pyedb/dotnet/{edb_core → database}/edb_data/raptor_x_simulation_setup_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/simulation_configuration.py +10 -10
- pyedb/dotnet/{edb_core → database}/edb_data/sources.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/variables.py +1 -1
- pyedb/dotnet/{edb_core → database}/geometry/polygon_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/hfss.py +8 -8
- pyedb/dotnet/{edb_core → database}/layout_obj_instance.py +1 -1
- pyedb/dotnet/{edb_core → database}/layout_validation.py +2 -2
- pyedb/dotnet/{edb_core → database}/materials.py +23 -8
- pyedb/dotnet/{edb_core → database}/modeler.py +27 -27
- pyedb/dotnet/{edb_core → database}/net_class.py +8 -8
- pyedb/dotnet/{edb_core → database}/nets.py +12 -12
- pyedb/dotnet/{edb_core → database}/padstack.py +15 -15
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/mesh_operation.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/settings.py +3 -3
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sim_setup_info.py +2 -2
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/simulation_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sweep_data.py +1 -1
- pyedb/dotnet/{edb_core → database}/siwave.py +10 -10
- pyedb/dotnet/{edb_core → database}/stackup.py +12 -12
- pyedb/dotnet/{edb_core → database}/utilities/hfss_simulation_setup.py +15 -15
- pyedb/dotnet/{edb_core → database}/utilities/obj_base.py +1 -1
- pyedb/dotnet/{edb_core → database}/utilities/simulation_setup.py +3 -3
- pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
- pyedb/dotnet/edb.py +117 -112
- pyedb/generic/design_types.py +26 -19
- pyedb/generic/general_methods.py +1 -1
- pyedb/generic/plot.py +0 -2
- pyedb/grpc/database/__init__.py +1 -0
- pyedb/grpc/database/components.py +2354 -0
- pyedb/grpc/database/control_file.py +1277 -0
- pyedb/grpc/database/definition/component_def.py +218 -0
- pyedb/grpc/database/definition/component_model.py +39 -0
- pyedb/grpc/database/definition/component_pin.py +32 -0
- pyedb/grpc/database/definition/materials.py +1207 -0
- pyedb/grpc/database/definition/n_port_component_model.py +34 -0
- pyedb/grpc/database/definition/package_def.py +227 -0
- pyedb/grpc/database/definition/padstack_def.py +842 -0
- pyedb/grpc/database/definitions.py +70 -0
- pyedb/grpc/database/general.py +43 -0
- pyedb/grpc/database/geometry/__init__.py +0 -0
- pyedb/grpc/database/geometry/arc_data.py +93 -0
- pyedb/grpc/database/geometry/point_3d_data.py +79 -0
- pyedb/grpc/database/geometry/point_data.py +30 -0
- pyedb/grpc/database/geometry/polygon_data.py +133 -0
- pyedb/grpc/database/hfss.py +1279 -0
- pyedb/grpc/database/hierarchy/__init__.py +0 -0
- pyedb/grpc/database/hierarchy/component.py +1301 -0
- pyedb/grpc/database/hierarchy/model.py +31 -0
- pyedb/grpc/database/hierarchy/netlist_model.py +30 -0
- pyedb/grpc/database/hierarchy/pin_pair_model.py +128 -0
- pyedb/grpc/database/hierarchy/pingroup.py +245 -0
- pyedb/grpc/database/hierarchy/s_parameter_model.py +33 -0
- pyedb/grpc/database/hierarchy/spice_model.py +48 -0
- pyedb/grpc/database/layers/__init__.py +0 -0
- pyedb/grpc/database/layers/layer.py +57 -0
- pyedb/grpc/database/layers/stackup_layer.py +410 -0
- pyedb/grpc/database/layout/__init__.py +0 -0
- pyedb/grpc/database/layout/cell.py +30 -0
- pyedb/grpc/database/layout/layout.py +196 -0
- pyedb/grpc/database/layout/voltage_regulator.py +149 -0
- pyedb/grpc/database/layout_validation.py +319 -0
- pyedb/grpc/database/modeler.py +1468 -0
- pyedb/grpc/database/net/__init__.py +0 -0
- pyedb/grpc/database/net/differential_pair.py +138 -0
- pyedb/grpc/database/net/extended_net.py +340 -0
- pyedb/grpc/database/net/net.py +198 -0
- pyedb/grpc/database/net/net_class.py +93 -0
- pyedb/grpc/database/nets.py +633 -0
- pyedb/grpc/database/padstacks.py +1500 -0
- pyedb/grpc/database/ports/__init__.py +0 -0
- pyedb/grpc/database/ports/ports.py +396 -0
- pyedb/grpc/database/primitive/__init__.py +3 -0
- pyedb/grpc/database/primitive/bondwire.py +181 -0
- pyedb/grpc/database/primitive/circle.py +75 -0
- pyedb/grpc/database/primitive/padstack_instance.py +1116 -0
- pyedb/grpc/database/primitive/path.py +346 -0
- pyedb/grpc/database/primitive/polygon.py +276 -0
- pyedb/grpc/database/primitive/primitive.py +739 -0
- pyedb/grpc/database/primitive/rectangle.py +146 -0
- pyedb/grpc/database/simulation_setup/__init__.py +0 -0
- pyedb/grpc/database/simulation_setup/adaptive_frequency.py +33 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +32 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +59 -0
- pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +35 -0
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +61 -0
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +78 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +118 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +355 -0
- pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/mesh_operation.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +33 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +64 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +125 -0
- pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +34 -0
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +119 -0
- pyedb/grpc/database/simulation_setup/sweep_data.py +32 -0
- pyedb/grpc/database/siwave.py +1023 -0
- pyedb/grpc/database/source_excitations.py +2572 -0
- pyedb/grpc/database/stackup.py +2574 -0
- pyedb/grpc/database/terminal/__init__.py +0 -0
- pyedb/grpc/database/terminal/bundle_terminal.py +218 -0
- pyedb/grpc/database/terminal/edge_terminal.py +51 -0
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +171 -0
- pyedb/grpc/database/terminal/pingroup_terminal.py +162 -0
- pyedb/grpc/database/terminal/point_terminal.py +99 -0
- pyedb/grpc/database/terminal/terminal.py +470 -0
- pyedb/grpc/database/utility/__init__.py +3 -0
- pyedb/grpc/database/utility/constants.py +25 -0
- pyedb/grpc/database/utility/heat_sink.py +124 -0
- pyedb/grpc/database/utility/hfss_extent_info.py +448 -0
- pyedb/grpc/database/utility/layout_statistics.py +277 -0
- pyedb/grpc/database/utility/rlc.py +80 -0
- pyedb/grpc/database/utility/simulation_configuration.py +3305 -0
- pyedb/grpc/database/utility/sources.py +388 -0
- pyedb/grpc/database/utility/sweep_data_distribution.py +83 -0
- pyedb/grpc/database/utility/xml_control_file.py +1277 -0
- pyedb/grpc/edb.py +4151 -0
- pyedb/grpc/edb_init.py +481 -0
- pyedb/grpc/rpc_session.py +177 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +3 -2
- pyedb/ipc2581/ecad/cad_data/feature.py +4 -3
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +32 -20
- pyedb/ipc2581/ecad/cad_data/outline.py +3 -2
- pyedb/ipc2581/ecad/cad_data/package.py +4 -3
- pyedb/ipc2581/ecad/cad_data/path.py +82 -31
- pyedb/ipc2581/ecad/cad_data/polygon.py +122 -60
- pyedb/ipc2581/ecad/cad_data/profile.py +13 -12
- pyedb/ipc2581/ecad/cad_data/step.py +53 -21
- pyedb/ipc2581/ipc2581.py +47 -49
- pyedb/modeler/geometry_operators.py +1 -1
- {pyedb-0.38.0.dist-info → pyedb-0.39.0.dist-info}/METADATA +5 -2
- pyedb-0.39.0.dist-info/RECORD +288 -0
- pyedb-0.38.0.dist-info/RECORD +0 -195
- /pyedb/dotnet/{edb_core → database}/__init__.py +0 -0
- /pyedb/dotnet/{application → database/cell}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell → database/cell/hierarchy}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/netlist_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/pin_pair_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/s_parameter_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/spice_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/primitive/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/hierarchy → database/cell/terminal}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/terminal → database/definition}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/definition → database/dotnet}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/dotnet → database/edb_data}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/design_options.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/edbvalue.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/layer_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/utilities.py +0 -0
- /pyedb/dotnet/{edb_core → database}/general.py +0 -0
- /pyedb/dotnet/{edb_core/edb_data → database/geometry}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/geometry/point_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/adaptive_frequency_data.py +0 -0
- /pyedb/dotnet/{edb_core/geometry → database/sim_setup_data/io}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/io/siwave.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/heatsink.py +0 -0
- /pyedb/{dotnet/edb_core/sim_setup_data/io → grpc/database/definition}/__init__.py +0 -0
- {pyedb-0.38.0.dist-info → pyedb-0.39.0.dist-info}/LICENSE +0 -0
- {pyedb-0.38.0.dist-info → pyedb-0.39.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,1500 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
This module contains the `EdbPadstacks` class.
|
|
25
|
+
"""
|
|
26
|
+
import math
|
|
27
|
+
import warnings
|
|
28
|
+
|
|
29
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
30
|
+
PadGeometryType as GrpcPadGeometryType,
|
|
31
|
+
)
|
|
32
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
33
|
+
PadstackDefData as GrpcPadstackDefData,
|
|
34
|
+
)
|
|
35
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
36
|
+
PadstackHoleRange as GrpcPadstackHoleRange,
|
|
37
|
+
)
|
|
38
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
39
|
+
SolderballPlacement as GrpcSolderballPlacement,
|
|
40
|
+
)
|
|
41
|
+
from ansys.edb.core.definition.padstack_def_data import (
|
|
42
|
+
SolderballShape as GrpcSolderballShape,
|
|
43
|
+
)
|
|
44
|
+
from ansys.edb.core.definition.padstack_def_data import PadType as GrpcPadType
|
|
45
|
+
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
46
|
+
from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
|
|
47
|
+
from ansys.edb.core.utility.value import Value as GrpcValue
|
|
48
|
+
import rtree
|
|
49
|
+
|
|
50
|
+
from pyedb.generic.general_methods import generate_unique_name
|
|
51
|
+
from pyedb.grpc.database.definition.padstack_def import PadstackDef
|
|
52
|
+
from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
|
|
53
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Padstacks(object):
|
|
57
|
+
"""Manages EDB methods for nets management accessible from `Edb.padstacks` property.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> from pyedb import Edb
|
|
62
|
+
>>> edbapp = Edb("myaedbfolder", edbversion="2024.2")
|
|
63
|
+
>>> edb_padstacks = edbapp.padstacks
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __getitem__(self, name):
|
|
67
|
+
"""Get a padstack definition or instance from the Edb project.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
name : str, int
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
:class:`pyedb.dotnet.database.cell.hierarchy.component.EDBComponent`
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
if isinstance(name, int) and name in self.instances:
|
|
79
|
+
return self.instances(name)
|
|
80
|
+
elif name in self.definitions:
|
|
81
|
+
return self.definitions[name]
|
|
82
|
+
else:
|
|
83
|
+
for i in list(self.instances.values()):
|
|
84
|
+
if i.name == name or i.aedt_name == name:
|
|
85
|
+
return i
|
|
86
|
+
self._pedb.logger.error("Component or definition not found.")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
def __init__(self, p_edb):
|
|
90
|
+
self._pedb = p_edb
|
|
91
|
+
self._instances = {}
|
|
92
|
+
self._definitions = {}
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def _active_layout(self):
|
|
96
|
+
""" """
|
|
97
|
+
return self._pedb.active_layout
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def _layout(self):
|
|
101
|
+
""" """
|
|
102
|
+
return self._pedb.layout
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def db(self):
|
|
106
|
+
"""Db object."""
|
|
107
|
+
return self._pedb.active_db
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def _logger(self):
|
|
111
|
+
""" """
|
|
112
|
+
return self._pedb.logger
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def _layers(self):
|
|
116
|
+
""" """
|
|
117
|
+
return self._pedb.stackup.layers
|
|
118
|
+
|
|
119
|
+
def int_to_pad_type(self, val=0):
|
|
120
|
+
"""Convert an integer to an EDB.PadGeometryType.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
val : int
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
object
|
|
129
|
+
EDB.PadType enumerator value.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
if val == 0:
|
|
133
|
+
return GrpcPadType.REGULAR_PAD
|
|
134
|
+
elif val == 1:
|
|
135
|
+
return GrpcPadType.ANTI_PAD
|
|
136
|
+
elif val == 2:
|
|
137
|
+
return GrpcPadType.THERMAL_PAD
|
|
138
|
+
elif val == 3:
|
|
139
|
+
return GrpcPadType.HOLE
|
|
140
|
+
elif val == 4:
|
|
141
|
+
return GrpcPadType.UNKNOWN_GEOM_TYPE
|
|
142
|
+
else:
|
|
143
|
+
return val
|
|
144
|
+
|
|
145
|
+
def int_to_geometry_type(self, val=0):
|
|
146
|
+
"""Convert an integer to an EDB.PadGeometryType.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
val : int
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
-------
|
|
154
|
+
object
|
|
155
|
+
EDB.PadGeometryType enumerator value.
|
|
156
|
+
"""
|
|
157
|
+
if val == 0:
|
|
158
|
+
return GrpcPadGeometryType.PADGEOMTYPE_NO_GEOMETRY
|
|
159
|
+
elif val == 1:
|
|
160
|
+
return GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
|
|
161
|
+
elif val == 2:
|
|
162
|
+
return GrpcPadGeometryType.PADGEOMTYPE_SQUARE
|
|
163
|
+
elif val == 3:
|
|
164
|
+
return GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
|
|
165
|
+
elif val == 4:
|
|
166
|
+
return GrpcPadGeometryType.PADGEOMTYPE_OVAL
|
|
167
|
+
elif val == 5:
|
|
168
|
+
return GrpcPadGeometryType.PADGEOMTYPE_BULLET
|
|
169
|
+
elif val == 6:
|
|
170
|
+
return GrpcPadGeometryType.PADGEOMTYPE_NSIDED_POLYGON
|
|
171
|
+
elif val == 7:
|
|
172
|
+
return GrpcPadGeometryType.PADGEOMTYPE_POLYGON
|
|
173
|
+
elif val == 8:
|
|
174
|
+
return GrpcPadGeometryType.PADGEOMTYPE_ROUND45
|
|
175
|
+
elif val == 9:
|
|
176
|
+
return GrpcPadGeometryType.PADGEOMTYPE_ROUND90
|
|
177
|
+
elif val == 10:
|
|
178
|
+
return GrpcPadGeometryType.PADGEOMTYPE_SQUARE45
|
|
179
|
+
elif val == 11:
|
|
180
|
+
return GrpcPadGeometryType.PADGEOMTYPE_SQUARE90
|
|
181
|
+
else:
|
|
182
|
+
return val
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def definitions(self):
|
|
186
|
+
"""Padstack definitions.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
dict[str, :class:`pyedb.dotnet.database.edb_data.padstacks_data.EdbPadstack`]
|
|
191
|
+
List of definitions via padstack definitions.
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
if len(self._definitions) == len(self.db.padstack_defs):
|
|
195
|
+
return self._definitions
|
|
196
|
+
self._definitions = {}
|
|
197
|
+
for padstack_def in self._pedb.db.padstack_defs:
|
|
198
|
+
if len(padstack_def.data.layer_names) >= 1:
|
|
199
|
+
self._definitions[padstack_def.name] = PadstackDef(self._pedb, padstack_def)
|
|
200
|
+
return self._definitions
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def instances(self):
|
|
204
|
+
"""Dictionary of all padstack instances (vias and pins).
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
dict[int, :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`]
|
|
209
|
+
List of padstack instances.
|
|
210
|
+
|
|
211
|
+
"""
|
|
212
|
+
pad_stack_inst = self._pedb.layout.padstack_instances
|
|
213
|
+
if len(self._instances) == len(pad_stack_inst):
|
|
214
|
+
return self._instances
|
|
215
|
+
self._instances = {i.edb_uid: PadstackInstance(self._pedb, i) for i in pad_stack_inst}
|
|
216
|
+
return self._instances
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def instances_by_name(self):
|
|
220
|
+
"""Dictionary of all padstack instances (vias and pins) by name.
|
|
221
|
+
|
|
222
|
+
Returns
|
|
223
|
+
-------
|
|
224
|
+
dict[str, :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`]
|
|
225
|
+
List of padstack instances.
|
|
226
|
+
|
|
227
|
+
"""
|
|
228
|
+
padstack_instances = {}
|
|
229
|
+
for _, edb_padstack_instance in self.instances.items():
|
|
230
|
+
if edb_padstack_instance.aedt_name:
|
|
231
|
+
padstack_instances[edb_padstack_instance.aedt_name] = edb_padstack_instance
|
|
232
|
+
return padstack_instances
|
|
233
|
+
|
|
234
|
+
def find_instance_by_id(self, value: int):
|
|
235
|
+
"""Find a padstack instance by database id.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
value : int
|
|
240
|
+
"""
|
|
241
|
+
return self._pedb.modeler.find_object_by_id(value)
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def pins(self):
|
|
245
|
+
"""Dictionary of all pins instances (belonging to component).
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
dic[str, :class:`dotnet.database.edb_data.definitions.EDBPadstackInstance`]
|
|
250
|
+
Dictionary of EDBPadstackInstance Components.
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
Examples
|
|
254
|
+
--------
|
|
255
|
+
>>> edbapp = dotnet.Edb("myproject.aedb")
|
|
256
|
+
>>> pin_net_name = edbapp.pins[424968329].netname
|
|
257
|
+
"""
|
|
258
|
+
pins = {}
|
|
259
|
+
for instancename, instance in self.instances.items():
|
|
260
|
+
if instance.is_pin and instance.component:
|
|
261
|
+
pins[instancename] = instance
|
|
262
|
+
return pins
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def vias(self):
|
|
266
|
+
"""Dictionary of all vias instances not belonging to component.
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
dic[str, :class:`dotnet.database.edb_data.definitions.EDBPadstackInstance`]
|
|
271
|
+
Dictionary of EDBPadstackInstance Components.
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
Examples
|
|
275
|
+
--------
|
|
276
|
+
>>> edbapp = dotnet.Edb("myproject.aedb")
|
|
277
|
+
>>> pin_net_name = edbapp.pins[424968329].netname
|
|
278
|
+
"""
|
|
279
|
+
pnames = list(self.pins.keys())
|
|
280
|
+
vias = {i: j for i, j in self.instances.items() if i not in pnames}
|
|
281
|
+
return vias
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def pingroups(self):
|
|
285
|
+
"""All Layout Pin groups.
|
|
286
|
+
|
|
287
|
+
. deprecated:: pyedb 0.28.0
|
|
288
|
+
Use :func:`pyedb.grpc.core.layout.pin_groups` instead.
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
list
|
|
293
|
+
List of all layout pin groups.
|
|
294
|
+
"""
|
|
295
|
+
warnings.warn(
|
|
296
|
+
"`pingroups` is deprecated and is now located here " "`pyedb.grpc.core.layout.pin_groups` instead.",
|
|
297
|
+
DeprecationWarning,
|
|
298
|
+
)
|
|
299
|
+
return self._layout.pin_groups
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def pad_type(self):
|
|
303
|
+
"""Return a PadType Enumerator."""
|
|
304
|
+
|
|
305
|
+
def create_circular_padstack(
|
|
306
|
+
self,
|
|
307
|
+
padstackname=None,
|
|
308
|
+
holediam="300um",
|
|
309
|
+
paddiam="400um",
|
|
310
|
+
antipaddiam="600um",
|
|
311
|
+
startlayer=None,
|
|
312
|
+
endlayer=None,
|
|
313
|
+
):
|
|
314
|
+
"""Create a circular padstack.
|
|
315
|
+
|
|
316
|
+
Parameters
|
|
317
|
+
----------
|
|
318
|
+
padstackname : str, optional
|
|
319
|
+
Name of the padstack. The default is ``None``.
|
|
320
|
+
holediam : str, optional
|
|
321
|
+
Diameter of the hole with units. The default is ``"300um"``.
|
|
322
|
+
paddiam : str, optional
|
|
323
|
+
Diameter of the pad with units. The default is ``"400um"``.
|
|
324
|
+
antipaddiam : str, optional
|
|
325
|
+
Diameter of the antipad with units. The default is ``"600um"``.
|
|
326
|
+
startlayer : str, optional
|
|
327
|
+
Starting layer. The default is ``None``, in which case the top
|
|
328
|
+
is the starting layer.
|
|
329
|
+
endlayer : str, optional
|
|
330
|
+
Ending layer. The default is ``None``, in which case the bottom
|
|
331
|
+
is the ending layer.
|
|
332
|
+
|
|
333
|
+
Returns
|
|
334
|
+
-------
|
|
335
|
+
str
|
|
336
|
+
Name of the padstack if the operation is successful.
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
padstack_def = PadstackDef.create(self._pedb.db, padstackname)
|
|
340
|
+
|
|
341
|
+
padstack_data = GrpcPadstackDefData.create()
|
|
342
|
+
list_values = [GrpcValue(holediam), GrpcValue(paddiam), GrpcValue(antipaddiam)]
|
|
343
|
+
padstack_data.set_hole_parameters(
|
|
344
|
+
offset_x=GrpcValue(0),
|
|
345
|
+
offset_y=GrpcValue(0),
|
|
346
|
+
rotation=GrpcValue(0),
|
|
347
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
348
|
+
sizes=list_values,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
padstack_data.hole_range = GrpcPadstackHoleRange.UPPER_PAD_TO_LOWER_PAD
|
|
352
|
+
layers = list(self._pedb.stackup.signal_layers.keys())
|
|
353
|
+
if not startlayer:
|
|
354
|
+
startlayer = layers[0]
|
|
355
|
+
if not endlayer:
|
|
356
|
+
endlayer = layers[len(layers) - 1]
|
|
357
|
+
|
|
358
|
+
antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
|
|
359
|
+
started = False
|
|
360
|
+
padstack_data.set_pad_parameters(
|
|
361
|
+
layer="Default",
|
|
362
|
+
pad_type=GrpcPadType.REGULAR_PAD,
|
|
363
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
364
|
+
offset_x=GrpcValue(0),
|
|
365
|
+
offset_y=GrpcValue(0),
|
|
366
|
+
rotation=GrpcValue(0),
|
|
367
|
+
sizes=[GrpcValue(paddiam)],
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
padstack_data.set_pad_parameters(
|
|
371
|
+
layer="Default",
|
|
372
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
373
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
374
|
+
offset_x=GrpcValue(0),
|
|
375
|
+
offset_y=GrpcValue(0),
|
|
376
|
+
rotation=GrpcValue(0),
|
|
377
|
+
sizes=[GrpcValue(antipaddiam)],
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
for layer in layers:
|
|
381
|
+
if layer == startlayer:
|
|
382
|
+
started = True
|
|
383
|
+
if layer == endlayer:
|
|
384
|
+
started = False
|
|
385
|
+
if started:
|
|
386
|
+
padstack_data.set_pad_parameters(
|
|
387
|
+
layer=layer,
|
|
388
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
389
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
390
|
+
offset_x=GrpcValue(0),
|
|
391
|
+
offset_y=GrpcValue(0),
|
|
392
|
+
rotation=GrpcValue(0),
|
|
393
|
+
sizes=[GrpcValue(antipaddiam)],
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
padstack_data.set_pad_parameters(
|
|
397
|
+
layer=layer,
|
|
398
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
399
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
400
|
+
offset_x=GrpcValue(0),
|
|
401
|
+
offset_y=GrpcValue(0),
|
|
402
|
+
rotation=GrpcValue(0),
|
|
403
|
+
sizes=[GrpcValue(antipaddiam)],
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
padstack_def.data = padstack_data
|
|
407
|
+
|
|
408
|
+
def delete_padstack_instances(self, net_names): # pragma: no cover
|
|
409
|
+
"""Delete padstack instances by net names.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
net_names : str, list
|
|
414
|
+
Names of the nets to delete.
|
|
415
|
+
|
|
416
|
+
Returns
|
|
417
|
+
-------
|
|
418
|
+
bool
|
|
419
|
+
``True`` when successful, ``False`` when failed.
|
|
420
|
+
|
|
421
|
+
References
|
|
422
|
+
----------
|
|
423
|
+
|
|
424
|
+
>>> Edb.padstacks.delete_padstack_instances(net_names=["GND"])
|
|
425
|
+
"""
|
|
426
|
+
if not isinstance(net_names, list): # pragma: no cover
|
|
427
|
+
net_names = [net_names]
|
|
428
|
+
|
|
429
|
+
for p_id, p in self.instances.items():
|
|
430
|
+
if p.net_name in net_names:
|
|
431
|
+
if not p.delete(): # pragma: no cover
|
|
432
|
+
return False
|
|
433
|
+
return True
|
|
434
|
+
|
|
435
|
+
def set_solderball(self, padstackInst, sballLayer_name, isTopPlaced=True, ballDiam=100e-6):
|
|
436
|
+
"""Set solderball for the given PadstackInstance.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
padstackInst : Edb.Cell.Primitive.PadstackInstance or int
|
|
441
|
+
Padstack instance id or object.
|
|
442
|
+
sballLayer_name : str,
|
|
443
|
+
Name of the layer where the solder ball is placed. No default values.
|
|
444
|
+
isTopPlaced : bool, optional.
|
|
445
|
+
Bollean triggering is the solder ball is placed on Top or Bottom of the layer stackup.
|
|
446
|
+
ballDiam : double, optional,
|
|
447
|
+
Solder ball diameter value.
|
|
448
|
+
|
|
449
|
+
Returns
|
|
450
|
+
-------
|
|
451
|
+
bool
|
|
452
|
+
|
|
453
|
+
"""
|
|
454
|
+
if isinstance(padstackInst, int):
|
|
455
|
+
psdef = self.definitions[self.instances[padstackInst].padstack_definition].edb_padstack
|
|
456
|
+
padstackInst = self.instances[padstackInst]
|
|
457
|
+
|
|
458
|
+
else:
|
|
459
|
+
psdef = padstackInst.padstack_def
|
|
460
|
+
newdefdata = GrpcPadstackDefData.create(psdef.data)
|
|
461
|
+
newdefdata.solder_ball_shape = GrpcSolderballShape.SOLDERBALL_CYLINDER
|
|
462
|
+
newdefdata.solder_ball_param(GrpcValue(ballDiam), GrpcValue(ballDiam))
|
|
463
|
+
sball_placement = (
|
|
464
|
+
GrpcSolderballPlacement.ABOVE_PADSTACK if isTopPlaced else GrpcSolderballPlacement.BELOW_PADSTACK
|
|
465
|
+
)
|
|
466
|
+
newdefdata.solder_ball_placement = sball_placement
|
|
467
|
+
psdef.data = newdefdata
|
|
468
|
+
sball_layer = [lay._edb_layer for lay in list(self._layers.values()) if lay.name == sballLayer_name][0]
|
|
469
|
+
if sball_layer is not None:
|
|
470
|
+
padstackInst.solder_ball_layer = sball_layer
|
|
471
|
+
return True
|
|
472
|
+
|
|
473
|
+
return False
|
|
474
|
+
|
|
475
|
+
def create_coax_port(self, padstackinstance, use_dot_separator=True, name=None):
|
|
476
|
+
"""Create HFSS 3Dlayout coaxial lumped port on a pastack
|
|
477
|
+
Requires to have solder ball defined before calling this method.
|
|
478
|
+
|
|
479
|
+
. deprecated:: pyedb 0.28.0
|
|
480
|
+
Use :func:`pyedb.grpc.core.excitations.create_source_on_component` instead.
|
|
481
|
+
|
|
482
|
+
Parameters
|
|
483
|
+
----------
|
|
484
|
+
padstackinstance : `Edb.Cell.Primitive.PadstackInstance` or int
|
|
485
|
+
Padstack instance object.
|
|
486
|
+
use_dot_separator : bool, optional
|
|
487
|
+
Whether to use ``.`` as the separator for the naming convention, which
|
|
488
|
+
is ``[component][net][pin]``. The default is ``True``. If ``False``, ``_`` is
|
|
489
|
+
used as the separator instead.
|
|
490
|
+
name : str
|
|
491
|
+
Port name for overwriting the default port-naming convention,
|
|
492
|
+
which is ``[component][net][pin]``. The port name must be unique.
|
|
493
|
+
If a port with the specified name already exists, the
|
|
494
|
+
default naming convention is used so that port creation does
|
|
495
|
+
not fail.
|
|
496
|
+
|
|
497
|
+
Returns
|
|
498
|
+
-------
|
|
499
|
+
str
|
|
500
|
+
Terminal name.
|
|
501
|
+
|
|
502
|
+
"""
|
|
503
|
+
warnings.warn(
|
|
504
|
+
"`create_coax_port` is deprecated and is now located here "
|
|
505
|
+
"`pyedb.grpc.core.excitations.create_coax_port` instead.",
|
|
506
|
+
DeprecationWarning,
|
|
507
|
+
)
|
|
508
|
+
self._pedb.excitations.create_coax_port(self, padstackinstance, use_dot_separator=use_dot_separator, name=name)
|
|
509
|
+
|
|
510
|
+
def get_pin_from_component_and_net(self, refdes=None, netname=None):
|
|
511
|
+
"""Retrieve pins given a component's reference designator and net name.
|
|
512
|
+
|
|
513
|
+
Parameters
|
|
514
|
+
----------
|
|
515
|
+
refdes : str, optional
|
|
516
|
+
Reference designator of the component. The default is ``None``.
|
|
517
|
+
netname : str optional
|
|
518
|
+
Name of the net. The default is ``None``.
|
|
519
|
+
|
|
520
|
+
Returns
|
|
521
|
+
-------
|
|
522
|
+
dict
|
|
523
|
+
Dictionary of pins if the operation is successful.
|
|
524
|
+
``False`` is returned if the net does not belong to the component.
|
|
525
|
+
|
|
526
|
+
"""
|
|
527
|
+
pinlist = []
|
|
528
|
+
if refdes:
|
|
529
|
+
if refdes in self._pedb.components.instances:
|
|
530
|
+
if netname:
|
|
531
|
+
for pin, val in self._pedb.components.instances[refdes].pins.items():
|
|
532
|
+
if val.net_name == netname:
|
|
533
|
+
pinlist.append(val)
|
|
534
|
+
else:
|
|
535
|
+
for pin in self._pedb.components.instances[refdes].pins.values():
|
|
536
|
+
pinlist.append(pin)
|
|
537
|
+
elif netname:
|
|
538
|
+
for pin in self._pedb.pins:
|
|
539
|
+
if pin.net_name == netname:
|
|
540
|
+
pinlist.append(pin)
|
|
541
|
+
else:
|
|
542
|
+
self._logger.error("At least a component or a net name has to be provided")
|
|
543
|
+
|
|
544
|
+
return pinlist
|
|
545
|
+
|
|
546
|
+
def get_pinlist_from_component_and_net(self, refdes=None, netname=None):
|
|
547
|
+
"""Retrieve pins given a component's reference designator and net name.
|
|
548
|
+
|
|
549
|
+
. deprecated:: pyedb 0.28.0
|
|
550
|
+
Use :func:`get_pin_from_component_and_net` instead.
|
|
551
|
+
|
|
552
|
+
Parameters
|
|
553
|
+
----------
|
|
554
|
+
refdes : str, optional
|
|
555
|
+
Reference designator of the component. The default is ``None``.
|
|
556
|
+
netname : str optional
|
|
557
|
+
Name of the net. The default is ``None``.
|
|
558
|
+
|
|
559
|
+
Returns
|
|
560
|
+
-------
|
|
561
|
+
dict
|
|
562
|
+
Dictionary of pins if the operation is successful.
|
|
563
|
+
``False`` is returned if the net does not belong to the component.
|
|
564
|
+
|
|
565
|
+
"""
|
|
566
|
+
warnings.warn(
|
|
567
|
+
"`get_pinlist_from_component_and_net` is deprecated use `get_pin_from_component_and_net` instead.",
|
|
568
|
+
DeprecationWarning,
|
|
569
|
+
)
|
|
570
|
+
return self.get_pin_from_component_and_net(refdes=refdes, netname=netname)
|
|
571
|
+
|
|
572
|
+
def get_pad_parameters(self, pin, layername, pad_type="regular_pad"):
|
|
573
|
+
"""Get Padstack Parameters from Pin or Padstack Definition.
|
|
574
|
+
|
|
575
|
+
Parameters
|
|
576
|
+
----------
|
|
577
|
+
pin : Edb.definition.PadstackDef or Edb.definition.PadstackInstance
|
|
578
|
+
Pin or PadstackDef on which get values.
|
|
579
|
+
layername : str
|
|
580
|
+
Layer on which get properties.
|
|
581
|
+
pad_type : str
|
|
582
|
+
Pad Type, `"pad"`, `"anti_pad"`, `"thermal_pad"`
|
|
583
|
+
|
|
584
|
+
Returns
|
|
585
|
+
-------
|
|
586
|
+
tuple
|
|
587
|
+
Tuple of (GeometryType, ParameterList, OffsetX, OffsetY, Rot).
|
|
588
|
+
"""
|
|
589
|
+
if pad_type == "regular_pad":
|
|
590
|
+
pad_type = GrpcPadType.REGULAR_PAD
|
|
591
|
+
elif pad_type == "anti_pad":
|
|
592
|
+
pad_type = GrpcPadType.ANTI_PAD
|
|
593
|
+
elif pad_type == "thermal_pad":
|
|
594
|
+
pad_type = GrpcPadType.THERMAL_PAD
|
|
595
|
+
else:
|
|
596
|
+
pad_type = pad_type = GrpcPadType.REGULAR_PAD
|
|
597
|
+
padparams = pin.padstack_def.data.get_pad_parameters(layername, pad_type)
|
|
598
|
+
if len(padparams) == 5: # non polygon via
|
|
599
|
+
geometry_type = padparams[0]
|
|
600
|
+
parameters = [i.value for i in padparams[1]]
|
|
601
|
+
offset_x = padparams[2].value
|
|
602
|
+
offset_y = padparams[3].value
|
|
603
|
+
rotation = padparams[4].value
|
|
604
|
+
return geometry_type.name, parameters, offset_x, offset_y, rotation
|
|
605
|
+
elif len(padparams) == 4: # polygon based
|
|
606
|
+
from ansys.edb.core.geometry.polygon_data import (
|
|
607
|
+
PolygonData as GrpcPolygonData,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
if isinstance(padparams[0], GrpcPolygonData):
|
|
611
|
+
points = [[pt.x.value, pt.y.value] for pt in padparams[0].points]
|
|
612
|
+
offset_x = padparams[1]
|
|
613
|
+
offset_y = padparams[2]
|
|
614
|
+
rotation = padparams[3]
|
|
615
|
+
geometry_type = GrpcPadGeometryType.PADGEOMTYPE_POLYGON
|
|
616
|
+
return geometry_type.name, points, offset_x, offset_y, rotation
|
|
617
|
+
return 0, [0], 0, 0, 0
|
|
618
|
+
|
|
619
|
+
def set_all_antipad_value(self, value):
|
|
620
|
+
"""Set all anti-pads from all pad-stack definition to the given value.
|
|
621
|
+
|
|
622
|
+
Parameters
|
|
623
|
+
----------
|
|
624
|
+
value : float, str
|
|
625
|
+
Anti-pad value.
|
|
626
|
+
|
|
627
|
+
Returns
|
|
628
|
+
-------
|
|
629
|
+
bool
|
|
630
|
+
``True`` when successful, ``False`` if an anti-pad value fails to be assigned.
|
|
631
|
+
"""
|
|
632
|
+
if self.definitions:
|
|
633
|
+
all_succeed = True
|
|
634
|
+
for padstack in list(self.definitions.values()):
|
|
635
|
+
cloned_padstack_data = GrpcPadstackDefData(padstack.data.msg)
|
|
636
|
+
layers_name = cloned_padstack_data.layer_names
|
|
637
|
+
for layer in layers_name:
|
|
638
|
+
try:
|
|
639
|
+
geom_type, points, offset_x, offset_y, rotation = cloned_padstack_data.get_pad_parameters(
|
|
640
|
+
layer, GrpcPadType.ANTI_PAD
|
|
641
|
+
)
|
|
642
|
+
if geom_type == GrpcPadGeometryType.PADGEOMTYPE_CIRCLE.name:
|
|
643
|
+
cloned_padstack_data.set_pad_parameters(
|
|
644
|
+
layer=layer,
|
|
645
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
646
|
+
offset_x=GrpcValue(offset_x),
|
|
647
|
+
offset_y=GrpcValue(offset_y),
|
|
648
|
+
rotation=GrpcValue(rotation),
|
|
649
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
650
|
+
sizes=[GrpcValue(value)],
|
|
651
|
+
)
|
|
652
|
+
self._logger.info(
|
|
653
|
+
"Pad-stack definition {}, anti-pad on layer {}, has been set to {}".format(
|
|
654
|
+
padstack.edb_padstack.GetName(), layer, str(value)
|
|
655
|
+
)
|
|
656
|
+
)
|
|
657
|
+
else: # pragma no cover
|
|
658
|
+
self._logger.error(
|
|
659
|
+
f"Failed to reassign anti-pad value {value} on Pads-stack definition {padstack.name},"
|
|
660
|
+
f" layer{layer}. This feature only support circular shape anti-pads."
|
|
661
|
+
)
|
|
662
|
+
all_succeed = False
|
|
663
|
+
except:
|
|
664
|
+
self._pedb.logger.info(
|
|
665
|
+
f"No antipad defined for padstack definition {padstack.name}-layer{layer}"
|
|
666
|
+
)
|
|
667
|
+
padstack.data = cloned_padstack_data
|
|
668
|
+
return all_succeed
|
|
669
|
+
|
|
670
|
+
def check_and_fix_via_plating(self, minimum_value_to_replace=0.0, default_plating_ratio=0.2):
|
|
671
|
+
"""Check for minimum via plating ration value, values found below the minimum one are replaced by default
|
|
672
|
+
plating ratio.
|
|
673
|
+
|
|
674
|
+
Parameters
|
|
675
|
+
----------
|
|
676
|
+
minimum_value_to_replace : float
|
|
677
|
+
Plating ratio that is below or equal to this value is to be replaced
|
|
678
|
+
with the value specified for the next parameter. Default value ``0.0``.
|
|
679
|
+
default_plating_ratio : float
|
|
680
|
+
Default value to use for plating ratio. The default value is ``0.2``.
|
|
681
|
+
|
|
682
|
+
Returns
|
|
683
|
+
-------
|
|
684
|
+
bool
|
|
685
|
+
``True`` when successful, ``False`` if an anti-pad value fails to be assigned.
|
|
686
|
+
"""
|
|
687
|
+
for padstack_def in list(self.definitions.values()):
|
|
688
|
+
if padstack_def.hole_plating_ratio <= minimum_value_to_replace:
|
|
689
|
+
padstack_def.hole_plating_ratio = default_plating_ratio
|
|
690
|
+
self._logger.info(
|
|
691
|
+
"Padstack definition with zero plating ratio, defaulting to 20%".format(padstack_def.name)
|
|
692
|
+
)
|
|
693
|
+
return True
|
|
694
|
+
|
|
695
|
+
def get_via_instance_from_net(self, net_list=None):
|
|
696
|
+
"""Get the list for EDB vias from a net name list.
|
|
697
|
+
|
|
698
|
+
Parameters
|
|
699
|
+
----------
|
|
700
|
+
net_list : str or list
|
|
701
|
+
The list of the net name to be used for filtering vias. If no net is provided the command will
|
|
702
|
+
return an all vias list.
|
|
703
|
+
|
|
704
|
+
Returns
|
|
705
|
+
-------
|
|
706
|
+
list of Edb.Cell.Primitive.PadstackInstance
|
|
707
|
+
List of EDB vias.
|
|
708
|
+
"""
|
|
709
|
+
if net_list and not isinstance(net_list, list):
|
|
710
|
+
net_list = [net_list]
|
|
711
|
+
via_list = []
|
|
712
|
+
for inst in self._layout.padstack_instances:
|
|
713
|
+
pad_layers_name = inst.padstack_def.data.layer_names
|
|
714
|
+
if len(pad_layers_name) > 1:
|
|
715
|
+
if not net_list:
|
|
716
|
+
via_list.append(inst)
|
|
717
|
+
elif not inst.net.is_null:
|
|
718
|
+
if inst.net.name in net_list:
|
|
719
|
+
via_list.append(inst)
|
|
720
|
+
return via_list
|
|
721
|
+
|
|
722
|
+
def create(
|
|
723
|
+
self,
|
|
724
|
+
padstackname=None,
|
|
725
|
+
holediam="300um",
|
|
726
|
+
paddiam="400um",
|
|
727
|
+
antipaddiam="600um",
|
|
728
|
+
pad_shape="Circle",
|
|
729
|
+
antipad_shape="Circle",
|
|
730
|
+
x_size="600um",
|
|
731
|
+
y_size="600um",
|
|
732
|
+
corner_radius="300um",
|
|
733
|
+
offset_x="0.0",
|
|
734
|
+
offset_y="0.0",
|
|
735
|
+
rotation="0.0",
|
|
736
|
+
has_hole=True,
|
|
737
|
+
pad_offset_x="0.0",
|
|
738
|
+
pad_offset_y="0.0",
|
|
739
|
+
pad_rotation="0.0",
|
|
740
|
+
pad_polygon=None,
|
|
741
|
+
antipad_polygon=None,
|
|
742
|
+
polygon_hole=None,
|
|
743
|
+
start_layer=None,
|
|
744
|
+
stop_layer=None,
|
|
745
|
+
add_default_layer=False,
|
|
746
|
+
anti_pad_x_size="600um",
|
|
747
|
+
anti_pad_y_size="600um",
|
|
748
|
+
hole_range="upper_pad_to_lower_pad",
|
|
749
|
+
):
|
|
750
|
+
"""Create a padstack.
|
|
751
|
+
|
|
752
|
+
Parameters
|
|
753
|
+
----------
|
|
754
|
+
padstackname : str, optional
|
|
755
|
+
Name of the padstack. The default is ``None``.
|
|
756
|
+
holediam : str, optional
|
|
757
|
+
Diameter of the hole with units. The default is ``"300um"``.
|
|
758
|
+
paddiam : str, optional
|
|
759
|
+
Diameter of the pad with units, used with ``"Circle"`` shape. The default is ``"400um"``.
|
|
760
|
+
antipaddiam : str, optional
|
|
761
|
+
Diameter of the antipad with units. The default is ``"600um"``.
|
|
762
|
+
pad_shape : str, optional
|
|
763
|
+
Shape of the pad. The default is ``"Circle``. Options are ``"Circle"``, ``"Rectangle"`` and ``"Polygon"``.
|
|
764
|
+
antipad_shape : str, optional
|
|
765
|
+
Shape of the antipad. The default is ``"Circle"``. Options are ``"Circle"`` ``"Rectangle"`` and
|
|
766
|
+
``"Bullet"``.
|
|
767
|
+
x_size : str, optional
|
|
768
|
+
Only applicable to bullet and rectangle shape. The default is ``"600um"``.
|
|
769
|
+
y_size : str, optional
|
|
770
|
+
Only applicable to bullet and rectangle shape. The default is ``"600um"``.
|
|
771
|
+
corner_radius :
|
|
772
|
+
Only applicable to bullet shape. The default is ``"300um"``.
|
|
773
|
+
offset_x : str, optional
|
|
774
|
+
X offset of antipad. The default is ``"0.0"``.
|
|
775
|
+
offset_y : str, optional
|
|
776
|
+
Y offset of antipad. The default is ``"0.0"``.
|
|
777
|
+
rotation : str, optional
|
|
778
|
+
rotation of antipad. The default is ``"0.0"``.
|
|
779
|
+
has_hole : bool, optional
|
|
780
|
+
Whether this padstack has a hole.
|
|
781
|
+
pad_offset_x : str, optional
|
|
782
|
+
Padstack offset in X direction.
|
|
783
|
+
pad_offset_y : str, optional
|
|
784
|
+
Padstack offset in Y direction.
|
|
785
|
+
pad_rotation : str, optional
|
|
786
|
+
Padstack rotation.
|
|
787
|
+
start_layer : str, optional
|
|
788
|
+
Start layer of the padstack definition.
|
|
789
|
+
stop_layer : str, optional
|
|
790
|
+
Stop layer of the padstack definition.
|
|
791
|
+
add_default_layer : bool, optional
|
|
792
|
+
Add ``"Default"`` to padstack definition. Default is ``False``.
|
|
793
|
+
anti_pad_x_size : str, optional
|
|
794
|
+
Only applicable to bullet and rectangle shape. The default is ``"600um"``.
|
|
795
|
+
anti_pad_y_size : str, optional
|
|
796
|
+
Only applicable to bullet and rectangle shape. The default is ``"600um"``.
|
|
797
|
+
hole_range : str, optional
|
|
798
|
+
Define the padstack hole range. Arguments supported, ``"through"``, ``"begin_on_upper_pad"``,
|
|
799
|
+
``"end_on_lower_pad"``, ``"upper_pad_to_lower_pad"``.
|
|
800
|
+
|
|
801
|
+
Returns
|
|
802
|
+
-------
|
|
803
|
+
str
|
|
804
|
+
Name of the padstack if the operation is successful.
|
|
805
|
+
"""
|
|
806
|
+
holediam = GrpcValue(holediam)
|
|
807
|
+
paddiam = GrpcValue(paddiam)
|
|
808
|
+
antipaddiam = GrpcValue(antipaddiam)
|
|
809
|
+
layers = list(self._pedb.stackup.signal_layers.keys())[:]
|
|
810
|
+
value0 = GrpcValue("0.0")
|
|
811
|
+
if not padstackname:
|
|
812
|
+
padstackname = generate_unique_name("VIA")
|
|
813
|
+
padstack_data = GrpcPadstackDefData.create()
|
|
814
|
+
if has_hole and not polygon_hole:
|
|
815
|
+
hole_param = [holediam, holediam]
|
|
816
|
+
padstack_data.set_hole_parameters(
|
|
817
|
+
offset_x=value0,
|
|
818
|
+
offset_y=value0,
|
|
819
|
+
rotation=value0,
|
|
820
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
821
|
+
sizes=hole_param,
|
|
822
|
+
)
|
|
823
|
+
padstack_data.plating_percentage = GrpcValue(20.0)
|
|
824
|
+
elif polygon_hole:
|
|
825
|
+
if isinstance(polygon_hole, list):
|
|
826
|
+
polygon_hole = GrpcPolygonData(points=polygon_hole)
|
|
827
|
+
|
|
828
|
+
padstack_data.set_hole_parameters(
|
|
829
|
+
offset_x=value0,
|
|
830
|
+
offset_y=value0,
|
|
831
|
+
rotation=value0,
|
|
832
|
+
type_geom=GrpcPadGeometryType.PADGEOMTYPE_POLYGON,
|
|
833
|
+
sizes=polygon_hole,
|
|
834
|
+
)
|
|
835
|
+
padstack_data.plating_percentage = GrpcValue(20.0)
|
|
836
|
+
else:
|
|
837
|
+
pass
|
|
838
|
+
|
|
839
|
+
x_size = GrpcValue(x_size)
|
|
840
|
+
y_size = GrpcValue(y_size)
|
|
841
|
+
corner_radius = GrpcValue(corner_radius)
|
|
842
|
+
pad_offset_x = GrpcValue(pad_offset_x)
|
|
843
|
+
pad_offset_y = GrpcValue(pad_offset_y)
|
|
844
|
+
pad_rotation = GrpcValue(pad_rotation)
|
|
845
|
+
anti_pad_x_size = GrpcValue(anti_pad_x_size)
|
|
846
|
+
anti_pad_y_size = GrpcValue(anti_pad_y_size)
|
|
847
|
+
|
|
848
|
+
if hole_range == "through": # pragma no cover
|
|
849
|
+
padstack_data.hole_range = GrpcPadstackHoleRange.THROUGH
|
|
850
|
+
elif hole_range == "begin_on_upper_pad": # pragma no cover
|
|
851
|
+
padstack_data.hole_range = GrpcPadstackHoleRange.BEGIN_ON_UPPER_PAD
|
|
852
|
+
elif hole_range == "end_on_lower_pad": # pragma no cover
|
|
853
|
+
padstack_data.hole_range = GrpcPadstackHoleRange.END_ON_LOWER_PAD
|
|
854
|
+
elif hole_range == "upper_pad_to_lower_pad": # pragma no cover
|
|
855
|
+
padstack_data.hole_range = GrpcPadstackHoleRange.UPPER_PAD_TO_LOWER_PAD
|
|
856
|
+
else: # pragma no cover
|
|
857
|
+
self._logger.error("Unknown padstack hole range")
|
|
858
|
+
padstack_data.material = "copper"
|
|
859
|
+
|
|
860
|
+
if start_layer and start_layer in layers: # pragma no cover
|
|
861
|
+
layers = layers[layers.index(start_layer) :]
|
|
862
|
+
if stop_layer and stop_layer in layers: # pragma no cover
|
|
863
|
+
layers = layers[: layers.index(stop_layer) + 1]
|
|
864
|
+
if not isinstance(paddiam, list):
|
|
865
|
+
pad_array = [paddiam]
|
|
866
|
+
else:
|
|
867
|
+
pad_array = paddiam
|
|
868
|
+
if pad_shape == "Circle": # pragma no cover
|
|
869
|
+
pad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
|
|
870
|
+
elif pad_shape == "Rectangle": # pragma no cover
|
|
871
|
+
pad_array = [x_size, y_size]
|
|
872
|
+
pad_shape = GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
|
|
873
|
+
elif pad_shape == "Polygon":
|
|
874
|
+
if isinstance(pad_polygon, list):
|
|
875
|
+
pad_array = GrpcPolygonData(points=pad_polygon)
|
|
876
|
+
elif isinstance(pad_polygon, GrpcPolygonData):
|
|
877
|
+
pad_array = pad_polygon
|
|
878
|
+
if antipad_shape == "Bullet": # pragma no cover
|
|
879
|
+
antipad_array = [x_size, y_size, corner_radius]
|
|
880
|
+
antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_BULLET
|
|
881
|
+
elif antipad_shape == "Rectangle": # pragma no cover
|
|
882
|
+
antipad_array = [anti_pad_x_size, anti_pad_y_size]
|
|
883
|
+
antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE
|
|
884
|
+
elif antipad_shape == "Polygon":
|
|
885
|
+
if isinstance(antipad_polygon, list):
|
|
886
|
+
antipad_array = GrpcPolygonData(points=antipad_polygon)
|
|
887
|
+
elif isinstance(antipad_polygon, GrpcPolygonData):
|
|
888
|
+
antipad_array = antipad_polygon
|
|
889
|
+
else:
|
|
890
|
+
if not isinstance(antipaddiam, list):
|
|
891
|
+
antipad_array = [antipaddiam]
|
|
892
|
+
else:
|
|
893
|
+
antipad_array = antipaddiam
|
|
894
|
+
antipad_shape = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
|
|
895
|
+
if add_default_layer: # pragma no cover
|
|
896
|
+
layers = layers + ["Default"]
|
|
897
|
+
if antipad_shape == "Polygon" and pad_shape == "Polygon":
|
|
898
|
+
for layer in layers:
|
|
899
|
+
padstack_data.set_pad_parameters(
|
|
900
|
+
layer=layer,
|
|
901
|
+
pad_type=GrpcPadType.REGULAR_PAD,
|
|
902
|
+
offset_x=pad_offset_x,
|
|
903
|
+
offset_y=pad_offset_y,
|
|
904
|
+
rotation=pad_rotation,
|
|
905
|
+
fp=pad_array,
|
|
906
|
+
)
|
|
907
|
+
padstack_data.set_pad_parameters(
|
|
908
|
+
layer=layer,
|
|
909
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
910
|
+
offset_x=pad_offset_x,
|
|
911
|
+
offset_y=pad_offset_y,
|
|
912
|
+
rotation=pad_rotation,
|
|
913
|
+
fp=antipad_array,
|
|
914
|
+
)
|
|
915
|
+
else:
|
|
916
|
+
for layer in layers:
|
|
917
|
+
padstack_data.set_pad_parameters(
|
|
918
|
+
layer=layer,
|
|
919
|
+
pad_type=GrpcPadType.REGULAR_PAD,
|
|
920
|
+
offset_x=pad_offset_x,
|
|
921
|
+
offset_y=pad_offset_y,
|
|
922
|
+
rotation=pad_rotation,
|
|
923
|
+
type_geom=pad_shape,
|
|
924
|
+
sizes=pad_array,
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
padstack_data.set_pad_parameters(
|
|
928
|
+
layer=layer,
|
|
929
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
930
|
+
offset_x=pad_offset_x,
|
|
931
|
+
offset_y=pad_offset_y,
|
|
932
|
+
rotation=pad_rotation,
|
|
933
|
+
type_geom=antipad_shape,
|
|
934
|
+
sizes=antipad_array,
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
padstack_definition = PadstackDef.create(self.db, padstackname)
|
|
938
|
+
padstack_definition.data = padstack_data
|
|
939
|
+
self._logger.info(f"Padstack {padstackname} create correctly")
|
|
940
|
+
return padstackname
|
|
941
|
+
|
|
942
|
+
def _get_pin_layer_range(self, pin):
|
|
943
|
+
layers = pin.get_layer_range()
|
|
944
|
+
if layers:
|
|
945
|
+
return layers[0], layers[1]
|
|
946
|
+
else:
|
|
947
|
+
return False
|
|
948
|
+
|
|
949
|
+
def duplicate(self, target_padstack_name, new_padstack_name=""):
|
|
950
|
+
"""Duplicate a padstack.
|
|
951
|
+
|
|
952
|
+
Parameters
|
|
953
|
+
----------
|
|
954
|
+
target_padstack_name : str
|
|
955
|
+
Name of the padstack to be duplicated.
|
|
956
|
+
new_padstack_name : str, optional
|
|
957
|
+
Name of the new padstack.
|
|
958
|
+
|
|
959
|
+
Returns
|
|
960
|
+
-------
|
|
961
|
+
str
|
|
962
|
+
Name of the new padstack.
|
|
963
|
+
"""
|
|
964
|
+
new_padstack_definition_data = GrpcPadstackDefData(self.definitions[target_padstack_name].data.msg)
|
|
965
|
+
if not new_padstack_name:
|
|
966
|
+
new_padstack_name = generate_unique_name(target_padstack_name)
|
|
967
|
+
padstack_definition = PadstackDef.create(self.db, new_padstack_name)
|
|
968
|
+
padstack_definition.data = new_padstack_definition_data
|
|
969
|
+
return new_padstack_name
|
|
970
|
+
|
|
971
|
+
def place(
|
|
972
|
+
self,
|
|
973
|
+
position,
|
|
974
|
+
definition_name,
|
|
975
|
+
net_name="",
|
|
976
|
+
via_name="",
|
|
977
|
+
rotation=0.0,
|
|
978
|
+
fromlayer=None,
|
|
979
|
+
tolayer=None,
|
|
980
|
+
solderlayer=None,
|
|
981
|
+
is_pin=False,
|
|
982
|
+
):
|
|
983
|
+
"""Place a via.
|
|
984
|
+
|
|
985
|
+
Parameters
|
|
986
|
+
----------
|
|
987
|
+
position : list
|
|
988
|
+
List of float values for the [x,y] positions where the via is to be placed.
|
|
989
|
+
definition_name : str
|
|
990
|
+
Name of the padstack definition.
|
|
991
|
+
net_name : str, optional
|
|
992
|
+
Name of the net. The default is ``""``.
|
|
993
|
+
via_name : str, optional
|
|
994
|
+
The default is ``""``.
|
|
995
|
+
rotation : float, optional
|
|
996
|
+
Rotation of the padstack in degrees. The default
|
|
997
|
+
is ``0``.
|
|
998
|
+
fromlayer :
|
|
999
|
+
The default is ``None``.
|
|
1000
|
+
tolayer :
|
|
1001
|
+
The default is ``None``.
|
|
1002
|
+
solderlayer :
|
|
1003
|
+
The default is ``None``.
|
|
1004
|
+
is_pin : bool, optional
|
|
1005
|
+
Whether if the padstack is a pin or not. Default is `False`.
|
|
1006
|
+
|
|
1007
|
+
Returns
|
|
1008
|
+
-------
|
|
1009
|
+
:class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`
|
|
1010
|
+
"""
|
|
1011
|
+
padstack_def = None
|
|
1012
|
+
for pad in list(self.definitions.keys()):
|
|
1013
|
+
if pad == definition_name:
|
|
1014
|
+
padstack_def = self.definitions[pad]
|
|
1015
|
+
position = GrpcPointData(
|
|
1016
|
+
[GrpcValue(position[0], self._pedb.active_cell), GrpcValue(position[1], self._pedb.active_cell)]
|
|
1017
|
+
)
|
|
1018
|
+
net = self._pedb.nets.find_or_create_net(net_name)
|
|
1019
|
+
rotation = GrpcValue(rotation * math.pi / 180)
|
|
1020
|
+
sign_layers_values = {i: v for i, v in self._pedb.stackup.signal_layers.items()}
|
|
1021
|
+
sign_layers = list(sign_layers_values.keys())
|
|
1022
|
+
if not fromlayer:
|
|
1023
|
+
try:
|
|
1024
|
+
fromlayer = sign_layers_values[list(self.definitions[pad].pad_by_layer.keys())[0]]
|
|
1025
|
+
except KeyError:
|
|
1026
|
+
fromlayer = sign_layers_values[sign_layers[0]]
|
|
1027
|
+
else:
|
|
1028
|
+
fromlayer = sign_layers_values[fromlayer]
|
|
1029
|
+
|
|
1030
|
+
if not tolayer:
|
|
1031
|
+
try:
|
|
1032
|
+
tolayer = sign_layers_values[list(self.definitions[pad].pad_by_layer.keys())[-1]]
|
|
1033
|
+
except KeyError:
|
|
1034
|
+
tolayer = sign_layers_values[sign_layers[-1]]
|
|
1035
|
+
else:
|
|
1036
|
+
tolayer = sign_layers_values[tolayer]
|
|
1037
|
+
if solderlayer:
|
|
1038
|
+
solderlayer = sign_layers_values[solderlayer]
|
|
1039
|
+
if not via_name:
|
|
1040
|
+
via_name = generate_unique_name(padstack_def.name)
|
|
1041
|
+
if padstack_def:
|
|
1042
|
+
padstack_instance = PadstackInstance.create(
|
|
1043
|
+
layout=self._active_layout,
|
|
1044
|
+
net=net,
|
|
1045
|
+
name=via_name,
|
|
1046
|
+
padstack_def=padstack_def,
|
|
1047
|
+
position_x=position.x,
|
|
1048
|
+
position_y=position.y,
|
|
1049
|
+
rotation=rotation,
|
|
1050
|
+
top_layer=fromlayer,
|
|
1051
|
+
bottom_layer=tolayer,
|
|
1052
|
+
solder_ball_layer=solderlayer,
|
|
1053
|
+
layer_map=None,
|
|
1054
|
+
)
|
|
1055
|
+
padstack_instance.is_layout_pin = is_pin
|
|
1056
|
+
return PadstackInstance(self._pedb, padstack_instance)
|
|
1057
|
+
else:
|
|
1058
|
+
return False
|
|
1059
|
+
|
|
1060
|
+
def remove_pads_from_padstack(self, padstack_name, layer_name=None):
|
|
1061
|
+
"""Remove the Pad from a padstack on a specific layer by setting it as a 0 thickness circle.
|
|
1062
|
+
|
|
1063
|
+
Parameters
|
|
1064
|
+
----------
|
|
1065
|
+
padstack_name : str
|
|
1066
|
+
padstack name
|
|
1067
|
+
layer_name : str, optional
|
|
1068
|
+
Layer name on which remove the PadParameters. If None, all layers will be taken.
|
|
1069
|
+
|
|
1070
|
+
Returns
|
|
1071
|
+
-------
|
|
1072
|
+
bool
|
|
1073
|
+
``True`` if successful.
|
|
1074
|
+
"""
|
|
1075
|
+
pad_type = GrpcPadType.REGULAR_PAD
|
|
1076
|
+
pad_geo = GrpcPadGeometryType.PADGEOMTYPE_CIRCLE
|
|
1077
|
+
vals = GrpcValue(0)
|
|
1078
|
+
params = [GrpcValue(0)]
|
|
1079
|
+
new_padstack_definition_data = GrpcPadstackDefData(self.definitions[padstack_name].data)
|
|
1080
|
+
if not layer_name:
|
|
1081
|
+
layer_name = list(self._pedb.stackup.signal_layers.keys())
|
|
1082
|
+
elif isinstance(layer_name, str):
|
|
1083
|
+
layer_name = [layer_name]
|
|
1084
|
+
for lay in layer_name:
|
|
1085
|
+
new_padstack_definition_data.set_pad_parameters(
|
|
1086
|
+
layer=lay,
|
|
1087
|
+
pad_type=pad_type,
|
|
1088
|
+
offset_x=vals,
|
|
1089
|
+
offset_y=vals,
|
|
1090
|
+
rotation=vals,
|
|
1091
|
+
type_geom=pad_geo,
|
|
1092
|
+
sizes=params,
|
|
1093
|
+
)
|
|
1094
|
+
self.definitions[padstack_name].data = new_padstack_definition_data
|
|
1095
|
+
return True
|
|
1096
|
+
|
|
1097
|
+
def set_pad_property(
|
|
1098
|
+
self,
|
|
1099
|
+
padstack_name,
|
|
1100
|
+
layer_name=None,
|
|
1101
|
+
pad_shape="Circle",
|
|
1102
|
+
pad_params=0,
|
|
1103
|
+
pad_x_offset=0,
|
|
1104
|
+
pad_y_offset=0,
|
|
1105
|
+
pad_rotation=0,
|
|
1106
|
+
antipad_shape="Circle",
|
|
1107
|
+
antipad_params=0,
|
|
1108
|
+
antipad_x_offset=0,
|
|
1109
|
+
antipad_y_offset=0,
|
|
1110
|
+
antipad_rotation=0,
|
|
1111
|
+
):
|
|
1112
|
+
"""Set pad and antipad properties of the padstack.
|
|
1113
|
+
|
|
1114
|
+
Parameters
|
|
1115
|
+
----------
|
|
1116
|
+
padstack_name : str
|
|
1117
|
+
Name of the padstack.
|
|
1118
|
+
layer_name : str, optional
|
|
1119
|
+
Name of the layer. If None, all layers will be taken.
|
|
1120
|
+
pad_shape : str, optional
|
|
1121
|
+
Shape of the pad. The default is ``"Circle"``. Options are ``"Circle"``, ``"Square"``, ``"Rectangle"``,
|
|
1122
|
+
``"Oval"`` and ``"Bullet"``.
|
|
1123
|
+
pad_params : str, optional
|
|
1124
|
+
Dimension of the pad. The default is ``"0"``.
|
|
1125
|
+
pad_x_offset : str, optional
|
|
1126
|
+
X offset of the pad. The default is ``"0"``.
|
|
1127
|
+
pad_y_offset : str, optional
|
|
1128
|
+
Y offset of the pad. The default is ``"0"``.
|
|
1129
|
+
pad_rotation : str, optional
|
|
1130
|
+
Rotation of the pad. The default is ``"0"``.
|
|
1131
|
+
antipad_shape : str, optional
|
|
1132
|
+
Shape of the antipad. The default is ``"0"``.
|
|
1133
|
+
antipad_params : str, optional
|
|
1134
|
+
Dimension of the antipad. The default is ``"0"``.
|
|
1135
|
+
antipad_x_offset : str, optional
|
|
1136
|
+
X offset of the antipad. The default is ``"0"``.
|
|
1137
|
+
antipad_y_offset : str, optional
|
|
1138
|
+
Y offset of the antipad. The default is ``"0"``.
|
|
1139
|
+
antipad_rotation : str, optional
|
|
1140
|
+
Rotation of the antipad. The default is ``"0"``.
|
|
1141
|
+
|
|
1142
|
+
Returns
|
|
1143
|
+
-------
|
|
1144
|
+
bool
|
|
1145
|
+
``True`` if successful.
|
|
1146
|
+
"""
|
|
1147
|
+
shape_dict = {
|
|
1148
|
+
"Circle": GrpcPadGeometryType.PADGEOMTYPE_CIRCLE,
|
|
1149
|
+
"Square": GrpcPadGeometryType.PADGEOMTYPE_SQUARE,
|
|
1150
|
+
"Rectangle": GrpcPadGeometryType.PADGEOMTYPE_RECTANGLE,
|
|
1151
|
+
"Oval": GrpcPadGeometryType.PADGEOMTYPE_OVAL,
|
|
1152
|
+
"Bullet": GrpcPadGeometryType.PADGEOMTYPE_BULLET,
|
|
1153
|
+
}
|
|
1154
|
+
pad_shape = shape_dict[pad_shape]
|
|
1155
|
+
if not isinstance(pad_params, list):
|
|
1156
|
+
pad_params = [pad_params]
|
|
1157
|
+
pad_params = [GrpcValue(i) for i in pad_params]
|
|
1158
|
+
pad_x_offset = GrpcValue(pad_x_offset)
|
|
1159
|
+
pad_y_offset = GrpcValue(pad_y_offset)
|
|
1160
|
+
pad_rotation = GrpcValue(pad_rotation)
|
|
1161
|
+
|
|
1162
|
+
antipad_shape = shape_dict[antipad_shape]
|
|
1163
|
+
if not isinstance(antipad_params, list):
|
|
1164
|
+
antipad_params = [antipad_params]
|
|
1165
|
+
antipad_params = [GrpcValue(i) for i in antipad_params]
|
|
1166
|
+
antipad_x_offset = GrpcValue(antipad_x_offset)
|
|
1167
|
+
antipad_y_offset = GrpcValue(antipad_y_offset)
|
|
1168
|
+
antipad_rotation = GrpcValue(antipad_rotation)
|
|
1169
|
+
new_padstack_def = GrpcPadstackDefData(self.definitions[padstack_name].data.msg)
|
|
1170
|
+
if not layer_name:
|
|
1171
|
+
layer_name = list(self._pedb.stackup.signal_layers.keys())
|
|
1172
|
+
elif isinstance(layer_name, str):
|
|
1173
|
+
layer_name = [layer_name]
|
|
1174
|
+
for layer in layer_name:
|
|
1175
|
+
new_padstack_def.set_pad_parameters(
|
|
1176
|
+
layer=layer,
|
|
1177
|
+
pad_type=GrpcPadType.REGULAR_PAD,
|
|
1178
|
+
offset_x=pad_x_offset,
|
|
1179
|
+
offset_y=pad_y_offset,
|
|
1180
|
+
rotation=pad_rotation,
|
|
1181
|
+
type_geom=pad_shape,
|
|
1182
|
+
sizes=pad_params,
|
|
1183
|
+
)
|
|
1184
|
+
new_padstack_def.set_pad_parameters(
|
|
1185
|
+
layer=layer,
|
|
1186
|
+
pad_type=GrpcPadType.ANTI_PAD,
|
|
1187
|
+
offset_x=antipad_x_offset,
|
|
1188
|
+
offset_y=antipad_y_offset,
|
|
1189
|
+
rotation=antipad_rotation,
|
|
1190
|
+
type_geom=antipad_shape,
|
|
1191
|
+
sizes=antipad_params,
|
|
1192
|
+
)
|
|
1193
|
+
self.definitions[padstack_name].data = new_padstack_def
|
|
1194
|
+
return True
|
|
1195
|
+
|
|
1196
|
+
def get_instances(
|
|
1197
|
+
self,
|
|
1198
|
+
name=None,
|
|
1199
|
+
pid=None,
|
|
1200
|
+
definition_name=None,
|
|
1201
|
+
net_name=None,
|
|
1202
|
+
component_reference_designator=None,
|
|
1203
|
+
component_pin=None,
|
|
1204
|
+
):
|
|
1205
|
+
"""Get padstack instances by conditions.
|
|
1206
|
+
|
|
1207
|
+
Parameters
|
|
1208
|
+
----------
|
|
1209
|
+
name : str, optional
|
|
1210
|
+
Name of the padstack.
|
|
1211
|
+
pid : int, optional
|
|
1212
|
+
Id of the padstack.
|
|
1213
|
+
definition_name : str, list, optional
|
|
1214
|
+
Name of the padstack definition.
|
|
1215
|
+
net_name : str, optional
|
|
1216
|
+
The net name to be used for filtering padstack instances.
|
|
1217
|
+
component_pin: str, optional
|
|
1218
|
+
Pin Number of the component.
|
|
1219
|
+
Returns
|
|
1220
|
+
-------
|
|
1221
|
+
list
|
|
1222
|
+
List of :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`.
|
|
1223
|
+
"""
|
|
1224
|
+
|
|
1225
|
+
instances_by_id = self.instances
|
|
1226
|
+
if pid:
|
|
1227
|
+
return instances_by_id[pid]
|
|
1228
|
+
elif name:
|
|
1229
|
+
instances = [inst for inst in list(self.instances.values()) if inst.name == name]
|
|
1230
|
+
if instances:
|
|
1231
|
+
return instances
|
|
1232
|
+
else:
|
|
1233
|
+
instances = list(instances_by_id.values())
|
|
1234
|
+
if definition_name:
|
|
1235
|
+
definition_name = definition_name if isinstance(definition_name, list) else [definition_name]
|
|
1236
|
+
instances = [inst for inst in instances if inst.padstack_def.name in definition_name]
|
|
1237
|
+
if net_name:
|
|
1238
|
+
net_name = net_name if isinstance(net_name, list) else [net_name]
|
|
1239
|
+
instances = [inst for inst in instances if inst.net_name in net_name]
|
|
1240
|
+
if component_reference_designator:
|
|
1241
|
+
refdes = (
|
|
1242
|
+
component_reference_designator
|
|
1243
|
+
if isinstance(component_reference_designator, list)
|
|
1244
|
+
else [component_reference_designator]
|
|
1245
|
+
)
|
|
1246
|
+
instances = [inst for inst in instances if inst.component]
|
|
1247
|
+
instances = [inst for inst in instances if inst.component.refdes in refdes]
|
|
1248
|
+
if component_pin:
|
|
1249
|
+
component_pin = component_pin if isinstance(component_pin, list) else [component_pin]
|
|
1250
|
+
instances = [inst for inst in instances if inst.component_pin in component_pin]
|
|
1251
|
+
return instances
|
|
1252
|
+
|
|
1253
|
+
def get_reference_pins(
|
|
1254
|
+
self, positive_pin, reference_net="gnd", search_radius=5e-3, max_limit=0, component_only=True
|
|
1255
|
+
):
|
|
1256
|
+
"""Search for reference pins using given criteria.
|
|
1257
|
+
|
|
1258
|
+
Parameters
|
|
1259
|
+
----------
|
|
1260
|
+
positive_pin : EDBPadstackInstance
|
|
1261
|
+
Pin used for evaluating the distance on the reference pins found.
|
|
1262
|
+
reference_net : str, optional
|
|
1263
|
+
Reference net. The default is ``"gnd"``.
|
|
1264
|
+
search_radius : float, optional
|
|
1265
|
+
Search radius for finding padstack instances. The default is ``5e-3``.
|
|
1266
|
+
max_limit : int, optional
|
|
1267
|
+
Maximum limit for the padstack instances found. The default is ``0``, in which
|
|
1268
|
+
case no limit is applied. The maximum limit value occurs on the nearest
|
|
1269
|
+
reference pins from the positive one that is found.
|
|
1270
|
+
component_only : bool, optional
|
|
1271
|
+
Whether to limit the search to component padstack instances only. The
|
|
1272
|
+
default is ``True``. When ``False``, the search is extended to the entire layout.
|
|
1273
|
+
|
|
1274
|
+
Returns
|
|
1275
|
+
-------
|
|
1276
|
+
list
|
|
1277
|
+
List of :class:`dotnet.database.edb_data.padstacks_data.EDBPadstackInstance`.
|
|
1278
|
+
|
|
1279
|
+
Examples
|
|
1280
|
+
--------
|
|
1281
|
+
>>> edbapp = Edb("target_path")
|
|
1282
|
+
>>> pin = edbapp.components.instances["J5"].pins["19"]
|
|
1283
|
+
>>> reference_pins = edbapp.padstacks.get_reference_pins(positive_pin=pin, reference_net="GND",
|
|
1284
|
+
>>> search_radius=5e-3, max_limit=0, component_only=True)
|
|
1285
|
+
"""
|
|
1286
|
+
pinlist = []
|
|
1287
|
+
if not positive_pin:
|
|
1288
|
+
search_radius = 10e-2
|
|
1289
|
+
component_only = True
|
|
1290
|
+
if component_only:
|
|
1291
|
+
references_pins = [
|
|
1292
|
+
pin for pin in list(positive_pin.component.pins.values()) if pin.net_name == reference_net
|
|
1293
|
+
]
|
|
1294
|
+
if not references_pins:
|
|
1295
|
+
return pinlist
|
|
1296
|
+
else:
|
|
1297
|
+
references_pins = self.get_instances(net_name=reference_net)
|
|
1298
|
+
if not references_pins:
|
|
1299
|
+
return pinlist
|
|
1300
|
+
pinlist = [
|
|
1301
|
+
p
|
|
1302
|
+
for p in references_pins
|
|
1303
|
+
if GeometryOperators.points_distance(positive_pin.position, p.position) <= search_radius
|
|
1304
|
+
]
|
|
1305
|
+
if max_limit and len(pinlist) > max_limit:
|
|
1306
|
+
pin_dict = {GeometryOperators.points_distance(positive_pin.position, p.position): p for p in pinlist}
|
|
1307
|
+
pinlist = [pin[1] for pin in sorted(pin_dict.items())[:max_limit]]
|
|
1308
|
+
return pinlist
|
|
1309
|
+
|
|
1310
|
+
def get_padstack_instances_rtree_index(self, nets=None):
|
|
1311
|
+
"""Returns padstack instances Rtree index.
|
|
1312
|
+
|
|
1313
|
+
Parameters
|
|
1314
|
+
----------
|
|
1315
|
+
nets : str or list, optional
|
|
1316
|
+
net name of list of nets name applying filtering on padstack instances selection. If ``None`` is provided
|
|
1317
|
+
all instances are included in the index. Default value is ``None``.
|
|
1318
|
+
|
|
1319
|
+
Returns
|
|
1320
|
+
-------
|
|
1321
|
+
Rtree index object.
|
|
1322
|
+
|
|
1323
|
+
"""
|
|
1324
|
+
if isinstance(nets, str):
|
|
1325
|
+
nets = [nets]
|
|
1326
|
+
padstack_instances_index = rtree.index.Index()
|
|
1327
|
+
if nets:
|
|
1328
|
+
instances = [inst for inst in list(self.instances.values()) if inst.net_name in nets]
|
|
1329
|
+
else:
|
|
1330
|
+
instances = list(self.instances.values())
|
|
1331
|
+
for inst in instances:
|
|
1332
|
+
padstack_instances_index.insert(inst.id, inst.position)
|
|
1333
|
+
return padstack_instances_index
|
|
1334
|
+
|
|
1335
|
+
def get_padstack_instances_intersecting_bounding_box(self, bounding_box, nets=None):
|
|
1336
|
+
"""Returns the list of padstack instances ID intersecting a given bounding box and nets.
|
|
1337
|
+
|
|
1338
|
+
Parameters
|
|
1339
|
+
----------
|
|
1340
|
+
bounding_box : tuple or list.
|
|
1341
|
+
bounding box, [x1, y1, x2, y2]
|
|
1342
|
+
nets : str or list, optional
|
|
1343
|
+
net name of list of nets name applying filtering on padstack instances selection. If ``None`` is provided
|
|
1344
|
+
all instances are included in the index. Default value is ``None``.
|
|
1345
|
+
|
|
1346
|
+
Returns
|
|
1347
|
+
-------
|
|
1348
|
+
List of padstack instances ID intersecting the bounding box.
|
|
1349
|
+
"""
|
|
1350
|
+
if not bounding_box:
|
|
1351
|
+
raise Exception("No bounding box was provided")
|
|
1352
|
+
index = self.get_padstack_instances_rtree_index(nets=nets)
|
|
1353
|
+
if not len(bounding_box) == 4:
|
|
1354
|
+
raise Exception("The bounding box length must be equal to 4")
|
|
1355
|
+
if isinstance(bounding_box, list):
|
|
1356
|
+
bounding_box = tuple(bounding_box)
|
|
1357
|
+
return list(index.intersection(bounding_box))
|
|
1358
|
+
|
|
1359
|
+
def merge_via_along_lines(
|
|
1360
|
+
self, net_name="GND", distance_threshold=5e-3, minimum_via_number=6, selected_angles=None
|
|
1361
|
+
):
|
|
1362
|
+
"""Replace padstack instances along lines into a single polygon.
|
|
1363
|
+
|
|
1364
|
+
Detect all padstack instances that are placed along lines and replace them by a single polygon based one
|
|
1365
|
+
forming a wall shape. This method is designed to simplify meshing on via fence usually added to shield RF traces
|
|
1366
|
+
on PCB.
|
|
1367
|
+
|
|
1368
|
+
Parameters
|
|
1369
|
+
----------
|
|
1370
|
+
net_name : str
|
|
1371
|
+
Net name used for detected padstack instances. Default value is ``"GND"``.
|
|
1372
|
+
|
|
1373
|
+
distance_threshold : float, None, optional
|
|
1374
|
+
If two points in a line are separated by a distance larger than `distance_threshold`,
|
|
1375
|
+
the line is divided in two parts. Default is ``5e-3`` (5mm), in which case the control is not performed.
|
|
1376
|
+
|
|
1377
|
+
minimum_via_number : int, optional
|
|
1378
|
+
The minimum number of points that a line must contain. Default is ``6``.
|
|
1379
|
+
|
|
1380
|
+
selected_angles : list[int, float]
|
|
1381
|
+
Specify angle in degrees to detected, for instance [0, 180] is only detecting horizontal and vertical lines.
|
|
1382
|
+
Other values can be assigned like 45 degrees. When `None` is provided all lines are detected. Default value
|
|
1383
|
+
is `None`.
|
|
1384
|
+
|
|
1385
|
+
Returns
|
|
1386
|
+
-------
|
|
1387
|
+
bool
|
|
1388
|
+
``True`` when succeeded ``False`` when failed. <
|
|
1389
|
+
|
|
1390
|
+
"""
|
|
1391
|
+
_def = list(set([inst.padstack_def for inst in list(self.instances.values()) if inst.net_name == net_name]))
|
|
1392
|
+
if not _def:
|
|
1393
|
+
self._logger.error(f"No padstack definition found for net {net_name}")
|
|
1394
|
+
return False
|
|
1395
|
+
_instances_to_delete = []
|
|
1396
|
+
padstack_instances = []
|
|
1397
|
+
for pdstk_def in _def:
|
|
1398
|
+
padstack_instances.append(
|
|
1399
|
+
[inst for inst in self.definitions[pdstk_def.name].instances if inst.net_name == net_name]
|
|
1400
|
+
)
|
|
1401
|
+
for pdstk_series in padstack_instances:
|
|
1402
|
+
instances_location = [inst.position for inst in pdstk_series]
|
|
1403
|
+
lines, line_indexes = GeometryOperators.find_points_along_lines(
|
|
1404
|
+
points=instances_location,
|
|
1405
|
+
minimum_number_of_points=minimum_via_number,
|
|
1406
|
+
distance_threshold=distance_threshold,
|
|
1407
|
+
selected_angles=selected_angles,
|
|
1408
|
+
)
|
|
1409
|
+
for line in line_indexes:
|
|
1410
|
+
[_instances_to_delete.append(pdstk_series[ind]) for ind in line]
|
|
1411
|
+
start_point = pdstk_series[line[0]]
|
|
1412
|
+
stop_point = pdstk_series[line[-1]]
|
|
1413
|
+
padstack_def = start_point.padstack_def
|
|
1414
|
+
trace_width = (
|
|
1415
|
+
self.definitions[padstack_def.name].pad_by_layer[stop_point.start_layer].parameters_values[0]
|
|
1416
|
+
)
|
|
1417
|
+
trace = self._pedb.modeler.create_trace(
|
|
1418
|
+
path_list=[start_point.position, stop_point.position],
|
|
1419
|
+
layer_name=start_point.start_layer,
|
|
1420
|
+
width=trace_width,
|
|
1421
|
+
)
|
|
1422
|
+
polygon_data = trace.polygon_data
|
|
1423
|
+
trace.delete()
|
|
1424
|
+
new_padstack_def = generate_unique_name(padstack_def.name)
|
|
1425
|
+
if not self.create(
|
|
1426
|
+
padstackname=new_padstack_def,
|
|
1427
|
+
pad_shape="Polygon",
|
|
1428
|
+
antipad_shape="Polygon",
|
|
1429
|
+
pad_polygon=polygon_data,
|
|
1430
|
+
antipad_polygon=polygon_data,
|
|
1431
|
+
polygon_hole=polygon_data,
|
|
1432
|
+
):
|
|
1433
|
+
self._logger.error(f"Failed to create padstack definition {new_padstack_def.name}")
|
|
1434
|
+
if not self.place(position=[0, 0], definition_name=new_padstack_def, net_name=net_name):
|
|
1435
|
+
self._logger.error(f"Failed to place padstack instance {new_padstack_def.name}")
|
|
1436
|
+
for inst in _instances_to_delete:
|
|
1437
|
+
inst.delete()
|
|
1438
|
+
return True
|
|
1439
|
+
|
|
1440
|
+
def merge_via(self, contour_boxes, net_filter=None, start_layer=None, stop_layer=None):
|
|
1441
|
+
"""Evaluate padstack instances included on the provided point list and replace all by single instance.
|
|
1442
|
+
|
|
1443
|
+
Parameters
|
|
1444
|
+
----------
|
|
1445
|
+
contour_boxes : List[List[List[float, float]]]
|
|
1446
|
+
Nested list of polygon with points [x,y].
|
|
1447
|
+
net_filter : optional
|
|
1448
|
+
List[str: net_name] apply a net filter,
|
|
1449
|
+
nets included in the filter are excluded from the via merge.
|
|
1450
|
+
start_layer : optional, str
|
|
1451
|
+
Padstack instance start layer, if `None` the top layer is selected.
|
|
1452
|
+
stop_layer : optional, str
|
|
1453
|
+
Padstack instance stop layer, if `None` the bottom layer is selected.
|
|
1454
|
+
|
|
1455
|
+
Return
|
|
1456
|
+
------
|
|
1457
|
+
List[str], list of created padstack instances ID.
|
|
1458
|
+
|
|
1459
|
+
"""
|
|
1460
|
+
merged_via_ids = []
|
|
1461
|
+
if not contour_boxes:
|
|
1462
|
+
self._pedb.logger.error("No contour box provided, you need to pass a nested list as argument.")
|
|
1463
|
+
return False
|
|
1464
|
+
if not start_layer:
|
|
1465
|
+
start_layer = list(self._pedb.stackup.layers.values())[0].name
|
|
1466
|
+
if not stop_layer:
|
|
1467
|
+
stop_layer = list(self._pedb.stackup.layers.values())[-1].name
|
|
1468
|
+
instances_index = {}
|
|
1469
|
+
for id, inst in self.instances.items():
|
|
1470
|
+
instances_index[id] = inst.position
|
|
1471
|
+
for contour_box in contour_boxes:
|
|
1472
|
+
instances = self.get_padstack_instances_id_intersecting_polygon(
|
|
1473
|
+
points=contour_box, padstack_instances_index=instances_index
|
|
1474
|
+
)
|
|
1475
|
+
if net_filter:
|
|
1476
|
+
instances = [self.instances[id] for id in instances if not self.instances[id].net.name in net_filter]
|
|
1477
|
+
net = self.instances[instances[0]].net.name
|
|
1478
|
+
instances_pts = np.array([self.instances[id].position for id in instances])
|
|
1479
|
+
convex_hull_contour = ConvexHull(instances_pts)
|
|
1480
|
+
contour_points = list(instances_pts[convex_hull_contour.vertices])
|
|
1481
|
+
layer = list(self._pedb.stackup.layers.values())[0].name
|
|
1482
|
+
polygon = self._pedb.modeler.create_polygon(main_shape=contour_points, layer_name=layer)
|
|
1483
|
+
polygon_data = polygon.polygon_data
|
|
1484
|
+
polygon.delete()
|
|
1485
|
+
new_padstack_def = generate_unique_name("test")
|
|
1486
|
+
if not self.create(
|
|
1487
|
+
padstackname=new_padstack_def,
|
|
1488
|
+
pad_shape="Polygon",
|
|
1489
|
+
antipad_shape="Polygon",
|
|
1490
|
+
pad_polygon=polygon_data,
|
|
1491
|
+
antipad_polygon=polygon_data,
|
|
1492
|
+
polygon_hole=polygon_data,
|
|
1493
|
+
start_layer=start_layer,
|
|
1494
|
+
stop_layer=stop_layer,
|
|
1495
|
+
):
|
|
1496
|
+
self._logger.error(f"Failed to create padstack definition {new_padstack_def}")
|
|
1497
|
+
merged_instance = self.place(position=[0, 0], definition_name=new_padstack_def, net_name=net)
|
|
1498
|
+
merged_via_ids.append(merged_instance.id)
|
|
1499
|
+
[self.instances[id].delete() for id in instances]
|
|
1500
|
+
return merged_via_ids
|