pyedb 0.37.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_common.py +1 -1
- pyedb/configuration/cfg_components.py +229 -201
- pyedb/configuration/cfg_data.py +3 -1
- pyedb/configuration/cfg_general.py +4 -2
- pyedb/configuration/cfg_modeler.py +7 -7
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +346 -290
- pyedb/configuration/cfg_ports_sources.py +243 -65
- pyedb/configuration/configuration.py +23 -3
- 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 +19 -19
- 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 +60 -73
- 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 +17 -16
- 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 +18 -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 +4 -4
- 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 +4 -3
- pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
- pyedb/dotnet/edb.py +118 -113
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +1151 -0
- 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 +52 -20
- pyedb/ipc2581/ipc2581.py +47 -49
- pyedb/modeler/geometry_operators.py +1 -1
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/METADATA +9 -6
- pyedb-0.39.0.dist-info/RECORD +288 -0
- pyedb-0.37.0.dist-info/RECORD +0 -194
- /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.37.0.dist-info → pyedb-0.39.0.dist-info}/LICENSE +0 -0
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,2572 @@
|
|
|
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
|
+
from ansys.edb.core.database import ProductIdType as GrpcProductIdType
|
|
24
|
+
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
25
|
+
from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData
|
|
26
|
+
from ansys.edb.core.hierarchy.component_group import (
|
|
27
|
+
ComponentGroup as GrpcComponentGroup,
|
|
28
|
+
)
|
|
29
|
+
from ansys.edb.core.terminal.terminals import BoundaryType as GrpcBoundaryType
|
|
30
|
+
from ansys.edb.core.terminal.terminals import EdgeTerminal as GrpcEdgeTerminal
|
|
31
|
+
from ansys.edb.core.terminal.terminals import PrimitiveEdge as GrpcPrimitiveEdge
|
|
32
|
+
from ansys.edb.core.utility.rlc import Rlc as GrpcRlc
|
|
33
|
+
from ansys.edb.core.utility.value import Value as GrpcValue
|
|
34
|
+
|
|
35
|
+
from pyedb.generic.general_methods import generate_unique_name
|
|
36
|
+
from pyedb.grpc.database.layers.stackup_layer import StackupLayer
|
|
37
|
+
from pyedb.grpc.database.net.net import Net
|
|
38
|
+
from pyedb.grpc.database.ports.ports import BundleWavePort, WavePort
|
|
39
|
+
from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
|
|
40
|
+
from pyedb.grpc.database.primitive.primitive import Primitive
|
|
41
|
+
from pyedb.grpc.database.terminal.bundle_terminal import BundleTerminal
|
|
42
|
+
from pyedb.grpc.database.terminal.padstack_instance_terminal import (
|
|
43
|
+
PadstackInstanceTerminal,
|
|
44
|
+
)
|
|
45
|
+
from pyedb.grpc.database.terminal.pingroup_terminal import PinGroupTerminal
|
|
46
|
+
from pyedb.grpc.database.terminal.point_terminal import PointTerminal
|
|
47
|
+
from pyedb.grpc.database.utility.sources import Source, SourceType
|
|
48
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SourceExcitation:
|
|
52
|
+
def __init__(self, pedb):
|
|
53
|
+
self._pedb = pedb
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def _logger(self):
|
|
57
|
+
return self._pedb.logger
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def excitations(self):
|
|
61
|
+
"""Get all excitations."""
|
|
62
|
+
return self._pedb.excitations
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def sources(self):
|
|
66
|
+
"""Get all sources."""
|
|
67
|
+
return self._pedb.sources
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def probes(self):
|
|
71
|
+
"""Get all probes."""
|
|
72
|
+
return self._pedb.probes
|
|
73
|
+
|
|
74
|
+
def create_source_on_component(self, sources=None):
|
|
75
|
+
"""Create voltage, current source, or resistor on component.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
sources : list[Source]
|
|
80
|
+
List of ``pyedb.grpc.utility.sources.Source`` objects.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
bool
|
|
85
|
+
``True`` when successful, ``False`` when failed.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
if not sources: # pragma: no cover
|
|
90
|
+
return False
|
|
91
|
+
if isinstance(sources, Source): # pragma: no cover
|
|
92
|
+
sources = [sources]
|
|
93
|
+
if isinstance(sources, list): # pragma: no cover
|
|
94
|
+
for src in sources:
|
|
95
|
+
if not isinstance(src, Source): # pragma: no cover
|
|
96
|
+
self._pedb.logger.error("List of source objects must be passed as an argument.")
|
|
97
|
+
return False
|
|
98
|
+
for source in sources:
|
|
99
|
+
positive_pins = self._pedb.padstack.get_instances(source.positive_node.component, source.positive_node.net)
|
|
100
|
+
negative_pins = self._pedb.padstack.get_instances(source.negative_node.component, source.negative_node.net)
|
|
101
|
+
positive_pin_group = self._pedb.components.create_pingroup_from_pins(positive_pins)
|
|
102
|
+
if not positive_pin_group: # pragma: no cover
|
|
103
|
+
return False
|
|
104
|
+
positive_pin_group = self._pedb.siwave.pin_groups[positive_pin_group.name]
|
|
105
|
+
negative_pin_group = self._pedb.components.create_pingroup_from_pins(negative_pins)
|
|
106
|
+
if not negative_pin_group: # pragma: no cover
|
|
107
|
+
return False
|
|
108
|
+
negative_pin_group = self._pedb.siwave.pin_groups[negative_pin_group.GetName()]
|
|
109
|
+
if source.source_type == SourceType.Vsource: # pragma: no cover
|
|
110
|
+
positive_pin_group_term = self._pedb.components._create_pin_group_terminal(
|
|
111
|
+
positive_pin_group,
|
|
112
|
+
)
|
|
113
|
+
negative_pin_group_term = self._pedb.components._create_pin_group_terminal(
|
|
114
|
+
negative_pin_group, isref=True
|
|
115
|
+
)
|
|
116
|
+
positive_pin_group_term.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
117
|
+
negative_pin_group_term.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
118
|
+
term_name = source.name
|
|
119
|
+
positive_pin_group_term.SetName(term_name)
|
|
120
|
+
negative_pin_group_term.SetName("{}_ref".format(term_name))
|
|
121
|
+
positive_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
|
|
122
|
+
negative_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
|
|
123
|
+
positive_pin_group_term.source_phase = GrpcValue(source.phase)
|
|
124
|
+
negative_pin_group_term.source_phase = GrpcValue(source.phase)
|
|
125
|
+
positive_pin_group_term.impedance = GrpcValue(source.impedance)
|
|
126
|
+
negative_pin_group_term.impedance = GrpcValue(source.impedance)
|
|
127
|
+
positive_pin_group_term.reference_terminal = negative_pin_group_term
|
|
128
|
+
elif source.source_type == SourceType.Isource: # pragma: no cover
|
|
129
|
+
positive_pin_group_term = self._pedb.components._create_pin_group_terminal(
|
|
130
|
+
positive_pin_group,
|
|
131
|
+
)
|
|
132
|
+
negative_pin_group_term = self._pedb.components._create_pin_group_terminal(
|
|
133
|
+
negative_pin_group, isref=True
|
|
134
|
+
)
|
|
135
|
+
positive_pin_group_term.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
136
|
+
negative_pin_group_term.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
137
|
+
positive_pin_group_term.name = source.name
|
|
138
|
+
negative_pin_group_term.name = "{}_ref".format(source.name)
|
|
139
|
+
positive_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
|
|
140
|
+
negative_pin_group_term.source_amplitude = GrpcValue(source.amplitude)
|
|
141
|
+
positive_pin_group_term.source_phase = GrpcValue(source.phase)
|
|
142
|
+
negative_pin_group_term.source_phase = GrpcValue(source.phase)
|
|
143
|
+
positive_pin_group_term.impedance = GrpcValue(source.impedance)
|
|
144
|
+
negative_pin_group_term.impedance = GrpcValue(source.impedance)
|
|
145
|
+
positive_pin_group_term.reference_terminal = negative_pin_group_term
|
|
146
|
+
elif source.source_type == SourceType.Rlc: # pragma: no cover
|
|
147
|
+
self._pedb.components.create(
|
|
148
|
+
pins=[positive_pins[0], negative_pins[0]],
|
|
149
|
+
component_name=source.name,
|
|
150
|
+
is_rlc=True,
|
|
151
|
+
r_value=source.r_value,
|
|
152
|
+
l_value=source.l_value,
|
|
153
|
+
c_value=source.c_value,
|
|
154
|
+
)
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
def create_port_on_pins(
|
|
158
|
+
self,
|
|
159
|
+
refdes,
|
|
160
|
+
pins,
|
|
161
|
+
reference_pins,
|
|
162
|
+
impedance=50.0,
|
|
163
|
+
port_name=None,
|
|
164
|
+
pec_boundary=False,
|
|
165
|
+
pingroup_on_single_pin=False,
|
|
166
|
+
):
|
|
167
|
+
"""Create circuit port between pins and reference ones.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
refdes : Component reference designator
|
|
172
|
+
str or EDBComponent object.
|
|
173
|
+
pins : pin name where the terminal has to be created. Single pin or several ones can be provided.If several
|
|
174
|
+
pins are provided a pin group will is created. Pin names can be the EDB name or the EDBPadstackInstance one.
|
|
175
|
+
For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1`` or ``Pin1`` can be provided and
|
|
176
|
+
will be handled.
|
|
177
|
+
str, [str], EDBPadstackInstance, [EDBPadstackInstance]
|
|
178
|
+
reference_pins : reference pin name used for terminal reference. Single pin or several ones can be provided.
|
|
179
|
+
If several pins are provided a pin group will is created. Pin names can be the EDB name or the
|
|
180
|
+
EDBPadstackInstance one. For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1``
|
|
181
|
+
or ``Pin1`` can be provided and will be handled.
|
|
182
|
+
str, [str], EDBPadstackInstance, [EDBPadstackInstance]
|
|
183
|
+
impedance : Port impedance
|
|
184
|
+
str, float
|
|
185
|
+
port_name : str, optional
|
|
186
|
+
Port name. The default is ``None``, in which case a name is automatically assigned.
|
|
187
|
+
pec_boundary : bool, optional
|
|
188
|
+
Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
|
|
189
|
+
a perfect short is created between the pin and impedance is ignored. This
|
|
190
|
+
parameter is only supported on a port created between two pins, such as
|
|
191
|
+
when there is no pin group.
|
|
192
|
+
pingroup_on_single_pin : bool
|
|
193
|
+
If ``True`` force using pingroup definition on single pin to have the port created at the pad center. If
|
|
194
|
+
``False`` the port is created at the pad edge. Default value is ``False``.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
EDB terminal created, or False if failed to create.
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
>>> from pyedb import Edb
|
|
202
|
+
>>> edb = Edb(path_to_edb_file)
|
|
203
|
+
>>> pin = "AJ6"
|
|
204
|
+
>>> ref_pins = ["AM7", "AM4"]
|
|
205
|
+
Or to take all reference pins
|
|
206
|
+
>>> ref_pins = [pin for pin in list(edb.components["U2A5"].pins.values()) if pin.net_name == "GND"]
|
|
207
|
+
>>> edb.components.create_port_on_pins(refdes="U2A5", pins=pin, reference_pins=ref_pins)
|
|
208
|
+
>>> edb.save_edb()
|
|
209
|
+
>>> edb.close_edb()
|
|
210
|
+
"""
|
|
211
|
+
from pyedb.grpc.database.components import Component
|
|
212
|
+
|
|
213
|
+
if isinstance(pins, str):
|
|
214
|
+
pins = [pins]
|
|
215
|
+
elif isinstance(pins, PadstackInstance):
|
|
216
|
+
pins = [pins.name]
|
|
217
|
+
if not reference_pins:
|
|
218
|
+
self._logger.error("No reference pin provided.")
|
|
219
|
+
return False
|
|
220
|
+
if isinstance(reference_pins, str):
|
|
221
|
+
reference_pins = [reference_pins]
|
|
222
|
+
elif isinstance(reference_pins, int):
|
|
223
|
+
reference_pins = [reference_pins]
|
|
224
|
+
elif isinstance(reference_pins, PadstackInstance):
|
|
225
|
+
reference_pins = [reference_pins]
|
|
226
|
+
if isinstance(reference_pins, list):
|
|
227
|
+
_temp = []
|
|
228
|
+
for ref_pin in reference_pins:
|
|
229
|
+
if isinstance(ref_pin, int):
|
|
230
|
+
pins = self._pedb.padstacks.instances
|
|
231
|
+
reference_pins = [pins[ref_pin] for ref_pin in reference_pins if ref_pin in pins]
|
|
232
|
+
# if reference_pins in pins:
|
|
233
|
+
# reference_pins = pins[reference_pins]
|
|
234
|
+
elif isinstance(ref_pin, str):
|
|
235
|
+
component_pins = self._pedb.components.instances[refdes].pins
|
|
236
|
+
if ref_pin in component_pins:
|
|
237
|
+
_temp.append(component_pins[ref_pin])
|
|
238
|
+
else:
|
|
239
|
+
p = [pp for pp in list(self._pedb.padstack.instances.values()) if pp.name == ref_pin]
|
|
240
|
+
if p:
|
|
241
|
+
_temp.extend(p)
|
|
242
|
+
elif isinstance(ref_pin, PadstackInstance):
|
|
243
|
+
_temp.append(ref_pin)
|
|
244
|
+
reference_pins = _temp
|
|
245
|
+
if isinstance(refdes, str):
|
|
246
|
+
refdes = self._pedb.components.instances[refdes]
|
|
247
|
+
elif isinstance(refdes, GrpcComponentGroup):
|
|
248
|
+
refdes = Component(self._pedb, refdes)
|
|
249
|
+
refdes_pins = refdes.pins
|
|
250
|
+
if any(refdes.rlc_values):
|
|
251
|
+
return self._pedb.components.deactivate_rlc_component(component=refdes, create_circuit_port=True)
|
|
252
|
+
if len([pin for pin in pins if isinstance(pin, str)]) == len(pins):
|
|
253
|
+
cmp_pins = []
|
|
254
|
+
for pin_name in pins:
|
|
255
|
+
cmp_pins = [pin for pin in list(refdes_pins.values()) if pin_name == pin.name]
|
|
256
|
+
if not cmp_pins:
|
|
257
|
+
for pin in list(refdes_pins.values()):
|
|
258
|
+
if pin.name and "-" in pin.name:
|
|
259
|
+
if pin_name == pin.name.split("-")[1]:
|
|
260
|
+
cmp_pins.append(pin)
|
|
261
|
+
if not cmp_pins:
|
|
262
|
+
self._logger.warning("No pin found during port creation. Port is not defined.")
|
|
263
|
+
return
|
|
264
|
+
pins = cmp_pins
|
|
265
|
+
if not len([pin for pin in pins if isinstance(pin, PadstackInstance)]) == len(pins):
|
|
266
|
+
self._logger.error("Pin list must contain only pins instances")
|
|
267
|
+
return False
|
|
268
|
+
if not port_name:
|
|
269
|
+
pin = pins[0]
|
|
270
|
+
if pin.net.is_null:
|
|
271
|
+
pin_net_name = "no_net"
|
|
272
|
+
else:
|
|
273
|
+
pin_net_name = pin.net.name
|
|
274
|
+
port_name = f"Port_{pin_net_name}_{refdes.name}_{pins[0].name}"
|
|
275
|
+
|
|
276
|
+
ref_cmp_pins = []
|
|
277
|
+
for ref_pin in reference_pins:
|
|
278
|
+
if ref_pin.name in refdes_pins:
|
|
279
|
+
ref_cmp_pins.append(ref_pin)
|
|
280
|
+
elif "-" in ref_pin.name:
|
|
281
|
+
if ref_pin.name.split("-")[1] in refdes_pins:
|
|
282
|
+
ref_cmp_pins.append(ref_pin)
|
|
283
|
+
elif "via" in ref_pin.name:
|
|
284
|
+
_ref_pin = [
|
|
285
|
+
pin for pin in list(self._pedb.padstacks.instances.values()) if pin.aedt_name == ref_pin.name
|
|
286
|
+
]
|
|
287
|
+
if _ref_pin:
|
|
288
|
+
_ref_pin[0].is_layout_pin = True
|
|
289
|
+
ref_cmp_pins.append(_ref_pin[0])
|
|
290
|
+
if not ref_cmp_pins:
|
|
291
|
+
self._logger.error("No reference pins found.")
|
|
292
|
+
return False
|
|
293
|
+
reference_pins = ref_cmp_pins
|
|
294
|
+
if len(pins) > 1 or pingroup_on_single_pin:
|
|
295
|
+
pec_boundary = False
|
|
296
|
+
self._logger.info(
|
|
297
|
+
"Disabling PEC boundary creation, this feature is supported on single pin "
|
|
298
|
+
"ports only, {} pins found".format(len(pins))
|
|
299
|
+
)
|
|
300
|
+
group_name = "group_{}".format(port_name)
|
|
301
|
+
pin_group = self._pedb.components.create_pingroup_from_pins(pins, group_name)
|
|
302
|
+
term = self._create_pin_group_terminal(pingroup=pin_group, term_name=port_name)
|
|
303
|
+
|
|
304
|
+
else:
|
|
305
|
+
term = self._create_terminal(pins[0], term_name=port_name)
|
|
306
|
+
term.is_circuit_port = True
|
|
307
|
+
if len(reference_pins) > 1 or pingroup_on_single_pin:
|
|
308
|
+
pec_boundary = False
|
|
309
|
+
self._logger.info(
|
|
310
|
+
"Disabling PEC boundary creation. This feature is supported on single pin"
|
|
311
|
+
"ports only {} reference pins found.".format(len(reference_pins))
|
|
312
|
+
)
|
|
313
|
+
ref_group_name = "group_{}_ref".format(port_name)
|
|
314
|
+
ref_pin_group = self._pedb.components.create_pingroup_from_pins(reference_pins, ref_group_name)
|
|
315
|
+
ref_pin_group = self._pedb.siwave.pin_groups[ref_pin_group.name]
|
|
316
|
+
ref_term = self._create_pin_group_terminal(pingroup=ref_pin_group, term_name=port_name + "_ref")
|
|
317
|
+
|
|
318
|
+
else:
|
|
319
|
+
ref_term = self._create_terminal(reference_pins[0], term_name=port_name + "_ref")
|
|
320
|
+
ref_term.is_circuit_port = True
|
|
321
|
+
term.impedance = GrpcValue(impedance)
|
|
322
|
+
term.reference_terminal = ref_term
|
|
323
|
+
if pec_boundary:
|
|
324
|
+
term.is_circuit_port = False
|
|
325
|
+
ref_term.is_circuit_port = False
|
|
326
|
+
term.boundary_type = GrpcBoundaryType.PEC
|
|
327
|
+
ref_term.boundary_type = GrpcBoundaryType.PEC
|
|
328
|
+
self._logger.info(
|
|
329
|
+
"PEC boundary created between pin {} and reference pin {}".format(pins[0].name, reference_pins[0].name)
|
|
330
|
+
)
|
|
331
|
+
if term:
|
|
332
|
+
return term
|
|
333
|
+
return False
|
|
334
|
+
|
|
335
|
+
def create_port_on_component(
|
|
336
|
+
self,
|
|
337
|
+
component,
|
|
338
|
+
net_list,
|
|
339
|
+
port_type=SourceType.CoaxPort,
|
|
340
|
+
do_pingroup=True,
|
|
341
|
+
reference_net="gnd",
|
|
342
|
+
port_name=None,
|
|
343
|
+
solder_balls_height=None,
|
|
344
|
+
solder_balls_size=None,
|
|
345
|
+
solder_balls_mid_size=None,
|
|
346
|
+
extend_reference_pins_outside_component=False,
|
|
347
|
+
):
|
|
348
|
+
"""Create ports on a component.
|
|
349
|
+
|
|
350
|
+
Parameters
|
|
351
|
+
----------
|
|
352
|
+
component : str or self._pedb.component
|
|
353
|
+
EDB component or str component name.
|
|
354
|
+
net_list : str or list of string.
|
|
355
|
+
List of nets where ports must be created on the component.
|
|
356
|
+
If the net is not part of the component, this parameter is skipped.
|
|
357
|
+
port_type : str, optional
|
|
358
|
+
Type of port to create. ``coax_port`` generates solder balls.
|
|
359
|
+
``circuit_port`` generates circuit ports on pins belonging to the net list.
|
|
360
|
+
do_pingroup : bool
|
|
361
|
+
True activate pingroup during port creation (only used with combination of CircPort),
|
|
362
|
+
False will take the closest reference pin and generate one port per signal pin.
|
|
363
|
+
refnet : string or list of string.
|
|
364
|
+
list of the reference net.
|
|
365
|
+
port_name : str
|
|
366
|
+
Port name for overwriting the default port-naming convention,
|
|
367
|
+
which is ``[component][net][pin]``. The port name must be unique.
|
|
368
|
+
If a port with the specified name already exists, the
|
|
369
|
+
default naming convention is used so that port creation does
|
|
370
|
+
not fail.
|
|
371
|
+
solder_balls_height : float, optional
|
|
372
|
+
Solder balls height used for the component. When provided default value is overwritten and must be
|
|
373
|
+
provided in meter.
|
|
374
|
+
solder_balls_size : float, optional
|
|
375
|
+
Solder balls diameter. When provided auto evaluation based on padstack size will be disabled.
|
|
376
|
+
solder_balls_mid_size : float, optional
|
|
377
|
+
Solder balls mid-diameter. When provided if value is different than solder balls size, spheroid shape will
|
|
378
|
+
be switched.
|
|
379
|
+
extend_reference_pins_outside_component : bool
|
|
380
|
+
When no reference pins are found on the component extend the pins search with taking the closest one. If
|
|
381
|
+
`do_pingroup` is `True` will be set to `False`. Default value is `False`.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
double, bool
|
|
386
|
+
Salder ball height vale, ``False`` when failed.
|
|
387
|
+
|
|
388
|
+
Examples
|
|
389
|
+
--------
|
|
390
|
+
|
|
391
|
+
>>> from pyedb import Edb
|
|
392
|
+
>>> edbapp = Edb("myaedbfolder")
|
|
393
|
+
>>> net_list = ["M_DQ<1>", "M_DQ<2>", "M_DQ<3>", "M_DQ<4>", "M_DQ<5>"]
|
|
394
|
+
>>> edbapp.components.create_port_on_component(cmp="U2A5", net_list=net_list,
|
|
395
|
+
>>> port_type=SourceType.CoaxPort, do_pingroup=False, refnet="GND")
|
|
396
|
+
|
|
397
|
+
"""
|
|
398
|
+
if isinstance(component, str):
|
|
399
|
+
component = self._pedb.components.instances[component]
|
|
400
|
+
if not isinstance(net_list, list):
|
|
401
|
+
net_list = [net_list]
|
|
402
|
+
for net in net_list:
|
|
403
|
+
if not isinstance(net, str):
|
|
404
|
+
try:
|
|
405
|
+
net_name = net.name
|
|
406
|
+
if net_name:
|
|
407
|
+
net_list.append(net_name)
|
|
408
|
+
except:
|
|
409
|
+
pass
|
|
410
|
+
if reference_net in net_list:
|
|
411
|
+
net_list.remove(reference_net)
|
|
412
|
+
cmp_pins = [p for p in list(component.pins.values()) if p.net_name in net_list]
|
|
413
|
+
for p in cmp_pins: # pragma no cover
|
|
414
|
+
p.is_layout_pin = True
|
|
415
|
+
if len(cmp_pins) == 0:
|
|
416
|
+
self._logger.info(f"No pins found on component {component.name}, searching padstack instances instead")
|
|
417
|
+
return False
|
|
418
|
+
pin_layers = cmp_pins[0].padstack_def.data.layer_names
|
|
419
|
+
if port_type == "coax_port":
|
|
420
|
+
if not solder_balls_height:
|
|
421
|
+
solder_balls_height = self._pedb.components.instances[component.name].solder_ball_height
|
|
422
|
+
if not solder_balls_size:
|
|
423
|
+
solder_balls_size = self._pedb.components.instances[component.name].solder_ball_diameter[0]
|
|
424
|
+
if not solder_balls_mid_size:
|
|
425
|
+
solder_balls_mid_size = self._pedb.components.instances[component.name].solder_ball_diameter[1]
|
|
426
|
+
ref_pins = [p for p in list(component.pins.values()) if p.net_name in reference_net]
|
|
427
|
+
if not ref_pins:
|
|
428
|
+
self._logger.error(
|
|
429
|
+
"No reference pins found on component. You might consider"
|
|
430
|
+
"using Circuit port instead since reference pins can be extended"
|
|
431
|
+
"outside the component when not found if argument extend_reference_pins_outside_component is True."
|
|
432
|
+
)
|
|
433
|
+
return False
|
|
434
|
+
pad_params = self._pedb.padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
|
|
435
|
+
if not pad_params[0] == 7:
|
|
436
|
+
if not solder_balls_size: # pragma no cover
|
|
437
|
+
sball_diam = min([GrpcValue(val).value for val in pad_params[1]])
|
|
438
|
+
sball_mid_diam = sball_diam
|
|
439
|
+
else: # pragma no cover
|
|
440
|
+
sball_diam = solder_balls_size
|
|
441
|
+
if solder_balls_mid_size:
|
|
442
|
+
sball_mid_diam = solder_balls_mid_size
|
|
443
|
+
else:
|
|
444
|
+
sball_mid_diam = solder_balls_size
|
|
445
|
+
if not solder_balls_height: # pragma no cover
|
|
446
|
+
solder_balls_height = 2 * sball_diam / 3
|
|
447
|
+
else: # pragma no cover
|
|
448
|
+
if not solder_balls_size:
|
|
449
|
+
bbox = pad_params[1]
|
|
450
|
+
sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
|
|
451
|
+
else:
|
|
452
|
+
sball_diam = solder_balls_size
|
|
453
|
+
if not solder_balls_height:
|
|
454
|
+
solder_balls_height = 2 * sball_diam / 3
|
|
455
|
+
if solder_balls_mid_size:
|
|
456
|
+
sball_mid_diam = solder_balls_mid_size
|
|
457
|
+
else:
|
|
458
|
+
sball_mid_diam = sball_diam
|
|
459
|
+
sball_shape = "Cylinder"
|
|
460
|
+
if not sball_diam == sball_mid_diam:
|
|
461
|
+
sball_shape = "Spheroid"
|
|
462
|
+
self._pedb.components.set_solder_ball(
|
|
463
|
+
component=component,
|
|
464
|
+
sball_height=solder_balls_height,
|
|
465
|
+
sball_diam=sball_diam,
|
|
466
|
+
sball_mid_diam=sball_mid_diam,
|
|
467
|
+
shape=sball_shape,
|
|
468
|
+
)
|
|
469
|
+
for pin in cmp_pins:
|
|
470
|
+
self._pedb.padstack.create_coax_port(padstackinstance=pin, name=port_name)
|
|
471
|
+
|
|
472
|
+
elif port_type == "circuit_port": # pragma no cover
|
|
473
|
+
ref_pins = [p for p in list(component.pins.values()) if p.net_name in reference_net]
|
|
474
|
+
for p in ref_pins:
|
|
475
|
+
p.is_layout_pin = True
|
|
476
|
+
if not ref_pins:
|
|
477
|
+
self._logger.warning("No reference pins found on component")
|
|
478
|
+
if not extend_reference_pins_outside_component:
|
|
479
|
+
self._logger.warning(
|
|
480
|
+
"argument extend_reference_pins_outside_component is False. You might want "
|
|
481
|
+
"setting to True to extend the reference pin search outside the component"
|
|
482
|
+
)
|
|
483
|
+
else:
|
|
484
|
+
do_pingroup = False
|
|
485
|
+
if do_pingroup:
|
|
486
|
+
if len(ref_pins) == 1:
|
|
487
|
+
ref_pins.is_pin = True
|
|
488
|
+
ref_pin_group_term = self._create_terminal(ref_pins[0])
|
|
489
|
+
else:
|
|
490
|
+
for pin in ref_pins:
|
|
491
|
+
pin.is_pin = True
|
|
492
|
+
ref_pin_group = self._pedb.components.create_pingroup_from_pins(ref_pins)
|
|
493
|
+
if ref_pin_group.is_null:
|
|
494
|
+
self._logger.error(f"Failed to create reference pin group on component {component.GetName()}.")
|
|
495
|
+
return False
|
|
496
|
+
ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=False)
|
|
497
|
+
if not ref_pin_group_term:
|
|
498
|
+
self._logger.error(
|
|
499
|
+
f"Failed to create reference pin group terminal on component {component.GetName()}"
|
|
500
|
+
)
|
|
501
|
+
return False
|
|
502
|
+
for net in net_list:
|
|
503
|
+
pins = [pin for pin in list(component.pins.values()) if pin.net_name == net]
|
|
504
|
+
if pins:
|
|
505
|
+
if len(pins) == 1:
|
|
506
|
+
pin_term = self._create_terminal(pins[0])
|
|
507
|
+
if pin_term:
|
|
508
|
+
pin_term.reference_terminal = ref_pin_group_term
|
|
509
|
+
else:
|
|
510
|
+
pin_group = self._pedb.components.create_pingroup_from_pins(pins)
|
|
511
|
+
if pin_group.is_null:
|
|
512
|
+
self._logger.error(
|
|
513
|
+
f"Failed to create pin group terminal on component {component.GetName()}"
|
|
514
|
+
)
|
|
515
|
+
return False
|
|
516
|
+
pin_group_term = self._create_pin_group_terminal(pin_group)
|
|
517
|
+
if pin_group_term:
|
|
518
|
+
pin_group_term.reference_terminal = ref_pin_group_term
|
|
519
|
+
else:
|
|
520
|
+
self._logger.info("No pins found on component {} for the net {}".format(component, net))
|
|
521
|
+
else:
|
|
522
|
+
for net in net_list:
|
|
523
|
+
pins = [pin for pin in list(component.pins.values()) if pin.net_name == net]
|
|
524
|
+
for pin in pins:
|
|
525
|
+
if ref_pins:
|
|
526
|
+
self.create_port_on_pins(component, pin, ref_pins)
|
|
527
|
+
else:
|
|
528
|
+
if extend_reference_pins_outside_component:
|
|
529
|
+
_pin = PadstackInstance(self._pedb, pin)
|
|
530
|
+
ref_pin = _pin.get_reference_pins(
|
|
531
|
+
reference_net=reference_net[0],
|
|
532
|
+
max_limit=1,
|
|
533
|
+
component_only=False,
|
|
534
|
+
search_radius=3e-3,
|
|
535
|
+
)
|
|
536
|
+
if ref_pin:
|
|
537
|
+
if not isinstance(ref_pin, list):
|
|
538
|
+
ref_pin = [ref_pin]
|
|
539
|
+
self.create_port_on_pins(component, [pin.name], ref_pin[0])
|
|
540
|
+
else:
|
|
541
|
+
self._logger.error("Skipping port creation no reference pin found.")
|
|
542
|
+
return True
|
|
543
|
+
|
|
544
|
+
def _create_terminal(self, pin, term_name=None):
|
|
545
|
+
"""Create terminal on component pin.
|
|
546
|
+
|
|
547
|
+
Parameters
|
|
548
|
+
----------
|
|
549
|
+
pin : Edb padstack instance.
|
|
550
|
+
|
|
551
|
+
term_name : Terminal name (Optional).
|
|
552
|
+
str.
|
|
553
|
+
|
|
554
|
+
Returns
|
|
555
|
+
-------
|
|
556
|
+
EDB terminal.
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
from_layer, _ = pin.get_layer_range()
|
|
560
|
+
if term_name is None:
|
|
561
|
+
term_name = "{}.{}.{}".format(pin.component.name, pin.name, pin.net.name)
|
|
562
|
+
for term in list(self._pedb.active_layout.terminals):
|
|
563
|
+
if term.name == term_name:
|
|
564
|
+
return term
|
|
565
|
+
return PadstackInstanceTerminal.create(
|
|
566
|
+
layout=self._pedb.layout, name=term_name, padstack_instance=pin, layer=from_layer, net=pin.net, is_ref=False
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
def add_port_on_rlc_component(self, component=None, circuit_ports=True, pec_boundary=False):
|
|
570
|
+
"""Deactivate RLC component and replace it with a circuit port.
|
|
571
|
+
The circuit port supports only two-pin components.
|
|
572
|
+
|
|
573
|
+
Parameters
|
|
574
|
+
----------
|
|
575
|
+
component : str
|
|
576
|
+
Reference designator of the RLC component.
|
|
577
|
+
|
|
578
|
+
circuit_ports : bool
|
|
579
|
+
``True`` will replace RLC component by circuit ports, ``False`` gap ports compatible with HFSS 3D modeler
|
|
580
|
+
export.
|
|
581
|
+
|
|
582
|
+
pec_boundary : bool, optional
|
|
583
|
+
Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
|
|
584
|
+
a perfect short is created between the pin and impedance is ignored. This
|
|
585
|
+
parameter is only supported on a port created between two pins, such as
|
|
586
|
+
when there is no pin group.
|
|
587
|
+
|
|
588
|
+
Returns
|
|
589
|
+
-------
|
|
590
|
+
bool
|
|
591
|
+
``True`` when successful, ``False`` when failed.
|
|
592
|
+
"""
|
|
593
|
+
from pyedb.grpc.database.components import Component
|
|
594
|
+
|
|
595
|
+
if isinstance(component, str):
|
|
596
|
+
component = self._pedb.components.instances[component]
|
|
597
|
+
if not isinstance(component, Component): # pragma: no cover
|
|
598
|
+
return False
|
|
599
|
+
self._pedb.components.set_component_rlc(component.refdes)
|
|
600
|
+
pins = list(self._pedb.components.instances[component.refdes].pins.values())
|
|
601
|
+
if len(pins) == 2:
|
|
602
|
+
pin_layers = pins[0].get_layer_range()
|
|
603
|
+
pos_pin_term = PadstackInstanceTerminal.create(
|
|
604
|
+
layout=self._pedb.active_layout,
|
|
605
|
+
name=f"{component.name}_{pins[0].name}",
|
|
606
|
+
padstack_instance=pins[0],
|
|
607
|
+
layer=pin_layers[0],
|
|
608
|
+
net=pins[0].net,
|
|
609
|
+
is_ref=False,
|
|
610
|
+
)
|
|
611
|
+
if not pos_pin_term: # pragma: no cover
|
|
612
|
+
return False
|
|
613
|
+
neg_pin_term = PadstackInstanceTerminal.create(
|
|
614
|
+
layout=self._pedb.active_layout,
|
|
615
|
+
name="{}_{}_ref".format(component.name, pins[1].name),
|
|
616
|
+
padstack_instance=pins[1],
|
|
617
|
+
layer=pin_layers[0],
|
|
618
|
+
net=pins[1].net,
|
|
619
|
+
is_ref=False,
|
|
620
|
+
)
|
|
621
|
+
if not neg_pin_term: # pragma: no cover
|
|
622
|
+
return False
|
|
623
|
+
if pec_boundary:
|
|
624
|
+
pos_pin_term.boundary_type = GrpcBoundaryType.PEC
|
|
625
|
+
neg_pin_term.boundary_type = GrpcBoundaryType.PEC
|
|
626
|
+
else:
|
|
627
|
+
pos_pin_term.boundary_type = GrpcBoundaryType.PORT
|
|
628
|
+
neg_pin_term.boundary_type = GrpcBoundaryType.PORT
|
|
629
|
+
pos_pin_term.name = component.name
|
|
630
|
+
pos_pin_term.reference_terminal = neg_pin_term
|
|
631
|
+
if circuit_ports and not pec_boundary:
|
|
632
|
+
pos_pin_term.is_circuit_port = True
|
|
633
|
+
neg_pin_term.is_circuit_port = True
|
|
634
|
+
elif pec_boundary:
|
|
635
|
+
pos_pin_term.is_circuit_port = False
|
|
636
|
+
neg_pin_term.is_circuit_port = False
|
|
637
|
+
else:
|
|
638
|
+
pos_pin_term.is_circuit_port = False
|
|
639
|
+
neg_pin_term.is_circuit_port = False
|
|
640
|
+
self._logger.info(f"Component {component.refdes} has been replaced by port")
|
|
641
|
+
return True
|
|
642
|
+
return False
|
|
643
|
+
|
|
644
|
+
def add_rlc_boundary(self, component=None, circuit_type=True):
|
|
645
|
+
"""Add RLC gap boundary on component and replace it with a circuit port.
|
|
646
|
+
The circuit port supports only 2-pin components.
|
|
647
|
+
|
|
648
|
+
Parameters
|
|
649
|
+
----------
|
|
650
|
+
component : str
|
|
651
|
+
Reference designator of the RLC component.
|
|
652
|
+
circuit_type : bool
|
|
653
|
+
When ``True`` circuit type are defined, if ``False`` gap type will be used instead (compatible with HFSS 3D
|
|
654
|
+
modeler). Default value is ``True``.
|
|
655
|
+
|
|
656
|
+
Returns
|
|
657
|
+
-------
|
|
658
|
+
bool
|
|
659
|
+
``True`` when successful, ``False`` when failed.
|
|
660
|
+
"""
|
|
661
|
+
from pyedb.grpc.database.components import Component
|
|
662
|
+
|
|
663
|
+
if isinstance(component, str): # pragma: no cover
|
|
664
|
+
component = self._pedb.components.instances[component]
|
|
665
|
+
if not isinstance(component, Component): # pragma: no cover
|
|
666
|
+
return False
|
|
667
|
+
self._pedb.components.set_component_rlc(component.name)
|
|
668
|
+
pins = list(component.pins.values())
|
|
669
|
+
if len(pins) == 2: # pragma: no cover
|
|
670
|
+
pin_layer = pins[0].get_layer_range()[0]
|
|
671
|
+
pos_pin_term = PadstackInstanceTerminal.create(
|
|
672
|
+
layout=self._pedb.active_layout,
|
|
673
|
+
net=pins[0].net,
|
|
674
|
+
name=f"{component.name}_{pins[0].name}",
|
|
675
|
+
padstack_instance=pins[0],
|
|
676
|
+
layer=pin_layer,
|
|
677
|
+
is_ref=False,
|
|
678
|
+
)
|
|
679
|
+
if not pos_pin_term: # pragma: no cover
|
|
680
|
+
return False
|
|
681
|
+
neg_pin_term = PadstackInstanceTerminal.create(
|
|
682
|
+
layout=self._pedb.active_layout,
|
|
683
|
+
net=pins[1].net,
|
|
684
|
+
name="{}_{}_ref".format(component.name, pins[1].name),
|
|
685
|
+
padstack_instance=pins[1],
|
|
686
|
+
layer=pin_layer,
|
|
687
|
+
is_ref=True,
|
|
688
|
+
)
|
|
689
|
+
if not neg_pin_term: # pragma: no cover
|
|
690
|
+
return False
|
|
691
|
+
pos_pin_term.boundary_type = GrpcBoundaryType.RLC
|
|
692
|
+
if not circuit_type:
|
|
693
|
+
pos_pin_term.is_circuit_port = False
|
|
694
|
+
else:
|
|
695
|
+
pos_pin_term.is_circuit_port = True
|
|
696
|
+
pos_pin_term.name = component.name
|
|
697
|
+
neg_pin_term.boundary_type = GrpcBoundaryType.RLC
|
|
698
|
+
if not circuit_type:
|
|
699
|
+
neg_pin_term.is_circuit_port = False
|
|
700
|
+
else:
|
|
701
|
+
neg_pin_term.is_circuit_port = True
|
|
702
|
+
pos_pin_term.reference_terminal = neg_pin_term
|
|
703
|
+
rlc_values = component.rlc_values
|
|
704
|
+
rlc = GrpcRlc()
|
|
705
|
+
if rlc_values[0]:
|
|
706
|
+
rlc.r_enabled = True
|
|
707
|
+
rlc.r = GrpcValue(rlc_values[0])
|
|
708
|
+
if rlc_values[1]:
|
|
709
|
+
rlc.l_enabled = True
|
|
710
|
+
rlc.l = GrpcValue(rlc_values[1])
|
|
711
|
+
if rlc_values[2]:
|
|
712
|
+
rlc.c_enabled = True
|
|
713
|
+
rlc.c = GrpcValue(rlc_values[2])
|
|
714
|
+
rlc.is_parallel = component.is_parallel_rlc
|
|
715
|
+
pos_pin_term.rlc_boundary = rlc
|
|
716
|
+
self._logger.info("Component {} has been replaced by port".format(component.refdes))
|
|
717
|
+
return True
|
|
718
|
+
|
|
719
|
+
def _create_pin_group_terminal(self, pingroup, isref=False, term_name=None, term_type="circuit"):
|
|
720
|
+
"""Creates an EDB pin group terminal from a given EDB pin group.
|
|
721
|
+
|
|
722
|
+
Parameters
|
|
723
|
+
----------
|
|
724
|
+
pingroup : Pin group.
|
|
725
|
+
|
|
726
|
+
isref : bool
|
|
727
|
+
Specify if this terminal a reference terminal.
|
|
728
|
+
|
|
729
|
+
term_name : Terminal name (Optional). If not provided default name is Component name, Pin name, Net name.
|
|
730
|
+
str.
|
|
731
|
+
|
|
732
|
+
term_type: Type of terminal, gap, circuit or auto.
|
|
733
|
+
str.
|
|
734
|
+
Returns
|
|
735
|
+
-------
|
|
736
|
+
Edb pin group terminal.
|
|
737
|
+
"""
|
|
738
|
+
if pingroup.is_null:
|
|
739
|
+
self._logger.error(f"{pingroup} is null")
|
|
740
|
+
pin = PadstackInstance(self._pedb, pingroup.pins[0])
|
|
741
|
+
if term_name is None:
|
|
742
|
+
term_name = f"{pin.component.name}.{pin.name}.{pin.net_name}"
|
|
743
|
+
for t in self._pedb.active_layout.terminals:
|
|
744
|
+
if t.name == term_name:
|
|
745
|
+
self._logger.warning(
|
|
746
|
+
f"Terminal {term_name} already created in current layout. Returning the "
|
|
747
|
+
f"already defined one. Make sure to delete the terminal before to create a new one."
|
|
748
|
+
)
|
|
749
|
+
return t
|
|
750
|
+
pingroup_term = PinGroupTerminal.create(
|
|
751
|
+
layout=self._pedb.active_layout, name=term_name, net=pingroup.net, pin_group=pingroup, is_ref=isref
|
|
752
|
+
)
|
|
753
|
+
if term_type == "circuit" or "auto":
|
|
754
|
+
pingroup_term.is_circuit_port = True
|
|
755
|
+
return pingroup_term
|
|
756
|
+
|
|
757
|
+
def create_coax_port(self, padstackinstance, use_dot_separator=True, name=None, create_on_top=True):
|
|
758
|
+
"""Create HFSS 3Dlayout coaxial lumped port on a pastack
|
|
759
|
+
Requires to have solder ball defined before calling this method.
|
|
760
|
+
|
|
761
|
+
Parameters
|
|
762
|
+
----------
|
|
763
|
+
padstackinstance : `Edb.Cell.Primitive.PadstackInstance` or int
|
|
764
|
+
Padstack instance object.
|
|
765
|
+
use_dot_separator : bool, optional
|
|
766
|
+
Whether to use ``.`` as the separator for the naming convention, which
|
|
767
|
+
is ``[component][net][pin]``. The default is ``True``. If ``False``, ``_`` is
|
|
768
|
+
used as the separator instead.
|
|
769
|
+
name : str
|
|
770
|
+
Port name for overwriting the default port-naming convention,
|
|
771
|
+
which is ``[component][net][pin]``. The port name must be unique.
|
|
772
|
+
If a port with the specified name already exists, the
|
|
773
|
+
default naming convention is used so that port creation does
|
|
774
|
+
not fail.
|
|
775
|
+
|
|
776
|
+
Returns
|
|
777
|
+
-------
|
|
778
|
+
str
|
|
779
|
+
Terminal name.
|
|
780
|
+
|
|
781
|
+
"""
|
|
782
|
+
if isinstance(padstackinstance, int):
|
|
783
|
+
padstackinstance = self._pedb.padstacks.instances[padstackinstance]
|
|
784
|
+
cmp_name = padstackinstance.component.name
|
|
785
|
+
if cmp_name == "":
|
|
786
|
+
cmp_name = "no_comp"
|
|
787
|
+
net_name = padstackinstance.net.name
|
|
788
|
+
if net_name == "":
|
|
789
|
+
net_name = "no_net"
|
|
790
|
+
pin_name = padstackinstance.name
|
|
791
|
+
if pin_name == "":
|
|
792
|
+
pin_name = "no_pin_name"
|
|
793
|
+
if use_dot_separator:
|
|
794
|
+
port_name = "{0}.{1}.{2}".format(cmp_name, pin_name, net_name)
|
|
795
|
+
else:
|
|
796
|
+
port_name = "{0}_{1}_{2}".format(cmp_name, pin_name, net_name)
|
|
797
|
+
padstackinstance.is_layout_pin = True
|
|
798
|
+
layer_range = padstackinstance.get_layer_range()
|
|
799
|
+
if create_on_top:
|
|
800
|
+
terminal_layer = layer_range[0]
|
|
801
|
+
else:
|
|
802
|
+
terminal_layer = layer_range[1]
|
|
803
|
+
if name:
|
|
804
|
+
port_name = name
|
|
805
|
+
if self._port_exist(port_name):
|
|
806
|
+
port_name = generate_unique_name(port_name, n=2)
|
|
807
|
+
self._logger.info("An existing port already has this same name. Renaming to {}.".format(port_name))
|
|
808
|
+
PadstackInstanceTerminal.create(
|
|
809
|
+
layout=self._pedb._active_layout,
|
|
810
|
+
name=port_name,
|
|
811
|
+
padstack_instance=padstackinstance,
|
|
812
|
+
layer=terminal_layer,
|
|
813
|
+
net=padstackinstance.net,
|
|
814
|
+
is_ref=False,
|
|
815
|
+
)
|
|
816
|
+
return port_name
|
|
817
|
+
|
|
818
|
+
def _port_exist(self, port_name):
|
|
819
|
+
return any(port for port in list(self._pedb.excitations.keys()) if port == port_name)
|
|
820
|
+
|
|
821
|
+
def _create_edge_terminal(self, prim_id, point_on_edge, terminal_name=None, is_ref=False):
|
|
822
|
+
"""Create an edge terminal.
|
|
823
|
+
|
|
824
|
+
Parameters
|
|
825
|
+
----------
|
|
826
|
+
prim_id : int
|
|
827
|
+
Primitive ID.
|
|
828
|
+
point_on_edge : list
|
|
829
|
+
Coordinate of the point to define the edge terminal.
|
|
830
|
+
The point must be on the target edge but not on the two
|
|
831
|
+
ends of the edge.
|
|
832
|
+
terminal_name : str, optional
|
|
833
|
+
Name of the terminal. The default is ``None``, in which case the
|
|
834
|
+
default name is assigned.
|
|
835
|
+
is_ref : bool, optional
|
|
836
|
+
Whether it is a reference terminal. The default is ``False``.
|
|
837
|
+
|
|
838
|
+
Returns
|
|
839
|
+
-------
|
|
840
|
+
Edb.Cell.Terminal.EdgeTerminal
|
|
841
|
+
"""
|
|
842
|
+
if not terminal_name:
|
|
843
|
+
terminal_name = generate_unique_name("Terminal_")
|
|
844
|
+
if isinstance(point_on_edge, tuple):
|
|
845
|
+
point_on_edge = GrpcPointData(point_on_edge)
|
|
846
|
+
prim = [i for i in self._pedb.modeler.primitives if i.id == prim_id]
|
|
847
|
+
if not prim:
|
|
848
|
+
self._pedb.logger.error(f"No primitive found for ID {prim_id}")
|
|
849
|
+
return False
|
|
850
|
+
prim = prim[0]
|
|
851
|
+
pos_edge = [GrpcPrimitiveEdge.create(prim, point_on_edge)]
|
|
852
|
+
return GrpcEdgeTerminal.create(
|
|
853
|
+
layout=prim.layout, name=terminal_name, edges=pos_edge, net=prim.net, is_ref=is_ref
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
def create_circuit_port_on_pin(self, pos_pin, neg_pin, impedance=50, port_name=None):
|
|
857
|
+
"""Create a circuit port on a pin.
|
|
858
|
+
|
|
859
|
+
Parameters
|
|
860
|
+
----------
|
|
861
|
+
pos_pin : Object
|
|
862
|
+
Edb Pin
|
|
863
|
+
neg_pin : Object
|
|
864
|
+
Edb Pin
|
|
865
|
+
impedance : float
|
|
866
|
+
Port Impedance
|
|
867
|
+
port_name : str, optional
|
|
868
|
+
Port Name
|
|
869
|
+
|
|
870
|
+
Returns
|
|
871
|
+
-------
|
|
872
|
+
str
|
|
873
|
+
Port Name.
|
|
874
|
+
|
|
875
|
+
Examples
|
|
876
|
+
--------
|
|
877
|
+
|
|
878
|
+
>>> from pyedb import Edb
|
|
879
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
880
|
+
>>> pins = edbapp.components.get_pin_from_component("U2A5")
|
|
881
|
+
>>> edbapp.siwave.create_circuit_port_on_pin(pins[0], pins[1], 50, "port_name")
|
|
882
|
+
"""
|
|
883
|
+
if not port_name:
|
|
884
|
+
port_name = f"Port_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
|
|
885
|
+
return self._create_terminal_on_pins(
|
|
886
|
+
positive_pin=pos_pin, negative_pin=neg_pin, impedance=impedance, name=port_name
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
def _create_terminal_on_pins(
|
|
890
|
+
self,
|
|
891
|
+
positive_pin,
|
|
892
|
+
negative_pin,
|
|
893
|
+
name=None,
|
|
894
|
+
use_pin_top_layer=True,
|
|
895
|
+
source_type="circuit_port",
|
|
896
|
+
impedance=50,
|
|
897
|
+
magnitude=0,
|
|
898
|
+
phase=0,
|
|
899
|
+
r=0,
|
|
900
|
+
l=0,
|
|
901
|
+
c=0,
|
|
902
|
+
):
|
|
903
|
+
"""Create a terminal on pins.
|
|
904
|
+
|
|
905
|
+
Parameters
|
|
906
|
+
----------
|
|
907
|
+
positive_pin : :class: `PadstackInstance`
|
|
908
|
+
Positive padstack instance.
|
|
909
|
+
negative_pin : :class: `PadstackInstance`
|
|
910
|
+
Negative padstack instance.
|
|
911
|
+
name : str, optional
|
|
912
|
+
terminal name
|
|
913
|
+
use_pin_top_layer : bool, optional
|
|
914
|
+
Use :class: `PadstackInstance` top layer or bottom for terminal assignment.
|
|
915
|
+
source_type : str, optional
|
|
916
|
+
Specify the source type created. Supported values: `"circuit_port"`, `"lumped_port"`, `"current_source"`,
|
|
917
|
+
`"voltage_port"`, `"rlc"`.
|
|
918
|
+
impedance : float, int or str, optional
|
|
919
|
+
Terminal impedance value
|
|
920
|
+
magnitude : float, int or str, optional
|
|
921
|
+
Terminal magnitude.
|
|
922
|
+
phase : float, int or str, optional
|
|
923
|
+
Terminal phase
|
|
924
|
+
r : float, int
|
|
925
|
+
Resistor value
|
|
926
|
+
l : float, int
|
|
927
|
+
Inductor value
|
|
928
|
+
c : float, int
|
|
929
|
+
Capacitor value
|
|
930
|
+
"""
|
|
931
|
+
|
|
932
|
+
top_layer_pos, bottom_layer_pos = positive_pin.get_layer_range()
|
|
933
|
+
top_layer_neg, bottom_layer_neg = negative_pin.get_layer_range()
|
|
934
|
+
pos_term_layer = bottom_layer_pos
|
|
935
|
+
neg_term_layer = bottom_layer_neg
|
|
936
|
+
if use_pin_top_layer:
|
|
937
|
+
pos_term_layer = top_layer_pos
|
|
938
|
+
neg_term_layer = top_layer_neg
|
|
939
|
+
if not name:
|
|
940
|
+
name = positive_pin.name
|
|
941
|
+
pos_terminal = PadstackInstanceTerminal.create(
|
|
942
|
+
layout=self._pedb.active_layout,
|
|
943
|
+
padstack_instance=positive_pin,
|
|
944
|
+
name=name,
|
|
945
|
+
layer=pos_term_layer,
|
|
946
|
+
is_ref=False,
|
|
947
|
+
net=positive_pin.net,
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
neg_terminal = PadstackInstanceTerminal.create(
|
|
951
|
+
layout=self._pedb.active_layout,
|
|
952
|
+
padstack_instance=negative_pin,
|
|
953
|
+
name=negative_pin.name,
|
|
954
|
+
layer=neg_term_layer,
|
|
955
|
+
is_ref=False,
|
|
956
|
+
net=negative_pin.net,
|
|
957
|
+
)
|
|
958
|
+
if source_type in ["circuit_port", "lumped_port"]:
|
|
959
|
+
pos_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
960
|
+
neg_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
961
|
+
pos_terminal.impedance = GrpcValue(impedance)
|
|
962
|
+
if source_type == "lumped_port":
|
|
963
|
+
pos_terminal.is_circuit_port = False
|
|
964
|
+
neg_terminal.is_circuit_port = False
|
|
965
|
+
else:
|
|
966
|
+
pos_terminal.is_circuit_port = True
|
|
967
|
+
neg_terminal.is_circuit_port = True
|
|
968
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
969
|
+
pos_terminal.name = name
|
|
970
|
+
|
|
971
|
+
elif source_type == "current_source":
|
|
972
|
+
pos_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
973
|
+
neg_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
974
|
+
pos_terminal.source_amplitude = GrpcValue(magnitude)
|
|
975
|
+
pos_terminal.source_phase = GrpcValue(phase)
|
|
976
|
+
pos_terminal.impedance = GrpcValue(impedance)
|
|
977
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
978
|
+
pos_terminal.name = name
|
|
979
|
+
|
|
980
|
+
elif source_type == "voltage_source":
|
|
981
|
+
pos_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
982
|
+
neg_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
983
|
+
pos_terminal.source_amplitude = GrpcValue(magnitude)
|
|
984
|
+
pos_terminal.impedance = GrpcValue(impedance)
|
|
985
|
+
pos_terminal.source_phase = GrpcValue(phase)
|
|
986
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
987
|
+
pos_terminal.name = name
|
|
988
|
+
|
|
989
|
+
elif source_type == "rlc":
|
|
990
|
+
pos_terminal.boundary_type = GrpcBoundaryType.RLC
|
|
991
|
+
neg_terminal.boundary_type = GrpcBoundaryType.RLC
|
|
992
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
993
|
+
rlc = GrpcRlc()
|
|
994
|
+
rlc.r_enabled = bool(r)
|
|
995
|
+
rlc.l_enabled = bool(l)
|
|
996
|
+
rlc.c_enabled = bool(c)
|
|
997
|
+
rlc.r = GrpcValue(r)
|
|
998
|
+
rlc.l = GrpcValue(l)
|
|
999
|
+
rlc.c = GrpcValue(c)
|
|
1000
|
+
pos_terminal.rlc_boundary_parameters = rlc
|
|
1001
|
+
pos_terminal.name = name
|
|
1002
|
+
|
|
1003
|
+
else:
|
|
1004
|
+
self._pedb.logger.error("No valid source type specified.")
|
|
1005
|
+
return False
|
|
1006
|
+
return pos_terminal.name
|
|
1007
|
+
|
|
1008
|
+
def create_voltage_source_on_pin(self, pos_pin, neg_pin, voltage_value=0, phase_value=0, source_name=None):
|
|
1009
|
+
"""Create a voltage source.
|
|
1010
|
+
|
|
1011
|
+
Parameters
|
|
1012
|
+
----------
|
|
1013
|
+
pos_pin : Object
|
|
1014
|
+
Positive Pin.
|
|
1015
|
+
neg_pin : Object
|
|
1016
|
+
Negative Pin.
|
|
1017
|
+
voltage_value : float, optional
|
|
1018
|
+
Value for the voltage. The default is ``3.3``.
|
|
1019
|
+
phase_value : optional
|
|
1020
|
+
Value for the phase. The default is ``0``.
|
|
1021
|
+
source_name : str, optional
|
|
1022
|
+
Name of the source. The default is ``""``.
|
|
1023
|
+
|
|
1024
|
+
Returns
|
|
1025
|
+
-------
|
|
1026
|
+
str
|
|
1027
|
+
Source Name.
|
|
1028
|
+
|
|
1029
|
+
Examples
|
|
1030
|
+
--------
|
|
1031
|
+
|
|
1032
|
+
>>> from pyedb import Edb
|
|
1033
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1034
|
+
>>> pins = edbapp.components.get_pin_from_component("U2A5")
|
|
1035
|
+
>>> edbapp.excitations.create_voltage_source_on_pin(pins[0], pins[1], 50, "source_name")
|
|
1036
|
+
"""
|
|
1037
|
+
|
|
1038
|
+
if not source_name:
|
|
1039
|
+
source_name = (
|
|
1040
|
+
f"VSource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
|
|
1041
|
+
)
|
|
1042
|
+
return self._create_terminal_on_pins(
|
|
1043
|
+
positive_pin=pos_pin,
|
|
1044
|
+
negative_pin=neg_pin,
|
|
1045
|
+
name=source_name,
|
|
1046
|
+
magnitude=voltage_value,
|
|
1047
|
+
phase=phase_value,
|
|
1048
|
+
source_type="voltage_source",
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
def create_current_source_on_pin(self, pos_pin, neg_pin, current_value=0, phase_value=0, source_name=None):
|
|
1052
|
+
"""Create a voltage source.
|
|
1053
|
+
|
|
1054
|
+
Parameters
|
|
1055
|
+
----------
|
|
1056
|
+
pos_pin : Object
|
|
1057
|
+
Positive Pin.
|
|
1058
|
+
neg_pin : Object
|
|
1059
|
+
Negative Pin.
|
|
1060
|
+
voltage_value : float, optional
|
|
1061
|
+
Value for the voltage. The default is ``3.3``.
|
|
1062
|
+
phase_value : optional
|
|
1063
|
+
Value for the phase. The default is ``0``.
|
|
1064
|
+
source_name : str, optional
|
|
1065
|
+
Name of the source. The default is ``""``.
|
|
1066
|
+
|
|
1067
|
+
Returns
|
|
1068
|
+
-------
|
|
1069
|
+
str
|
|
1070
|
+
Source Name.
|
|
1071
|
+
|
|
1072
|
+
Examples
|
|
1073
|
+
--------
|
|
1074
|
+
|
|
1075
|
+
>>> from pyedb import Edb
|
|
1076
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1077
|
+
>>> pins = edbapp.components.get_pin_from_component("U2A5")
|
|
1078
|
+
>>> edbapp.excitations.create_voltage_source_on_pin(pins[0], pins[1], 50, "source_name")
|
|
1079
|
+
"""
|
|
1080
|
+
|
|
1081
|
+
if not source_name:
|
|
1082
|
+
source_name = (
|
|
1083
|
+
f"VSource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}"
|
|
1084
|
+
)
|
|
1085
|
+
return self._create_terminal_on_pins(
|
|
1086
|
+
positive_pin=pos_pin,
|
|
1087
|
+
negative_pin=neg_pin,
|
|
1088
|
+
name=source_name,
|
|
1089
|
+
magnitude=current_value,
|
|
1090
|
+
phase=phase_value,
|
|
1091
|
+
source_type="current_source",
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
def create_resistor_on_pin(self, pos_pin, neg_pin, rvalue=1, resistor_name=""):
|
|
1095
|
+
"""Create a Resistor boundary between two given pins..
|
|
1096
|
+
|
|
1097
|
+
Parameters
|
|
1098
|
+
----------
|
|
1099
|
+
pos_pin : Object
|
|
1100
|
+
Positive Pin.
|
|
1101
|
+
neg_pin : Object
|
|
1102
|
+
Negative Pin.
|
|
1103
|
+
rvalue : float, optional
|
|
1104
|
+
Resistance value. The default is ``1``.
|
|
1105
|
+
resistor_name : str, optional
|
|
1106
|
+
Name of the resistor. The default is ``""``.
|
|
1107
|
+
|
|
1108
|
+
Returns
|
|
1109
|
+
-------
|
|
1110
|
+
str
|
|
1111
|
+
Name of the resistor.
|
|
1112
|
+
|
|
1113
|
+
Examples
|
|
1114
|
+
--------
|
|
1115
|
+
|
|
1116
|
+
>>> from pyedb import Edb
|
|
1117
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1118
|
+
>>> pins =edbapp.components.get_pin_from_component("U2A5")
|
|
1119
|
+
>>> edbapp.excitation.create_resistor_on_pin(pins[0], pins[1],50,"res_name")
|
|
1120
|
+
"""
|
|
1121
|
+
if not resistor_name:
|
|
1122
|
+
resistor_name = (
|
|
1123
|
+
f"Res_{pos_pin.component.name}_{pos_pin.net.name}_{neg_pin.component.name}_{neg_pin.net.name}"
|
|
1124
|
+
)
|
|
1125
|
+
return self._create_terminal_on_pins(
|
|
1126
|
+
positive_pin=pos_pin, negative_pin=neg_pin, name=resistor_name, source_type="rlc", r=rvalue
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
def create_circuit_port_on_net(
|
|
1130
|
+
self,
|
|
1131
|
+
positive_component_name,
|
|
1132
|
+
positive_net_name,
|
|
1133
|
+
negative_component_name=None,
|
|
1134
|
+
negative_net_name=None,
|
|
1135
|
+
impedance=50,
|
|
1136
|
+
port_name="",
|
|
1137
|
+
):
|
|
1138
|
+
"""Create a circuit port on a NET.
|
|
1139
|
+
|
|
1140
|
+
It groups all pins belonging to the specified net and then applies the port on PinGroups.
|
|
1141
|
+
|
|
1142
|
+
Parameters
|
|
1143
|
+
----------
|
|
1144
|
+
positive_component_name : str
|
|
1145
|
+
Name of the positive component.
|
|
1146
|
+
positive_net_name : str
|
|
1147
|
+
Name of the positive net.
|
|
1148
|
+
negative_component_name : str, optional
|
|
1149
|
+
Name of the negative component. The default is ``None``, in which case the name of
|
|
1150
|
+
the positive net is assigned.
|
|
1151
|
+
negative_net_name : str, optional
|
|
1152
|
+
Name of the negative net name. The default is ``None`` which will look for GND Nets.
|
|
1153
|
+
impedance_value : float, optional
|
|
1154
|
+
Port impedance value. The default is ``50``.
|
|
1155
|
+
port_name : str, optional
|
|
1156
|
+
Name of the port. The default is ``""``.
|
|
1157
|
+
|
|
1158
|
+
Returns
|
|
1159
|
+
-------
|
|
1160
|
+
str
|
|
1161
|
+
The name of the port.
|
|
1162
|
+
|
|
1163
|
+
Examples
|
|
1164
|
+
--------
|
|
1165
|
+
|
|
1166
|
+
>>> from pyedb import Edb
|
|
1167
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1168
|
+
>>> edbapp.excitations.create_circuit_port_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 50, "port_name")
|
|
1169
|
+
"""
|
|
1170
|
+
if not negative_component_name:
|
|
1171
|
+
negative_component_name = positive_component_name
|
|
1172
|
+
if not negative_net_name:
|
|
1173
|
+
negative_net_name = self._check_gnd(negative_component_name)
|
|
1174
|
+
if not port_name:
|
|
1175
|
+
port_name = (
|
|
1176
|
+
f"Port_{positive_component_name}_{positive_net_name}_{negative_component_name}_{negative_net_name}"
|
|
1177
|
+
)
|
|
1178
|
+
positive_pins = []
|
|
1179
|
+
for pin in list(self._pedb.components.instances[positive_component_name].pins.values()):
|
|
1180
|
+
if pin and not pin.net.is_null:
|
|
1181
|
+
if pin.net_name == positive_net_name:
|
|
1182
|
+
positive_pins.append(pin)
|
|
1183
|
+
if not positive_pins:
|
|
1184
|
+
self._pedb.logger.error(
|
|
1185
|
+
f"No positive pins found component {positive_component_name} net {positive_net_name}"
|
|
1186
|
+
)
|
|
1187
|
+
return False
|
|
1188
|
+
negative_pins = []
|
|
1189
|
+
for pin in list(self._pedb.components.instances[negative_component_name].pins.values()):
|
|
1190
|
+
if pin and not pin.net.is_null:
|
|
1191
|
+
if pin.net_name == negative_net_name:
|
|
1192
|
+
negative_pins.append(pin)
|
|
1193
|
+
if not negative_pins:
|
|
1194
|
+
self._pedb.logger.error(
|
|
1195
|
+
f"No negative pins found component {negative_component_name} net {negative_net_name}"
|
|
1196
|
+
)
|
|
1197
|
+
return False
|
|
1198
|
+
|
|
1199
|
+
return self.create_pin_group_terminal(
|
|
1200
|
+
positive_pins=positive_pins,
|
|
1201
|
+
negatives_pins=negative_pins,
|
|
1202
|
+
name=port_name,
|
|
1203
|
+
impedance=impedance,
|
|
1204
|
+
source_type="circuit_port",
|
|
1205
|
+
)
|
|
1206
|
+
|
|
1207
|
+
def create_pin_group_terminal(
|
|
1208
|
+
self,
|
|
1209
|
+
positive_pins,
|
|
1210
|
+
negatives_pins,
|
|
1211
|
+
name=None,
|
|
1212
|
+
impedance=50,
|
|
1213
|
+
source_type="circuit_port",
|
|
1214
|
+
magnitude=1.0,
|
|
1215
|
+
phase=0,
|
|
1216
|
+
r=0.0,
|
|
1217
|
+
l=0.0,
|
|
1218
|
+
c=0.0,
|
|
1219
|
+
):
|
|
1220
|
+
"""Create a pin group terminal.
|
|
1221
|
+
|
|
1222
|
+
Parameters
|
|
1223
|
+
----------
|
|
1224
|
+
positive_pins : positive pins used.
|
|
1225
|
+
:class: `PadstackInstance` or List[:class: ´PadstackInstance´]
|
|
1226
|
+
negatives_pins : negative pins used.
|
|
1227
|
+
:class: `PadstackInstance` or List[:class: ´PadstackInstance´]
|
|
1228
|
+
impedance : float, int or str
|
|
1229
|
+
Terminal impedance. Default value is `50` Ohms.
|
|
1230
|
+
source_type : str
|
|
1231
|
+
Source type assigned on terminal. Supported values : `"circuit_port"`, `"lumped_port"`, `"current_source"`,
|
|
1232
|
+
`"voltage_source"`, `"rlc"`, `"dc_terminal"`. Default value is `"circuit_port"`.
|
|
1233
|
+
name : str, optional
|
|
1234
|
+
Source name.
|
|
1235
|
+
magnitude : float, int or str, optional
|
|
1236
|
+
source magnitude.
|
|
1237
|
+
phase : float, int or str, optional
|
|
1238
|
+
phase magnitude.
|
|
1239
|
+
r : float, optional
|
|
1240
|
+
Resistor value.
|
|
1241
|
+
l : float, optional
|
|
1242
|
+
Inductor value.
|
|
1243
|
+
c : float, optional
|
|
1244
|
+
Capacitor value.
|
|
1245
|
+
"""
|
|
1246
|
+
if isinstance(positive_pins, PadstackInstance):
|
|
1247
|
+
positive_pins = [positive_pins]
|
|
1248
|
+
if negatives_pins:
|
|
1249
|
+
if isinstance(negatives_pins, PadstackInstance):
|
|
1250
|
+
negatives_pins = [negatives_pins]
|
|
1251
|
+
if not name:
|
|
1252
|
+
name = (
|
|
1253
|
+
f"Port_{positive_pins[0].component.name}_{positive_pins[0].net.name}_{positive_pins[0].name}_"
|
|
1254
|
+
f"{negatives_pins.name}"
|
|
1255
|
+
)
|
|
1256
|
+
if name in [i.name for i in self._pedb.active_layout.terminals]:
|
|
1257
|
+
name = generate_unique_name(name, n=3)
|
|
1258
|
+
self._logger.warning(f"Port already exists with same name. Renaming to {name}")
|
|
1259
|
+
|
|
1260
|
+
pos_pin_group = self._pedb.components.create_pingroup_from_pins(positive_pins)
|
|
1261
|
+
pos_pingroup_terminal = PinGroupTerminal.create(
|
|
1262
|
+
layout=self._pedb.active_layout,
|
|
1263
|
+
name=name,
|
|
1264
|
+
pin_group=pos_pin_group,
|
|
1265
|
+
net=positive_pins[0].net,
|
|
1266
|
+
is_ref=False,
|
|
1267
|
+
)
|
|
1268
|
+
if not source_type == "dc_terminal":
|
|
1269
|
+
neg_pin_group = self._pedb.components.create_pingroup_from_pins(negatives_pins)
|
|
1270
|
+
neg_pingroup_terminal = PinGroupTerminal.create(
|
|
1271
|
+
layout=self._pedb.active_layout,
|
|
1272
|
+
name=f"{name}_ref",
|
|
1273
|
+
pin_group=neg_pin_group,
|
|
1274
|
+
net=negatives_pins[0].net,
|
|
1275
|
+
is_ref=False,
|
|
1276
|
+
)
|
|
1277
|
+
if source_type in ["circuit_port", "lumped_port"]:
|
|
1278
|
+
pos_pingroup_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
1279
|
+
pos_pingroup_terminal.impedance = GrpcValue(impedance)
|
|
1280
|
+
if len(positive_pins) > 1 and len(negatives_pins) > 1:
|
|
1281
|
+
if source_type == "lumped_port":
|
|
1282
|
+
source_type = "circuit_port"
|
|
1283
|
+
if source_type == "circuit_port":
|
|
1284
|
+
pos_pingroup_terminal.is_circuit_port = True
|
|
1285
|
+
neg_pingroup_terminal.is_circuit_port = True
|
|
1286
|
+
else:
|
|
1287
|
+
pos_pingroup_terminal.is_circuit_port = False
|
|
1288
|
+
neg_pingroup_terminal.is_circuit_port = False
|
|
1289
|
+
pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
|
|
1290
|
+
pos_pingroup_terminal.name = name
|
|
1291
|
+
|
|
1292
|
+
elif source_type == "current_source":
|
|
1293
|
+
pos_pingroup_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
1294
|
+
neg_pingroup_terminal.boundary_type = GrpcBoundaryType.CURRENT_SOURCE
|
|
1295
|
+
pos_pingroup_terminal.source_amplitude = GrpcValue(magnitude)
|
|
1296
|
+
pos_pingroup_terminal.source_phase = GrpcValue(phase)
|
|
1297
|
+
pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
|
|
1298
|
+
pos_pingroup_terminal.name = name
|
|
1299
|
+
|
|
1300
|
+
elif source_type == "voltage_source":
|
|
1301
|
+
pos_pingroup_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
1302
|
+
neg_pingroup_terminal.boundary_type = GrpcBoundaryType.VOLTAGE_SOURCE
|
|
1303
|
+
pos_pingroup_terminal.source_amplitude = GrpcValue(magnitude)
|
|
1304
|
+
pos_pingroup_terminal.source_phase = GrpcValue(phase)
|
|
1305
|
+
pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
|
|
1306
|
+
pos_pingroup_terminal.name = name
|
|
1307
|
+
|
|
1308
|
+
elif source_type == "rlc":
|
|
1309
|
+
pos_pingroup_terminal.boundary_type = GrpcBoundaryType.RLC
|
|
1310
|
+
neg_pingroup_terminal.boundary_type = GrpcBoundaryType.RLC
|
|
1311
|
+
pos_pingroup_terminal.reference_terminal = neg_pingroup_terminal
|
|
1312
|
+
Rlc = GrpcRlc()
|
|
1313
|
+
Rlc.r_enabled = bool(r)
|
|
1314
|
+
Rlc.l_enabled = bool(l)
|
|
1315
|
+
Rlc.c_enabled = bool(c)
|
|
1316
|
+
Rlc.r = GrpcValue(r)
|
|
1317
|
+
Rlc.l = GrpcValue(l)
|
|
1318
|
+
Rlc.c = GrpcValue(c)
|
|
1319
|
+
pos_pingroup_terminal.rlc_boundary_parameters = Rlc
|
|
1320
|
+
|
|
1321
|
+
elif source_type == "dc_terminal":
|
|
1322
|
+
pos_pingroup_terminal.boundary_type = GrpcBoundaryType.DC_TERMINAL
|
|
1323
|
+
else:
|
|
1324
|
+
pass
|
|
1325
|
+
return pos_pingroup_terminal.name
|
|
1326
|
+
|
|
1327
|
+
def _check_gnd(self, component_name):
|
|
1328
|
+
negative_net_name = None
|
|
1329
|
+
if self._pedb.nets.is_net_in_component(component_name, "GND"):
|
|
1330
|
+
negative_net_name = "GND"
|
|
1331
|
+
elif self._pedb.nets.is_net_in_component(component_name, "PGND"):
|
|
1332
|
+
negative_net_name = "PGND"
|
|
1333
|
+
elif self._pedb.nets.is_net_in_component(component_name, "AGND"):
|
|
1334
|
+
negative_net_name = "AGND"
|
|
1335
|
+
elif self._pedb.nets.is_net_in_component(component_name, "DGND"):
|
|
1336
|
+
negative_net_name = "DGND"
|
|
1337
|
+
if not negative_net_name:
|
|
1338
|
+
raise ValueError("No GND, PGND, AGND, DGND found. Please setup the negative net name manually.")
|
|
1339
|
+
return negative_net_name
|
|
1340
|
+
|
|
1341
|
+
def create_voltage_source_on_net(
|
|
1342
|
+
self,
|
|
1343
|
+
positive_component_name,
|
|
1344
|
+
positive_net_name,
|
|
1345
|
+
negative_component_name=None,
|
|
1346
|
+
negative_net_name=None,
|
|
1347
|
+
voltage_value=3.3,
|
|
1348
|
+
phase_value=0,
|
|
1349
|
+
source_name=None,
|
|
1350
|
+
):
|
|
1351
|
+
"""Create a voltage source.
|
|
1352
|
+
|
|
1353
|
+
Parameters
|
|
1354
|
+
----------
|
|
1355
|
+
positive_component_name : str
|
|
1356
|
+
Name of the positive component.
|
|
1357
|
+
positive_net_name : str
|
|
1358
|
+
Name of the positive net.
|
|
1359
|
+
negative_component_name : str, optional
|
|
1360
|
+
Name of the negative component. The default is ``None``, in which case the name of
|
|
1361
|
+
the positive net is assigned.
|
|
1362
|
+
negative_net_name : str, optional
|
|
1363
|
+
Name of the negative net name. The default is ``None`` which will look for GND Nets.
|
|
1364
|
+
voltage_value : float, optional
|
|
1365
|
+
Value for the voltage. The default is ``3.3``.
|
|
1366
|
+
phase_value : optional
|
|
1367
|
+
Value for the phase. The default is ``0``.
|
|
1368
|
+
source_name : str, optional
|
|
1369
|
+
Name of the source. The default is ``""``.
|
|
1370
|
+
|
|
1371
|
+
Returns
|
|
1372
|
+
-------
|
|
1373
|
+
str
|
|
1374
|
+
The name of the source.
|
|
1375
|
+
|
|
1376
|
+
Examples
|
|
1377
|
+
--------
|
|
1378
|
+
|
|
1379
|
+
>>> from pyedb import Edb
|
|
1380
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1381
|
+
>>> edb.excitations.create_voltage_source_on_net("U2A5","V1P5_S3","U2A5","GND",3.3,0,"source_name")
|
|
1382
|
+
"""
|
|
1383
|
+
if not negative_component_name:
|
|
1384
|
+
negative_component_name = positive_component_name
|
|
1385
|
+
if not negative_net_name:
|
|
1386
|
+
negative_net_name = self._check_gnd(negative_component_name)
|
|
1387
|
+
pos_node_pins = self._pedb.components.get_pin_from_component(positive_component_name, positive_net_name)
|
|
1388
|
+
neg_node_pins = self._pedb.components.get_pin_from_component(negative_component_name, negative_net_name)
|
|
1389
|
+
|
|
1390
|
+
if not source_name:
|
|
1391
|
+
source_name = (
|
|
1392
|
+
f"Vsource_{positive_component_name}_{positive_net_name}_"
|
|
1393
|
+
f"{negative_component_name}_{negative_net_name}"
|
|
1394
|
+
)
|
|
1395
|
+
return self.create_pin_group_terminal(
|
|
1396
|
+
positive_pins=pos_node_pins,
|
|
1397
|
+
negatives_pins=neg_node_pins,
|
|
1398
|
+
name=source_name,
|
|
1399
|
+
magnitude=voltage_value,
|
|
1400
|
+
phase=phase_value,
|
|
1401
|
+
impedance=1e-6,
|
|
1402
|
+
source_type="voltage_source",
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
def create_current_source_on_net(
|
|
1406
|
+
self,
|
|
1407
|
+
positive_component_name,
|
|
1408
|
+
positive_net_name,
|
|
1409
|
+
negative_component_name=None,
|
|
1410
|
+
negative_net_name=None,
|
|
1411
|
+
current_value=3.3,
|
|
1412
|
+
phase_value=0,
|
|
1413
|
+
source_name=None,
|
|
1414
|
+
):
|
|
1415
|
+
"""Create a voltage source.
|
|
1416
|
+
|
|
1417
|
+
Parameters
|
|
1418
|
+
----------
|
|
1419
|
+
positive_component_name : str
|
|
1420
|
+
Name of the positive component.
|
|
1421
|
+
positive_net_name : str
|
|
1422
|
+
Name of the positive net.
|
|
1423
|
+
negative_component_name : str, optional
|
|
1424
|
+
Name of the negative component. The default is ``None``, in which case the name of
|
|
1425
|
+
the positive net is assigned.
|
|
1426
|
+
negative_net_name : str, optional
|
|
1427
|
+
Name of the negative net name. The default is ``None`` which will look for GND Nets.
|
|
1428
|
+
voltage_value : float, optional
|
|
1429
|
+
Value for the voltage. The default is ``3.3``.
|
|
1430
|
+
phase_value : optional
|
|
1431
|
+
Value for the phase. The default is ``0``.
|
|
1432
|
+
source_name : str, optional
|
|
1433
|
+
Name of the source. The default is ``""``.
|
|
1434
|
+
|
|
1435
|
+
Returns
|
|
1436
|
+
-------
|
|
1437
|
+
str
|
|
1438
|
+
The name of the source.
|
|
1439
|
+
|
|
1440
|
+
Examples
|
|
1441
|
+
--------
|
|
1442
|
+
|
|
1443
|
+
>>> from pyedb import Edb
|
|
1444
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
1445
|
+
>>> edb.excitations.create_voltage_source_on_net("U2A5","V1P5_S3","U2A5","GND",3.3,0,"source_name")
|
|
1446
|
+
"""
|
|
1447
|
+
if not negative_component_name:
|
|
1448
|
+
negative_component_name = positive_component_name
|
|
1449
|
+
if not negative_net_name:
|
|
1450
|
+
negative_net_name = self._check_gnd(negative_component_name)
|
|
1451
|
+
pos_node_pins = self._pedb.components.get_pin_from_component(positive_component_name, positive_net_name)
|
|
1452
|
+
neg_node_pins = self._pedb.components.get_pin_from_component(negative_component_name, negative_net_name)
|
|
1453
|
+
|
|
1454
|
+
if not source_name:
|
|
1455
|
+
source_name = (
|
|
1456
|
+
f"Vsource_{positive_component_name}_{positive_net_name}_"
|
|
1457
|
+
f"{negative_component_name}_{negative_net_name}"
|
|
1458
|
+
)
|
|
1459
|
+
return self.create_pin_group_terminal(
|
|
1460
|
+
positive_pins=pos_node_pins,
|
|
1461
|
+
negatives_pins=neg_node_pins,
|
|
1462
|
+
name=source_name,
|
|
1463
|
+
magnitude=current_value,
|
|
1464
|
+
phase=phase_value,
|
|
1465
|
+
impedance=1e6,
|
|
1466
|
+
source_type="current_source",
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
def create_coax_port_on_component(self, ref_des_list, net_list, delete_existing_terminal=False):
|
|
1470
|
+
"""Create a coaxial port on a component or component list on a net or net list.
|
|
1471
|
+
The name of the new coaxial port is automatically assigned.
|
|
1472
|
+
|
|
1473
|
+
Parameters
|
|
1474
|
+
----------
|
|
1475
|
+
ref_des_list : list, str
|
|
1476
|
+
List of one or more reference designators.
|
|
1477
|
+
|
|
1478
|
+
net_list : list, str
|
|
1479
|
+
List of one or more nets.
|
|
1480
|
+
|
|
1481
|
+
delete_existing_terminal : bool
|
|
1482
|
+
Delete existing terminal with same name if exists.
|
|
1483
|
+
Port naming convention is `ref_des`_`pin.net.name`_`pin.name`
|
|
1484
|
+
|
|
1485
|
+
Returns
|
|
1486
|
+
-------
|
|
1487
|
+
bool
|
|
1488
|
+
``True`` when successful, ``False`` when failed.
|
|
1489
|
+
|
|
1490
|
+
"""
|
|
1491
|
+
coax = []
|
|
1492
|
+
if not isinstance(ref_des_list, list):
|
|
1493
|
+
ref_des_list = [ref_des_list]
|
|
1494
|
+
if not isinstance(net_list, list):
|
|
1495
|
+
net_list = [net_list]
|
|
1496
|
+
for ref in ref_des_list:
|
|
1497
|
+
for _, pin in self._pedb.components.instances[ref].pins.items():
|
|
1498
|
+
try: # trying due to grpc crash when no net is defined on pin.
|
|
1499
|
+
try:
|
|
1500
|
+
pin_net = pin.net
|
|
1501
|
+
except:
|
|
1502
|
+
pin_net = None
|
|
1503
|
+
if pin_net and pin.net.is_null:
|
|
1504
|
+
self._logger.warning(f"Pin {pin.id} has no net defined")
|
|
1505
|
+
elif pin.net.name in net_list:
|
|
1506
|
+
pin.is_pin = True
|
|
1507
|
+
port_name = f"{ref}_{pin.net.name}_{pin.name}"
|
|
1508
|
+
if self.check_before_terminal_assignement(
|
|
1509
|
+
connectable=pin, delete_existing_terminal=delete_existing_terminal
|
|
1510
|
+
):
|
|
1511
|
+
top_layer = pin.get_layer_range()[0]
|
|
1512
|
+
term = PadstackInstanceTerminal.create(
|
|
1513
|
+
layout=pin.layout,
|
|
1514
|
+
name=port_name,
|
|
1515
|
+
padstack_instance=pin,
|
|
1516
|
+
layer=top_layer,
|
|
1517
|
+
net=pin.net,
|
|
1518
|
+
is_ref=False,
|
|
1519
|
+
)
|
|
1520
|
+
if not term.is_null:
|
|
1521
|
+
coax.append(port_name)
|
|
1522
|
+
except RuntimeError as error:
|
|
1523
|
+
self._logger.error(error)
|
|
1524
|
+
return coax
|
|
1525
|
+
|
|
1526
|
+
def check_before_terminal_assignement(self, connectable, delete_existing_terminal=False):
|
|
1527
|
+
if not connectable:
|
|
1528
|
+
return False
|
|
1529
|
+
existing_terminals = [term for term in self._pedb.active_layout.terminals if term.id == connectable.id]
|
|
1530
|
+
if existing_terminals:
|
|
1531
|
+
if not delete_existing_terminal:
|
|
1532
|
+
self._pedb.logger.error(
|
|
1533
|
+
f"Terminal {connectable.name} already defined in design, please make sure to have unique name."
|
|
1534
|
+
)
|
|
1535
|
+
return False
|
|
1536
|
+
else:
|
|
1537
|
+
if isinstance(connectable, PadstackInstanceTerminal):
|
|
1538
|
+
self._pedb.logger.error(
|
|
1539
|
+
f"Terminal {connectable.name} already defined, check status on bug "
|
|
1540
|
+
f"https://github.com/ansys/pyedb-core/issues/429"
|
|
1541
|
+
)
|
|
1542
|
+
return False
|
|
1543
|
+
else:
|
|
1544
|
+
return True
|
|
1545
|
+
|
|
1546
|
+
def create_differential_wave_port(
|
|
1547
|
+
self,
|
|
1548
|
+
positive_primitive_id,
|
|
1549
|
+
positive_points_on_edge,
|
|
1550
|
+
negative_primitive_id,
|
|
1551
|
+
negative_points_on_edge,
|
|
1552
|
+
port_name=None,
|
|
1553
|
+
horizontal_extent_factor=5,
|
|
1554
|
+
vertical_extent_factor=3,
|
|
1555
|
+
pec_launch_width="0.01mm",
|
|
1556
|
+
):
|
|
1557
|
+
"""Create a differential wave port.
|
|
1558
|
+
|
|
1559
|
+
Parameters
|
|
1560
|
+
----------
|
|
1561
|
+
positive_primitive_id : int, EDBPrimitives
|
|
1562
|
+
Primitive ID of the positive terminal.
|
|
1563
|
+
positive_points_on_edge : list
|
|
1564
|
+
Coordinate of the point to define the edge terminal.
|
|
1565
|
+
The point must be close to the target edge but not on the two
|
|
1566
|
+
ends of the edge.
|
|
1567
|
+
negative_primitive_id : int, EDBPrimitives
|
|
1568
|
+
Primitive ID of the negative terminal.
|
|
1569
|
+
negative_points_on_edge : list
|
|
1570
|
+
Coordinate of the point to define the edge terminal.
|
|
1571
|
+
The point must be close to the target edge but not on the two
|
|
1572
|
+
ends of the edge.
|
|
1573
|
+
port_name : str, optional
|
|
1574
|
+
Name of the port. The default is ``None``.
|
|
1575
|
+
horizontal_extent_factor : int, float, optional
|
|
1576
|
+
Horizontal extent factor. The default value is ``5``.
|
|
1577
|
+
vertical_extent_factor : int, float, optional
|
|
1578
|
+
Vertical extent factor. The default value is ``3``.
|
|
1579
|
+
pec_launch_width : str, optional
|
|
1580
|
+
Launch Width of PEC. The default value is ``"0.01mm"``.
|
|
1581
|
+
|
|
1582
|
+
Returns
|
|
1583
|
+
-------
|
|
1584
|
+
tuple
|
|
1585
|
+
The tuple contains: (port_name, pyedb.dotnet.database.edb_data.sources.ExcitationDifferential).
|
|
1586
|
+
|
|
1587
|
+
Examples
|
|
1588
|
+
--------
|
|
1589
|
+
>>> edb.hfss.create_differential_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
|
|
1590
|
+
"""
|
|
1591
|
+
if not port_name:
|
|
1592
|
+
port_name = generate_unique_name("diff")
|
|
1593
|
+
|
|
1594
|
+
if isinstance(positive_primitive_id, Primitive):
|
|
1595
|
+
positive_primitive_id = positive_primitive_id.id
|
|
1596
|
+
|
|
1597
|
+
if isinstance(negative_primitive_id, Primitive):
|
|
1598
|
+
negative_primitive_id = negative_primitive_id.id
|
|
1599
|
+
|
|
1600
|
+
_, pos_term = self.create_wave_port(
|
|
1601
|
+
positive_primitive_id,
|
|
1602
|
+
positive_points_on_edge,
|
|
1603
|
+
horizontal_extent_factor=horizontal_extent_factor,
|
|
1604
|
+
vertical_extent_factor=vertical_extent_factor,
|
|
1605
|
+
pec_launch_width=pec_launch_width,
|
|
1606
|
+
)
|
|
1607
|
+
_, neg_term = self.create_wave_port(
|
|
1608
|
+
negative_primitive_id,
|
|
1609
|
+
negative_points_on_edge,
|
|
1610
|
+
horizontal_extent_factor=horizontal_extent_factor,
|
|
1611
|
+
vertical_extent_factor=vertical_extent_factor,
|
|
1612
|
+
pec_launch_width=pec_launch_width,
|
|
1613
|
+
)
|
|
1614
|
+
edb_list = [pos_term, neg_term]
|
|
1615
|
+
|
|
1616
|
+
boundle_terminal = BundleTerminal.create(edb_list)
|
|
1617
|
+
boundle_terminal.name = port_name
|
|
1618
|
+
bundle_term = boundle_terminal.terminals
|
|
1619
|
+
bundle_term[0].name = port_name + ":T1"
|
|
1620
|
+
bundle_term[1].mame = port_name + ":T2"
|
|
1621
|
+
return port_name, boundle_terminal
|
|
1622
|
+
|
|
1623
|
+
def create_wave_port(
|
|
1624
|
+
self,
|
|
1625
|
+
prim_id,
|
|
1626
|
+
point_on_edge,
|
|
1627
|
+
port_name=None,
|
|
1628
|
+
impedance=50,
|
|
1629
|
+
horizontal_extent_factor=5,
|
|
1630
|
+
vertical_extent_factor=3,
|
|
1631
|
+
pec_launch_width="0.01mm",
|
|
1632
|
+
):
|
|
1633
|
+
"""Create a wave port.
|
|
1634
|
+
|
|
1635
|
+
Parameters
|
|
1636
|
+
----------
|
|
1637
|
+
prim_id : int, Primitive
|
|
1638
|
+
Primitive ID.
|
|
1639
|
+
point_on_edge : list
|
|
1640
|
+
Coordinate of the point to define the edge terminal.
|
|
1641
|
+
The point must be on the target edge but not on the two
|
|
1642
|
+
ends of the edge.
|
|
1643
|
+
port_name : str, optional
|
|
1644
|
+
Name of the port. The default is ``None``.
|
|
1645
|
+
impedance : int, float, optional
|
|
1646
|
+
Impedance of the port. The default value is ``50``.
|
|
1647
|
+
horizontal_extent_factor : int, float, optional
|
|
1648
|
+
Horizontal extent factor. The default value is ``5``.
|
|
1649
|
+
vertical_extent_factor : int, float, optional
|
|
1650
|
+
Vertical extent factor. The default value is ``3``.
|
|
1651
|
+
pec_launch_width : str, optional
|
|
1652
|
+
Launch Width of PEC. The default value is ``"0.01mm"``.
|
|
1653
|
+
|
|
1654
|
+
Returns
|
|
1655
|
+
-------
|
|
1656
|
+
tuple
|
|
1657
|
+
The tuple contains: (Port name, pyedb.dotnet.database.edb_data.sources.Excitation).
|
|
1658
|
+
|
|
1659
|
+
Examples
|
|
1660
|
+
--------
|
|
1661
|
+
>>> edb.excitations.create_wave_port(0, ["-50mm", "-0mm"])
|
|
1662
|
+
"""
|
|
1663
|
+
if not port_name:
|
|
1664
|
+
port_name = generate_unique_name("Terminal_")
|
|
1665
|
+
|
|
1666
|
+
if isinstance(prim_id, Primitive):
|
|
1667
|
+
prim_id = prim_id.id
|
|
1668
|
+
pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
|
|
1669
|
+
pos_edge_term.impedance = GrpcValue(impedance)
|
|
1670
|
+
wave_port = WavePort(self._pedb, pos_edge_term)
|
|
1671
|
+
wave_port.horizontal_extent_factor = horizontal_extent_factor
|
|
1672
|
+
wave_port.vertical_extent_factor = vertical_extent_factor
|
|
1673
|
+
wave_port.pec_launch_width = pec_launch_width
|
|
1674
|
+
wave_port.hfss_type = "Wave"
|
|
1675
|
+
wave_port.do_renormalize = True
|
|
1676
|
+
if pos_edge_term:
|
|
1677
|
+
return port_name, wave_port
|
|
1678
|
+
else:
|
|
1679
|
+
return False
|
|
1680
|
+
|
|
1681
|
+
def create_edge_port_vertical(
|
|
1682
|
+
self,
|
|
1683
|
+
prim_id,
|
|
1684
|
+
point_on_edge,
|
|
1685
|
+
port_name=None,
|
|
1686
|
+
impedance=50,
|
|
1687
|
+
reference_layer=None,
|
|
1688
|
+
hfss_type="Gap",
|
|
1689
|
+
horizontal_extent_factor=5,
|
|
1690
|
+
vertical_extent_factor=3,
|
|
1691
|
+
pec_launch_width="0.01mm",
|
|
1692
|
+
):
|
|
1693
|
+
"""Create a vertical edge port.
|
|
1694
|
+
|
|
1695
|
+
Parameters
|
|
1696
|
+
----------
|
|
1697
|
+
prim_id : int
|
|
1698
|
+
Primitive ID.
|
|
1699
|
+
point_on_edge : list
|
|
1700
|
+
Coordinate of the point to define the edge terminal.
|
|
1701
|
+
The point must be on the target edge but not on the two
|
|
1702
|
+
ends of the edge.
|
|
1703
|
+
port_name : str, optional
|
|
1704
|
+
Name of the port. The default is ``None``.
|
|
1705
|
+
impedance : int, float, optional
|
|
1706
|
+
Impedance of the port. The default value is ``50``.
|
|
1707
|
+
reference_layer : str, optional
|
|
1708
|
+
Reference layer of the port. The default is ``None``.
|
|
1709
|
+
hfss_type : str, optional
|
|
1710
|
+
Type of the port. The default value is ``"Gap"``. Options are ``"Gap"``, ``"Wave"``.
|
|
1711
|
+
horizontal_extent_factor : int, float, optional
|
|
1712
|
+
Horizontal extent factor. The default value is ``5``.
|
|
1713
|
+
vertical_extent_factor : int, float, optional
|
|
1714
|
+
Vertical extent factor. The default value is ``3``.
|
|
1715
|
+
radial_extent_factor : int, float, optional
|
|
1716
|
+
Radial extent factor. The default value is ``0``.
|
|
1717
|
+
pec_launch_width : str, optional
|
|
1718
|
+
Launch Width of PEC. The default value is ``"0.01mm"``.
|
|
1719
|
+
|
|
1720
|
+
Returns
|
|
1721
|
+
-------
|
|
1722
|
+
str
|
|
1723
|
+
Port name.
|
|
1724
|
+
"""
|
|
1725
|
+
if not port_name:
|
|
1726
|
+
port_name = generate_unique_name("Terminal_")
|
|
1727
|
+
pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
|
|
1728
|
+
pos_edge_term.impedance = GrpcValue(impedance)
|
|
1729
|
+
if reference_layer:
|
|
1730
|
+
reference_layer = self._pedb.stackup.signal_layers[reference_layer]
|
|
1731
|
+
pos_edge_term.reference_layer = reference_layer
|
|
1732
|
+
|
|
1733
|
+
prop = ", ".join(
|
|
1734
|
+
[
|
|
1735
|
+
f"HFSS('HFSS Type'='{hfss_type}'",
|
|
1736
|
+
" Orientation='Vertical'",
|
|
1737
|
+
" 'Layer Alignment'='Upper'",
|
|
1738
|
+
f" 'Horizontal Extent Factor'='{horizontal_extent_factor}'",
|
|
1739
|
+
f" 'Vertical Extent Factor'='{vertical_extent_factor}'",
|
|
1740
|
+
f" 'PEC Launch Width'='{pec_launch_width}')",
|
|
1741
|
+
]
|
|
1742
|
+
)
|
|
1743
|
+
pos_edge_term.set_product_solver_option(
|
|
1744
|
+
GrpcProductIdType.DESIGNER,
|
|
1745
|
+
"HFSS",
|
|
1746
|
+
prop,
|
|
1747
|
+
)
|
|
1748
|
+
if not pos_edge_term.is_null:
|
|
1749
|
+
return pos_edge_term
|
|
1750
|
+
else:
|
|
1751
|
+
return False
|
|
1752
|
+
|
|
1753
|
+
def create_edge_port_horizontal(
|
|
1754
|
+
self,
|
|
1755
|
+
prim_id,
|
|
1756
|
+
point_on_edge,
|
|
1757
|
+
ref_prim_id=None,
|
|
1758
|
+
point_on_ref_edge=None,
|
|
1759
|
+
port_name=None,
|
|
1760
|
+
impedance=50,
|
|
1761
|
+
layer_alignment="Upper",
|
|
1762
|
+
):
|
|
1763
|
+
"""Create a horizontal edge port.
|
|
1764
|
+
|
|
1765
|
+
Parameters
|
|
1766
|
+
----------
|
|
1767
|
+
prim_id : int
|
|
1768
|
+
Primitive ID.
|
|
1769
|
+
point_on_edge : list
|
|
1770
|
+
Coordinate of the point to define the edge terminal.
|
|
1771
|
+
The point must be on the target edge but not on the two
|
|
1772
|
+
ends of the edge.
|
|
1773
|
+
ref_prim_id : int, optional
|
|
1774
|
+
Reference primitive ID. The default is ``None``.
|
|
1775
|
+
point_on_ref_edge : list, optional
|
|
1776
|
+
Coordinate of the point to define the reference edge
|
|
1777
|
+
terminal. The point must be on the target edge but not
|
|
1778
|
+
on the two ends of the edge. The default is ``None``.
|
|
1779
|
+
port_name : str, optional
|
|
1780
|
+
Name of the port. The default is ``None``.
|
|
1781
|
+
impedance : int, float, optional
|
|
1782
|
+
Impedance of the port. The default value is ``50``.
|
|
1783
|
+
layer_alignment : str, optional
|
|
1784
|
+
Layer alignment. The default value is ``Upper``. Options are ``"Upper"``, ``"Lower"``.
|
|
1785
|
+
|
|
1786
|
+
Returns
|
|
1787
|
+
-------
|
|
1788
|
+
str
|
|
1789
|
+
Name of the port.
|
|
1790
|
+
"""
|
|
1791
|
+
pos_edge_term = self._create_edge_terminal(prim_id, point_on_edge, port_name)
|
|
1792
|
+
neg_edge_term = self._create_edge_terminal(ref_prim_id, point_on_ref_edge, port_name + "_ref", is_ref=True)
|
|
1793
|
+
|
|
1794
|
+
pos_edge_term.impedance = GrpcValue(impedance)
|
|
1795
|
+
pos_edge_term.reference_terminal = neg_edge_term
|
|
1796
|
+
if not layer_alignment == "Upper":
|
|
1797
|
+
layer_alignment = "Lower"
|
|
1798
|
+
pos_edge_term.set_product_solver_option(
|
|
1799
|
+
GrpcProductIdType.DESIGNER,
|
|
1800
|
+
"HFSS",
|
|
1801
|
+
f"HFSS('HFSS Type'='Gap(coax)', Orientation='Horizontal', 'Layer Alignment'='{layer_alignment}')",
|
|
1802
|
+
)
|
|
1803
|
+
if pos_edge_term:
|
|
1804
|
+
return port_name
|
|
1805
|
+
else:
|
|
1806
|
+
return False
|
|
1807
|
+
|
|
1808
|
+
def create_lumped_port_on_net(
|
|
1809
|
+
self, nets=None, reference_layer=None, return_points_only=False, digit_resolution=6, at_bounding_box=True
|
|
1810
|
+
):
|
|
1811
|
+
"""Create an edge port on nets. This command looks for traces and polygons on the
|
|
1812
|
+
nets and tries to assign vertical lumped port.
|
|
1813
|
+
|
|
1814
|
+
Parameters
|
|
1815
|
+
----------
|
|
1816
|
+
nets : list, optional
|
|
1817
|
+
List of nets, str or Edb net.
|
|
1818
|
+
|
|
1819
|
+
reference_layer : str, Edb layer.
|
|
1820
|
+
Name or Edb layer object.
|
|
1821
|
+
|
|
1822
|
+
return_points_only : bool, optional
|
|
1823
|
+
Use this boolean when you want to return only the points from the edges and not creating ports. Default
|
|
1824
|
+
value is ``False``.
|
|
1825
|
+
|
|
1826
|
+
digit_resolution : int, optional
|
|
1827
|
+
The number of digits carried for the edge location accuracy. The default value is ``6``.
|
|
1828
|
+
|
|
1829
|
+
at_bounding_box : bool
|
|
1830
|
+
When ``True`` will keep the edges from traces at the layout bounding box location. This is recommended when
|
|
1831
|
+
a cutout has been performed before and lumped ports have to be created on ending traces. Default value is
|
|
1832
|
+
``True``.
|
|
1833
|
+
|
|
1834
|
+
Returns
|
|
1835
|
+
-------
|
|
1836
|
+
bool
|
|
1837
|
+
``True`` when successful, ``False`` when failed.
|
|
1838
|
+
"""
|
|
1839
|
+
if isinstance(nets, str):
|
|
1840
|
+
nets = [self._pedb.nets.signal[nets]]
|
|
1841
|
+
if isinstance(nets, Net):
|
|
1842
|
+
nets = [nets]
|
|
1843
|
+
nets = [self._pedb.nets.signal[net] for net in nets if isinstance(net, str)]
|
|
1844
|
+
port_created = False
|
|
1845
|
+
if nets:
|
|
1846
|
+
edges_pts = []
|
|
1847
|
+
if isinstance(reference_layer, str):
|
|
1848
|
+
try:
|
|
1849
|
+
reference_layer = self._pedb.stackup.signal_layers[reference_layer]
|
|
1850
|
+
except:
|
|
1851
|
+
raise Exception(f"Failed to get the layer {reference_layer}")
|
|
1852
|
+
if not isinstance(reference_layer, StackupLayer):
|
|
1853
|
+
return False
|
|
1854
|
+
layout_bbox = self._pedb.get_conformal_polygon_from_netlist(self._pedb.nets.netlist)
|
|
1855
|
+
layout_extent_segments = [pt for pt in list(layout_bbox.arc_data) if pt.is_segment]
|
|
1856
|
+
first_pt = layout_extent_segments[0]
|
|
1857
|
+
layout_extent_points = [
|
|
1858
|
+
[first_pt.start.x.value, first_pt.end.x.value],
|
|
1859
|
+
[first_pt.Start.y.value, first_pt.end.y.value],
|
|
1860
|
+
]
|
|
1861
|
+
for segment in layout_extent_segments[1:]:
|
|
1862
|
+
end_point = (segment.end.x.value, segment.end.y.value)
|
|
1863
|
+
layout_extent_points[0].append(end_point[0])
|
|
1864
|
+
layout_extent_points[1].append(end_point[1])
|
|
1865
|
+
for net in nets:
|
|
1866
|
+
net_primitives = self._pedb.nets[net.name].primitives
|
|
1867
|
+
net_paths = [pp for pp in net_primitives if pp.type == "Path"]
|
|
1868
|
+
for path in net_paths:
|
|
1869
|
+
trace_path_pts = list(path.center_line.Points)
|
|
1870
|
+
port_name = f"{net.name}_{path.id}"
|
|
1871
|
+
for pt in trace_path_pts:
|
|
1872
|
+
_pt = [
|
|
1873
|
+
round(pt.x.value, digit_resolution),
|
|
1874
|
+
round(pt.y.value, digit_resolution),
|
|
1875
|
+
]
|
|
1876
|
+
if at_bounding_box:
|
|
1877
|
+
if GeometryOperators.point_in_polygon(_pt, layout_extent_points) == 0:
|
|
1878
|
+
if return_points_only:
|
|
1879
|
+
edges_pts.append(_pt)
|
|
1880
|
+
else:
|
|
1881
|
+
term = self._create_edge_terminal(path.id, pt, port_name) # pragma no cover
|
|
1882
|
+
term.reference_layer = reference_layer
|
|
1883
|
+
port_created = True
|
|
1884
|
+
else:
|
|
1885
|
+
if return_points_only: # pragma: no cover
|
|
1886
|
+
edges_pts.append(_pt)
|
|
1887
|
+
else:
|
|
1888
|
+
term = self._create_edge_terminal(path.id, pt, port_name)
|
|
1889
|
+
term.reference_layer = reference_layer
|
|
1890
|
+
port_created = True
|
|
1891
|
+
net_poly = [pp for pp in net_primitives if pp.type == "Polygon"]
|
|
1892
|
+
for poly in net_poly:
|
|
1893
|
+
poly_segment = [aa for aa in poly.arcs if aa.is_segment]
|
|
1894
|
+
for segment in poly_segment:
|
|
1895
|
+
if (
|
|
1896
|
+
GeometryOperators.point_in_polygon(
|
|
1897
|
+
[segment.mid_point.x.value, segment.mid_point.y.value], layout_extent_points
|
|
1898
|
+
)
|
|
1899
|
+
== 0
|
|
1900
|
+
):
|
|
1901
|
+
if return_points_only:
|
|
1902
|
+
edges_pts.append(segment.mid_point)
|
|
1903
|
+
else:
|
|
1904
|
+
port_name = f"{net.name}_{poly.id}"
|
|
1905
|
+
term = self._create_edge_terminal(
|
|
1906
|
+
poly.id, segment.mid_point, port_name
|
|
1907
|
+
) # pragma no cover
|
|
1908
|
+
term.set_reference_layer = reference_layer
|
|
1909
|
+
port_created = True
|
|
1910
|
+
if return_points_only:
|
|
1911
|
+
return edges_pts
|
|
1912
|
+
return port_created
|
|
1913
|
+
|
|
1914
|
+
def create_vertical_circuit_port_on_clipped_traces(self, nets=None, reference_net=None, user_defined_extent=None):
|
|
1915
|
+
"""Create an edge port on clipped signal traces.
|
|
1916
|
+
|
|
1917
|
+
Parameters
|
|
1918
|
+
----------
|
|
1919
|
+
nets : list, optional
|
|
1920
|
+
String of one net or EDB net or a list of multiple nets or EDB nets.
|
|
1921
|
+
|
|
1922
|
+
reference_net : str, Edb net.
|
|
1923
|
+
Name or EDB reference net.
|
|
1924
|
+
|
|
1925
|
+
user_defined_extent : [x, y], EDB PolygonData
|
|
1926
|
+
Use this point list or PolygonData object to check if ports are at this polygon border.
|
|
1927
|
+
|
|
1928
|
+
Returns
|
|
1929
|
+
-------
|
|
1930
|
+
[[str]]
|
|
1931
|
+
Nested list of str, with net name as first value, X value for point at border, Y value for point at border,
|
|
1932
|
+
and terminal name.
|
|
1933
|
+
"""
|
|
1934
|
+
if not isinstance(nets, list):
|
|
1935
|
+
if isinstance(nets, str):
|
|
1936
|
+
nets = list(self._pedb.nets.signal.values())
|
|
1937
|
+
else:
|
|
1938
|
+
nets = [self._pedb.nets.signal[net] for net in nets]
|
|
1939
|
+
if nets:
|
|
1940
|
+
if isinstance(reference_net, str):
|
|
1941
|
+
reference_net = self._pedb.nets.nets[reference_net]
|
|
1942
|
+
if not reference_net:
|
|
1943
|
+
self._logger.error("No reference net provided for creating port")
|
|
1944
|
+
return False
|
|
1945
|
+
if user_defined_extent:
|
|
1946
|
+
if isinstance(user_defined_extent, GrpcPolygonData):
|
|
1947
|
+
_points = [pt for pt in list(user_defined_extent.points)]
|
|
1948
|
+
_x = []
|
|
1949
|
+
_y = []
|
|
1950
|
+
for pt in _points:
|
|
1951
|
+
if pt.x.value < 1e100 and pt.y.value < 1e100:
|
|
1952
|
+
_x.append(pt.x.value)
|
|
1953
|
+
_y.append(pt.y.value)
|
|
1954
|
+
user_defined_extent = [_x, _y]
|
|
1955
|
+
terminal_info = []
|
|
1956
|
+
for net in nets:
|
|
1957
|
+
net_polygons = [prim for prim in self._pedb.modeler.primitives if prim.type in ["polygon", "rectangle"]]
|
|
1958
|
+
for poly in net_polygons:
|
|
1959
|
+
mid_points = [[arc.midpoint.x.value, arc.midpoint.y.value] for arc in poly.arcs]
|
|
1960
|
+
for mid_point in mid_points:
|
|
1961
|
+
if GeometryOperators.point_in_polygon(mid_point, user_defined_extent) == 0:
|
|
1962
|
+
port_name = generate_unique_name(f"{poly.net_name}_{poly.id}")
|
|
1963
|
+
term = self._create_edge_terminal(poly.id, mid_point, port_name) # pragma no cover
|
|
1964
|
+
if not term.is_null:
|
|
1965
|
+
self._logger.info(f"Terminal {term.name} created")
|
|
1966
|
+
term.is_circuit_port = True
|
|
1967
|
+
terminal_info.append([poly.net_name, mid_point[0], mid_point[1], term.name])
|
|
1968
|
+
mid_pt_data = GrpcPointData(mid_point)
|
|
1969
|
+
ref_prim = [
|
|
1970
|
+
prim
|
|
1971
|
+
for prim in reference_net.primitives
|
|
1972
|
+
if prim.polygon_data.point_in_polygon(mid_pt_data)
|
|
1973
|
+
]
|
|
1974
|
+
if not ref_prim:
|
|
1975
|
+
self._logger.warning("no reference primitive found, trying to extend scanning area")
|
|
1976
|
+
scanning_zone = [
|
|
1977
|
+
(mid_point[0] - mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
|
|
1978
|
+
(mid_point[0] - mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
|
|
1979
|
+
(mid_point[0] + mid_point[0] * 1e-3, mid_point[1] + mid_point[1] * 1e-3),
|
|
1980
|
+
(mid_point[0] + mid_point[0] * 1e-3, mid_point[1] - mid_point[1] * 1e-3),
|
|
1981
|
+
]
|
|
1982
|
+
for new_point in scanning_zone:
|
|
1983
|
+
mid_pt_data = GrpcPointData(new_point)
|
|
1984
|
+
ref_prim = [
|
|
1985
|
+
prim
|
|
1986
|
+
for prim in reference_net.primitives
|
|
1987
|
+
if prim.polygon_data.point_in_polygon(mid_pt_data)
|
|
1988
|
+
]
|
|
1989
|
+
if ref_prim:
|
|
1990
|
+
self._logger.info("Reference primitive found")
|
|
1991
|
+
break
|
|
1992
|
+
if not ref_prim:
|
|
1993
|
+
self._logger.error("Failed to collect valid reference primitives for terminal")
|
|
1994
|
+
if ref_prim:
|
|
1995
|
+
reference_layer = ref_prim[0].layer
|
|
1996
|
+
if term.reference_layer == reference_layer:
|
|
1997
|
+
self._logger.info(f"Port {port_name} created")
|
|
1998
|
+
return terminal_info
|
|
1999
|
+
return False
|
|
2000
|
+
|
|
2001
|
+
def create_bundle_wave_port(
|
|
2002
|
+
self,
|
|
2003
|
+
primitives_id,
|
|
2004
|
+
points_on_edge,
|
|
2005
|
+
port_name=None,
|
|
2006
|
+
horizontal_extent_factor=5,
|
|
2007
|
+
vertical_extent_factor=3,
|
|
2008
|
+
pec_launch_width="0.01mm",
|
|
2009
|
+
):
|
|
2010
|
+
"""Create a bundle wave port.
|
|
2011
|
+
|
|
2012
|
+
Parameters
|
|
2013
|
+
----------
|
|
2014
|
+
primitives_id : list
|
|
2015
|
+
Primitive ID of the positive terminal.
|
|
2016
|
+
points_on_edge : list
|
|
2017
|
+
Coordinate of the point to define the edge terminal.
|
|
2018
|
+
The point must be close to the target edge but not on the two
|
|
2019
|
+
ends of the edge.
|
|
2020
|
+
port_name : str, optional
|
|
2021
|
+
Name of the port. The default is ``None``.
|
|
2022
|
+
horizontal_extent_factor : int, float, optional
|
|
2023
|
+
Horizontal extent factor. The default value is ``5``.
|
|
2024
|
+
vertical_extent_factor : int, float, optional
|
|
2025
|
+
Vertical extent factor. The default value is ``3``.
|
|
2026
|
+
pec_launch_width : str, optional
|
|
2027
|
+
Launch Width of PEC. The default value is ``"0.01mm"``.
|
|
2028
|
+
|
|
2029
|
+
Returns
|
|
2030
|
+
-------
|
|
2031
|
+
tuple
|
|
2032
|
+
The tuple contains: (port_name, pyedb.egacy.database.edb_data.sources.ExcitationDifferential).
|
|
2033
|
+
|
|
2034
|
+
Examples
|
|
2035
|
+
--------
|
|
2036
|
+
>>> edb.excitations.create_bundle_wave_port(0, ["-50mm", "-0mm"], 1, ["-50mm", "-0.2mm"])
|
|
2037
|
+
"""
|
|
2038
|
+
if not port_name:
|
|
2039
|
+
port_name = generate_unique_name("bundle_port")
|
|
2040
|
+
|
|
2041
|
+
if isinstance(primitives_id[0], Primitive):
|
|
2042
|
+
primitives_id = [i.id for i in primitives_id]
|
|
2043
|
+
|
|
2044
|
+
terminals = []
|
|
2045
|
+
_port_name = port_name
|
|
2046
|
+
for p_id, loc in list(zip(primitives_id, points_on_edge)):
|
|
2047
|
+
_, term = self.create_wave_port(
|
|
2048
|
+
p_id,
|
|
2049
|
+
loc,
|
|
2050
|
+
port_name=_port_name,
|
|
2051
|
+
horizontal_extent_factor=horizontal_extent_factor,
|
|
2052
|
+
vertical_extent_factor=vertical_extent_factor,
|
|
2053
|
+
pec_launch_width=pec_launch_width,
|
|
2054
|
+
)
|
|
2055
|
+
_port_name = None
|
|
2056
|
+
terminals.append(term)
|
|
2057
|
+
|
|
2058
|
+
_edb_bundle_terminal = BundleTerminal.create(terminals)
|
|
2059
|
+
return port_name, BundleWavePort(self._pedb, _edb_bundle_terminal)
|
|
2060
|
+
|
|
2061
|
+
def create_hfss_ports_on_padstack(self, pinpos, portname=None):
|
|
2062
|
+
"""Create an HFSS port on a padstack.
|
|
2063
|
+
|
|
2064
|
+
Parameters
|
|
2065
|
+
----------
|
|
2066
|
+
pinpos :
|
|
2067
|
+
Position of the pin.
|
|
2068
|
+
|
|
2069
|
+
portname : str, optional
|
|
2070
|
+
Name of the port. The default is ``None``.
|
|
2071
|
+
|
|
2072
|
+
Returns
|
|
2073
|
+
-------
|
|
2074
|
+
bool
|
|
2075
|
+
``True`` when successful, ``False`` when failed.
|
|
2076
|
+
"""
|
|
2077
|
+
top_layer, bottom_layer = pinpos.get_layer_range()
|
|
2078
|
+
|
|
2079
|
+
if not portname:
|
|
2080
|
+
portname = generate_unique_name("Port_" + pinpos.net.name)
|
|
2081
|
+
edbpointTerm_pos = PadstackInstanceTerminal.create(
|
|
2082
|
+
padstack_instance=pinpos, name=portname, layer=top_layer, is_ref=False
|
|
2083
|
+
)
|
|
2084
|
+
if edbpointTerm_pos:
|
|
2085
|
+
return True
|
|
2086
|
+
else:
|
|
2087
|
+
return False
|
|
2088
|
+
|
|
2089
|
+
def get_ports_number(self):
|
|
2090
|
+
"""Return the total number of excitation ports in a layout.
|
|
2091
|
+
|
|
2092
|
+
Parameters
|
|
2093
|
+
----------
|
|
2094
|
+
None
|
|
2095
|
+
|
|
2096
|
+
Returns
|
|
2097
|
+
-------
|
|
2098
|
+
int
|
|
2099
|
+
Number of ports.
|
|
2100
|
+
|
|
2101
|
+
"""
|
|
2102
|
+
terms = [term for term in self._pedb.layout.terminals]
|
|
2103
|
+
return len([i for i in terms if not i.is_reference_terminal])
|
|
2104
|
+
|
|
2105
|
+
def create_rlc_boundary_on_pins(self, positive_pin=None, negative_pin=None, rvalue=0.0, lvalue=0.0, cvalue=0.0):
|
|
2106
|
+
"""Create hfss rlc boundary on pins.
|
|
2107
|
+
|
|
2108
|
+
Parameters
|
|
2109
|
+
----------
|
|
2110
|
+
positive_pin : Positive pin.
|
|
2111
|
+
Edb.Cell.Primitive.PadstackInstance
|
|
2112
|
+
|
|
2113
|
+
negative_pin : Negative pin.
|
|
2114
|
+
Edb.Cell.Primitive.PadstackInstance
|
|
2115
|
+
|
|
2116
|
+
rvalue : Resistance value
|
|
2117
|
+
|
|
2118
|
+
lvalue : Inductance value
|
|
2119
|
+
|
|
2120
|
+
cvalue . Capacitance value.
|
|
2121
|
+
|
|
2122
|
+
Returns
|
|
2123
|
+
-------
|
|
2124
|
+
bool
|
|
2125
|
+
``True`` when successful, ``False`` when failed.
|
|
2126
|
+
|
|
2127
|
+
"""
|
|
2128
|
+
|
|
2129
|
+
if positive_pin and negative_pin:
|
|
2130
|
+
positive_pin_term = positive_pin.get_terminal(create_new_terminal=True)
|
|
2131
|
+
negative_pin_term = negative_pin.get_terminal(create_new_terminal=True)
|
|
2132
|
+
positive_pin_term.boundary_type = GrpcBoundaryType.RLC
|
|
2133
|
+
negative_pin_term.boundary_type = GrpcBoundaryType.RLC
|
|
2134
|
+
rlc = GrpcRlc()
|
|
2135
|
+
rlc.is_parallel = True
|
|
2136
|
+
rlc.r_enabled = True
|
|
2137
|
+
rlc.l_enabled = True
|
|
2138
|
+
rlc.c_enabled = True
|
|
2139
|
+
rlc.r = GrpcValue(rvalue)
|
|
2140
|
+
rlc.l = GrpcValue(lvalue)
|
|
2141
|
+
rlc.c = GrpcValue(cvalue)
|
|
2142
|
+
positive_pin_term.rlc_boundary_parameters = rlc
|
|
2143
|
+
term_name = f"{positive_pin.component.name}_{positive_pin.net.name}_{positive_pin.name}"
|
|
2144
|
+
positive_pin_term.name = term_name
|
|
2145
|
+
negative_pin_term.name = f"{term_name}_ref"
|
|
2146
|
+
positive_pin_term.reference_terminal = negative_pin_term
|
|
2147
|
+
return positive_pin_term
|
|
2148
|
+
return False
|
|
2149
|
+
|
|
2150
|
+
def create_edge_port_on_polygon(
|
|
2151
|
+
self,
|
|
2152
|
+
polygon=None,
|
|
2153
|
+
reference_polygon=None,
|
|
2154
|
+
terminal_point=None,
|
|
2155
|
+
reference_point=None,
|
|
2156
|
+
reference_layer=None,
|
|
2157
|
+
port_name=None,
|
|
2158
|
+
port_impedance=50.0,
|
|
2159
|
+
force_circuit_port=False,
|
|
2160
|
+
):
|
|
2161
|
+
"""Create lumped port between two edges from two different polygons. Can also create a vertical port when
|
|
2162
|
+
the reference layer name is only provided. When a port is created between two edge from two polygons which don't
|
|
2163
|
+
belong to the same layer, a circuit port will be automatically created instead of lumped. To enforce the circuit
|
|
2164
|
+
port instead of lumped,use the boolean force_circuit_port.
|
|
2165
|
+
|
|
2166
|
+
Parameters
|
|
2167
|
+
----------
|
|
2168
|
+
polygon : The EDB polygon object used to assign the port.
|
|
2169
|
+
Edb.Cell.Primitive.Polygon object.
|
|
2170
|
+
|
|
2171
|
+
reference_polygon : The EDB polygon object used to define the port reference.
|
|
2172
|
+
Edb.Cell.Primitive.Polygon object.
|
|
2173
|
+
|
|
2174
|
+
terminal_point : The coordinate of the point to define the edge terminal of the port. This point must be
|
|
2175
|
+
located on the edge of the polygon where the port has to be placed. For instance taking the middle point
|
|
2176
|
+
of an edge is a good practice but any point of the edge should be valid. Taking a corner might cause unwanted
|
|
2177
|
+
port location.
|
|
2178
|
+
list[float, float] with values provided in meter.
|
|
2179
|
+
|
|
2180
|
+
reference_point : same as terminal_point but used for defining the reference location on the edge.
|
|
2181
|
+
list[float, float] with values provided in meter.
|
|
2182
|
+
|
|
2183
|
+
reference_layer : Name used to define port reference for vertical ports.
|
|
2184
|
+
str the layer name.
|
|
2185
|
+
|
|
2186
|
+
port_name : Name of the port.
|
|
2187
|
+
str.
|
|
2188
|
+
|
|
2189
|
+
port_impedance : port impedance value. Default value is 50 Ohms.
|
|
2190
|
+
float, impedance value.
|
|
2191
|
+
|
|
2192
|
+
force_circuit_port ; used to force circuit port creation instead of lumped. Works for vertical and coplanar
|
|
2193
|
+
ports.
|
|
2194
|
+
|
|
2195
|
+
Examples
|
|
2196
|
+
--------
|
|
2197
|
+
|
|
2198
|
+
>>> edb_path = path_to_edb
|
|
2199
|
+
>>> edb = Edb(edb_path)
|
|
2200
|
+
>>> poly_list = [poly for poly in list(edb.layout.primitives) if poly.GetPrimitiveType() == 2]
|
|
2201
|
+
>>> port_poly = [poly for poly in poly_list if poly.GetId() == 17][0]
|
|
2202
|
+
>>> ref_poly = [poly for poly in poly_list if poly.GetId() == 19][0]
|
|
2203
|
+
>>> port_location = [-65e-3, -13e-3]
|
|
2204
|
+
>>> ref_location = [-63e-3, -13e-3]
|
|
2205
|
+
>>> edb.hfss.create_edge_port_on_polygon(polygon=port_poly, reference_polygon=ref_poly,
|
|
2206
|
+
>>> terminal_point=port_location, reference_point=ref_location)
|
|
2207
|
+
|
|
2208
|
+
"""
|
|
2209
|
+
if not polygon:
|
|
2210
|
+
self._logger.error("No polygon provided for port {} creation".format(port_name))
|
|
2211
|
+
return False
|
|
2212
|
+
if reference_layer:
|
|
2213
|
+
reference_layer = self._pedb.stackup.signal_layers[reference_layer]
|
|
2214
|
+
if not reference_layer:
|
|
2215
|
+
self._logger.error("Specified layer for port {} creation was not found".format(port_name))
|
|
2216
|
+
if not isinstance(terminal_point, list):
|
|
2217
|
+
self._logger.error("Terminal point must be a list of float with providing the point location in meter")
|
|
2218
|
+
return False
|
|
2219
|
+
terminal_point = GrpcPointData(terminal_point)
|
|
2220
|
+
if reference_point and isinstance(reference_point, list):
|
|
2221
|
+
reference_point = GrpcPointData(reference_point)
|
|
2222
|
+
if not port_name:
|
|
2223
|
+
port_name = generate_unique_name("Port_")
|
|
2224
|
+
edge = GrpcPrimitiveEdge.create(polygon, terminal_point)
|
|
2225
|
+
edges = [edge]
|
|
2226
|
+
edge_term = GrpcEdgeTerminal.create(
|
|
2227
|
+
layout=polygon.layout, edges=edges, net=polygon.net, name=port_name, is_ref=False
|
|
2228
|
+
)
|
|
2229
|
+
if force_circuit_port:
|
|
2230
|
+
edge_term.is_circuit_port = True
|
|
2231
|
+
else:
|
|
2232
|
+
edge_term.is_circuit_port = False
|
|
2233
|
+
|
|
2234
|
+
if port_impedance:
|
|
2235
|
+
edge_term.impedance = GrpcValue(port_impedance)
|
|
2236
|
+
edge_term.name = port_name
|
|
2237
|
+
if reference_polygon and reference_point:
|
|
2238
|
+
ref_edge = GrpcPrimitiveEdge.create(reference_polygon, reference_point)
|
|
2239
|
+
ref_edges = [ref_edge]
|
|
2240
|
+
ref_edge_term = GrpcEdgeTerminal.create(
|
|
2241
|
+
layout=reference_polygon.layout,
|
|
2242
|
+
name=port_name + "_ref",
|
|
2243
|
+
edges=ref_edges,
|
|
2244
|
+
net=reference_polygon.net,
|
|
2245
|
+
is_ref=True,
|
|
2246
|
+
)
|
|
2247
|
+
if reference_layer:
|
|
2248
|
+
ref_edge_term.reference_layer = reference_layer
|
|
2249
|
+
if force_circuit_port:
|
|
2250
|
+
ref_edge_term.is_circuit_port = True
|
|
2251
|
+
else:
|
|
2252
|
+
ref_edge_term.is_circuit_port = False
|
|
2253
|
+
|
|
2254
|
+
if port_impedance:
|
|
2255
|
+
ref_edge_term.impedance = GrpcValue(port_impedance)
|
|
2256
|
+
edge_term.reference_terminal = ref_edge_term
|
|
2257
|
+
return True
|
|
2258
|
+
|
|
2259
|
+
def create_port_between_pin_and_layer(
|
|
2260
|
+
self, component_name=None, pins_name=None, layer_name=None, reference_net=None, impedance=50.0
|
|
2261
|
+
):
|
|
2262
|
+
"""Create circuit port between pin and a reference layer.
|
|
2263
|
+
|
|
2264
|
+
Parameters
|
|
2265
|
+
----------
|
|
2266
|
+
component_name : str
|
|
2267
|
+
Component name. The default is ``None``.
|
|
2268
|
+
pins_name : str
|
|
2269
|
+
Pin name or list of pin names. The default is ``None``.
|
|
2270
|
+
layer_name : str
|
|
2271
|
+
Layer name. The default is ``None``.
|
|
2272
|
+
reference_net : str
|
|
2273
|
+
Reference net name. The default is ``None``.
|
|
2274
|
+
impedance : float, optional
|
|
2275
|
+
Port impedance. The default is ``50.0`` in ohms.
|
|
2276
|
+
|
|
2277
|
+
Returns
|
|
2278
|
+
-------
|
|
2279
|
+
PadstackInstanceTerminal
|
|
2280
|
+
Created terminal.
|
|
2281
|
+
|
|
2282
|
+
"""
|
|
2283
|
+
if not pins_name:
|
|
2284
|
+
pins_name = []
|
|
2285
|
+
if pins_name:
|
|
2286
|
+
if not isinstance(pins_name, list): # pragma no cover
|
|
2287
|
+
pins_name = [pins_name]
|
|
2288
|
+
if not reference_net:
|
|
2289
|
+
self._logger.info("no reference net provided, searching net {} instead.".format(layer_name))
|
|
2290
|
+
reference_net = self._pedb.nets.get_net_by_name(layer_name)
|
|
2291
|
+
if not reference_net: # pragma no cover
|
|
2292
|
+
self._logger.error("reference net {} not found.".format(layer_name))
|
|
2293
|
+
return False
|
|
2294
|
+
else:
|
|
2295
|
+
if not isinstance(reference_net, Net): # pragma no cover
|
|
2296
|
+
reference_net = self._pedb.nets.get_net_by_name(reference_net)
|
|
2297
|
+
if not reference_net:
|
|
2298
|
+
self._logger.error("Net {} not found".format(reference_net))
|
|
2299
|
+
return False
|
|
2300
|
+
terms = []
|
|
2301
|
+
pins = self._pedb.components.instances[component_name].pins
|
|
2302
|
+
for __pin in pins_name:
|
|
2303
|
+
if __pin in pins:
|
|
2304
|
+
pin = pins[__pin]
|
|
2305
|
+
term_name = f"{pin.component.name}_{pin.net.name}_{pin.component}"
|
|
2306
|
+
start_layer, stop_layer = pin.get_layer_range()
|
|
2307
|
+
if start_layer:
|
|
2308
|
+
positive_terminal = PadstackInstanceTerminal.create(
|
|
2309
|
+
layout=pin.layout, net=pin.net, padstack_instance=pin, name=term_name, layer=start_layer
|
|
2310
|
+
)
|
|
2311
|
+
positive_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
2312
|
+
positive_terminal.impedance = GrpcValue(impedance)
|
|
2313
|
+
positive_terminal.Is_circuit_port = True
|
|
2314
|
+
position = GrpcPointData(self._pedb.components.get_pin_position(pin))
|
|
2315
|
+
negative_terminal = PointTerminal.create(
|
|
2316
|
+
layout=self._pedb.active_layout,
|
|
2317
|
+
net=reference_net,
|
|
2318
|
+
layer=self._pedb.stackup.signal_layers[layer_name],
|
|
2319
|
+
name=f"{term_name}_ref",
|
|
2320
|
+
point=position,
|
|
2321
|
+
)
|
|
2322
|
+
negative_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
2323
|
+
negative_terminal.impedance = GrpcValue(impedance)
|
|
2324
|
+
negative_terminal.is_circuit_port = True
|
|
2325
|
+
positive_terminal.reference_terminal = negative_terminal
|
|
2326
|
+
self._logger.info("Port {} successfully created".format(term_name))
|
|
2327
|
+
if not positive_terminal.is_null:
|
|
2328
|
+
terms.append(positive_terminal)
|
|
2329
|
+
else:
|
|
2330
|
+
self._logger.error(f"pin {__pin} not found on component {component_name}")
|
|
2331
|
+
if terms:
|
|
2332
|
+
return terms
|
|
2333
|
+
return False
|
|
2334
|
+
|
|
2335
|
+
def create_current_source_on_pin_group(
|
|
2336
|
+
self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None
|
|
2337
|
+
):
|
|
2338
|
+
"""Create current source between two pin groups.
|
|
2339
|
+
|
|
2340
|
+
Parameters
|
|
2341
|
+
----------
|
|
2342
|
+
pos_pin_group_name : str
|
|
2343
|
+
Name of the positive pin group.
|
|
2344
|
+
neg_pin_group_name : str
|
|
2345
|
+
Name of the negative pin group.
|
|
2346
|
+
magnitude : int, float, optional
|
|
2347
|
+
Magnitude of the source.
|
|
2348
|
+
phase : int, float, optional
|
|
2349
|
+
Phase of the source
|
|
2350
|
+
|
|
2351
|
+
Returns
|
|
2352
|
+
-------
|
|
2353
|
+
bool
|
|
2354
|
+
|
|
2355
|
+
"""
|
|
2356
|
+
pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
|
|
2357
|
+
if not pos_pin_group:
|
|
2358
|
+
self._pedb.logger.error(f"Pin group {pos_pin_group_name} not found.")
|
|
2359
|
+
return False
|
|
2360
|
+
pos_terminal = pos_pin_group.create_current_source_terminal(magnitude, phase)
|
|
2361
|
+
if name:
|
|
2362
|
+
pos_terminal.name = name
|
|
2363
|
+
else:
|
|
2364
|
+
name = generate_unique_name("isource")
|
|
2365
|
+
pos_terminal.name = name
|
|
2366
|
+
neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
|
|
2367
|
+
if not neg_pin_group:
|
|
2368
|
+
self._pedb.logger.error(f"Pin group {pos_pin_group_name} not found.")
|
|
2369
|
+
return False
|
|
2370
|
+
neg_terminal = neg_pin_group.create_current_source_terminal()
|
|
2371
|
+
neg_terminal.name = f"{name}_ref"
|
|
2372
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
2373
|
+
return True
|
|
2374
|
+
|
|
2375
|
+
def create_voltage_source_on_pin_group(
|
|
2376
|
+
self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None, impedance=0.001
|
|
2377
|
+
):
|
|
2378
|
+
"""Create voltage source between two pin groups.
|
|
2379
|
+
|
|
2380
|
+
Parameters
|
|
2381
|
+
----------
|
|
2382
|
+
pos_pin_group_name : str
|
|
2383
|
+
Name of the positive pin group.
|
|
2384
|
+
neg_pin_group_name : str
|
|
2385
|
+
Name of the negative pin group.
|
|
2386
|
+
magnitude : int, float, optional
|
|
2387
|
+
Magnitude of the source.
|
|
2388
|
+
phase : int, float, optional
|
|
2389
|
+
Phase of the source
|
|
2390
|
+
|
|
2391
|
+
Returns
|
|
2392
|
+
-------
|
|
2393
|
+
bool
|
|
2394
|
+
|
|
2395
|
+
"""
|
|
2396
|
+
pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
|
|
2397
|
+
if not pos_pin_group:
|
|
2398
|
+
self._pedb.logger.error(f"Pingroup {pos_pin_group_name} not found.")
|
|
2399
|
+
return False
|
|
2400
|
+
pos_terminal = pos_pin_group.create_voltage_source_terminal(magnitude, phase, impedance)
|
|
2401
|
+
if name:
|
|
2402
|
+
pos_terminal.name = name
|
|
2403
|
+
else:
|
|
2404
|
+
name = generate_unique_name("vsource")
|
|
2405
|
+
pos_terminal.name = name
|
|
2406
|
+
neg_pin_group_name = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
|
|
2407
|
+
if not neg_pin_group_name:
|
|
2408
|
+
self._pedb.logger.error(f"Pingroup {neg_pin_group_name} not found.")
|
|
2409
|
+
return False
|
|
2410
|
+
neg_terminal = neg_pin_group_name.create_voltage_source_terminal(magnitude, phase)
|
|
2411
|
+
neg_terminal.name = f"{name}_ref"
|
|
2412
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
2413
|
+
return True
|
|
2414
|
+
|
|
2415
|
+
def create_voltage_probe_on_pin_group(self, probe_name, pos_pin_group_name, neg_pin_group_name, impedance=1000000):
|
|
2416
|
+
"""Create voltage probe between two pin groups.
|
|
2417
|
+
|
|
2418
|
+
Parameters
|
|
2419
|
+
----------
|
|
2420
|
+
probe_name : str
|
|
2421
|
+
Name of the probe.
|
|
2422
|
+
pos_pin_group_name : str
|
|
2423
|
+
Name of the positive pin group.
|
|
2424
|
+
neg_pin_group_name : str
|
|
2425
|
+
Name of the negative pin group.
|
|
2426
|
+
impedance : int, float, optional
|
|
2427
|
+
Phase of the source.
|
|
2428
|
+
|
|
2429
|
+
Returns
|
|
2430
|
+
-------
|
|
2431
|
+
bool
|
|
2432
|
+
|
|
2433
|
+
"""
|
|
2434
|
+
pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
|
|
2435
|
+
if not pos_pin_group:
|
|
2436
|
+
self._pedb.logger.error(f"Pingroup {pos_pin_group_name} not found.")
|
|
2437
|
+
return False
|
|
2438
|
+
pos_terminal = pos_pin_group.create_voltage_probe_terminal(impedance)
|
|
2439
|
+
if probe_name:
|
|
2440
|
+
pos_terminal.name = probe_name
|
|
2441
|
+
else:
|
|
2442
|
+
probe_name = generate_unique_name("vprobe")
|
|
2443
|
+
pos_terminal.name = probe_name
|
|
2444
|
+
neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
|
|
2445
|
+
if not neg_pin_group:
|
|
2446
|
+
self._pedb.logger.error(f"Pingroup {neg_pin_group_name} not found.")
|
|
2447
|
+
return False
|
|
2448
|
+
neg_terminal = neg_pin_group.create_voltage_probe_terminal()
|
|
2449
|
+
neg_terminal.name = f"{probe_name}_ref"
|
|
2450
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
2451
|
+
return not pos_terminal.is_null
|
|
2452
|
+
|
|
2453
|
+
def create_dc_terminal(
|
|
2454
|
+
self,
|
|
2455
|
+
component_name,
|
|
2456
|
+
net_name,
|
|
2457
|
+
source_name=None,
|
|
2458
|
+
):
|
|
2459
|
+
"""Create a dc terminal.
|
|
2460
|
+
|
|
2461
|
+
Parameters
|
|
2462
|
+
----------
|
|
2463
|
+
component_name : str
|
|
2464
|
+
Name of the positive component.
|
|
2465
|
+
net_name : str
|
|
2466
|
+
Name of the positive net.
|
|
2467
|
+
|
|
2468
|
+
source_name : str, optional
|
|
2469
|
+
Name of the source. The default is ``""``.
|
|
2470
|
+
|
|
2471
|
+
Returns
|
|
2472
|
+
-------
|
|
2473
|
+
str
|
|
2474
|
+
The name of the source.
|
|
2475
|
+
|
|
2476
|
+
Examples
|
|
2477
|
+
--------
|
|
2478
|
+
|
|
2479
|
+
>>> from pyedb import Edb
|
|
2480
|
+
>>> edbapp = Edb("myaedbfolder", "project name", "release version")
|
|
2481
|
+
>>> edb.siwave.create_dc_terminal("U2A5", "V1P5_S3", "source_name")
|
|
2482
|
+
"""
|
|
2483
|
+
|
|
2484
|
+
node_pin = self._pedb.components.get_pin_from_component(component_name, net_name)
|
|
2485
|
+
if node_pin:
|
|
2486
|
+
node_pin = node_pin[0]
|
|
2487
|
+
if not source_name:
|
|
2488
|
+
source_name = f"DC_{component_name}_{net_name}"
|
|
2489
|
+
return self.create_pin_group_terminal(
|
|
2490
|
+
positive_pins=node_pin, name=source_name, source_type="dc_terminal", negatives_pins=None
|
|
2491
|
+
)
|
|
2492
|
+
|
|
2493
|
+
def create_circuit_port_on_pin_group(self, pos_pin_group_name, neg_pin_group_name, impedance=50, name=None):
|
|
2494
|
+
"""Create a port between two pin groups.
|
|
2495
|
+
|
|
2496
|
+
Parameters
|
|
2497
|
+
----------
|
|
2498
|
+
pos_pin_group_name : str
|
|
2499
|
+
Name of the positive pin group.
|
|
2500
|
+
neg_pin_group_name : str
|
|
2501
|
+
Name of the negative pin group.
|
|
2502
|
+
impedance : int, float, optional
|
|
2503
|
+
Impedance of the port. Default is ``50``.
|
|
2504
|
+
name : str, optional
|
|
2505
|
+
Port name.
|
|
2506
|
+
|
|
2507
|
+
Returns
|
|
2508
|
+
-------
|
|
2509
|
+
bool
|
|
2510
|
+
|
|
2511
|
+
"""
|
|
2512
|
+
pos_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == pos_pin_group_name)
|
|
2513
|
+
if not pos_pin_group:
|
|
2514
|
+
self._pedb.logger.error("No positive pin group found")
|
|
2515
|
+
return False
|
|
2516
|
+
pos_terminal = pos_pin_group.create_port_terminal(impedance)
|
|
2517
|
+
if name: # pragma: no cover
|
|
2518
|
+
pos_terminal.name = name
|
|
2519
|
+
else:
|
|
2520
|
+
name = generate_unique_name("port")
|
|
2521
|
+
pos_terminal.name = name
|
|
2522
|
+
neg_pin_group = next(pg for pg in self._pedb.layout.pin_groups if pg.name == neg_pin_group_name)
|
|
2523
|
+
neg_terminal = neg_pin_group.create_port_terminal(impedance)
|
|
2524
|
+
neg_terminal.name = f"{name}_ref"
|
|
2525
|
+
pos_terminal.reference_terminal = neg_terminal
|
|
2526
|
+
return True
|
|
2527
|
+
|
|
2528
|
+
def place_voltage_probe(
|
|
2529
|
+
self,
|
|
2530
|
+
name,
|
|
2531
|
+
positive_net_name,
|
|
2532
|
+
positive_location,
|
|
2533
|
+
positive_layer,
|
|
2534
|
+
negative_net_name,
|
|
2535
|
+
negative_location,
|
|
2536
|
+
negative_layer,
|
|
2537
|
+
):
|
|
2538
|
+
"""Place a voltage probe between two points.
|
|
2539
|
+
|
|
2540
|
+
Parameters
|
|
2541
|
+
----------
|
|
2542
|
+
name : str,
|
|
2543
|
+
Name of the probe.
|
|
2544
|
+
positive_net_name : str
|
|
2545
|
+
Name of the positive net.
|
|
2546
|
+
positive_location : list
|
|
2547
|
+
Location of the positive terminal.
|
|
2548
|
+
positive_layer : str,
|
|
2549
|
+
Layer of the positive terminal.
|
|
2550
|
+
negative_net_name : str,
|
|
2551
|
+
Name of the negative net.
|
|
2552
|
+
negative_location : list
|
|
2553
|
+
Location of the negative terminal.
|
|
2554
|
+
negative_layer : str
|
|
2555
|
+
Layer of the negative terminal.
|
|
2556
|
+
"""
|
|
2557
|
+
p_terminal = PointTerminal.create(
|
|
2558
|
+
layout=self._pedb.active_layout,
|
|
2559
|
+
net=positive_net_name,
|
|
2560
|
+
layer=positive_layer,
|
|
2561
|
+
name=name,
|
|
2562
|
+
point=GrpcPointData(positive_location),
|
|
2563
|
+
)
|
|
2564
|
+
n_terminal = PointTerminal.create(
|
|
2565
|
+
layout=self._pedb.active_layout,
|
|
2566
|
+
net=negative_net_name,
|
|
2567
|
+
layer=negative_layer,
|
|
2568
|
+
name=f"{name}_ref",
|
|
2569
|
+
point=GrpcPointData(negative_location),
|
|
2570
|
+
)
|
|
2571
|
+
p_terminal.reference_terminal = n_terminal
|
|
2572
|
+
return self._pedb.create_voltage_probe(p_terminal, n_terminal)
|