pyedb 0.49.0__py3-none-any.whl → 0.50.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.

Files changed (31) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_modeler.py +42 -11
  3. pyedb/configuration/cfg_ports_sources.py +9 -1
  4. pyedb/dotnet/database/cell/hierarchy/component.py +3 -3
  5. pyedb/dotnet/database/components.py +3 -3
  6. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  7. pyedb/dotnet/database/edb_data/variables.py +3 -3
  8. pyedb/dotnet/database/materials.py +16 -16
  9. pyedb/dotnet/database/modeler.py +38 -4
  10. pyedb/dotnet/database/sim_setup_data/data/settings.py +28 -0
  11. pyedb/dotnet/database/stackup.py +1 -0
  12. pyedb/dotnet/database/utilities/hfss_simulation_setup.py +5 -6
  13. pyedb/dotnet/edb.py +22 -20
  14. pyedb/extensions/__init__.py +0 -0
  15. pyedb/extensions/via_design_backend.py +681 -0
  16. pyedb/grpc/database/components.py +57 -48
  17. pyedb/grpc/database/definition/materials.py +33 -33
  18. pyedb/grpc/database/definitions.py +6 -4
  19. pyedb/grpc/database/hfss.py +2 -2
  20. pyedb/grpc/database/hierarchy/component.py +3 -2
  21. pyedb/grpc/database/layers/stackup_layer.py +119 -22
  22. pyedb/grpc/database/layout_validation.py +5 -5
  23. pyedb/grpc/database/primitive/path.py +1 -1
  24. pyedb/grpc/database/source_excitations.py +156 -0
  25. pyedb/grpc/database/stackup.py +50 -24
  26. pyedb/grpc/edb.py +115 -105
  27. {pyedb-0.49.0.dist-info → pyedb-0.50.0.dist-info}/METADATA +1 -1
  28. {pyedb-0.49.0.dist-info → pyedb-0.50.0.dist-info}/RECORD +30 -29
  29. pyedb/extensions/pre_layout_design_toolkit/via_design.py +0 -1151
  30. {pyedb-0.49.0.dist-info → pyedb-0.50.0.dist-info}/LICENSE +0 -0
  31. {pyedb-0.49.0.dist-info → pyedb-0.50.0.dist-info}/WHEEL +0 -0
@@ -1,1151 +0,0 @@
1
- import json
2
- from pathlib import Path
3
-
4
- import numpy as np
5
- import pandas as pd
6
-
7
- from pyedb import Edb
8
- from pyedb.generic.general_methods import generate_unique_name
9
-
10
-
11
- class Pair:
12
- def __init__(self, name, net_names, **kwargs):
13
- self.design_type = ""
14
- self.name = name
15
- self.net_names = net_names
16
- self.core_pdef = kwargs.get("core_pdef", "blind_via")
17
- self.has_stitching_vias = kwargs.get("has_stitching_vias", False)
18
-
19
- self.bga_locations = []
20
- self.vias = None
21
- self.trace_out = []
22
-
23
- self.fanout_port_location = []
24
-
25
- self.p_via_loc = [] # [[via1 x, y], [via2 x, u]]
26
- self.n_via_loc = [] # [[via1 x, y], [via2 x, u]]
27
-
28
- self.via_to_via_traces = []
29
-
30
- self.pkg_trace_out_lower_port_location = None
31
- self.pkg_trace_out_upper_port_location = None
32
- self.pcb_trace_out_lower_port_location = None
33
- self.pcb_trace_out_upper_port_location = None
34
-
35
- @property
36
- def race_track_path_list(self):
37
- temp = []
38
- start_stop_layer = []
39
- for v in self.vias:
40
- start_stop_layer.append([v["padstack_definition"], v["start_layer"], v["stop_layer"]])
41
- temp.append(list(zip(start_stop_layer, self.p_via_loc, self.n_via_loc)))
42
- # ['blind_via', 'L2', 'L3', is_core_via], [x_loc, y_loc], [x_loc, y_loc]
43
- return temp
44
-
45
- @property
46
- def core_via_start_layer(self):
47
- start_layer = [i["start_layer"] for i in self.vias if i["padstack_definition"] == self.core_pdef][0]
48
- return start_layer
49
-
50
- @property
51
- def core_via_stop_layer(self):
52
- stop_layer = [i["stop_layer"] for i in self.vias if i["padstack_definition"] == self.core_pdef][0]
53
- return stop_layer
54
-
55
- @property
56
- def bga_pin_location(self):
57
- return [[f"{loc[1]}*pitch", f"{loc[0]}*pitch"] for loc in self.bga_locations]
58
-
59
- @property
60
- def fanout_via_location_outer(self):
61
- return [self.p_via_loc[-1], self.n_via_loc[-1]] # [[p_x, p_y], [n_x, n_y]]
62
-
63
- @property
64
- def fanout_via_location_inner(self):
65
- return [self.p_via_loc[0], self.n_via_loc[0]] # [[p_x, p_y], [n_x, n_y]]
66
-
67
- @property
68
- def pkg_trace_out_lower_params(self):
69
- if len(self.trace_out) == 2:
70
- temp = self.trace_out[1].copy()
71
- else:
72
- return
73
-
74
- temp["name"] = self.name
75
- temp["net_names"] = self.net_names
76
- temp["fanout_via_location"] = self.fanout_via_location_inner
77
- temp["base_via_loc"] = self.bga_locations
78
- temp["port_location"] = self.pkg_trace_out_lower_port_location
79
- return temp
80
-
81
- @property
82
- def pkg_trace_out_upper_params(self):
83
- temp = self.trace_out[0].copy()
84
- temp["name"] = self.name
85
- temp["net_names"] = self.net_names
86
- temp["fanout_via_location"] = self.fanout_via_location_outer
87
- temp["base_via_loc"] = self.bga_locations
88
- temp["port_location"] = self.pkg_trace_out_upper_port_location
89
- return temp
90
-
91
- @property
92
- def pcb_trace_out_lower_params(self):
93
- if len(self.trace_out) == 2:
94
- temp = self.trace_out[1].copy()
95
- else:
96
- temp = self.trace_out[0].copy()
97
-
98
- temp["name"] = self.name
99
- temp["net_names"] = self.net_names
100
- temp["fanout_via_location"] = self.fanout_via_location_outer
101
- temp["base_via_loc"] = self.fanout_via_location_outer
102
- temp["port_location"] = self.pcb_trace_out_lower_port_location
103
- return temp
104
-
105
- @property
106
- def pcb_trace_out_upper_params(self):
107
- if len(self.trace_out) == 2:
108
- temp = self.trace_out[0].copy()
109
- else:
110
- return
111
- temp["name"] = self.name
112
- temp["net_names"] = self.net_names
113
- temp["fanout_via_location"] = self.fanout_via_location_inner
114
- temp["base_via_loc"] = self.fanout_via_location_inner
115
- temp["port_location"] = self.pcb_trace_out_upper_port_location
116
- return temp
117
-
118
-
119
- class GroundVia:
120
- def __init__(self):
121
- self.bga_locations = []
122
- self.pattern_1_params = None # {"distance": "0.2mm", "core_via_start_layer": "L6", "core_via_stop_layer": "L7"}
123
-
124
-
125
- class ViaDesignConfig:
126
- @property
127
- def plane_size(self):
128
- y_size = len(self.pin_map["locations"])
129
- x_size = len(self.pin_map["locations"][0]) - 1
130
- return x_size, y_size
131
-
132
- @property
133
- def lower_left_point(self):
134
- return ["-plane_extend", "-plane_extend-pitch"]
135
-
136
- @property
137
- def upper_right_point(self):
138
- return [f"{self.plane_size[0]}*pitch+plane_extend", f"{self.plane_size[1]}*pitch+plane_extend"]
139
-
140
- def __init__(self, config_file: Path, version=""):
141
- self._version = version
142
- self._design_name = ""
143
- self._working_dir = ""
144
- self.pkg_stackup = []
145
- self.pcb_stackup = []
146
-
147
- self._variables = []
148
- self._padstack_instances = []
149
- self._padstack_definition = []
150
- self._traces = []
151
- self._planes = []
152
- self._voids = []
153
- self._components = []
154
- self._ports = []
155
-
156
- if isinstance(config_file, str):
157
- config_file = Path(config_file)
158
- self.config_file_path = config_file
159
-
160
- with open(config_file, "r") as f:
161
- self.config = json.load(f)
162
-
163
- with open(config_file.parent / self.config["materials"], "r") as f:
164
- self.materials = json.load(f)
165
-
166
- if self.design_type == "pkg":
167
- with open(config_file.parent / self.config["pkg_stackup"], "r") as f:
168
- self.pkg_stackup = json.load(f)
169
-
170
- if self.include_pcb or self.design_type == "pcb":
171
- with open(config_file.parent / self.config["pcb_stackup"], "r") as f:
172
- self.pcb_stackup = json.load(f)
173
-
174
- with open(config_file.parent / self.config["padstacks"], "r") as f:
175
- self.padstacks = json.load(f)
176
-
177
- with open(config_file.parent / self.config["pin_map"], "r") as f:
178
- self.pin_map = json.load(f)
179
-
180
- with open(config_file.parent / self.config["technology"], "r") as f:
181
- self.technology = json.load(f)
182
-
183
- with open(config_file.parent / self.config["setup"], "r") as f:
184
- self.setup = json.load(f)
185
-
186
- if self.include_pcb:
187
- pdef_bga = [i for i in self.padstacks if i["name"] == "bga"][0]
188
- pdef_bga["solder_ball_parameters"] = {
189
- "shape": self.technology["bga_component"]["solder_ball_shape"],
190
- "diameter": self.technology["bga_component"]["solder_ball_diameter"],
191
- "mid_diameter": self.technology["bga_component"].get("solder_ball_mid_diameter"),
192
- "placement": "above_padstack",
193
- "material": "solder",
194
- }
195
-
196
- locations = pd.DataFrame(self.pin_map["locations"])
197
-
198
- self.pcb_via_pairs = []
199
- self.pkg_via_pairs = []
200
-
201
- for name, nets in self.pin_map["signal_pairs"].items():
202
- bga_locations = []
203
- for net_name in nets:
204
- bga_locations.append((locations == net_name).stack()[(locations == net_name).stack()].index.tolist()[0])
205
-
206
- tech = self.technology["signal_pair"][name]
207
- if self.design_type == "pkg":
208
- sp = Pair(name, nets)
209
- sp.design_type = "pkg"
210
- sp.vias = tech["pkg_signal_via"]
211
- sp.trace_out = tech["pkg_trace"]
212
- sp.bga_locations = bga_locations
213
- self.pkg_via_pairs.append(sp)
214
-
215
- if self.include_pcb or self.design_type == "pcb":
216
- sp = Pair(name, nets, core_pdef="pcb_via")
217
- sp.design_type = "pcb"
218
- sp.vias = tech["pcb_signal_via"]
219
- sp.trace_out = tech["pcb_trace"]
220
- sp.bga_locations = bga_locations
221
- sp.has_stitching_vias = tech.get("has_stitching_vias", False)
222
- self.pcb_via_pairs.append(sp)
223
-
224
- if self.design_type == "pkg":
225
- self.pkg_ground_vias = GroundVia()
226
- for i in (locations == "GND").stack()[(locations == "GND").stack()].index.tolist():
227
- self.pkg_ground_vias.bga_locations.append(i)
228
- self.pkg_ground_vias.pattern_1_params = self.technology["pkg_ground_via"]
229
-
230
- if self.design_type == "pcb" or self.include_pcb:
231
- self.pcb_ground_vias = GroundVia()
232
- for i in (locations == "GND").stack()[(locations == "GND").stack()].index.tolist():
233
- self.pcb_ground_vias.bga_locations.append(i)
234
- self.pcb_ground_vias.pattern_1_params = self.technology["pcb_ground_via"]
235
-
236
- def create_ports_on_bga(self):
237
- ports = []
238
- for obj in self.pcb_via_pairs + self.pkg_via_pairs:
239
- for idx, i in enumerate(obj.net_names):
240
- port_name = f"{obj.name}_p_coax_port" if idx == 0 else f"{obj.name}_n_coax_port"
241
- port = {
242
- "name": port_name,
243
- "reference_designator": "U1",
244
- "type": "coax",
245
- "positive_terminal": {"net": i},
246
- }
247
- ports.append(port)
248
- return ports
249
-
250
- @property
251
- def signal_layers(self) -> list:
252
- return [i["name"] for i in self.stackup if i["type"].lower() == "signal"]
253
-
254
- @property
255
- def signal_layers_pkg(self) -> list:
256
- return [i["name"] for i in self.pkg_stackup if i["type"].lower() == "signal"]
257
-
258
- @property
259
- def signal_layers_pcb(self) -> list:
260
- return [i["name"] for i in self.pcb_stackup if i["type"].lower() == "signal"]
261
-
262
- @property
263
- def pcb_signal_layer_list(self) -> list:
264
- return [i["name"] for i in self.pcb_stackup if i["type"].lower() == "signal"]
265
-
266
- @property
267
- def stackup(self):
268
- temp = []
269
- for layer in self.pkg_stackup:
270
- l2 = layer.copy()
271
- temp.append(l2)
272
-
273
- if self.pcb_stackup:
274
- temp.append(
275
- {
276
- "name": "PKG_PCB_AIR",
277
- "type": "dielectric",
278
- "material": "air",
279
- "thickness": self.technology["bga_component"]["solder_ball_height"],
280
- }
281
- )
282
- for layer in self.pcb_stackup:
283
- l2 = layer.copy()
284
- temp.append(l2)
285
- return temp
286
-
287
- @property
288
- def working_dir(self):
289
- if not self._working_dir:
290
- wdir = self.config.get("working_directory")
291
- if wdir is not None:
292
- self._working_dir = Path(wdir)
293
- else:
294
- working_dir = self.config_file_path.parent / "via_designs"
295
- working_dir.mkdir(parents=True, exist_ok=True)
296
- self._working_dir = working_dir
297
- return self._working_dir
298
-
299
- @property
300
- def design_name(self):
301
- if not self._design_name:
302
- self._design_name = Path(generate_unique_name(self.config_file_path.stem))
303
- return self._design_name
304
-
305
- @property
306
- def design_type(self):
307
- return self.config["design_type"].lower()
308
-
309
- @property
310
- def include_pcb(self):
311
- return self.config["include_pcb"] if self.design_type == "pkg" else None
312
-
313
- @property
314
- def version(self):
315
- if self._version:
316
- return self._version
317
- else:
318
- return self.config["version"]
319
-
320
- def _create_variables(self):
321
- self._variables.extend(
322
- [
323
- {"name": "plane_extend", "value": self.technology["plane_extend"], "description": "general"},
324
- {"name": "pitch", "value": self.technology["pitch"], "description": "general"},
325
- ]
326
- )
327
-
328
- if self.design_type == "pkg":
329
- for name, _ in self.pin_map["signal_pairs"].items():
330
- die_side_stitching_via_dy = self.technology["signal_pair"][name]["pkg_trace"][0]["stitching_via_dy"]
331
- self._variables.extend(
332
- [
333
- {
334
- "name": f"{name}_die_side_stitching_via_dy",
335
- "value": die_side_stitching_via_dy,
336
- "description": "general",
337
- },
338
- ]
339
- )
340
- elif self.include_pcb or self.design_type == "pcb":
341
- self._variables.extend(
342
- [
343
- {"name": "pcb_stitching_via_distance", "value": "1mm", "description": "general"},
344
- ]
345
- )
346
-
347
- else:
348
- pass
349
-
350
- def create_design(self):
351
- self._create_variables()
352
-
353
- new_vars, padstack_defs = self._create_padstack_defs()
354
- self._variables.extend(new_vars)
355
- self._padstack_definition.extend(padstack_defs)
356
-
357
- if self.design_type == "pkg":
358
- for df in self.pkg_via_pairs:
359
- self._create_signal_via_transition(df, design_type="pkg")
360
-
361
- self._create_return_via_pkg(df)
362
-
363
- port_location = self._create_signal_fanout_type_1(df.pkg_trace_out_upper_params, design_type="pkg")
364
- layer = df.pkg_trace_out_upper_params["layer"]
365
- name = df.pkg_trace_out_upper_params["name"]
366
- port = {
367
- "name": f"pkg_{name}_{layer}_wave_port",
368
- "type": "diff_wave_port",
369
- "positive_terminal": {"primitive_name": port_location[0][0], "point_on_edge": port_location[0][1]},
370
- "negative_terminal": {"primitive_name": port_location[1][0], "point_on_edge": port_location[1][1]},
371
- "horizontal_extent_factor": 6,
372
- "vertical_extent_factor": 4,
373
- "pec_launch_width": "0.02mm",
374
- }
375
- self._ports.append(port)
376
-
377
- df.pkg_trace_out_upper_port_location = port_location
378
-
379
- if df.pkg_trace_out_lower_params:
380
- port_location = self._create_signal_fanout_type_1(df.pkg_trace_out_lower_params, design_type="pkg")
381
- layer = df.pkg_trace_out_lower_params["layer"]
382
- name = df.pkg_trace_out_lower_params["name"]
383
- port = {
384
- "name": f"pkg_{name}_{layer}_wave_port",
385
- "type": "diff_wave_port",
386
- "positive_terminal": {
387
- "primitive_name": port_location[0][0],
388
- "point_on_edge": port_location[0][1],
389
- },
390
- "negative_terminal": {
391
- "primitive_name": port_location[1][0],
392
- "point_on_edge": port_location[1][1],
393
- },
394
- "horizontal_extent_factor": 6,
395
- "vertical_extent_factor": 4,
396
- "pec_launch_width": "0.02mm",
397
- }
398
- self._ports.append(port)
399
-
400
- self._create_race_track(df, design_type="pkg")
401
- # self._create_bga_component(placement_layer="PKG_BOT")
402
- self._create_gnd_vias(design_type="pkg")
403
-
404
- if self.include_pcb or self.design_type == "pcb":
405
- for df in self.pcb_via_pairs:
406
- self._create_signal_via_transition(df, design_type="pcb")
407
- if df.has_stitching_vias:
408
- self._create_return_via_pcb(df)
409
-
410
- port_location = self._create_signal_fanout_type_1(df.pcb_trace_out_lower_params, design_type="pcb")
411
- layer = df.pcb_trace_out_lower_params["layer"]
412
- name = df.pcb_trace_out_lower_params["name"]
413
- port = {
414
- "name": f"pkg_{name}_{layer}_wave_port",
415
- "type": "diff_wave_port",
416
- "positive_terminal": {"primitive_name": port_location[0][0], "point_on_edge": port_location[0][1]},
417
- "negative_terminal": {"primitive_name": port_location[1][0], "point_on_edge": port_location[1][1]},
418
- "horizontal_extent_factor": 6,
419
- "vertical_extent_factor": 4,
420
- "pec_launch_width": "0.02mm",
421
- }
422
- self._ports.append(port)
423
-
424
- if df.pcb_trace_out_upper_params:
425
- port_location = self._create_signal_fanout_type_1(df.pcb_trace_out_upper_params, design_type="pcb")
426
- layer = df.pcb_trace_out_upper_params["layer"]
427
- name = df.pcb_trace_out_upper_params["name"]
428
- port = {
429
- "name": f"pkg_{name}_{layer}_wave_port",
430
- "type": "diff_wave_port",
431
- "positive_terminal": {
432
- "primitive_name": port_location[0][0],
433
- "point_on_edge": port_location[0][1],
434
- },
435
- "negative_terminal": {
436
- "primitive_name": port_location[1][0],
437
- "point_on_edge": port_location[1][1],
438
- },
439
- "horizontal_extent_factor": 6,
440
- "vertical_extent_factor": 4,
441
- "pec_launch_width": "0.02mm",
442
- }
443
- self._ports.append(port)
444
- # self._create_bga_component(placement_layer="PCB_TOP")
445
- self._create_race_track(df, design_type="pcb")
446
- self._create_gnd_vias(design_type="pcb")
447
-
448
- bga_params = self.technology["bga_component"]
449
- if self.design_type == "pcb":
450
- if bga_params["enabled"] is True:
451
- self._create_bga_component(bga_params, placement_layer="PCB_TOP")
452
- elif self.design_type == "pkg":
453
- if self.include_pcb:
454
- self._create_solder_ball()
455
- else:
456
- if bga_params["enabled"] is True:
457
- self._create_bga_component(bga_params, placement_layer="PKG_BOT")
458
-
459
- planes = self._create_planes()
460
- self._planes.extend(planes)
461
-
462
- if not self.include_pcb:
463
- self._ports.extend(self.create_ports_on_bga())
464
-
465
- data = {"general": {"suppress_pads": True, "anti_pads_always_on": True}}
466
-
467
- stackup = {"materials": self.materials, "layers": self.stackup}
468
- data["stackup"] = stackup
469
- data["variables"] = self._variables
470
- data["ports"] = self._ports
471
- data["setups"] = [self.setup]
472
-
473
- modeler = {
474
- "padstack_definitions": self._padstack_definition,
475
- "padstack_instances": self._padstack_instances,
476
- "traces": self._traces,
477
- "planes": self._planes,
478
- "components": self._components,
479
- }
480
- data["modeler"] = modeler
481
- return data
482
-
483
- def _create_padstack_defs(self):
484
- new_variables = []
485
- cfg_padstacks = []
486
- for pad in self.padstacks:
487
- name = pad["name"]
488
-
489
- hole_diameter = pad.get("hole_diameter")
490
- if hole_diameter:
491
- var_hole_diameter = f"${name}_hole_diameter"
492
- new_variables.append({"name": var_hole_diameter, "value": hole_diameter, "description": "padstack"})
493
- hole_parameters = {"shape": "circle", "diameter": var_hole_diameter}
494
- else:
495
- hole_parameters = None
496
-
497
- pad_diameter = pad["pad_diameter"]
498
- var_pad_diameter = f"${name}_pad_diameter"
499
- new_variables.append({"name": var_pad_diameter, "value": pad_diameter, "description": "padstack"})
500
- shape = pad["shape"]
501
- if shape == "circle":
502
- regular_pad = []
503
- for layer in self.signal_layers:
504
- regular_pad.append(
505
- {
506
- "layer_name": layer,
507
- "shape": shape,
508
- "diameter": var_pad_diameter,
509
- }
510
- )
511
- else: # shape == "rectangle":
512
- x_size = pad["x_size"]
513
- y_size = pad["y_size"]
514
- var_pad_x_size = f"${name}_pad_x_size"
515
- var_pad_y_size = f"${name}_pad_y_size"
516
- new_variables.append({"name": var_pad_x_size, "value": x_size, "description": "padstack"})
517
- new_variables.append({"name": var_pad_y_size, "value": y_size, "description": "padstack"})
518
-
519
- regular_pad = []
520
- for layer in self.signal_layers:
521
- regular_pad.append(
522
- {
523
- "layer_name": layer,
524
- "shape": shape,
525
- "x_size": var_pad_x_size,
526
- "y_size": var_pad_y_size,
527
- }
528
- )
529
-
530
- anti_pad_diameter = pad["anti_pad_diameter"]
531
-
532
- # anti_pad = []
533
- for layer in self.signal_layers:
534
- if name in ["bga", "blind_via"]:
535
- var_anti_pad_diameter = f"${name}_anti_pad_diameter"
536
- new_variables.append(
537
- {"name": var_anti_pad_diameter, "value": anti_pad_diameter, "description": "layer={l}"}
538
- )
539
- break
540
- else:
541
- var_anti_pad_diameter = f"${name}_anti_pad_diameter_{layer}"
542
- new_variables.append(
543
- {"name": var_anti_pad_diameter, "value": anti_pad_diameter, "description": f"layer={layer}"}
544
- )
545
-
546
- pad_parameters = {
547
- "regular_pad": regular_pad,
548
- # "anti_pad": anti_pad
549
- }
550
-
551
- new_p_def = {"name": name, "pad_parameters": pad_parameters}
552
- if hole_diameter:
553
- new_p_def.update(
554
- {
555
- "hole_parameters": hole_parameters,
556
- "hole_range": pad["hole_range"],
557
- }
558
- )
559
-
560
- if pad.get("solder_ball_parameters"):
561
- new_p_def["solder_ball_parameters"] = pad["solder_ball_parameters"]
562
- cfg_padstacks.append(new_p_def)
563
- return new_variables, cfg_padstacks
564
-
565
- def _create_race_track(self, diff_pair: Pair, design_type):
566
- voids = []
567
- for i in diff_pair.race_track_path_list:
568
- for j in i:
569
- pdef, p1, p2 = j
570
- pdef_name, start_layer, stop_layer = pdef
571
- flag = False
572
- for layer in self.signal_layers:
573
- if layer == start_layer:
574
- flag = True
575
- if layer in ["PCB_TOP", "PKG_BOT"]:
576
- if layer == stop_layer:
577
- flag = False
578
- continue
579
- if flag:
580
- width = (
581
- f"${pdef_name}_anti_pad_diameter"
582
- if pdef_name == "blind_via"
583
- else f"${pdef_name}_anti_pad_diameter_{layer}"
584
- )
585
- trace = {
586
- "path": [p1, p2],
587
- "width": width,
588
- "layer": layer,
589
- "name": f"{pdef_name}_{layer}_race_track",
590
- "net_name": "GND",
591
- }
592
- void = trace.copy()
593
- void["void_type"] = "trace"
594
- voids.append(void)
595
-
596
- if layer == stop_layer:
597
- flag = False
598
-
599
- if design_type == "pkg":
600
- for layer in self.signal_layers_pkg:
601
- if layer in ["PCB_TOP", "PKG_BOT"]:
602
- continue
603
- trace = {
604
- "path": diff_pair.bga_pin_location,
605
- "width": "$bga_anti_pad_diameter",
606
- "layer": layer,
607
- "name": f"bga_{layer}_race_track",
608
- "net_name": "GND",
609
- "void_type": "trace",
610
- }
611
- voids.append(trace)
612
- self._voids.extend(voids)
613
- # self._traces.extend(voids)
614
- return voids
615
-
616
- def _create_return_via_pkg(self, diff_pair: Pair):
617
- pd_instances = []
618
- for pin_idx, pin_loc in enumerate(diff_pair.bga_pin_location):
619
- for idx, layer in enumerate(self.signal_layers_pkg):
620
- if layer == self.signal_layers_pkg[-1]:
621
- break
622
- pdef = "blind_via" if layer == diff_pair.core_via_start_layer else "micro_via"
623
- if not pin_idx % 2:
624
- init_angle = "pi*5/8" if idx % 2 else "pi*1/2"
625
- else:
626
- init_angle = "pi*13/8" if idx % 2 else "pi*3/2"
627
- distance = f"$bga_anti_pad_diameter/2+${pdef}_hole_diameter/2"
628
- via_list = np.arange(4) if idx % 2 else np.arange(5)
629
- for i in via_list:
630
- angle = f"{init_angle}+{i}*1/4*pi"
631
- x_loc = f"{pin_loc[0]}+cos({angle})*({distance})"
632
- y_loc = f"{pin_loc[1]}+sin({angle})*({distance})"
633
- pd_instances.append(
634
- {
635
- # "name": f"{diff_pair.name}_p_return_",
636
- "definition": pdef,
637
- "layer_range": [layer, self.signal_layers_pkg[idx + 1]],
638
- "net_name": "GND",
639
- "position": [x_loc, y_loc],
640
- "is_pin": False,
641
- }
642
- )
643
-
644
- for idx, loc in enumerate(diff_pair.fanout_via_location_outer):
645
- x_loc = loc[0]
646
- y_loc = f"{loc[1]}+{diff_pair.name}_die_side_stitching_via_dy/2"
647
- pd_instances.append(
648
- {
649
- # "name": f"{diff_pair.name}_p_return_",
650
- "definition": "micro_via",
651
- "layer_range": [
652
- diff_pair.pkg_trace_out_upper_params["layer"],
653
- self.signal_layers_pkg[
654
- self.signal_layers_pkg.index(diff_pair.pkg_trace_out_upper_params["layer"]) + 1
655
- ],
656
- ],
657
- "net_name": "GND",
658
- "position": [x_loc, y_loc],
659
- "is_pin": False,
660
- }
661
- )
662
- self._padstack_instances.extend(pd_instances)
663
- return pd_instances
664
-
665
- def _create_return_via_pcb(self, diff_pair: Pair):
666
- pd_instances = []
667
- pdef = diff_pair.core_pdef
668
- for pin_idx, pin_loc in enumerate(diff_pair.bga_pin_location):
669
- if not pin_idx % 2:
670
- init_angle = "pi*4/8"
671
- else:
672
- init_angle = "pi*3/2"
673
- distance = "pcb_stitching_via_distance"
674
- via_list = np.arange(4)
675
- for i in via_list:
676
- angle = f"{init_angle}+{i}*1/3*pi"
677
- x_loc = f"{pin_loc[0]}+cos({angle})*({distance})"
678
- y_loc = f"{pin_loc[1]}+sin({angle})*({distance})"
679
- pd_instances.append(
680
- {
681
- # "name": f"{diff_pair.name}_p_return_",
682
- "definition": pdef,
683
- "layer_range": [diff_pair.core_via_start_layer, diff_pair.core_via_stop_layer],
684
- "net_name": "GND",
685
- "position": [x_loc, y_loc],
686
- "is_pin": False,
687
- }
688
- )
689
- self._padstack_instances.extend(pd_instances)
690
-
691
- def _create_bga_component(self, params, placement_layer):
692
- p_instances = []
693
- bga_comp_pins = []
694
- for row, row_obj in enumerate(self.pin_map["locations"]):
695
- for col, obj in enumerate(row_obj):
696
- if obj is None:
697
- continue
698
- net_name = obj
699
- x_loc = f"{col}*pitch"
700
- y_loc = f"{row}*pitch"
701
- name = f"via_{row}_{col}" if net_name == "GND" else net_name
702
- name = f"{name}_bga"
703
-
704
- temp = {
705
- "name": name,
706
- "definition": "bga",
707
- "net_name": net_name,
708
- "position": [x_loc, y_loc],
709
- "layer_range": [placement_layer, placement_layer],
710
- "is_pin": True,
711
- }
712
- p_instances.append(temp)
713
- bga_comp_pins.append(name)
714
-
715
- solder_ball_property = {
716
- "shape": self.technology["bga_component"]["solder_ball_shape"],
717
- "diameter": self.technology["bga_component"]["solder_ball_diameter"],
718
- "mid_diameter": self.technology["bga_component"]["solder_ball_mid_diameter"],
719
- "height": self.technology["bga_component"]["solder_ball_height"],
720
- }
721
- comp = {
722
- "reference_designator": "U1",
723
- "pins": bga_comp_pins,
724
- "part_type": "io",
725
- "definition": "BGA",
726
- "placement_layer": placement_layer,
727
- "solder_ball_properties": solder_ball_property,
728
- "port_properties": {
729
- "reference_offset": "0mm",
730
- "reference_size_auto": True,
731
- "reference_size_x": 0,
732
- "reference_size_y": 0,
733
- },
734
- }
735
- self._components.append(comp)
736
- self._padstack_instances.extend(p_instances)
737
- return comp, p_instances
738
-
739
- def _create_solder_ball(self):
740
- p_instances = []
741
- bga_comp_pins = []
742
- for row, row_obj in enumerate(self.pin_map["locations"]):
743
- for col, obj in enumerate(row_obj):
744
- if obj is None:
745
- continue
746
- net_name = obj
747
- x_loc = f"{col}*pitch"
748
- y_loc = f"{row}*pitch"
749
- name = f"via_{row}_{col}" if net_name == "GND" else net_name
750
- pcb_name = f"{name}_bga"
751
-
752
- temp_pcb_side = {
753
- "name": pcb_name,
754
- "definition": "bga",
755
- "net_name": net_name,
756
- "position": [x_loc, y_loc],
757
- "layer_range": ["PCB_TOP", "PCB_TOP"],
758
- "is_pin": True,
759
- "solder_ball_layer": "PKG_BOT",
760
- }
761
- p_instances.append(temp_pcb_side)
762
- bga_comp_pins.append(pcb_name)
763
-
764
- temp_pkg_side = temp_pcb_side.copy()
765
- temp_pkg_side["name"] = f"{name}_pkg"
766
- temp_pkg_side["layer_range"] = ["PKG_BOT", "PKG_BOT"]
767
- temp_pkg_side.pop("solder_ball_layer")
768
- p_instances.append(temp_pkg_side)
769
-
770
- self._padstack_instances.extend(p_instances)
771
- return p_instances
772
-
773
- def _create_gnd_vias(self, design_type):
774
- variables = []
775
- traces = []
776
- voids = []
777
-
778
- ground_vias = self.pkg_ground_vias if design_type == "pkg" else self.pcb_ground_vias
779
- distance = f"{design_type}_ground_via_distance"
780
-
781
- if ground_vias.bga_locations:
782
- variables.append(
783
- {"name": distance, "value": ground_vias.pattern_1_params["distance"], "description": "ground_via"}
784
- )
785
- pd_insts = []
786
-
787
- for location in ground_vias.bga_locations:
788
- row, col = location
789
- via_name = f"{design_type}_via_{row}_{col}"
790
- if design_type == "pcb" and self.technology["bga_component"]["enabled"] is True:
791
- dx = self.technology["bga_component"]["fanout_dx"]
792
- dy = self.technology["bga_component"]["fanout_dy"]
793
- x_loc_0 = f"{col}*pitch"
794
- y_loc_0 = f"{row}*pitch"
795
- x_loc = f"{x_loc_0}+{dx}"
796
- y_loc = f"{y_loc_0}-{dy}"
797
-
798
- trace_name = f"bga_fanout_trace_via_{row}_{col}"
799
- trace_width = self.technology["bga_component"]["fanout_width"]
800
- trace = {
801
- "path": [[x_loc_0, y_loc_0], [x_loc, y_loc]],
802
- "width": trace_width,
803
- "name": trace_name,
804
- "layer": "PCB_TOP",
805
- "net_name": "GND",
806
- }
807
- traces.append(trace)
808
- else:
809
- x_loc = f"{col}*pitch"
810
- y_loc = f"{row}*pitch"
811
-
812
- pd = {
813
- "name": f"{design_type}_{via_name}_cvia_{row}_{col}",
814
- "definition": "blind_via" if design_type == "pkg" else "pcb_via",
815
- "layer_range": [
816
- ground_vias.pattern_1_params["core_via_start_layer"],
817
- ground_vias.pattern_1_params["core_via_stop_layer"],
818
- ],
819
- "net_name": "GND",
820
- "position": [x_loc, y_loc],
821
- "is_pin": False,
822
- }
823
- pd_insts.append(pd)
824
-
825
- if design_type == "pkg":
826
- flag = True
827
- for idx, layer in enumerate(self.signal_layers_pkg):
828
- if layer == ground_vias.pattern_1_params["core_via_start_layer"]:
829
- flag = False
830
- elif layer == ground_vias.pattern_1_params["core_via_stop_layer"]:
831
- flag = True
832
-
833
- if idx < len(self.signal_layers_pkg) - 1:
834
- if flag:
835
- start_layer = layer
836
- end_layer = self.signal_layers_pkg[idx + 1]
837
- else:
838
- start_layer = ground_vias.pattern_1_params["core_via_start_layer"]
839
- end_layer = ground_vias.pattern_1_params["core_via_stop_layer"]
840
- else:
841
- break
842
-
843
- if flag:
844
- angle = 0
845
- for micro_via_idx, i in enumerate(["pi*1/2"] * 4):
846
- angle = f"{angle} + {i}"
847
- guvia_1_x_loc = f"{x_loc}+cos({angle})*{distance}"
848
- guvia_1_y_loc = f"{y_loc}+sin({angle})*{distance}"
849
-
850
- uvia_pd = {
851
- "name": f"{design_type}_{via_name}_uvia_{start_layer}_{end_layer}_{micro_via_idx}",
852
- "definition": "micro_via",
853
- "net_name": "GND",
854
- "position": [guvia_1_x_loc, guvia_1_y_loc],
855
- "layer_range": [start_layer, end_layer],
856
- "is_pin": False,
857
- }
858
- pd_insts.append(uvia_pd)
859
-
860
- self._padstack_instances.extend(pd_insts)
861
- self._variables.extend(variables)
862
- self._traces.extend(traces)
863
- self._voids.extend(voids)
864
-
865
- return variables, traces, voids, pd_insts
866
-
867
- def _create_signal_via_transition(self, diff_pair: Pair, design_type): # pragma no cover
868
- trace_layer_name = "stop_layer" if design_type == "pkg" else "start_layer"
869
-
870
- fanout_polarity = "1" if design_type == "pkg" else "-1"
871
- variables = []
872
- pd_insts = []
873
- traces = []
874
- voids = []
875
-
876
- local_traces = []
877
- for net_idx, net_name in enumerate(diff_pair.net_names):
878
- if design_type == "pkg":
879
- pn_coef = 1 if net_idx == 0 else -1
880
- else:
881
- pn_coef = 1
882
- x_loc = f"{diff_pair.bga_locations[net_idx][1]}*pitch"
883
- y_loc = f"{diff_pair.bga_locations[net_idx][0]}*pitch"
884
-
885
- path = [[x_loc, y_loc]]
886
- for _, v in enumerate(diff_pair.vias):
887
- trace_layer = v[trace_layer_name]
888
- if v.get("trace", True): # whether to create trace
889
- dx = f"{diff_pair.name}_via_{trace_layer}_dx"
890
- if net_idx == 1:
891
- variables.append({"name": dx, "value": v["dx"]})
892
-
893
- dy = f"{diff_pair.name}_via_{trace_layer}_dy"
894
- if net_idx == 1:
895
- variables.append({"name": dy, "value": v["dy"]})
896
-
897
- x_loc = f"{x_loc}+{dx}*({pn_coef})"
898
- y_loc = f"{y_loc}+{dy}*{fanout_polarity}"
899
-
900
- last_point = [x_loc, y_loc]
901
- path.append(last_point)
902
- trace = v.copy()
903
- trace["net_name"] = net_name
904
- trace["path"] = path
905
- trace["layer"] = trace_layer
906
-
907
- width = f"{diff_pair.name}_via_{trace_layer}_trace_width"
908
- if net_idx == 1:
909
- variables.append({"name": width, "value": v["trace_width"]})
910
- trace["width"] = width
911
-
912
- clearance = f"{diff_pair.name}_via_{trace_layer}_trace_clearance"
913
- if net_idx == 1:
914
- variables.append({"name": clearance, "value": f"{width}+2*{v['trace_clearance']}"})
915
- trace["void_width"] = clearance
916
-
917
- local_traces.append(trace)
918
- path = [last_point]
919
-
920
- start_layer = v["start_layer"]
921
- stop_layer = v["stop_layer"]
922
-
923
- pdef = v["padstack_definition"]
924
- if pdef == "micro_via":
925
- start_idx = self.signal_layers.index(start_layer)
926
- stop_idx = self.signal_layers.index(stop_layer)
927
- for i in np.arange(start_idx, stop_idx):
928
- temp_start = self.signal_layers[i]
929
- temp_stop = self.signal_layers[i + 1]
930
- temp = {
931
- "name": f"{net_name}_via_{start_layer}_{stop_layer}_{i}",
932
- "definition": pdef,
933
- "layer_range": [temp_start, temp_stop],
934
- "net_name": net_name,
935
- "position": [x_loc, y_loc],
936
- "is_pin": False,
937
- }
938
- pd_insts.append(temp)
939
- else:
940
- temp = {
941
- "name": f"{net_name}_via_{start_layer}_{stop_layer}",
942
- "definition": pdef,
943
- "layer_range": [start_layer, stop_layer],
944
- "net_name": net_name,
945
- "position": [x_loc, y_loc],
946
- "is_pin": False,
947
- }
948
-
949
- back_drilling = v.get("backdrill_parameters")
950
- if back_drilling:
951
- temp["backdrill_parameters"] = back_drilling
952
- pd_insts.append(temp)
953
-
954
- if True: # v.get("race_track"):
955
- if net_idx % 2 == 0:
956
- diff_pair.p_via_loc.append([x_loc, y_loc])
957
- else:
958
- diff_pair.n_via_loc.append([x_loc, y_loc])
959
-
960
- for idx, t in enumerate(local_traces):
961
- net_name = t["net_name"]
962
- path = t["path"]
963
- diff_pair.via_to_via_traces.append(path)
964
- trace = {
965
- "path": path,
966
- "width": t["width"],
967
- "name": f"{net_name}_via_{idx}_trace",
968
- "layer": t["layer"],
969
- "net_name": net_name,
970
- }
971
- traces.append(trace)
972
-
973
- if trace["layer"] not in ["PCB_TOP", "PKG_BOT"]:
974
- void = trace.copy()
975
- void["name"] = f"{net_name}_via_{idx}_clearance"
976
- void["width"] = t["void_width"]
977
- void["void_type"] = "trace"
978
- voids.append(void)
979
- # traces.append(void)
980
-
981
- self._variables.extend(variables)
982
- self._padstack_instances.extend(pd_insts)
983
- self._traces.extend(traces)
984
- self._voids.extend(voids)
985
- return variables, pd_insts, traces, voids
986
-
987
- def _create_planes(self):
988
- planes = []
989
-
990
- for layer in self.signal_layers:
991
- if layer in ["PCB_TOP", "PKG_BOT"]:
992
- continue
993
- p = dict()
994
- p["name"] = f"GND_{layer}"
995
- p["layer"] = layer
996
- p["net_name"] = "GND"
997
- p["lower_left_point"] = self.lower_left_point
998
- p["upper_right_point"] = self.upper_right_point
999
- temp = []
1000
- for v in self._voids:
1001
- if v["layer"] == layer:
1002
- temp.append(v["name"])
1003
- if v["void_type"] == "trace":
1004
- self._traces.append(v)
1005
- elif v["void_type"] == "plane":
1006
- self._planes.append(v)
1007
-
1008
- p["voids"] = temp
1009
- planes.append(p)
1010
- return planes
1011
-
1012
- def _create_signal_fanout_type_1(self, params, design_type):
1013
- trace_out_direction = 1 if params["trace_out_direction"] == "forward" else -1
1014
-
1015
- variables = []
1016
- traces = []
1017
- voids = []
1018
- planes = []
1019
-
1020
- die_side_port_location = []
1021
- trace_corner_location = []
1022
-
1023
- width, gap, clearance, shift, length = 0, 0, 0, 0, 0
1024
- for idx, via_loc in enumerate(params["fanout_via_location"]):
1025
- pn_polarity = -1 if idx == 0 else 1
1026
-
1027
- if idx == 0:
1028
- width = f"{design_type}_{params['name']}_{params['layer']}_trace_width"
1029
- variables.append({"name": width, "value": params["width"], "description": params["name"]})
1030
- gap = f"{design_type}_{params['name']}_{params['layer']}_trace_gap"
1031
- variables.append({"name": gap, "value": params["gap"], "description": params["name"]})
1032
- shift = f"{design_type}_{params['name']}_{params['layer']}_trace_corner_shift"
1033
- variables.append({"name": shift, "value": params["shift"], "description": params["name"]})
1034
- if design_type == "pkg":
1035
- length = f"{design_type}_{params['name']}_{params['layer']}_trace_length"
1036
- variables.append({"name": length, "value": params["length"], "description": params["name"]})
1037
-
1038
- path = [via_loc]
1039
- if design_type == "pkg":
1040
- x_loc = f"{params['base_via_loc'][idx][1]}*pitch+{pn_polarity}*(-0.5*pitch+{gap}/2+{width}/2)"
1041
- elif design_type == "pcb":
1042
- x_loc = f"{params['base_via_loc'][idx][0]}+{pn_polarity}*(-0.5*pitch+{gap}/2+{width}/2)"
1043
-
1044
- y_loc = f"{via_loc[1]}+{shift}*{trace_out_direction}"
1045
-
1046
- path.append([x_loc, y_loc])
1047
- trace_corner_location.append([x_loc, y_loc])
1048
-
1049
- if design_type == "pkg":
1050
- y_loc = f"{y_loc}+{length}*{trace_out_direction}"
1051
- elif trace_out_direction == 1:
1052
- y_loc = self.upper_right_point[1]
1053
- else:
1054
- y_loc = "-plane_extend-pitch"
1055
- path.append([x_loc, y_loc])
1056
-
1057
- trace_name = f"{design_type}_{params['name']}_{params['layer']}_trace_{params['net_names'][idx]}"
1058
- trace = {
1059
- "path": path,
1060
- "width": width,
1061
- "name": trace_name,
1062
- "layer": params["layer"],
1063
- "net_name": params["net_names"][idx],
1064
- "start_cap_style": "flat",
1065
- "end_cap_style": "flat",
1066
- "corner_style": "sharp",
1067
- }
1068
- if design_type == "pkg":
1069
- trace["path"] = path[1:]
1070
- traces.append(trace)
1071
-
1072
- if idx == 0:
1073
- clearance = f"{design_type}_{params['name']}_{params['layer']}_trace_clearance"
1074
- variables.append({"name": clearance, "value": params["clearance"], "description": params["name"]})
1075
- void = trace.copy()
1076
- void["path"] = path
1077
- void["width"] = f"{width}+2*{clearance}"
1078
- void["name"] = trace_name + "_clearance"
1079
- void["end_cap_style"] = "extended"
1080
- void["void_type"] = "trace"
1081
-
1082
- voids.append(void)
1083
- # traces.append(void)
1084
-
1085
- if design_type == "pkg":
1086
- # create teardrop
1087
- # td_angle1 = f"atan({shift}/(pitch/2-{gap}/2-{width}/2))"
1088
- trace_p0_x, trace_p0_y = path[0]
1089
- trace_p1_x, trace_p1_y = path[1]
1090
- trace_dx = f"abs({trace_p1_x}-({trace_p0_x}))"
1091
- trace_dy = f"abs({trace_p1_y}-({trace_p0_y}))"
1092
-
1093
- td_angle2 = f"atan({trace_dx}/{trace_dy})"
1094
-
1095
- td_dx = f"$micro_via_pad_diameter/2*cos({td_angle2})"
1096
- td_dy = f"$micro_via_pad_diameter/2*sin({td_angle2})"
1097
-
1098
- p0 = [f"{trace_p1_x}- {width}/2*{pn_polarity}", trace_p1_y]
1099
- p1 = [f"{trace_p1_x}+ {width}/2*{pn_polarity}", trace_p1_y]
1100
- p2 = [f"{td_dx}*{pn_polarity}", f"{td_dy}*{trace_out_direction}"]
1101
- p3 = [f"{td_dx}*{pn_polarity}*-1", f"{td_dy}*-1*{trace_out_direction}"]
1102
-
1103
- pts2 = [p0, p1]
1104
- for i in [p2, p3]:
1105
- pts2.append([f"{i[0]}+{via_loc[0]}", f"{i[1]}+{via_loc[1]}"])
1106
-
1107
- poly = {
1108
- "type": "polygon",
1109
- "name": f"{design_type}_{params['name']}_{params['layer']}_teardrop_{params['net_names'][idx]}",
1110
- "layer": params["layer"],
1111
- "net_name": params["net_names"][idx],
1112
- "points": pts2,
1113
- }
1114
- planes.append(poly)
1115
-
1116
- last_point = path[-1]
1117
- die_side_port_location.append([trace_name, last_point])
1118
-
1119
- # create void
1120
- v_loc = params["fanout_via_location"].copy()
1121
- v_loc.reverse()
1122
- pts = trace_corner_location + v_loc
1123
-
1124
- void = {
1125
- "type": "polygon",
1126
- "name": f"{design_type}_{params['name']}_{params['layer']}_trapezoid_void",
1127
- "layer": params["layer"],
1128
- "net_name": "GND",
1129
- "points": pts,
1130
- "void_type": "plane",
1131
- }
1132
- voids.append(void)
1133
- # planes.append(void)
1134
- self._variables.extend(variables)
1135
- self._traces.extend(traces)
1136
- self._voids.extend(voids)
1137
- self._planes.extend(planes)
1138
- return die_side_port_location
1139
-
1140
- def create_edb(self, data) -> str:
1141
- edb_path = str(self.working_dir / self.design_name.with_suffix(".aedb"))
1142
- print(edb_path)
1143
- edbapp = Edb(edbpath=edb_path, edbversion=self.version)
1144
- edbapp.configuration.load(data, apply_file=True)
1145
- edbapp.save()
1146
- edbapp.close()
1147
- return edb_path
1148
-
1149
- def save_cfg_to_file(self, data):
1150
- with open(self.working_dir / self.design_name.with_suffix(".json"), "w") as f:
1151
- json.dump(data, f, indent=4)