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