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.

Files changed (208) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/common/nets.py +53 -139
  3. pyedb/configuration/cfg_common.py +1 -1
  4. pyedb/configuration/cfg_components.py +229 -201
  5. pyedb/configuration/cfg_data.py +3 -1
  6. pyedb/configuration/cfg_general.py +4 -2
  7. pyedb/configuration/cfg_modeler.py +7 -7
  8. pyedb/configuration/cfg_package_definition.py +1 -1
  9. pyedb/configuration/cfg_padstacks.py +346 -290
  10. pyedb/configuration/cfg_ports_sources.py +243 -65
  11. pyedb/configuration/configuration.py +23 -3
  12. pyedb/dotnet/{application → database}/Variables.py +21 -21
  13. pyedb/dotnet/{edb_core → database}/cell/connectable.py +5 -5
  14. pyedb/dotnet/{edb_core → database}/cell/hierarchy/component.py +11 -11
  15. pyedb/dotnet/{edb_core → database}/cell/hierarchy/hierarchy_obj.py +1 -1
  16. pyedb/dotnet/{edb_core → database}/cell/hierarchy/model.py +1 -1
  17. pyedb/dotnet/{edb_core → database}/cell/layout.py +19 -19
  18. pyedb/dotnet/{edb_core → database}/cell/layout_obj.py +3 -3
  19. pyedb/dotnet/{edb_core → database}/cell/primitive/bondwire.py +1 -1
  20. pyedb/dotnet/{edb_core → database}/cell/primitive/path.py +4 -4
  21. pyedb/dotnet/{edb_core → database}/cell/primitive/primitive.py +14 -14
  22. pyedb/dotnet/{edb_core → database}/cell/terminal/bundle_terminal.py +2 -2
  23. pyedb/dotnet/{edb_core → database}/cell/terminal/edge_terminal.py +4 -4
  24. pyedb/dotnet/{edb_core → database}/cell/terminal/padstack_instance_terminal.py +2 -2
  25. pyedb/dotnet/{edb_core → database}/cell/terminal/pingroup_terminal.py +2 -2
  26. pyedb/dotnet/{edb_core → database}/cell/terminal/point_terminal.py +2 -2
  27. pyedb/dotnet/{edb_core → database}/cell/terminal/terminal.py +11 -11
  28. pyedb/dotnet/{edb_core → database}/cell/voltage_regulator.py +2 -2
  29. pyedb/dotnet/{edb_core → database}/components.py +101 -124
  30. pyedb/dotnet/{edb_core → database}/definition/component_def.py +5 -5
  31. pyedb/dotnet/{edb_core → database}/definition/component_model.py +1 -1
  32. pyedb/dotnet/{edb_core → database}/definition/definition_obj.py +1 -1
  33. pyedb/dotnet/{edb_core → database}/definition/definitions.py +2 -2
  34. pyedb/dotnet/{edb_core → database}/definition/package_def.py +4 -4
  35. pyedb/dotnet/{edb_core → database}/dotnet/database.py +8 -8
  36. pyedb/dotnet/{edb_core → database}/dotnet/primitive.py +9 -9
  37. pyedb/dotnet/{edb_core → database}/edb_data/control_file.py +12 -12
  38. pyedb/dotnet/{edb_core → database}/edb_data/hfss_extent_info.py +7 -7
  39. pyedb/dotnet/{edb_core → database}/edb_data/nets_data.py +10 -13
  40. pyedb/dotnet/{edb_core → database}/edb_data/padstacks_data.py +60 -73
  41. pyedb/dotnet/{edb_core → database}/edb_data/ports.py +4 -4
  42. pyedb/dotnet/{edb_core → database}/edb_data/primitives_data.py +5 -5
  43. pyedb/dotnet/{edb_core → database}/edb_data/raptor_x_simulation_setup_data.py +4 -4
  44. pyedb/dotnet/{edb_core → database}/edb_data/simulation_configuration.py +10 -10
  45. pyedb/dotnet/{edb_core → database}/edb_data/sources.py +4 -4
  46. pyedb/dotnet/{edb_core → database}/edb_data/variables.py +1 -1
  47. pyedb/dotnet/{edb_core → database}/geometry/polygon_data.py +4 -4
  48. pyedb/dotnet/{edb_core → database}/hfss.py +8 -8
  49. pyedb/dotnet/{edb_core → database}/layout_obj_instance.py +1 -1
  50. pyedb/dotnet/{edb_core → database}/layout_validation.py +2 -2
  51. pyedb/dotnet/{edb_core → database}/materials.py +23 -8
  52. pyedb/dotnet/{edb_core → database}/modeler.py +27 -27
  53. pyedb/dotnet/{edb_core → database}/net_class.py +8 -8
  54. pyedb/dotnet/{edb_core → database}/nets.py +12 -12
  55. pyedb/dotnet/{edb_core → database}/padstack.py +17 -16
  56. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/mesh_operation.py +1 -1
  57. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/settings.py +18 -3
  58. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sim_setup_info.py +2 -2
  59. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/simulation_settings.py +1 -1
  60. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
  61. pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sweep_data.py +4 -4
  62. pyedb/dotnet/{edb_core → database}/siwave.py +10 -10
  63. pyedb/dotnet/{edb_core → database}/stackup.py +12 -12
  64. pyedb/dotnet/{edb_core → database}/utilities/hfss_simulation_setup.py +15 -15
  65. pyedb/dotnet/{edb_core → database}/utilities/obj_base.py +1 -1
  66. pyedb/dotnet/{edb_core → database}/utilities/simulation_setup.py +4 -3
  67. pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
  68. pyedb/dotnet/edb.py +118 -113
  69. pyedb/extensions/pre_layout_design_toolkit/via_design.py +1151 -0
  70. pyedb/generic/design_types.py +26 -19
  71. pyedb/generic/general_methods.py +1 -1
  72. pyedb/generic/plot.py +0 -2
  73. pyedb/grpc/database/__init__.py +1 -0
  74. pyedb/grpc/database/components.py +2354 -0
  75. pyedb/grpc/database/control_file.py +1277 -0
  76. pyedb/grpc/database/definition/component_def.py +218 -0
  77. pyedb/grpc/database/definition/component_model.py +39 -0
  78. pyedb/grpc/database/definition/component_pin.py +32 -0
  79. pyedb/grpc/database/definition/materials.py +1207 -0
  80. pyedb/grpc/database/definition/n_port_component_model.py +34 -0
  81. pyedb/grpc/database/definition/package_def.py +227 -0
  82. pyedb/grpc/database/definition/padstack_def.py +842 -0
  83. pyedb/grpc/database/definitions.py +70 -0
  84. pyedb/grpc/database/general.py +43 -0
  85. pyedb/grpc/database/geometry/__init__.py +0 -0
  86. pyedb/grpc/database/geometry/arc_data.py +93 -0
  87. pyedb/grpc/database/geometry/point_3d_data.py +79 -0
  88. pyedb/grpc/database/geometry/point_data.py +30 -0
  89. pyedb/grpc/database/geometry/polygon_data.py +133 -0
  90. pyedb/grpc/database/hfss.py +1279 -0
  91. pyedb/grpc/database/hierarchy/__init__.py +0 -0
  92. pyedb/grpc/database/hierarchy/component.py +1301 -0
  93. pyedb/grpc/database/hierarchy/model.py +31 -0
  94. pyedb/grpc/database/hierarchy/netlist_model.py +30 -0
  95. pyedb/grpc/database/hierarchy/pin_pair_model.py +128 -0
  96. pyedb/grpc/database/hierarchy/pingroup.py +245 -0
  97. pyedb/grpc/database/hierarchy/s_parameter_model.py +33 -0
  98. pyedb/grpc/database/hierarchy/spice_model.py +48 -0
  99. pyedb/grpc/database/layers/__init__.py +0 -0
  100. pyedb/grpc/database/layers/layer.py +57 -0
  101. pyedb/grpc/database/layers/stackup_layer.py +410 -0
  102. pyedb/grpc/database/layout/__init__.py +0 -0
  103. pyedb/grpc/database/layout/cell.py +30 -0
  104. pyedb/grpc/database/layout/layout.py +196 -0
  105. pyedb/grpc/database/layout/voltage_regulator.py +149 -0
  106. pyedb/grpc/database/layout_validation.py +319 -0
  107. pyedb/grpc/database/modeler.py +1468 -0
  108. pyedb/grpc/database/net/__init__.py +0 -0
  109. pyedb/grpc/database/net/differential_pair.py +138 -0
  110. pyedb/grpc/database/net/extended_net.py +340 -0
  111. pyedb/grpc/database/net/net.py +198 -0
  112. pyedb/grpc/database/net/net_class.py +93 -0
  113. pyedb/grpc/database/nets.py +633 -0
  114. pyedb/grpc/database/padstacks.py +1500 -0
  115. pyedb/grpc/database/ports/__init__.py +0 -0
  116. pyedb/grpc/database/ports/ports.py +396 -0
  117. pyedb/grpc/database/primitive/__init__.py +3 -0
  118. pyedb/grpc/database/primitive/bondwire.py +181 -0
  119. pyedb/grpc/database/primitive/circle.py +75 -0
  120. pyedb/grpc/database/primitive/padstack_instance.py +1116 -0
  121. pyedb/grpc/database/primitive/path.py +346 -0
  122. pyedb/grpc/database/primitive/polygon.py +276 -0
  123. pyedb/grpc/database/primitive/primitive.py +739 -0
  124. pyedb/grpc/database/primitive/rectangle.py +146 -0
  125. pyedb/grpc/database/simulation_setup/__init__.py +0 -0
  126. pyedb/grpc/database/simulation_setup/adaptive_frequency.py +33 -0
  127. pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +32 -0
  128. pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +59 -0
  129. pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +35 -0
  130. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +61 -0
  131. pyedb/grpc/database/simulation_setup/hfss_settings_options.py +78 -0
  132. pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +118 -0
  133. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +355 -0
  134. pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +34 -0
  135. pyedb/grpc/database/simulation_setup/mesh_operation.py +34 -0
  136. pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +34 -0
  137. pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +33 -0
  138. pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +64 -0
  139. pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +125 -0
  140. pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +34 -0
  141. pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +119 -0
  142. pyedb/grpc/database/simulation_setup/sweep_data.py +32 -0
  143. pyedb/grpc/database/siwave.py +1023 -0
  144. pyedb/grpc/database/source_excitations.py +2572 -0
  145. pyedb/grpc/database/stackup.py +2574 -0
  146. pyedb/grpc/database/terminal/__init__.py +0 -0
  147. pyedb/grpc/database/terminal/bundle_terminal.py +218 -0
  148. pyedb/grpc/database/terminal/edge_terminal.py +51 -0
  149. pyedb/grpc/database/terminal/padstack_instance_terminal.py +171 -0
  150. pyedb/grpc/database/terminal/pingroup_terminal.py +162 -0
  151. pyedb/grpc/database/terminal/point_terminal.py +99 -0
  152. pyedb/grpc/database/terminal/terminal.py +470 -0
  153. pyedb/grpc/database/utility/__init__.py +3 -0
  154. pyedb/grpc/database/utility/constants.py +25 -0
  155. pyedb/grpc/database/utility/heat_sink.py +124 -0
  156. pyedb/grpc/database/utility/hfss_extent_info.py +448 -0
  157. pyedb/grpc/database/utility/layout_statistics.py +277 -0
  158. pyedb/grpc/database/utility/rlc.py +80 -0
  159. pyedb/grpc/database/utility/simulation_configuration.py +3305 -0
  160. pyedb/grpc/database/utility/sources.py +388 -0
  161. pyedb/grpc/database/utility/sweep_data_distribution.py +83 -0
  162. pyedb/grpc/database/utility/xml_control_file.py +1277 -0
  163. pyedb/grpc/edb.py +4151 -0
  164. pyedb/grpc/edb_init.py +481 -0
  165. pyedb/grpc/rpc_session.py +177 -0
  166. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +3 -2
  167. pyedb/ipc2581/ecad/cad_data/feature.py +4 -3
  168. pyedb/ipc2581/ecad/cad_data/layer_feature.py +32 -20
  169. pyedb/ipc2581/ecad/cad_data/outline.py +3 -2
  170. pyedb/ipc2581/ecad/cad_data/package.py +4 -3
  171. pyedb/ipc2581/ecad/cad_data/path.py +82 -31
  172. pyedb/ipc2581/ecad/cad_data/polygon.py +122 -60
  173. pyedb/ipc2581/ecad/cad_data/profile.py +13 -12
  174. pyedb/ipc2581/ecad/cad_data/step.py +52 -20
  175. pyedb/ipc2581/ipc2581.py +47 -49
  176. pyedb/modeler/geometry_operators.py +1 -1
  177. {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/METADATA +9 -6
  178. pyedb-0.39.0.dist-info/RECORD +288 -0
  179. pyedb-0.37.0.dist-info/RECORD +0 -194
  180. /pyedb/dotnet/{edb_core → database}/__init__.py +0 -0
  181. /pyedb/dotnet/{application → database/cell}/__init__.py +0 -0
  182. /pyedb/dotnet/{edb_core/cell → database/cell/hierarchy}/__init__.py +0 -0
  183. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/netlist_model.py +0 -0
  184. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/pin_pair_model.py +0 -0
  185. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/s_parameter_model.py +0 -0
  186. /pyedb/dotnet/{edb_core → database}/cell/hierarchy/spice_model.py +0 -0
  187. /pyedb/dotnet/{edb_core → database}/cell/primitive/__init__.py +0 -0
  188. /pyedb/dotnet/{edb_core/cell/hierarchy → database/cell/terminal}/__init__.py +0 -0
  189. /pyedb/dotnet/{edb_core/cell/terminal → database/definition}/__init__.py +0 -0
  190. /pyedb/dotnet/{edb_core/definition → database/dotnet}/__init__.py +0 -0
  191. /pyedb/dotnet/{edb_core/dotnet → database/edb_data}/__init__.py +0 -0
  192. /pyedb/dotnet/{edb_core → database}/edb_data/design_options.py +0 -0
  193. /pyedb/dotnet/{edb_core → database}/edb_data/edbvalue.py +0 -0
  194. /pyedb/dotnet/{edb_core → database}/edb_data/layer_data.py +0 -0
  195. /pyedb/dotnet/{edb_core → database}/edb_data/utilities.py +0 -0
  196. /pyedb/dotnet/{edb_core → database}/general.py +0 -0
  197. /pyedb/dotnet/{edb_core/edb_data → database/geometry}/__init__.py +0 -0
  198. /pyedb/dotnet/{edb_core → database}/geometry/point_data.py +0 -0
  199. /pyedb/dotnet/{edb_core → database}/sim_setup_data/__init__.py +0 -0
  200. /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/__init__.py +0 -0
  201. /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/adaptive_frequency_data.py +0 -0
  202. /pyedb/dotnet/{edb_core/geometry → database/sim_setup_data/io}/__init__.py +0 -0
  203. /pyedb/dotnet/{edb_core → database}/sim_setup_data/io/siwave.py +0 -0
  204. /pyedb/dotnet/{edb_core → database}/utilities/__init__.py +0 -0
  205. /pyedb/dotnet/{edb_core → database}/utilities/heatsink.py +0 -0
  206. /pyedb/{dotnet/edb_core/sim_setup_data/io → grpc/database/definition}/__init__.py +0 -0
  207. {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/LICENSE +0 -0
  208. {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,1151 @@
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)