pyedb 0.49.0__py3-none-any.whl → 0.50.1__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/configuration/cfg_modeler.py +42 -11
- pyedb/configuration/cfg_ports_sources.py +9 -1
- pyedb/dotnet/database/cell/hierarchy/component.py +6 -6
- pyedb/dotnet/database/components.py +3 -3
- pyedb/dotnet/database/edb_data/padstacks_data.py +13 -0
- pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
- pyedb/dotnet/database/edb_data/variables.py +3 -3
- pyedb/dotnet/database/materials.py +16 -16
- pyedb/dotnet/database/modeler.py +38 -4
- pyedb/dotnet/database/sim_setup_data/data/settings.py +28 -0
- pyedb/dotnet/database/stackup.py +1 -0
- pyedb/dotnet/database/utilities/hfss_simulation_setup.py +5 -6
- pyedb/dotnet/edb.py +22 -20
- pyedb/extensions/__init__.py +0 -0
- pyedb/extensions/via_design_backend.py +681 -0
- pyedb/grpc/database/components.py +537 -686
- pyedb/grpc/database/control_file.py +458 -149
- pyedb/grpc/database/definition/component_def.py +17 -14
- pyedb/grpc/database/definition/materials.py +60 -60
- pyedb/grpc/database/definition/package_def.py +8 -8
- pyedb/grpc/database/definition/padstack_def.py +31 -33
- pyedb/grpc/database/definitions.py +6 -4
- pyedb/grpc/database/geometry/arc_data.py +5 -5
- pyedb/grpc/database/geometry/point_3d_data.py +3 -3
- pyedb/grpc/database/geometry/polygon_data.py +5 -5
- pyedb/grpc/database/hfss.py +399 -397
- pyedb/grpc/database/hierarchy/component.py +60 -58
- pyedb/grpc/database/hierarchy/pin_pair_model.py +6 -6
- pyedb/grpc/database/hierarchy/pingroup.py +13 -11
- pyedb/grpc/database/hierarchy/s_parameter_model.py +1 -1
- pyedb/grpc/database/hierarchy/spice_model.py +1 -1
- pyedb/grpc/database/layers/layer.py +2 -2
- pyedb/grpc/database/layers/stackup_layer.py +144 -44
- pyedb/grpc/database/layout/layout.py +12 -12
- pyedb/grpc/database/layout/voltage_regulator.py +8 -8
- pyedb/grpc/database/layout_validation.py +5 -5
- pyedb/grpc/database/modeler.py +248 -245
- pyedb/grpc/database/net/differential_pair.py +4 -4
- pyedb/grpc/database/net/extended_net.py +7 -8
- pyedb/grpc/database/net/net.py +57 -46
- pyedb/grpc/database/nets.py +139 -122
- pyedb/grpc/database/padstacks.py +174 -190
- pyedb/grpc/database/ports/ports.py +23 -17
- pyedb/grpc/database/primitive/padstack_instance.py +45 -30
- pyedb/grpc/database/primitive/path.py +7 -7
- pyedb/grpc/database/primitive/polygon.py +9 -9
- pyedb/grpc/database/primitive/primitive.py +21 -21
- pyedb/grpc/database/primitive/rectangle.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +6 -6
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +2 -2
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +2 -2
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +1 -1
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +3 -3
- pyedb/grpc/database/siwave.py +166 -214
- pyedb/grpc/database/source_excitations.py +156 -0
- pyedb/grpc/database/stackup.py +415 -316
- pyedb/grpc/database/terminal/bundle_terminal.py +12 -12
- pyedb/grpc/database/terminal/edge_terminal.py +6 -5
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +13 -13
- pyedb/grpc/database/terminal/pingroup_terminal.py +12 -12
- pyedb/grpc/database/terminal/point_terminal.py +6 -6
- pyedb/grpc/database/terminal/terminal.py +26 -26
- pyedb/grpc/database/utility/heat_sink.py +5 -5
- pyedb/grpc/database/utility/hfss_extent_info.py +21 -21
- pyedb/grpc/database/utility/layout_statistics.py +13 -13
- pyedb/grpc/database/utility/rlc.py +3 -3
- pyedb/grpc/database/utility/sources.py +1 -1
- pyedb/grpc/database/utility/sweep_data_distribution.py +1 -1
- pyedb/grpc/edb.py +524 -764
- {pyedb-0.49.0.dist-info → pyedb-0.50.1.dist-info}/METADATA +1 -1
- {pyedb-0.49.0.dist-info → pyedb-0.50.1.dist-info}/RECORD +77 -77
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +0 -1151
- pyedb/grpc/database/utility/simulation_configuration.py +0 -3305
- {pyedb-0.49.0.dist-info → pyedb-0.50.1.dist-info}/LICENSE +0 -0
- {pyedb-0.49.0.dist-info → pyedb-0.50.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
from copy import deepcopy as copy
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import re
|
|
5
|
+
import tempfile
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import toml
|
|
11
|
+
|
|
12
|
+
from pyedb import Edb
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_variable(obj, name_suffix, value):
|
|
16
|
+
var_name = f"{obj.name}_{name_suffix}"
|
|
17
|
+
var_value = value
|
|
18
|
+
obj.variables.append(
|
|
19
|
+
{"name": var_name, "value": var_value, "description": f"Net name = {obj.net_name}"},
|
|
20
|
+
)
|
|
21
|
+
return var_name
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StitchingVias:
|
|
25
|
+
def __init__(self, p_via, start_angle, step_angle, number_of_vias, distance, clockwise=True):
|
|
26
|
+
self.p_via = p_via
|
|
27
|
+
self.name = f"{self.p_via.name}_stitching_via"
|
|
28
|
+
self.start_angle = int(start_angle)
|
|
29
|
+
self.step_angle = int(step_angle)
|
|
30
|
+
self.number_of_vias = int(number_of_vias)
|
|
31
|
+
self.distance = distance
|
|
32
|
+
self.clockwise = clockwise
|
|
33
|
+
|
|
34
|
+
self.vias = []
|
|
35
|
+
for idx, angle in enumerate(
|
|
36
|
+
np.arange(self.start_angle, start_angle + self.step_angle * self.number_of_vias, self.step_angle)
|
|
37
|
+
):
|
|
38
|
+
dx = f"cos({angle}deg)*({self.distance}+{self.p_via.anti_pad_diameter}/2)"
|
|
39
|
+
dy = f"sin({angle}deg)*({self.distance}+{self.p_via.anti_pad_diameter}/2)"
|
|
40
|
+
via = GroundVia(
|
|
41
|
+
p_signal=self.p_via.p_signal,
|
|
42
|
+
name=f"{self.name}_{idx}",
|
|
43
|
+
net_name="GND",
|
|
44
|
+
padstack_def=self.p_via.padstack_def,
|
|
45
|
+
start_layer=self.p_via.start_layer,
|
|
46
|
+
stop_layer=self.p_via.stop_layer,
|
|
47
|
+
base_x=self.p_via.x,
|
|
48
|
+
base_y=self.p_via.y,
|
|
49
|
+
dx=dx,
|
|
50
|
+
dy=dy,
|
|
51
|
+
flip_dx=self.p_via.flip_dx,
|
|
52
|
+
flip_dy=self.p_via.flip_dy,
|
|
53
|
+
connection_trace=False,
|
|
54
|
+
with_solder_ball=False,
|
|
55
|
+
backdrill_parameters=None,
|
|
56
|
+
conductor_layers=self.p_via.p_signal.p_board.conductor_layers,
|
|
57
|
+
)
|
|
58
|
+
self.vias.append(via)
|
|
59
|
+
|
|
60
|
+
def populate_config(self, cfg):
|
|
61
|
+
for via in self.vias:
|
|
62
|
+
via.populate_config(cfg)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Trace:
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
p_via,
|
|
69
|
+
name,
|
|
70
|
+
net_name,
|
|
71
|
+
layer,
|
|
72
|
+
width,
|
|
73
|
+
clearance,
|
|
74
|
+
incremental_path: list[list],
|
|
75
|
+
flip_dx,
|
|
76
|
+
flip_dy,
|
|
77
|
+
end_cap_style,
|
|
78
|
+
port: Union[dict, None],
|
|
79
|
+
):
|
|
80
|
+
self.p_via = p_via
|
|
81
|
+
self.variables = []
|
|
82
|
+
self.name = name
|
|
83
|
+
self.net_name = net_name
|
|
84
|
+
self.layer = layer
|
|
85
|
+
self.width = create_variable(self, "width", width)
|
|
86
|
+
self.clearance = create_variable(self, "clearance", clearance)
|
|
87
|
+
self.flip_dx = flip_dx
|
|
88
|
+
self.flip_dy = flip_dy
|
|
89
|
+
self.end_cap_style = end_cap_style
|
|
90
|
+
self.port = port
|
|
91
|
+
|
|
92
|
+
# self.voids = []
|
|
93
|
+
|
|
94
|
+
self.incremental_path = [
|
|
95
|
+
i if idx == 0 else [f"{i[0]}*{-1 if self.flip_dx else 1}", f"{i[1]}*{-1 if self.flip_dy else 1}"]
|
|
96
|
+
for idx, i in enumerate(incremental_path)
|
|
97
|
+
]
|
|
98
|
+
self.incremental_path = []
|
|
99
|
+
for idx, i in enumerate(incremental_path):
|
|
100
|
+
if idx == 0:
|
|
101
|
+
self.incremental_path.append(i)
|
|
102
|
+
else:
|
|
103
|
+
dx = create_variable(self, name_suffix=f"_dx_{idx}", value=i[0])
|
|
104
|
+
dy = create_variable(self, name_suffix=f"_dy_{idx}", value=i[1])
|
|
105
|
+
temp = [f"{dx}*{-1 if self.flip_dx else 1}", f"{dy}*{-1 if self.flip_dy else 1}"]
|
|
106
|
+
self.incremental_path.append(temp)
|
|
107
|
+
|
|
108
|
+
self.path = [self.incremental_path[0]]
|
|
109
|
+
x, y = self.incremental_path[0]
|
|
110
|
+
for x0, y0 in self.incremental_path[1:]:
|
|
111
|
+
x = f"{x}+({x0})"
|
|
112
|
+
y = f"{y}+({y0})"
|
|
113
|
+
self.path.append([x, y])
|
|
114
|
+
|
|
115
|
+
def populate_config(self, cfg):
|
|
116
|
+
cfg["variables"].extend(self.variables)
|
|
117
|
+
trace = {
|
|
118
|
+
"name": self.name,
|
|
119
|
+
"layer": self.layer,
|
|
120
|
+
"width": self.width,
|
|
121
|
+
# "incremental_path": self.incremental_path,
|
|
122
|
+
"path": self.path,
|
|
123
|
+
"net_name": self.net_name,
|
|
124
|
+
"start_cap_style": "round",
|
|
125
|
+
"end_cap_style": self.end_cap_style,
|
|
126
|
+
"corner_style": "round",
|
|
127
|
+
}
|
|
128
|
+
cfg["modeler"]["traces"].append(trace)
|
|
129
|
+
|
|
130
|
+
trace_void = copy(trace)
|
|
131
|
+
trace_void["name"] = f"{self.name}_void"
|
|
132
|
+
trace_void["width"] = f"{self.width}+2*{self.clearance}"
|
|
133
|
+
trace_void["end_cap_style"] = "round"
|
|
134
|
+
cfg["modeler"]["traces"].append(trace_void)
|
|
135
|
+
self.p_via.p_signal.p_board.voids.append(trace_void)
|
|
136
|
+
# self.voids.append(trace_void)
|
|
137
|
+
|
|
138
|
+
if self.port is not None:
|
|
139
|
+
port = self.get_port_cfg()
|
|
140
|
+
cfg["ports"].append(port)
|
|
141
|
+
|
|
142
|
+
def get_port_cfg(self):
|
|
143
|
+
return {
|
|
144
|
+
"name": f"port_{self.name}",
|
|
145
|
+
"type": "wave_port",
|
|
146
|
+
"primitive_name": self.name,
|
|
147
|
+
"point_on_edge": self.path[-1],
|
|
148
|
+
"horizontal_extent_factor": self.port["horizontal_extent_factor"],
|
|
149
|
+
"vertical_extent_factor": self.port["vertical_extent_factor"],
|
|
150
|
+
"pec_launch_width": "0.02mm",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class GroundVia:
|
|
155
|
+
@property
|
|
156
|
+
def x(self):
|
|
157
|
+
return f"{self.base_x}+{self.dx}"
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def y(self):
|
|
161
|
+
return f"{self.base_y}+{self.dy}"
|
|
162
|
+
|
|
163
|
+
def __init__(
|
|
164
|
+
self,
|
|
165
|
+
p_signal,
|
|
166
|
+
name,
|
|
167
|
+
net_name,
|
|
168
|
+
padstack_def,
|
|
169
|
+
start_layer,
|
|
170
|
+
stop_layer,
|
|
171
|
+
base_x,
|
|
172
|
+
base_y,
|
|
173
|
+
dx,
|
|
174
|
+
dy,
|
|
175
|
+
flip_dx,
|
|
176
|
+
flip_dy,
|
|
177
|
+
connection_trace: Union[dict, Trace],
|
|
178
|
+
with_solder_ball,
|
|
179
|
+
backdrill_parameters,
|
|
180
|
+
conductor_layers: list,
|
|
181
|
+
**kwargs,
|
|
182
|
+
):
|
|
183
|
+
self.p_signal = p_signal
|
|
184
|
+
self.variables = []
|
|
185
|
+
self.name = name
|
|
186
|
+
self.net_name = net_name
|
|
187
|
+
self.padstack_def = padstack_def
|
|
188
|
+
self.start_layer = start_layer
|
|
189
|
+
self.stop_layer = stop_layer
|
|
190
|
+
self.base_x = base_x
|
|
191
|
+
self.base_y = base_y
|
|
192
|
+
|
|
193
|
+
var_dx = create_variable(self, "dx", dx)
|
|
194
|
+
var_dy = create_variable(self, "dy", dy)
|
|
195
|
+
self.flip_dx = flip_dx
|
|
196
|
+
self.flip_dy = flip_dy
|
|
197
|
+
self.dx = var_dx if flip_dx is False else f"-1*({var_dx})"
|
|
198
|
+
self.dy = var_dy if flip_dy is False else f"-1*({var_dy})"
|
|
199
|
+
self.with_solder_ball = with_solder_ball
|
|
200
|
+
self.backdrill_parameters = backdrill_parameters
|
|
201
|
+
self.conductor_layers = conductor_layers
|
|
202
|
+
|
|
203
|
+
self.traces = []
|
|
204
|
+
self.fanout_traces = []
|
|
205
|
+
# self._voids = []
|
|
206
|
+
|
|
207
|
+
if connection_trace is not False:
|
|
208
|
+
trace = Trace(
|
|
209
|
+
p_via=self,
|
|
210
|
+
name=f"{self.name}_trace",
|
|
211
|
+
net_name=self.net_name,
|
|
212
|
+
layer=self.stop_layer,
|
|
213
|
+
width=connection_trace["width"],
|
|
214
|
+
clearance=connection_trace["clearance"],
|
|
215
|
+
incremental_path=[[base_x, base_y], [var_dx, var_dy]],
|
|
216
|
+
flip_dx=flip_dx,
|
|
217
|
+
flip_dy=flip_dy,
|
|
218
|
+
end_cap_style="round",
|
|
219
|
+
port=None,
|
|
220
|
+
)
|
|
221
|
+
self.traces.append(trace)
|
|
222
|
+
|
|
223
|
+
def populate_config(self, cfg):
|
|
224
|
+
cfg["variables"].extend(self.variables)
|
|
225
|
+
|
|
226
|
+
for trace in self.traces:
|
|
227
|
+
trace.populate_config(cfg)
|
|
228
|
+
|
|
229
|
+
for trace in self.fanout_traces:
|
|
230
|
+
trace.populate_config(cfg)
|
|
231
|
+
|
|
232
|
+
padstack_instance = {
|
|
233
|
+
"name": self.name,
|
|
234
|
+
"definition": self.padstack_def,
|
|
235
|
+
"layer_range": [self.start_layer, self.stop_layer],
|
|
236
|
+
"position": [self.x, self.y],
|
|
237
|
+
"net_name": self.net_name,
|
|
238
|
+
}
|
|
239
|
+
if self.with_solder_ball:
|
|
240
|
+
padstack_instance["solder_ball_layer"] = self.start_layer
|
|
241
|
+
padstack_instance["layer_range"] = [self.stop_layer, self.stop_layer]
|
|
242
|
+
|
|
243
|
+
padstack_instance_upper = copy(padstack_instance)
|
|
244
|
+
padstack_instance_upper["layer_range"] = [self.start_layer, self.start_layer]
|
|
245
|
+
if self.backdrill_parameters is not False:
|
|
246
|
+
padstack_instance["backdrill_parameters"] = self.backdrill_parameters
|
|
247
|
+
|
|
248
|
+
cfg["modeler"]["padstack_instances"].append(padstack_instance)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class Via(GroundVia):
|
|
252
|
+
def __init__(
|
|
253
|
+
self, anti_pad_diameter, fanout_trace: list[Union[dict, Trace]], stitching_vias: Union[dict, None], **kwargs
|
|
254
|
+
):
|
|
255
|
+
super().__init__(**kwargs)
|
|
256
|
+
|
|
257
|
+
self.anti_pad_diameter = create_variable(self, "anti_pad_diameter", anti_pad_diameter)
|
|
258
|
+
for t in fanout_trace:
|
|
259
|
+
layer = t["layer"]
|
|
260
|
+
|
|
261
|
+
incremental_path = copy([[self.x, self.y]])
|
|
262
|
+
incremental_path.extend(t["incremental_path"])
|
|
263
|
+
t_flip_dx = t["flip_dx"]
|
|
264
|
+
t_flip_dy = t["flip_dy"]
|
|
265
|
+
|
|
266
|
+
trace = Trace(
|
|
267
|
+
p_via=self,
|
|
268
|
+
name=f"{self.net_name}_{layer}_fanout",
|
|
269
|
+
net_name=self.net_name,
|
|
270
|
+
layer=layer,
|
|
271
|
+
width=t["width"],
|
|
272
|
+
clearance=t["clearance"],
|
|
273
|
+
incremental_path=incremental_path,
|
|
274
|
+
flip_dx=self.flip_dx ^ t_flip_dx,
|
|
275
|
+
flip_dy=self.flip_dy ^ t_flip_dy,
|
|
276
|
+
end_cap_style=t["end_cap_style"],
|
|
277
|
+
port=t["port"],
|
|
278
|
+
)
|
|
279
|
+
self.fanout_traces.append(trace)
|
|
280
|
+
self.stitching_vias = StitchingVias(self, **stitching_vias) if stitching_vias is not False else False
|
|
281
|
+
|
|
282
|
+
def populate_config(self, cfg):
|
|
283
|
+
super().populate_config(cfg)
|
|
284
|
+
if self.start_layer == self.stop_layer:
|
|
285
|
+
anti_pad = {
|
|
286
|
+
"type": "circle",
|
|
287
|
+
"name": f"{self.name}_anti_pad_{self.start_layer}",
|
|
288
|
+
"layer": self.start_layer,
|
|
289
|
+
"net_name": self.net_name,
|
|
290
|
+
"position": [self.x, self.y],
|
|
291
|
+
"radius": f"{self.anti_pad_diameter}/2",
|
|
292
|
+
}
|
|
293
|
+
cfg["modeler"]["planes"].append(anti_pad)
|
|
294
|
+
# self.voids.append(anti_pad)
|
|
295
|
+
self.p_signal.p_board.voids.append(anti_pad)
|
|
296
|
+
else:
|
|
297
|
+
start_layer_idx = self.conductor_layers.index(self.start_layer)
|
|
298
|
+
stop_layer_idx = self.conductor_layers.index(self.stop_layer)
|
|
299
|
+
for i in np.arange(start_layer_idx, stop_layer_idx + 1):
|
|
300
|
+
anti_pad = {
|
|
301
|
+
"type": "circle",
|
|
302
|
+
"name": f"{self.name}_anti_pad_{self.conductor_layers[i]}",
|
|
303
|
+
"layer": self.conductor_layers[i],
|
|
304
|
+
"net_name": self.net_name,
|
|
305
|
+
"position": [self.x, self.y],
|
|
306
|
+
"radius": f"{self.anti_pad_diameter}/2",
|
|
307
|
+
}
|
|
308
|
+
cfg["modeler"]["planes"].append(anti_pad)
|
|
309
|
+
# self.voids.append(anti_pad)
|
|
310
|
+
self.p_signal.p_board.voids.append(anti_pad)
|
|
311
|
+
|
|
312
|
+
if self.stitching_vias is not False:
|
|
313
|
+
self.stitching_vias.populate_config(cfg)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class Signal:
|
|
317
|
+
"""vias and traces."""
|
|
318
|
+
|
|
319
|
+
def __init__(
|
|
320
|
+
self,
|
|
321
|
+
p_board,
|
|
322
|
+
signal_name,
|
|
323
|
+
name_suffix: Union[None, str],
|
|
324
|
+
base_x,
|
|
325
|
+
base_y,
|
|
326
|
+
stacked_vias,
|
|
327
|
+
flip_x,
|
|
328
|
+
flip_y,
|
|
329
|
+
):
|
|
330
|
+
self.p_board = p_board
|
|
331
|
+
self.net_name = signal_name if name_suffix is None else f"{signal_name}_{name_suffix}"
|
|
332
|
+
self.name_suffix = name_suffix
|
|
333
|
+
self.base_x = base_x
|
|
334
|
+
self.base_y = base_y
|
|
335
|
+
|
|
336
|
+
self.vias = []
|
|
337
|
+
x = self.base_x
|
|
338
|
+
y = self.base_y
|
|
339
|
+
for v_idx, i in enumerate(stacked_vias):
|
|
340
|
+
dx = i["dx"]
|
|
341
|
+
dy = i["dy"]
|
|
342
|
+
|
|
343
|
+
connection_trace = i["connection_trace"]
|
|
344
|
+
start_layer = i["start_layer"]
|
|
345
|
+
stop_layer = i["stop_layer"]
|
|
346
|
+
|
|
347
|
+
flip_x_1 = not i["flip_dx"] if flip_x else i["flip_dx"]
|
|
348
|
+
flip_y_1 = not i["flip_dy"] if flip_y else i["flip_dy"]
|
|
349
|
+
if i["padstack_def"].startswith("BGA"):
|
|
350
|
+
flip_x_1 = False
|
|
351
|
+
flip_y_1 = False
|
|
352
|
+
|
|
353
|
+
if self.net_name.startswith("GND"):
|
|
354
|
+
via_class = GroundVia
|
|
355
|
+
name = f"{self.net_name}_{start_layer}_{stop_layer}_{v_idx}"
|
|
356
|
+
net_name = "GND"
|
|
357
|
+
else:
|
|
358
|
+
via_class = Via
|
|
359
|
+
name = f"{self.net_name}_{start_layer}_{stop_layer}"
|
|
360
|
+
net_name = self.net_name
|
|
361
|
+
|
|
362
|
+
via = via_class(
|
|
363
|
+
p_signal=self,
|
|
364
|
+
name=name,
|
|
365
|
+
net_name=net_name,
|
|
366
|
+
padstack_def=i["padstack_def"],
|
|
367
|
+
start_layer=start_layer,
|
|
368
|
+
stop_layer=stop_layer,
|
|
369
|
+
base_x=x,
|
|
370
|
+
base_y=y,
|
|
371
|
+
dx=dx,
|
|
372
|
+
dy=dy,
|
|
373
|
+
flip_dx=flip_x_1,
|
|
374
|
+
flip_dy=flip_y_1,
|
|
375
|
+
connection_trace=connection_trace,
|
|
376
|
+
with_solder_ball=i["with_solder_ball"],
|
|
377
|
+
backdrill_parameters=i["backdrill_parameters"],
|
|
378
|
+
conductor_layers=self.p_board.conductor_layers,
|
|
379
|
+
stitching_vias=i["stitching_vias"],
|
|
380
|
+
anti_pad_diameter=i["anti_pad_diameter"],
|
|
381
|
+
fanout_trace=i.get("fanout_trace", []),
|
|
382
|
+
)
|
|
383
|
+
x = via.x
|
|
384
|
+
y = via.y
|
|
385
|
+
self.vias.append(via)
|
|
386
|
+
|
|
387
|
+
def populate_config(self, cfg_modeler):
|
|
388
|
+
for i in self.vias:
|
|
389
|
+
i.populate_config(cfg_modeler)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class DiffSignal:
|
|
393
|
+
def __init__(self, p_board, name, signals, fanout_trace, stacked_vias):
|
|
394
|
+
self.p_board = p_board
|
|
395
|
+
self.name = name
|
|
396
|
+
self.signal_p_name, self.signal_n_name = signals
|
|
397
|
+
self.fanout_trace = fanout_trace
|
|
398
|
+
self.stacked_vias = stacked_vias
|
|
399
|
+
for i in self.stacked_vias:
|
|
400
|
+
i["fanout_trace"] = []
|
|
401
|
+
|
|
402
|
+
self.variables = []
|
|
403
|
+
# self.voids = []
|
|
404
|
+
self.diff_ports = []
|
|
405
|
+
|
|
406
|
+
p_x, p_y = self.p_board.get_signal_location(self.signal_p_name)[0]
|
|
407
|
+
n_x, n_y = self.p_board.get_signal_location(self.signal_n_name)[0]
|
|
408
|
+
p_x = f"{p_x}*pitch"
|
|
409
|
+
p_y = f"{p_y}*pitch"
|
|
410
|
+
n_x = f"{n_x}*pitch"
|
|
411
|
+
n_y = f"{n_y}*pitch"
|
|
412
|
+
|
|
413
|
+
vars_sep = {}
|
|
414
|
+
for trace in self.fanout_trace:
|
|
415
|
+
via_index = trace["via_index"]
|
|
416
|
+
trace2 = dict()
|
|
417
|
+
trace2["layer"] = trace["layer"]
|
|
418
|
+
trace2["width"] = trace["width"]
|
|
419
|
+
trace2["clearance"] = trace["clearance"]
|
|
420
|
+
trace2["flip_dx"] = trace["flip_dx"]
|
|
421
|
+
trace2["flip_dy"] = trace["flip_dy"]
|
|
422
|
+
trace2["end_cap_style"] = trace["end_cap_style"]
|
|
423
|
+
trace2["port"] = trace["port"]
|
|
424
|
+
|
|
425
|
+
incremental_path_dy = trace["incremental_path_dy"]
|
|
426
|
+
incremental_path = [[0, incremental_path_dy[0]], [0, incremental_path_dy[1]]]
|
|
427
|
+
trace2["incremental_path"] = incremental_path
|
|
428
|
+
|
|
429
|
+
self.stacked_vias[via_index]["fanout_trace"].append(trace2)
|
|
430
|
+
|
|
431
|
+
var_separation = f"{self.name}_{trace['layer']}_fanout_separation"
|
|
432
|
+
self.variables.append(
|
|
433
|
+
{"name": var_separation, "value": trace["separation"]},
|
|
434
|
+
)
|
|
435
|
+
vars_sep[trace["layer"]] = var_separation
|
|
436
|
+
|
|
437
|
+
stacked_vias_reversed = list(reversed(stacked_vias))
|
|
438
|
+
|
|
439
|
+
pcb_fanout_center = f"{p_x}+pitch/2"
|
|
440
|
+
pkg_fanout_center = f"{p_x}+pitch"
|
|
441
|
+
# fanout_x = f"{diff_center}-({var_separation})/2"
|
|
442
|
+
|
|
443
|
+
self.signal_p = Signal(
|
|
444
|
+
p_board=self.p_board,
|
|
445
|
+
signal_name=self.name,
|
|
446
|
+
name_suffix="P",
|
|
447
|
+
base_x=p_x,
|
|
448
|
+
base_y=p_y,
|
|
449
|
+
stacked_vias=stacked_vias_reversed,
|
|
450
|
+
flip_x=False,
|
|
451
|
+
flip_y=False,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
for v in self.signal_p.vias:
|
|
455
|
+
for t in v.fanout_traces:
|
|
456
|
+
var_sep = vars_sep[t.layer]
|
|
457
|
+
if t.layer.startswith("PCB"):
|
|
458
|
+
t.path[1][0] = f"{pcb_fanout_center}-{var_sep}"
|
|
459
|
+
t.path[2][0] = f"{pcb_fanout_center}-{var_sep}"
|
|
460
|
+
else:
|
|
461
|
+
t.path[1][0] = f"{pkg_fanout_center}-{var_sep}"
|
|
462
|
+
t.path[2][0] = f"{pkg_fanout_center}-{var_sep}"
|
|
463
|
+
|
|
464
|
+
self.signal_n = Signal(
|
|
465
|
+
p_board=self.p_board,
|
|
466
|
+
signal_name=self.name,
|
|
467
|
+
name_suffix="N",
|
|
468
|
+
base_x=n_x,
|
|
469
|
+
base_y=n_y,
|
|
470
|
+
stacked_vias=stacked_vias_reversed,
|
|
471
|
+
flip_x=True,
|
|
472
|
+
flip_y=False,
|
|
473
|
+
)
|
|
474
|
+
for v in self.signal_n.vias:
|
|
475
|
+
for t in v.fanout_traces:
|
|
476
|
+
var_sep = vars_sep[t.layer]
|
|
477
|
+
if t.layer.startswith("PCB"):
|
|
478
|
+
t.path[1][0] = f"{pcb_fanout_center}+{var_sep}"
|
|
479
|
+
t.path[2][0] = f"{pcb_fanout_center}+{var_sep}"
|
|
480
|
+
else:
|
|
481
|
+
t.path[1][0] = f"{pkg_fanout_center}+{var_sep}"
|
|
482
|
+
t.path[2][0] = f"{pkg_fanout_center}+{var_sep}"
|
|
483
|
+
|
|
484
|
+
for v_idx, v in enumerate(self.signal_p.vias):
|
|
485
|
+
for t_idx, t_p in enumerate(v.fanout_traces):
|
|
486
|
+
port_p = t_p.get_port_cfg()
|
|
487
|
+
t_p.port = None
|
|
488
|
+
t_n = self.signal_n.vias[v_idx].fanout_traces[t_idx]
|
|
489
|
+
port_n = t_n.get_port_cfg()
|
|
490
|
+
t_n.port = None
|
|
491
|
+
pattern = r"^(.*)_([NPnp])_(.*)$"
|
|
492
|
+
m1 = re.match(pattern, port_p["name"])
|
|
493
|
+
|
|
494
|
+
diff_port = {
|
|
495
|
+
"name": f"{m1.group(1)}_{m1.group(3)}",
|
|
496
|
+
"type": "diff_wave_port",
|
|
497
|
+
"positive_terminal": {
|
|
498
|
+
"primitive_name": port_p["primitive_name"],
|
|
499
|
+
"point_on_edge": port_p["point_on_edge"],
|
|
500
|
+
},
|
|
501
|
+
"negative_terminal": {
|
|
502
|
+
"primitive_name": port_n["primitive_name"],
|
|
503
|
+
"point_on_edge": port_n["point_on_edge"],
|
|
504
|
+
},
|
|
505
|
+
"horizontal_extent_factor": port_p["horizontal_extent_factor"],
|
|
506
|
+
"vertical_extent_factor": port_n["vertical_extent_factor"],
|
|
507
|
+
"pec_launch_width": "0.02mm",
|
|
508
|
+
}
|
|
509
|
+
self.diff_ports.append(diff_port)
|
|
510
|
+
|
|
511
|
+
def populate_config(self, cfg):
|
|
512
|
+
cfg["variables"].extend(self.variables)
|
|
513
|
+
self.signal_p.populate_config(cfg)
|
|
514
|
+
# self.voids.extend(self.signal_p.voids)
|
|
515
|
+
self.signal_n.populate_config(cfg)
|
|
516
|
+
# self.voids.extend(self.signal_n.voids)
|
|
517
|
+
cfg["ports"].extend(self.diff_ports)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class Board:
|
|
521
|
+
@property
|
|
522
|
+
def conductor_layers(self):
|
|
523
|
+
return [i["name"] for i in self.stackup if i["type"] == "signal"]
|
|
524
|
+
|
|
525
|
+
def __init__(self, stackup, padstack_defs, outline_extent, pitch, pin_map, signals, differential_signals):
|
|
526
|
+
self.voids = []
|
|
527
|
+
self.variables = [{"name": "pitch", "value": pitch, "description": ""}]
|
|
528
|
+
|
|
529
|
+
self.stackup = stackup
|
|
530
|
+
self.padstack_defs = padstack_defs
|
|
531
|
+
self.outline_extent = outline_extent
|
|
532
|
+
|
|
533
|
+
self.pin_map = pin_map
|
|
534
|
+
self.signals = self.parser_signals(signals) if signals is not False else []
|
|
535
|
+
self.differential_signals = (
|
|
536
|
+
self.parser_differential_signals(differential_signals) if differential_signals is not False else []
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
def get_signal_location(self, signal_name):
|
|
540
|
+
pin_map = pd.DataFrame(self.pin_map)
|
|
541
|
+
temp = (pin_map == signal_name).stack()
|
|
542
|
+
xy = [[i[1], i[0]] for i in temp[temp].index.tolist()]
|
|
543
|
+
return xy
|
|
544
|
+
|
|
545
|
+
def parser_signals(self, data):
|
|
546
|
+
signals = []
|
|
547
|
+
|
|
548
|
+
for name, signal_data in data.items():
|
|
549
|
+
fanout = signal_data["fanout_trace"]
|
|
550
|
+
stacked_vias = signal_data["stacked_vias"]
|
|
551
|
+
for f in fanout:
|
|
552
|
+
idx = f["via_index"]
|
|
553
|
+
stacked_vias[idx]["fanout_trace"].append(f)
|
|
554
|
+
|
|
555
|
+
stacked_vias_reversed = list(reversed(stacked_vias))
|
|
556
|
+
for x, y in self.get_signal_location(name):
|
|
557
|
+
s = Signal(
|
|
558
|
+
p_board=self,
|
|
559
|
+
signal_name=name if name != "GND" else f"{name}_{x}{y}",
|
|
560
|
+
name_suffix=None,
|
|
561
|
+
base_x=f"{x}*pitch",
|
|
562
|
+
base_y=f"{y}*pitch",
|
|
563
|
+
stacked_vias=stacked_vias_reversed,
|
|
564
|
+
flip_x=False,
|
|
565
|
+
flip_y=False,
|
|
566
|
+
)
|
|
567
|
+
signals.append(s)
|
|
568
|
+
return signals
|
|
569
|
+
|
|
570
|
+
def parser_differential_signals(self, data):
|
|
571
|
+
diff_signals = []
|
|
572
|
+
for name, temp in data.items():
|
|
573
|
+
signals = temp["signals"]
|
|
574
|
+
fanout_trace = temp["fanout_trace"]
|
|
575
|
+
stacked_vias = temp["stacked_vias"]
|
|
576
|
+
diff_signal = DiffSignal(self, name, signals, fanout_trace, stacked_vias)
|
|
577
|
+
diff_signals.append(diff_signal)
|
|
578
|
+
return diff_signals
|
|
579
|
+
|
|
580
|
+
def populate_config(self, cfg):
|
|
581
|
+
cfg["variables"].extend(self.variables)
|
|
582
|
+
|
|
583
|
+
cfg["stackup"]["layers"] = self.stackup
|
|
584
|
+
for p in self.padstack_defs:
|
|
585
|
+
regular_pad = []
|
|
586
|
+
for layer in self.conductor_layers:
|
|
587
|
+
regular_pad.append(
|
|
588
|
+
{
|
|
589
|
+
"layer_name": layer,
|
|
590
|
+
"shape": "circle",
|
|
591
|
+
"diameter": p["pad_diameter"],
|
|
592
|
+
}
|
|
593
|
+
)
|
|
594
|
+
pdef = copy(p)
|
|
595
|
+
pdef["material"] = "copper"
|
|
596
|
+
pdef["hole_range"] = "upper_pad_to_lower_pad"
|
|
597
|
+
pdef["pad_parameters"] = {"regular_pad": regular_pad}
|
|
598
|
+
pdef["hole_parameters"] = {
|
|
599
|
+
"shape": "circle",
|
|
600
|
+
"diameter": p["hole_diameter"],
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
cfg["modeler"]["padstack_definitions"].append(pdef)
|
|
604
|
+
|
|
605
|
+
# voids = []
|
|
606
|
+
for signal in self.signals:
|
|
607
|
+
signal.populate_config(cfg)
|
|
608
|
+
# voids.extend(signal.voids)
|
|
609
|
+
|
|
610
|
+
for diff_signal in self.differential_signals:
|
|
611
|
+
diff_signal.populate_config(cfg)
|
|
612
|
+
# voids.extend(diff_signal.voids)
|
|
613
|
+
|
|
614
|
+
matrix = np.array(self.pin_map)
|
|
615
|
+
y_size_count, x_size_count = matrix.shape
|
|
616
|
+
x_lower_left = f"-1*({self.outline_extent})"
|
|
617
|
+
x_upper_right = f"({self.outline_extent})+({x_size_count}-1)*pitch"
|
|
618
|
+
y_lower_left = f"-1*({self.outline_extent})"
|
|
619
|
+
y_upper_right = f"({self.outline_extent})+({y_size_count}-1)*pitch"
|
|
620
|
+
for l in self.conductor_layers:
|
|
621
|
+
p = {
|
|
622
|
+
"type": "rectangle",
|
|
623
|
+
"name": f"GND_{l}",
|
|
624
|
+
"layer": l,
|
|
625
|
+
"net_name": "GND",
|
|
626
|
+
"lower_left_point": [x_lower_left, y_lower_left],
|
|
627
|
+
"upper_right_point": [x_upper_right, y_upper_right],
|
|
628
|
+
"voids": [],
|
|
629
|
+
}
|
|
630
|
+
for v in self.voids:
|
|
631
|
+
if v["layer"] == l:
|
|
632
|
+
p["voids"].append(v["name"])
|
|
633
|
+
cfg["modeler"]["planes"].append(p)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class ViaDesignBackend:
|
|
637
|
+
_OUTPUT_DIR = None
|
|
638
|
+
|
|
639
|
+
@property
|
|
640
|
+
def output_dir(self):
|
|
641
|
+
if self._OUTPUT_DIR is None:
|
|
642
|
+
output_dir = self.cfg["general"]["output_dir"]
|
|
643
|
+
if output_dir == "":
|
|
644
|
+
self._OUTPUT_DIR = Path(tempfile.TemporaryDirectory(suffix=".ansys").name)
|
|
645
|
+
else:
|
|
646
|
+
self._OUTPUT_DIR = Path(output_dir)
|
|
647
|
+
return self._OUTPUT_DIR
|
|
648
|
+
|
|
649
|
+
def __init__(self, cfg):
|
|
650
|
+
cfg_json = {
|
|
651
|
+
"stackup": {"layers": [], "materials": []},
|
|
652
|
+
"variables": [],
|
|
653
|
+
"ports": [],
|
|
654
|
+
"modeler": {"traces": [], "planes": [], "padstack_definitions": [], "padstack_instances": []},
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if isinstance(cfg, str):
|
|
658
|
+
self.cfg = toml.load(cfg) if cfg.endswith(".toml") else json.load(cfg)
|
|
659
|
+
else:
|
|
660
|
+
self.cfg = cfg
|
|
661
|
+
self.version = self.cfg["general"]["version"]
|
|
662
|
+
outline_extent = self.cfg["general"]["outline_extent"]
|
|
663
|
+
pitch = self.cfg["general"]["pitch"]
|
|
664
|
+
|
|
665
|
+
board = Board(
|
|
666
|
+
stackup=self.cfg["stackup"],
|
|
667
|
+
padstack_defs=self.cfg["padstack_defs"],
|
|
668
|
+
outline_extent=outline_extent,
|
|
669
|
+
pitch=pitch,
|
|
670
|
+
pin_map=self.cfg["pin_map"],
|
|
671
|
+
signals=self.cfg["signals"],
|
|
672
|
+
differential_signals=self.cfg["differential_signals"],
|
|
673
|
+
)
|
|
674
|
+
board.populate_config(cfg_json)
|
|
675
|
+
|
|
676
|
+
self.app = Edb(
|
|
677
|
+
edbpath=str((Path(self.output_dir) / self.cfg["title"]).with_suffix(".aedb")), edbversion=self.version
|
|
678
|
+
)
|
|
679
|
+
self.app.configuration.load(cfg_json, apply_file=True)
|
|
680
|
+
self.app.save_edb()
|
|
681
|
+
self.app.close_edb()
|