diff --git a/src/pyedb/configuration/cfg_modeler.py b/src/pyedb/configuration/cfg_modeler.py index bf8def111d..47d79832da 100644 --- a/src/pyedb/configuration/cfg_modeler.py +++ b/src/pyedb/configuration/cfg_modeler.py @@ -29,13 +29,15 @@ class CfgTrace: def __init__(self, **kwargs): self.name = kwargs.get("name", "") self.layer = kwargs["layer"] - self.path = kwargs["path"] + self.path = kwargs.get("path") self.width = kwargs["width"] self.net_name = kwargs.get("net_name", "") self.start_cap_style = kwargs.get("start_cap_style", "round") self.end_cap_style = kwargs.get("end_cap_style", "round") self.corner_style = kwargs.get("corner_style", "sharp") + self.incremental_path = kwargs.get("incremental_path") + class CfgPlane: def __init__(self, **kwargs): @@ -54,6 +56,10 @@ def __init__(self, **kwargs): # polygon self.points = kwargs.get("points", []) + # circle + self.radius = kwargs.get("radius", 0) + self.position = kwargs.get("position", [0, 0]) + class CfgModeler: """Manage configuration general settings.""" @@ -153,16 +159,30 @@ def __init__(self, parent): def set_parameter_to_edb(self): if self.parent.traces: for t in self.parent.traces: - obj = self._pedb.modeler.create_trace( - path_list=t.path, - layer_name=t.layer, - net_name=t.net_name, - width=t.width, - start_cap_style=t.start_cap_style, - end_cap_style=t.end_cap_style, - corner_style=t.corner_style, - ) - obj.aedt_name = t.name + if t.path: + obj = self._pedb.modeler.create_trace( + path_list=t.path, + layer_name=t.layer, + net_name=t.net_name, + width=t.width, + start_cap_style=t.start_cap_style, + end_cap_style=t.end_cap_style, + corner_style=t.corner_style, + ) + obj.aedt_name = t.name + else: + obj = self._pedb.modeler.create_trace( + path_list=[t.incremental_path[0]], + layer_name=t.layer, + net_name=t.net_name, + width=t.width, + start_cap_style=t.start_cap_style, + end_cap_style=t.end_cap_style, + corner_style=t.corner_style, + ) + obj.aedt_name = t.name + for x, y in t.incremental_path[1:]: + obj.add_point(x, y, True) if self.parent.padstack_defs: for p in self.parent.padstack_defs: @@ -202,6 +222,17 @@ def set_parameter_to_edb(self): main_shape=p.points, layer_name=p.layer, net_name=p.net_name ) obj.aedt_name = p.name + elif p.type == "circle": + obj = self._pedb.modeler.create_circle( + layer_name=p.layer, + net_name=p.net_name, + x=p.position[0], + y=p.position[1], + radius=p.radius, + ) + obj.aedt_name = p.name + else: + raise for v in p.voids: for i in self._pedb.layout.primitives: diff --git a/src/pyedb/extensions/__init__.py b/src/pyedb/extensions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pyedb/extensions/pre_layout_design_toolkit/via_design.py b/src/pyedb/extensions/pre_layout_design_toolkit/via_design.py deleted file mode 100644 index a425a78144..0000000000 --- a/src/pyedb/extensions/pre_layout_design_toolkit/via_design.py +++ /dev/null @@ -1,1151 +0,0 @@ -import json -from pathlib import Path - -import numpy as np -import pandas as pd - -from pyedb import Edb -from pyedb.generic.general_methods import generate_unique_name - - -class Pair: - def __init__(self, name, net_names, **kwargs): - self.design_type = "" - self.name = name - self.net_names = net_names - self.core_pdef = kwargs.get("core_pdef", "blind_via") - self.has_stitching_vias = kwargs.get("has_stitching_vias", False) - - self.bga_locations = [] - self.vias = None - self.trace_out = [] - - self.fanout_port_location = [] - - self.p_via_loc = [] # [[via1 x, y], [via2 x, u]] - self.n_via_loc = [] # [[via1 x, y], [via2 x, u]] - - self.via_to_via_traces = [] - - self.pkg_trace_out_lower_port_location = None - self.pkg_trace_out_upper_port_location = None - self.pcb_trace_out_lower_port_location = None - self.pcb_trace_out_upper_port_location = None - - @property - def race_track_path_list(self): - temp = [] - start_stop_layer = [] - for v in self.vias: - start_stop_layer.append([v["padstack_definition"], v["start_layer"], v["stop_layer"]]) - temp.append(list(zip(start_stop_layer, self.p_via_loc, self.n_via_loc))) - # ['blind_via', 'L2', 'L3', is_core_via], [x_loc, y_loc], [x_loc, y_loc] - return temp - - @property - def core_via_start_layer(self): - start_layer = [i["start_layer"] for i in self.vias if i["padstack_definition"] == self.core_pdef][0] - return start_layer - - @property - def core_via_stop_layer(self): - stop_layer = [i["stop_layer"] for i in self.vias if i["padstack_definition"] == self.core_pdef][0] - return stop_layer - - @property - def bga_pin_location(self): - return [[f"{loc[1]}*pitch", f"{loc[0]}*pitch"] for loc in self.bga_locations] - - @property - def fanout_via_location_outer(self): - return [self.p_via_loc[-1], self.n_via_loc[-1]] # [[p_x, p_y], [n_x, n_y]] - - @property - def fanout_via_location_inner(self): - return [self.p_via_loc[0], self.n_via_loc[0]] # [[p_x, p_y], [n_x, n_y]] - - @property - def pkg_trace_out_lower_params(self): - if len(self.trace_out) == 2: - temp = self.trace_out[1].copy() - else: - return - - temp["name"] = self.name - temp["net_names"] = self.net_names - temp["fanout_via_location"] = self.fanout_via_location_inner - temp["base_via_loc"] = self.bga_locations - temp["port_location"] = self.pkg_trace_out_lower_port_location - return temp - - @property - def pkg_trace_out_upper_params(self): - temp = self.trace_out[0].copy() - temp["name"] = self.name - temp["net_names"] = self.net_names - temp["fanout_via_location"] = self.fanout_via_location_outer - temp["base_via_loc"] = self.bga_locations - temp["port_location"] = self.pkg_trace_out_upper_port_location - return temp - - @property - def pcb_trace_out_lower_params(self): - if len(self.trace_out) == 2: - temp = self.trace_out[1].copy() - else: - temp = self.trace_out[0].copy() - - temp["name"] = self.name - temp["net_names"] = self.net_names - temp["fanout_via_location"] = self.fanout_via_location_outer - temp["base_via_loc"] = self.fanout_via_location_outer - temp["port_location"] = self.pcb_trace_out_lower_port_location - return temp - - @property - def pcb_trace_out_upper_params(self): - if len(self.trace_out) == 2: - temp = self.trace_out[0].copy() - else: - return - temp["name"] = self.name - temp["net_names"] = self.net_names - temp["fanout_via_location"] = self.fanout_via_location_inner - temp["base_via_loc"] = self.fanout_via_location_inner - temp["port_location"] = self.pcb_trace_out_upper_port_location - return temp - - -class GroundVia: - def __init__(self): - self.bga_locations = [] - self.pattern_1_params = None # {"distance": "0.2mm", "core_via_start_layer": "L6", "core_via_stop_layer": "L7"} - - -class ViaDesignConfig: - @property - def plane_size(self): - y_size = len(self.pin_map["locations"]) - x_size = len(self.pin_map["locations"][0]) - 1 - return x_size, y_size - - @property - def lower_left_point(self): - return ["-plane_extend", "-plane_extend-pitch"] - - @property - def upper_right_point(self): - return [f"{self.plane_size[0]}*pitch+plane_extend", f"{self.plane_size[1]}*pitch+plane_extend"] - - def __init__(self, config_file: Path, version=""): - self._version = version - self._design_name = "" - self._working_dir = "" - self.pkg_stackup = [] - self.pcb_stackup = [] - - self._variables = [] - self._padstack_instances = [] - self._padstack_definition = [] - self._traces = [] - self._planes = [] - self._voids = [] - self._components = [] - self._ports = [] - - if isinstance(config_file, str): - config_file = Path(config_file) - self.config_file_path = config_file - - with open(config_file, "r") as f: - self.config = json.load(f) - - with open(config_file.parent / self.config["materials"], "r") as f: - self.materials = json.load(f) - - if self.design_type == "pkg": - with open(config_file.parent / self.config["pkg_stackup"], "r") as f: - self.pkg_stackup = json.load(f) - - if self.include_pcb or self.design_type == "pcb": - with open(config_file.parent / self.config["pcb_stackup"], "r") as f: - self.pcb_stackup = json.load(f) - - with open(config_file.parent / self.config["padstacks"], "r") as f: - self.padstacks = json.load(f) - - with open(config_file.parent / self.config["pin_map"], "r") as f: - self.pin_map = json.load(f) - - with open(config_file.parent / self.config["technology"], "r") as f: - self.technology = json.load(f) - - with open(config_file.parent / self.config["setup"], "r") as f: - self.setup = json.load(f) - - if self.include_pcb: - pdef_bga = [i for i in self.padstacks if i["name"] == "bga"][0] - pdef_bga["solder_ball_parameters"] = { - "shape": self.technology["bga_component"]["solder_ball_shape"], - "diameter": self.technology["bga_component"]["solder_ball_diameter"], - "mid_diameter": self.technology["bga_component"].get("solder_ball_mid_diameter"), - "placement": "above_padstack", - "material": "solder", - } - - locations = pd.DataFrame(self.pin_map["locations"]) - - self.pcb_via_pairs = [] - self.pkg_via_pairs = [] - - for name, nets in self.pin_map["signal_pairs"].items(): - bga_locations = [] - for net_name in nets: - bga_locations.append((locations == net_name).stack()[(locations == net_name).stack()].index.tolist()[0]) - - tech = self.technology["signal_pair"][name] - if self.design_type == "pkg": - sp = Pair(name, nets) - sp.design_type = "pkg" - sp.vias = tech["pkg_signal_via"] - sp.trace_out = tech["pkg_trace"] - sp.bga_locations = bga_locations - self.pkg_via_pairs.append(sp) - - if self.include_pcb or self.design_type == "pcb": - sp = Pair(name, nets, core_pdef="pcb_via") - sp.design_type = "pcb" - sp.vias = tech["pcb_signal_via"] - sp.trace_out = tech["pcb_trace"] - sp.bga_locations = bga_locations - sp.has_stitching_vias = tech.get("has_stitching_vias", False) - self.pcb_via_pairs.append(sp) - - if self.design_type == "pkg": - self.pkg_ground_vias = GroundVia() - for i in (locations == "GND").stack()[(locations == "GND").stack()].index.tolist(): - self.pkg_ground_vias.bga_locations.append(i) - self.pkg_ground_vias.pattern_1_params = self.technology["pkg_ground_via"] - - if self.design_type == "pcb" or self.include_pcb: - self.pcb_ground_vias = GroundVia() - for i in (locations == "GND").stack()[(locations == "GND").stack()].index.tolist(): - self.pcb_ground_vias.bga_locations.append(i) - self.pcb_ground_vias.pattern_1_params = self.technology["pcb_ground_via"] - - def create_ports_on_bga(self): - ports = [] - for obj in self.pcb_via_pairs + self.pkg_via_pairs: - for idx, i in enumerate(obj.net_names): - port_name = f"{obj.name}_p_coax_port" if idx == 0 else f"{obj.name}_n_coax_port" - port = { - "name": port_name, - "reference_designator": "U1", - "type": "coax", - "positive_terminal": {"net": i}, - } - ports.append(port) - return ports - - @property - def signal_layers(self) -> list: - return [i["name"] for i in self.stackup if i["type"].lower() == "signal"] - - @property - def signal_layers_pkg(self) -> list: - return [i["name"] for i in self.pkg_stackup if i["type"].lower() == "signal"] - - @property - def signal_layers_pcb(self) -> list: - return [i["name"] for i in self.pcb_stackup if i["type"].lower() == "signal"] - - @property - def pcb_signal_layer_list(self) -> list: - return [i["name"] for i in self.pcb_stackup if i["type"].lower() == "signal"] - - @property - def stackup(self): - temp = [] - for layer in self.pkg_stackup: - l2 = layer.copy() - temp.append(l2) - - if self.pcb_stackup: - temp.append( - { - "name": "PKG_PCB_AIR", - "type": "dielectric", - "material": "air", - "thickness": self.technology["bga_component"]["solder_ball_height"], - } - ) - for layer in self.pcb_stackup: - l2 = layer.copy() - temp.append(l2) - return temp - - @property - def working_dir(self): - if not self._working_dir: - wdir = self.config.get("working_directory") - if wdir is not None: - self._working_dir = Path(wdir) - else: - working_dir = self.config_file_path.parent / "via_designs" - working_dir.mkdir(parents=True, exist_ok=True) - self._working_dir = working_dir - return self._working_dir - - @property - def design_name(self): - if not self._design_name: - self._design_name = Path(generate_unique_name(self.config_file_path.stem)) - return self._design_name - - @property - def design_type(self): - return self.config["design_type"].lower() - - @property - def include_pcb(self): - return self.config["include_pcb"] if self.design_type == "pkg" else None - - @property - def version(self): - if self._version: - return self._version - else: - return self.config["version"] - - def _create_variables(self): - self._variables.extend( - [ - {"name": "plane_extend", "value": self.technology["plane_extend"], "description": "general"}, - {"name": "pitch", "value": self.technology["pitch"], "description": "general"}, - ] - ) - - if self.design_type == "pkg": - for name, _ in self.pin_map["signal_pairs"].items(): - die_side_stitching_via_dy = self.technology["signal_pair"][name]["pkg_trace"][0]["stitching_via_dy"] - self._variables.extend( - [ - { - "name": f"{name}_die_side_stitching_via_dy", - "value": die_side_stitching_via_dy, - "description": "general", - }, - ] - ) - elif self.include_pcb or self.design_type == "pcb": - self._variables.extend( - [ - {"name": "pcb_stitching_via_distance", "value": "1mm", "description": "general"}, - ] - ) - - else: - pass - - def create_design(self): - self._create_variables() - - new_vars, padstack_defs = self._create_padstack_defs() - self._variables.extend(new_vars) - self._padstack_definition.extend(padstack_defs) - - if self.design_type == "pkg": - for df in self.pkg_via_pairs: - self._create_signal_via_transition(df, design_type="pkg") - - self._create_return_via_pkg(df) - - port_location = self._create_signal_fanout_type_1(df.pkg_trace_out_upper_params, design_type="pkg") - layer = df.pkg_trace_out_upper_params["layer"] - name = df.pkg_trace_out_upper_params["name"] - port = { - "name": f"pkg_{name}_{layer}_wave_port", - "type": "diff_wave_port", - "positive_terminal": {"primitive_name": port_location[0][0], "point_on_edge": port_location[0][1]}, - "negative_terminal": {"primitive_name": port_location[1][0], "point_on_edge": port_location[1][1]}, - "horizontal_extent_factor": 6, - "vertical_extent_factor": 4, - "pec_launch_width": "0.02mm", - } - self._ports.append(port) - - df.pkg_trace_out_upper_port_location = port_location - - if df.pkg_trace_out_lower_params: - port_location = self._create_signal_fanout_type_1(df.pkg_trace_out_lower_params, design_type="pkg") - layer = df.pkg_trace_out_lower_params["layer"] - name = df.pkg_trace_out_lower_params["name"] - port = { - "name": f"pkg_{name}_{layer}_wave_port", - "type": "diff_wave_port", - "positive_terminal": { - "primitive_name": port_location[0][0], - "point_on_edge": port_location[0][1], - }, - "negative_terminal": { - "primitive_name": port_location[1][0], - "point_on_edge": port_location[1][1], - }, - "horizontal_extent_factor": 6, - "vertical_extent_factor": 4, - "pec_launch_width": "0.02mm", - } - self._ports.append(port) - - self._create_race_track(df, design_type="pkg") - # self._create_bga_component(placement_layer="PKG_BOT") - self._create_gnd_vias(design_type="pkg") - - if self.include_pcb or self.design_type == "pcb": - for df in self.pcb_via_pairs: - self._create_signal_via_transition(df, design_type="pcb") - if df.has_stitching_vias: - self._create_return_via_pcb(df) - - port_location = self._create_signal_fanout_type_1(df.pcb_trace_out_lower_params, design_type="pcb") - layer = df.pcb_trace_out_lower_params["layer"] - name = df.pcb_trace_out_lower_params["name"] - port = { - "name": f"pkg_{name}_{layer}_wave_port", - "type": "diff_wave_port", - "positive_terminal": {"primitive_name": port_location[0][0], "point_on_edge": port_location[0][1]}, - "negative_terminal": {"primitive_name": port_location[1][0], "point_on_edge": port_location[1][1]}, - "horizontal_extent_factor": 6, - "vertical_extent_factor": 4, - "pec_launch_width": "0.02mm", - } - self._ports.append(port) - - if df.pcb_trace_out_upper_params: - port_location = self._create_signal_fanout_type_1(df.pcb_trace_out_upper_params, design_type="pcb") - layer = df.pcb_trace_out_upper_params["layer"] - name = df.pcb_trace_out_upper_params["name"] - port = { - "name": f"pkg_{name}_{layer}_wave_port", - "type": "diff_wave_port", - "positive_terminal": { - "primitive_name": port_location[0][0], - "point_on_edge": port_location[0][1], - }, - "negative_terminal": { - "primitive_name": port_location[1][0], - "point_on_edge": port_location[1][1], - }, - "horizontal_extent_factor": 6, - "vertical_extent_factor": 4, - "pec_launch_width": "0.02mm", - } - self._ports.append(port) - # self._create_bga_component(placement_layer="PCB_TOP") - self._create_race_track(df, design_type="pcb") - self._create_gnd_vias(design_type="pcb") - - bga_params = self.technology["bga_component"] - if self.design_type == "pcb": - if bga_params["enabled"] is True: - self._create_bga_component(bga_params, placement_layer="PCB_TOP") - elif self.design_type == "pkg": - if self.include_pcb: - self._create_solder_ball() - else: - if bga_params["enabled"] is True: - self._create_bga_component(bga_params, placement_layer="PKG_BOT") - - planes = self._create_planes() - self._planes.extend(planes) - - if not self.include_pcb: - self._ports.extend(self.create_ports_on_bga()) - - data = {"general": {"suppress_pads": True, "anti_pads_always_on": True}} - - stackup = {"materials": self.materials, "layers": self.stackup} - data["stackup"] = stackup - data["variables"] = self._variables - data["ports"] = self._ports - data["setups"] = [self.setup] - - modeler = { - "padstack_definitions": self._padstack_definition, - "padstack_instances": self._padstack_instances, - "traces": self._traces, - "planes": self._planes, - "components": self._components, - } - data["modeler"] = modeler - return data - - def _create_padstack_defs(self): - new_variables = [] - cfg_padstacks = [] - for pad in self.padstacks: - name = pad["name"] - - hole_diameter = pad.get("hole_diameter") - if hole_diameter: - var_hole_diameter = f"${name}_hole_diameter" - new_variables.append({"name": var_hole_diameter, "value": hole_diameter, "description": "padstack"}) - hole_parameters = {"shape": "circle", "diameter": var_hole_diameter} - else: - hole_parameters = None - - pad_diameter = pad["pad_diameter"] - var_pad_diameter = f"${name}_pad_diameter" - new_variables.append({"name": var_pad_diameter, "value": pad_diameter, "description": "padstack"}) - shape = pad["shape"] - if shape == "circle": - regular_pad = [] - for layer in self.signal_layers: - regular_pad.append( - { - "layer_name": layer, - "shape": shape, - "diameter": var_pad_diameter, - } - ) - else: # shape == "rectangle": - x_size = pad["x_size"] - y_size = pad["y_size"] - var_pad_x_size = f"${name}_pad_x_size" - var_pad_y_size = f"${name}_pad_y_size" - new_variables.append({"name": var_pad_x_size, "value": x_size, "description": "padstack"}) - new_variables.append({"name": var_pad_y_size, "value": y_size, "description": "padstack"}) - - regular_pad = [] - for layer in self.signal_layers: - regular_pad.append( - { - "layer_name": layer, - "shape": shape, - "x_size": var_pad_x_size, - "y_size": var_pad_y_size, - } - ) - - anti_pad_diameter = pad["anti_pad_diameter"] - - # anti_pad = [] - for layer in self.signal_layers: - if name in ["bga", "blind_via"]: - var_anti_pad_diameter = f"${name}_anti_pad_diameter" - new_variables.append( - {"name": var_anti_pad_diameter, "value": anti_pad_diameter, "description": "layer={l}"} - ) - break - else: - var_anti_pad_diameter = f"${name}_anti_pad_diameter_{layer}" - new_variables.append( - {"name": var_anti_pad_diameter, "value": anti_pad_diameter, "description": f"layer={layer}"} - ) - - pad_parameters = { - "regular_pad": regular_pad, - # "anti_pad": anti_pad - } - - new_p_def = {"name": name, "pad_parameters": pad_parameters} - if hole_diameter: - new_p_def.update( - { - "hole_parameters": hole_parameters, - "hole_range": pad["hole_range"], - } - ) - - if pad.get("solder_ball_parameters"): - new_p_def["solder_ball_parameters"] = pad["solder_ball_parameters"] - cfg_padstacks.append(new_p_def) - return new_variables, cfg_padstacks - - def _create_race_track(self, diff_pair: Pair, design_type): - voids = [] - for i in diff_pair.race_track_path_list: - for j in i: - pdef, p1, p2 = j - pdef_name, start_layer, stop_layer = pdef - flag = False - for layer in self.signal_layers: - if layer == start_layer: - flag = True - if layer in ["PCB_TOP", "PKG_BOT"]: - if layer == stop_layer: - flag = False - continue - if flag: - width = ( - f"${pdef_name}_anti_pad_diameter" - if pdef_name == "blind_via" - else f"${pdef_name}_anti_pad_diameter_{layer}" - ) - trace = { - "path": [p1, p2], - "width": width, - "layer": layer, - "name": f"{pdef_name}_{layer}_race_track", - "net_name": "GND", - } - void = trace.copy() - void["void_type"] = "trace" - voids.append(void) - - if layer == stop_layer: - flag = False - - if design_type == "pkg": - for layer in self.signal_layers_pkg: - if layer in ["PCB_TOP", "PKG_BOT"]: - continue - trace = { - "path": diff_pair.bga_pin_location, - "width": "$bga_anti_pad_diameter", - "layer": layer, - "name": f"bga_{layer}_race_track", - "net_name": "GND", - "void_type": "trace", - } - voids.append(trace) - self._voids.extend(voids) - # self._traces.extend(voids) - return voids - - def _create_return_via_pkg(self, diff_pair: Pair): - pd_instances = [] - for pin_idx, pin_loc in enumerate(diff_pair.bga_pin_location): - for idx, layer in enumerate(self.signal_layers_pkg): - if layer == self.signal_layers_pkg[-1]: - break - pdef = "blind_via" if layer == diff_pair.core_via_start_layer else "micro_via" - if not pin_idx % 2: - init_angle = "pi*5/8" if idx % 2 else "pi*1/2" - else: - init_angle = "pi*13/8" if idx % 2 else "pi*3/2" - distance = f"$bga_anti_pad_diameter/2+${pdef}_hole_diameter/2" - via_list = np.arange(4) if idx % 2 else np.arange(5) - for i in via_list: - angle = f"{init_angle}+{i}*1/4*pi" - x_loc = f"{pin_loc[0]}+cos({angle})*({distance})" - y_loc = f"{pin_loc[1]}+sin({angle})*({distance})" - pd_instances.append( - { - # "name": f"{diff_pair.name}_p_return_", - "definition": pdef, - "layer_range": [layer, self.signal_layers_pkg[idx + 1]], - "net_name": "GND", - "position": [x_loc, y_loc], - "is_pin": False, - } - ) - - for idx, loc in enumerate(diff_pair.fanout_via_location_outer): - x_loc = loc[0] - y_loc = f"{loc[1]}+{diff_pair.name}_die_side_stitching_via_dy/2" - pd_instances.append( - { - # "name": f"{diff_pair.name}_p_return_", - "definition": "micro_via", - "layer_range": [ - diff_pair.pkg_trace_out_upper_params["layer"], - self.signal_layers_pkg[ - self.signal_layers_pkg.index(diff_pair.pkg_trace_out_upper_params["layer"]) + 1 - ], - ], - "net_name": "GND", - "position": [x_loc, y_loc], - "is_pin": False, - } - ) - self._padstack_instances.extend(pd_instances) - return pd_instances - - def _create_return_via_pcb(self, diff_pair: Pair): - pd_instances = [] - pdef = diff_pair.core_pdef - for pin_idx, pin_loc in enumerate(diff_pair.bga_pin_location): - if not pin_idx % 2: - init_angle = "pi*4/8" - else: - init_angle = "pi*3/2" - distance = "pcb_stitching_via_distance" - via_list = np.arange(4) - for i in via_list: - angle = f"{init_angle}+{i}*1/3*pi" - x_loc = f"{pin_loc[0]}+cos({angle})*({distance})" - y_loc = f"{pin_loc[1]}+sin({angle})*({distance})" - pd_instances.append( - { - # "name": f"{diff_pair.name}_p_return_", - "definition": pdef, - "layer_range": [diff_pair.core_via_start_layer, diff_pair.core_via_stop_layer], - "net_name": "GND", - "position": [x_loc, y_loc], - "is_pin": False, - } - ) - self._padstack_instances.extend(pd_instances) - - def _create_bga_component(self, params, placement_layer): - p_instances = [] - bga_comp_pins = [] - for row, row_obj in enumerate(self.pin_map["locations"]): - for col, obj in enumerate(row_obj): - if obj is None: - continue - net_name = obj - x_loc = f"{col}*pitch" - y_loc = f"{row}*pitch" - name = f"via_{row}_{col}" if net_name == "GND" else net_name - name = f"{name}_bga" - - temp = { - "name": name, - "definition": "bga", - "net_name": net_name, - "position": [x_loc, y_loc], - "layer_range": [placement_layer, placement_layer], - "is_pin": True, - } - p_instances.append(temp) - bga_comp_pins.append(name) - - solder_ball_property = { - "shape": self.technology["bga_component"]["solder_ball_shape"], - "diameter": self.technology["bga_component"]["solder_ball_diameter"], - "mid_diameter": self.technology["bga_component"]["solder_ball_mid_diameter"], - "height": self.technology["bga_component"]["solder_ball_height"], - } - comp = { - "reference_designator": "U1", - "pins": bga_comp_pins, - "part_type": "io", - "definition": "BGA", - "placement_layer": placement_layer, - "solder_ball_properties": solder_ball_property, - "port_properties": { - "reference_offset": "0mm", - "reference_size_auto": True, - "reference_size_x": 0, - "reference_size_y": 0, - }, - } - self._components.append(comp) - self._padstack_instances.extend(p_instances) - return comp, p_instances - - def _create_solder_ball(self): - p_instances = [] - bga_comp_pins = [] - for row, row_obj in enumerate(self.pin_map["locations"]): - for col, obj in enumerate(row_obj): - if obj is None: - continue - net_name = obj - x_loc = f"{col}*pitch" - y_loc = f"{row}*pitch" - name = f"via_{row}_{col}" if net_name == "GND" else net_name - pcb_name = f"{name}_bga" - - temp_pcb_side = { - "name": pcb_name, - "definition": "bga", - "net_name": net_name, - "position": [x_loc, y_loc], - "layer_range": ["PCB_TOP", "PCB_TOP"], - "is_pin": True, - "solder_ball_layer": "PKG_BOT", - } - p_instances.append(temp_pcb_side) - bga_comp_pins.append(pcb_name) - - temp_pkg_side = temp_pcb_side.copy() - temp_pkg_side["name"] = f"{name}_pkg" - temp_pkg_side["layer_range"] = ["PKG_BOT", "PKG_BOT"] - temp_pkg_side.pop("solder_ball_layer") - p_instances.append(temp_pkg_side) - - self._padstack_instances.extend(p_instances) - return p_instances - - def _create_gnd_vias(self, design_type): - variables = [] - traces = [] - voids = [] - - ground_vias = self.pkg_ground_vias if design_type == "pkg" else self.pcb_ground_vias - distance = f"{design_type}_ground_via_distance" - - if ground_vias.bga_locations: - variables.append( - {"name": distance, "value": ground_vias.pattern_1_params["distance"], "description": "ground_via"} - ) - pd_insts = [] - - for location in ground_vias.bga_locations: - row, col = location - via_name = f"{design_type}_via_{row}_{col}" - if design_type == "pcb" and self.technology["bga_component"]["enabled"] is True: - dx = self.technology["bga_component"]["fanout_dx"] - dy = self.technology["bga_component"]["fanout_dy"] - x_loc_0 = f"{col}*pitch" - y_loc_0 = f"{row}*pitch" - x_loc = f"{x_loc_0}+{dx}" - y_loc = f"{y_loc_0}-{dy}" - - trace_name = f"bga_fanout_trace_via_{row}_{col}" - trace_width = self.technology["bga_component"]["fanout_width"] - trace = { - "path": [[x_loc_0, y_loc_0], [x_loc, y_loc]], - "width": trace_width, - "name": trace_name, - "layer": "PCB_TOP", - "net_name": "GND", - } - traces.append(trace) - else: - x_loc = f"{col}*pitch" - y_loc = f"{row}*pitch" - - pd = { - "name": f"{design_type}_{via_name}_cvia_{row}_{col}", - "definition": "blind_via" if design_type == "pkg" else "pcb_via", - "layer_range": [ - ground_vias.pattern_1_params["core_via_start_layer"], - ground_vias.pattern_1_params["core_via_stop_layer"], - ], - "net_name": "GND", - "position": [x_loc, y_loc], - "is_pin": False, - } - pd_insts.append(pd) - - if design_type == "pkg": - flag = True - for idx, layer in enumerate(self.signal_layers_pkg): - if layer == ground_vias.pattern_1_params["core_via_start_layer"]: - flag = False - elif layer == ground_vias.pattern_1_params["core_via_stop_layer"]: - flag = True - - if idx < len(self.signal_layers_pkg) - 1: - if flag: - start_layer = layer - end_layer = self.signal_layers_pkg[idx + 1] - else: - start_layer = ground_vias.pattern_1_params["core_via_start_layer"] - end_layer = ground_vias.pattern_1_params["core_via_stop_layer"] - else: - break - - if flag: - angle = 0 - for micro_via_idx, i in enumerate(["pi*1/2"] * 4): - angle = f"{angle} + {i}" - guvia_1_x_loc = f"{x_loc}+cos({angle})*{distance}" - guvia_1_y_loc = f"{y_loc}+sin({angle})*{distance}" - - uvia_pd = { - "name": f"{design_type}_{via_name}_uvia_{start_layer}_{end_layer}_{micro_via_idx}", - "definition": "micro_via", - "net_name": "GND", - "position": [guvia_1_x_loc, guvia_1_y_loc], - "layer_range": [start_layer, end_layer], - "is_pin": False, - } - pd_insts.append(uvia_pd) - - self._padstack_instances.extend(pd_insts) - self._variables.extend(variables) - self._traces.extend(traces) - self._voids.extend(voids) - - return variables, traces, voids, pd_insts - - def _create_signal_via_transition(self, diff_pair: Pair, design_type): # pragma no cover - trace_layer_name = "stop_layer" if design_type == "pkg" else "start_layer" - - fanout_polarity = "1" if design_type == "pkg" else "-1" - variables = [] - pd_insts = [] - traces = [] - voids = [] - - local_traces = [] - for net_idx, net_name in enumerate(diff_pair.net_names): - if design_type == "pkg": - pn_coef = 1 if net_idx == 0 else -1 - else: - pn_coef = 1 - x_loc = f"{diff_pair.bga_locations[net_idx][1]}*pitch" - y_loc = f"{diff_pair.bga_locations[net_idx][0]}*pitch" - - path = [[x_loc, y_loc]] - for _, v in enumerate(diff_pair.vias): - trace_layer = v[trace_layer_name] - if v.get("trace", True): # whether to create trace - dx = f"{diff_pair.name}_via_{trace_layer}_dx" - if net_idx == 1: - variables.append({"name": dx, "value": v["dx"]}) - - dy = f"{diff_pair.name}_via_{trace_layer}_dy" - if net_idx == 1: - variables.append({"name": dy, "value": v["dy"]}) - - x_loc = f"{x_loc}+{dx}*({pn_coef})" - y_loc = f"{y_loc}+{dy}*{fanout_polarity}" - - last_point = [x_loc, y_loc] - path.append(last_point) - trace = v.copy() - trace["net_name"] = net_name - trace["path"] = path - trace["layer"] = trace_layer - - width = f"{diff_pair.name}_via_{trace_layer}_trace_width" - if net_idx == 1: - variables.append({"name": width, "value": v["trace_width"]}) - trace["width"] = width - - clearance = f"{diff_pair.name}_via_{trace_layer}_trace_clearance" - if net_idx == 1: - variables.append({"name": clearance, "value": f"{width}+2*{v['trace_clearance']}"}) - trace["void_width"] = clearance - - local_traces.append(trace) - path = [last_point] - - start_layer = v["start_layer"] - stop_layer = v["stop_layer"] - - pdef = v["padstack_definition"] - if pdef == "micro_via": - start_idx = self.signal_layers.index(start_layer) - stop_idx = self.signal_layers.index(stop_layer) - for i in np.arange(start_idx, stop_idx): - temp_start = self.signal_layers[i] - temp_stop = self.signal_layers[i + 1] - temp = { - "name": f"{net_name}_via_{start_layer}_{stop_layer}_{i}", - "definition": pdef, - "layer_range": [temp_start, temp_stop], - "net_name": net_name, - "position": [x_loc, y_loc], - "is_pin": False, - } - pd_insts.append(temp) - else: - temp = { - "name": f"{net_name}_via_{start_layer}_{stop_layer}", - "definition": pdef, - "layer_range": [start_layer, stop_layer], - "net_name": net_name, - "position": [x_loc, y_loc], - "is_pin": False, - } - - back_drilling = v.get("backdrill_parameters") - if back_drilling: - temp["backdrill_parameters"] = back_drilling - pd_insts.append(temp) - - if True: # v.get("race_track"): - if net_idx % 2 == 0: - diff_pair.p_via_loc.append([x_loc, y_loc]) - else: - diff_pair.n_via_loc.append([x_loc, y_loc]) - - for idx, t in enumerate(local_traces): - net_name = t["net_name"] - path = t["path"] - diff_pair.via_to_via_traces.append(path) - trace = { - "path": path, - "width": t["width"], - "name": f"{net_name}_via_{idx}_trace", - "layer": t["layer"], - "net_name": net_name, - } - traces.append(trace) - - if trace["layer"] not in ["PCB_TOP", "PKG_BOT"]: - void = trace.copy() - void["name"] = f"{net_name}_via_{idx}_clearance" - void["width"] = t["void_width"] - void["void_type"] = "trace" - voids.append(void) - # traces.append(void) - - self._variables.extend(variables) - self._padstack_instances.extend(pd_insts) - self._traces.extend(traces) - self._voids.extend(voids) - return variables, pd_insts, traces, voids - - def _create_planes(self): - planes = [] - - for layer in self.signal_layers: - if layer in ["PCB_TOP", "PKG_BOT"]: - continue - p = dict() - p["name"] = f"GND_{layer}" - p["layer"] = layer - p["net_name"] = "GND" - p["lower_left_point"] = self.lower_left_point - p["upper_right_point"] = self.upper_right_point - temp = [] - for v in self._voids: - if v["layer"] == layer: - temp.append(v["name"]) - if v["void_type"] == "trace": - self._traces.append(v) - elif v["void_type"] == "plane": - self._planes.append(v) - - p["voids"] = temp - planes.append(p) - return planes - - def _create_signal_fanout_type_1(self, params, design_type): - trace_out_direction = 1 if params["trace_out_direction"] == "forward" else -1 - - variables = [] - traces = [] - voids = [] - planes = [] - - die_side_port_location = [] - trace_corner_location = [] - - width, gap, clearance, shift, length = 0, 0, 0, 0, 0 - for idx, via_loc in enumerate(params["fanout_via_location"]): - pn_polarity = -1 if idx == 0 else 1 - - if idx == 0: - width = f"{design_type}_{params['name']}_{params['layer']}_trace_width" - variables.append({"name": width, "value": params["width"], "description": params["name"]}) - gap = f"{design_type}_{params['name']}_{params['layer']}_trace_gap" - variables.append({"name": gap, "value": params["gap"], "description": params["name"]}) - shift = f"{design_type}_{params['name']}_{params['layer']}_trace_corner_shift" - variables.append({"name": shift, "value": params["shift"], "description": params["name"]}) - if design_type == "pkg": - length = f"{design_type}_{params['name']}_{params['layer']}_trace_length" - variables.append({"name": length, "value": params["length"], "description": params["name"]}) - - path = [via_loc] - if design_type == "pkg": - x_loc = f"{params['base_via_loc'][idx][1]}*pitch+{pn_polarity}*(-0.5*pitch+{gap}/2+{width}/2)" - elif design_type == "pcb": - x_loc = f"{params['base_via_loc'][idx][0]}+{pn_polarity}*(-0.5*pitch+{gap}/2+{width}/2)" - - y_loc = f"{via_loc[1]}+{shift}*{trace_out_direction}" - - path.append([x_loc, y_loc]) - trace_corner_location.append([x_loc, y_loc]) - - if design_type == "pkg": - y_loc = f"{y_loc}+{length}*{trace_out_direction}" - elif trace_out_direction == 1: - y_loc = self.upper_right_point[1] - else: - y_loc = "-plane_extend-pitch" - path.append([x_loc, y_loc]) - - trace_name = f"{design_type}_{params['name']}_{params['layer']}_trace_{params['net_names'][idx]}" - trace = { - "path": path, - "width": width, - "name": trace_name, - "layer": params["layer"], - "net_name": params["net_names"][idx], - "start_cap_style": "flat", - "end_cap_style": "flat", - "corner_style": "sharp", - } - if design_type == "pkg": - trace["path"] = path[1:] - traces.append(trace) - - if idx == 0: - clearance = f"{design_type}_{params['name']}_{params['layer']}_trace_clearance" - variables.append({"name": clearance, "value": params["clearance"], "description": params["name"]}) - void = trace.copy() - void["path"] = path - void["width"] = f"{width}+2*{clearance}" - void["name"] = trace_name + "_clearance" - void["end_cap_style"] = "extended" - void["void_type"] = "trace" - - voids.append(void) - # traces.append(void) - - if design_type == "pkg": - # create teardrop - # td_angle1 = f"atan({shift}/(pitch/2-{gap}/2-{width}/2))" - trace_p0_x, trace_p0_y = path[0] - trace_p1_x, trace_p1_y = path[1] - trace_dx = f"abs({trace_p1_x}-({trace_p0_x}))" - trace_dy = f"abs({trace_p1_y}-({trace_p0_y}))" - - td_angle2 = f"atan({trace_dx}/{trace_dy})" - - td_dx = f"$micro_via_pad_diameter/2*cos({td_angle2})" - td_dy = f"$micro_via_pad_diameter/2*sin({td_angle2})" - - p0 = [f"{trace_p1_x}- {width}/2*{pn_polarity}", trace_p1_y] - p1 = [f"{trace_p1_x}+ {width}/2*{pn_polarity}", trace_p1_y] - p2 = [f"{td_dx}*{pn_polarity}", f"{td_dy}*{trace_out_direction}"] - p3 = [f"{td_dx}*{pn_polarity}*-1", f"{td_dy}*-1*{trace_out_direction}"] - - pts2 = [p0, p1] - for i in [p2, p3]: - pts2.append([f"{i[0]}+{via_loc[0]}", f"{i[1]}+{via_loc[1]}"]) - - poly = { - "type": "polygon", - "name": f"{design_type}_{params['name']}_{params['layer']}_teardrop_{params['net_names'][idx]}", - "layer": params["layer"], - "net_name": params["net_names"][idx], - "points": pts2, - } - planes.append(poly) - - last_point = path[-1] - die_side_port_location.append([trace_name, last_point]) - - # create void - v_loc = params["fanout_via_location"].copy() - v_loc.reverse() - pts = trace_corner_location + v_loc - - void = { - "type": "polygon", - "name": f"{design_type}_{params['name']}_{params['layer']}_trapezoid_void", - "layer": params["layer"], - "net_name": "GND", - "points": pts, - "void_type": "plane", - } - voids.append(void) - # planes.append(void) - self._variables.extend(variables) - self._traces.extend(traces) - self._voids.extend(voids) - self._planes.extend(planes) - return die_side_port_location - - def create_edb(self, data) -> str: - edb_path = str(self.working_dir / self.design_name.with_suffix(".aedb")) - print(edb_path) - edbapp = Edb(edbpath=edb_path, edbversion=self.version) - edbapp.configuration.load(data, apply_file=True) - edbapp.save() - edbapp.close() - return edb_path - - def save_cfg_to_file(self, data): - with open(self.working_dir / self.design_name.with_suffix(".json"), "w") as f: - json.dump(data, f, indent=4) diff --git a/src/pyedb/extensions/via_design_backend.py b/src/pyedb/extensions/via_design_backend.py new file mode 100644 index 0000000000..063e8e61c4 --- /dev/null +++ b/src/pyedb/extensions/via_design_backend.py @@ -0,0 +1,681 @@ +from copy import deepcopy as copy +import json +from pathlib import Path +import re +import tempfile +from typing import Union + +import numpy as np +import pandas as pd +import toml + +from pyedb import Edb + + +def create_variable(obj, name_suffix, value): + var_name = f"{obj.name}_{name_suffix}" + var_value = value + obj.variables.append( + {"name": var_name, "value": var_value, "description": f"Net name = {obj.net_name}"}, + ) + return var_name + + +class StitchingVias: + def __init__(self, p_via, start_angle, step_angle, number_of_vias, distance, clockwise=True): + self.p_via = p_via + self.name = f"{self.p_via.name}_stitching_via" + self.start_angle = int(start_angle) + self.step_angle = int(step_angle) + self.number_of_vias = int(number_of_vias) + self.distance = distance + self.clockwise = clockwise + + self.vias = [] + for idx, angle in enumerate( + np.arange(self.start_angle, start_angle + self.step_angle * self.number_of_vias, self.step_angle) + ): + dx = f"cos({angle}deg)*({self.distance}+{self.p_via.anti_pad_diameter}/2)" + dy = f"sin({angle}deg)*({self.distance}+{self.p_via.anti_pad_diameter}/2)" + via = GroundVia( + p_signal=self.p_via.p_signal, + name=f"{self.name}_{idx}", + net_name="GND", + padstack_def=self.p_via.padstack_def, + start_layer=self.p_via.start_layer, + stop_layer=self.p_via.stop_layer, + base_x=self.p_via.x, + base_y=self.p_via.y, + dx=dx, + dy=dy, + flip_dx=self.p_via.flip_dx, + flip_dy=self.p_via.flip_dy, + connection_trace=False, + with_solder_ball=False, + backdrill_parameters=None, + conductor_layers=self.p_via.p_signal.p_board.conductor_layers, + ) + self.vias.append(via) + + def populate_config(self, cfg): + for via in self.vias: + via.populate_config(cfg) + + +class Trace: + def __init__( + self, + p_via, + name, + net_name, + layer, + width, + clearance, + incremental_path: list[list], + flip_dx, + flip_dy, + end_cap_style, + port: Union[dict, None], + ): + self.p_via = p_via + self.variables = [] + self.name = name + self.net_name = net_name + self.layer = layer + self.width = create_variable(self, "width", width) + self.clearance = create_variable(self, "clearance", clearance) + self.flip_dx = flip_dx + self.flip_dy = flip_dy + self.end_cap_style = end_cap_style + self.port = port + + # self.voids = [] + + self.incremental_path = [ + i if idx == 0 else [f"{i[0]}*{-1 if self.flip_dx else 1}", f"{i[1]}*{-1 if self.flip_dy else 1}"] + for idx, i in enumerate(incremental_path) + ] + self.incremental_path = [] + for idx, i in enumerate(incremental_path): + if idx == 0: + self.incremental_path.append(i) + else: + dx = create_variable(self, name_suffix=f"_dx_{idx}", value=i[0]) + dy = create_variable(self, name_suffix=f"_dy_{idx}", value=i[1]) + temp = [f"{dx}*{-1 if self.flip_dx else 1}", f"{dy}*{-1 if self.flip_dy else 1}"] + self.incremental_path.append(temp) + + self.path = [self.incremental_path[0]] + x, y = self.incremental_path[0] + for x0, y0 in self.incremental_path[1:]: + x = f"{x}+({x0})" + y = f"{y}+({y0})" + self.path.append([x, y]) + + def populate_config(self, cfg): + cfg["variables"].extend(self.variables) + trace = { + "name": self.name, + "layer": self.layer, + "width": self.width, + # "incremental_path": self.incremental_path, + "path": self.path, + "net_name": self.net_name, + "start_cap_style": "round", + "end_cap_style": self.end_cap_style, + "corner_style": "round", + } + cfg["modeler"]["traces"].append(trace) + + trace_void = copy(trace) + trace_void["name"] = f"{self.name}_void" + trace_void["width"] = f"{self.width}+2*{self.clearance}" + trace_void["end_cap_style"] = "round" + cfg["modeler"]["traces"].append(trace_void) + self.p_via.p_signal.p_board.voids.append(trace_void) + # self.voids.append(trace_void) + + if self.port is not None: + port = self.get_port_cfg() + cfg["ports"].append(port) + + def get_port_cfg(self): + return { + "name": f"port_{self.name}", + "type": "wave_port", + "primitive_name": self.name, + "point_on_edge": self.path[-1], + "horizontal_extent_factor": self.port["horizontal_extent_factor"], + "vertical_extent_factor": self.port["vertical_extent_factor"], + "pec_launch_width": "0.02mm", + } + + +class GroundVia: + @property + def x(self): + return f"{self.base_x}+{self.dx}" + + @property + def y(self): + return f"{self.base_y}+{self.dy}" + + def __init__( + self, + p_signal, + name, + net_name, + padstack_def, + start_layer, + stop_layer, + base_x, + base_y, + dx, + dy, + flip_dx, + flip_dy, + connection_trace: Union[dict, Trace], + with_solder_ball, + backdrill_parameters, + conductor_layers: list, + **kwargs, + ): + self.p_signal = p_signal + self.variables = [] + self.name = name + self.net_name = net_name + self.padstack_def = padstack_def + self.start_layer = start_layer + self.stop_layer = stop_layer + self.base_x = base_x + self.base_y = base_y + + var_dx = create_variable(self, "dx", dx) + var_dy = create_variable(self, "dy", dy) + self.flip_dx = flip_dx + self.flip_dy = flip_dy + self.dx = var_dx if flip_dx is False else f"-1*({var_dx})" + self.dy = var_dy if flip_dy is False else f"-1*({var_dy})" + self.with_solder_ball = with_solder_ball + self.backdrill_parameters = backdrill_parameters + self.conductor_layers = conductor_layers + + self.traces = [] + self.fanout_traces = [] + # self._voids = [] + + if connection_trace is not False: + trace = Trace( + p_via=self, + name=f"{self.name}_trace", + net_name=self.net_name, + layer=self.stop_layer, + width=connection_trace["width"], + clearance=connection_trace["clearance"], + incremental_path=[[base_x, base_y], [var_dx, var_dy]], + flip_dx=flip_dx, + flip_dy=flip_dy, + end_cap_style="round", + port=None, + ) + self.traces.append(trace) + + def populate_config(self, cfg): + cfg["variables"].extend(self.variables) + + for trace in self.traces: + trace.populate_config(cfg) + + for trace in self.fanout_traces: + trace.populate_config(cfg) + + padstack_instance = { + "name": self.name, + "definition": self.padstack_def, + "layer_range": [self.start_layer, self.stop_layer], + "position": [self.x, self.y], + "net_name": self.net_name, + } + if self.with_solder_ball: + padstack_instance["solder_ball_layer"] = self.start_layer + padstack_instance["layer_range"] = [self.stop_layer, self.stop_layer] + + padstack_instance_upper = copy(padstack_instance) + padstack_instance_upper["layer_range"] = [self.start_layer, self.start_layer] + if self.backdrill_parameters is not False: + padstack_instance["backdrill_parameters"] = self.backdrill_parameters + + cfg["modeler"]["padstack_instances"].append(padstack_instance) + + +class Via(GroundVia): + def __init__( + self, anti_pad_diameter, fanout_trace: list[Union[dict, Trace]], stitching_vias: Union[dict, None], **kwargs + ): + super().__init__(**kwargs) + + self.anti_pad_diameter = create_variable(self, "anti_pad_diameter", anti_pad_diameter) + for t in fanout_trace: + layer = t["layer"] + + incremental_path = copy([[self.x, self.y]]) + incremental_path.extend(t["incremental_path"]) + t_flip_dx = t["flip_dx"] + t_flip_dy = t["flip_dy"] + + trace = Trace( + p_via=self, + name=f"{self.net_name}_{layer}_fanout", + net_name=self.net_name, + layer=layer, + width=t["width"], + clearance=t["clearance"], + incremental_path=incremental_path, + flip_dx=self.flip_dx ^ t_flip_dx, + flip_dy=self.flip_dy ^ t_flip_dy, + end_cap_style=t["end_cap_style"], + port=t["port"], + ) + self.fanout_traces.append(trace) + self.stitching_vias = StitchingVias(self, **stitching_vias) if stitching_vias is not False else False + + def populate_config(self, cfg): + super().populate_config(cfg) + if self.start_layer == self.stop_layer: + anti_pad = { + "type": "circle", + "name": f"{self.name}_anti_pad_{self.start_layer}", + "layer": self.start_layer, + "net_name": self.net_name, + "position": [self.x, self.y], + "radius": f"{self.anti_pad_diameter}/2", + } + cfg["modeler"]["planes"].append(anti_pad) + # self.voids.append(anti_pad) + self.p_signal.p_board.voids.append(anti_pad) + else: + start_layer_idx = self.conductor_layers.index(self.start_layer) + stop_layer_idx = self.conductor_layers.index(self.stop_layer) + for i in np.arange(start_layer_idx, stop_layer_idx + 1): + anti_pad = { + "type": "circle", + "name": f"{self.name}_anti_pad_{self.conductor_layers[i]}", + "layer": self.conductor_layers[i], + "net_name": self.net_name, + "position": [self.x, self.y], + "radius": f"{self.anti_pad_diameter}/2", + } + cfg["modeler"]["planes"].append(anti_pad) + # self.voids.append(anti_pad) + self.p_signal.p_board.voids.append(anti_pad) + + if self.stitching_vias is not False: + self.stitching_vias.populate_config(cfg) + + +class Signal: + """vias and traces.""" + + def __init__( + self, + p_board, + signal_name, + name_suffix: Union[None, str], + base_x, + base_y, + stacked_vias, + flip_x, + flip_y, + ): + self.p_board = p_board + self.net_name = signal_name if name_suffix is None else f"{signal_name}_{name_suffix}" + self.name_suffix = name_suffix + self.base_x = base_x + self.base_y = base_y + + self.vias = [] + x = self.base_x + y = self.base_y + for v_idx, i in enumerate(stacked_vias): + dx = i["dx"] + dy = i["dy"] + + connection_trace = i["connection_trace"] + start_layer = i["start_layer"] + stop_layer = i["stop_layer"] + + flip_x_1 = not i["flip_dx"] if flip_x else i["flip_dx"] + flip_y_1 = not i["flip_dy"] if flip_y else i["flip_dy"] + if i["padstack_def"].startswith("BGA"): + flip_x_1 = False + flip_y_1 = False + + if self.net_name.startswith("GND"): + via_class = GroundVia + name = f"{self.net_name}_{start_layer}_{stop_layer}_{v_idx}" + net_name = "GND" + else: + via_class = Via + name = f"{self.net_name}_{start_layer}_{stop_layer}" + net_name = self.net_name + + via = via_class( + p_signal=self, + name=name, + net_name=net_name, + padstack_def=i["padstack_def"], + start_layer=start_layer, + stop_layer=stop_layer, + base_x=x, + base_y=y, + dx=dx, + dy=dy, + flip_dx=flip_x_1, + flip_dy=flip_y_1, + connection_trace=connection_trace, + with_solder_ball=i["with_solder_ball"], + backdrill_parameters=i["backdrill_parameters"], + conductor_layers=self.p_board.conductor_layers, + stitching_vias=i["stitching_vias"], + anti_pad_diameter=i["anti_pad_diameter"], + fanout_trace=i.get("fanout_trace", []), + ) + x = via.x + y = via.y + self.vias.append(via) + + def populate_config(self, cfg_modeler): + for i in self.vias: + i.populate_config(cfg_modeler) + + +class DiffSignal: + def __init__(self, p_board, name, signals, fanout_trace, stacked_vias): + self.p_board = p_board + self.name = name + self.signal_p_name, self.signal_n_name = signals + self.fanout_trace = fanout_trace + self.stacked_vias = stacked_vias + for i in self.stacked_vias: + i["fanout_trace"] = [] + + self.variables = [] + # self.voids = [] + self.diff_ports = [] + + p_x, p_y = self.p_board.get_signal_location(self.signal_p_name)[0] + n_x, n_y = self.p_board.get_signal_location(self.signal_n_name)[0] + p_x = f"{p_x}*pitch" + p_y = f"{p_y}*pitch" + n_x = f"{n_x}*pitch" + n_y = f"{n_y}*pitch" + + vars_sep = {} + for trace in self.fanout_trace: + via_index = trace["via_index"] + trace2 = dict() + trace2["layer"] = trace["layer"] + trace2["width"] = trace["width"] + trace2["clearance"] = trace["clearance"] + trace2["flip_dx"] = trace["flip_dx"] + trace2["flip_dy"] = trace["flip_dy"] + trace2["end_cap_style"] = trace["end_cap_style"] + trace2["port"] = trace["port"] + + incremental_path_dy = trace["incremental_path_dy"] + incremental_path = [[0, incremental_path_dy[0]], [0, incremental_path_dy[1]]] + trace2["incremental_path"] = incremental_path + + self.stacked_vias[via_index]["fanout_trace"].append(trace2) + + var_separation = f"{self.name}_{trace['layer']}_fanout_separation" + self.variables.append( + {"name": var_separation, "value": trace["separation"]}, + ) + vars_sep[trace["layer"]] = var_separation + + stacked_vias_reversed = list(reversed(stacked_vias)) + + pcb_fanout_center = f"{p_x}+pitch/2" + pkg_fanout_center = f"{p_x}+pitch" + # fanout_x = f"{diff_center}-({var_separation})/2" + + self.signal_p = Signal( + p_board=self.p_board, + signal_name=self.name, + name_suffix="P", + base_x=p_x, + base_y=p_y, + stacked_vias=stacked_vias_reversed, + flip_x=False, + flip_y=False, + ) + + for v in self.signal_p.vias: + for t in v.fanout_traces: + var_sep = vars_sep[t.layer] + if t.layer.startswith("PCB"): + t.path[1][0] = f"{pcb_fanout_center}-{var_sep}" + t.path[2][0] = f"{pcb_fanout_center}-{var_sep}" + else: + t.path[1][0] = f"{pkg_fanout_center}-{var_sep}" + t.path[2][0] = f"{pkg_fanout_center}-{var_sep}" + + self.signal_n = Signal( + p_board=self.p_board, + signal_name=self.name, + name_suffix="N", + base_x=n_x, + base_y=n_y, + stacked_vias=stacked_vias_reversed, + flip_x=True, + flip_y=False, + ) + for v in self.signal_n.vias: + for t in v.fanout_traces: + var_sep = vars_sep[t.layer] + if t.layer.startswith("PCB"): + t.path[1][0] = f"{pcb_fanout_center}+{var_sep}" + t.path[2][0] = f"{pcb_fanout_center}+{var_sep}" + else: + t.path[1][0] = f"{pkg_fanout_center}+{var_sep}" + t.path[2][0] = f"{pkg_fanout_center}+{var_sep}" + + for v_idx, v in enumerate(self.signal_p.vias): + for t_idx, t_p in enumerate(v.fanout_traces): + port_p = t_p.get_port_cfg() + t_p.port = None + t_n = self.signal_n.vias[v_idx].fanout_traces[t_idx] + port_n = t_n.get_port_cfg() + t_n.port = None + pattern = r"^(.*)_([NPnp])_(.*)$" + m1 = re.match(pattern, port_p["name"]) + + diff_port = { + "name": f"{m1.group(1)}_{m1.group(3)}", + "type": "diff_wave_port", + "positive_terminal": { + "primitive_name": port_p["primitive_name"], + "point_on_edge": port_p["point_on_edge"], + }, + "negative_terminal": { + "primitive_name": port_n["primitive_name"], + "point_on_edge": port_n["point_on_edge"], + }, + "horizontal_extent_factor": port_p["horizontal_extent_factor"], + "vertical_extent_factor": port_n["vertical_extent_factor"], + "pec_launch_width": "0.02mm", + } + self.diff_ports.append(diff_port) + + def populate_config(self, cfg): + cfg["variables"].extend(self.variables) + self.signal_p.populate_config(cfg) + # self.voids.extend(self.signal_p.voids) + self.signal_n.populate_config(cfg) + # self.voids.extend(self.signal_n.voids) + cfg["ports"].extend(self.diff_ports) + + +class Board: + @property + def conductor_layers(self): + return [i["name"] for i in self.stackup if i["type"] == "signal"] + + def __init__(self, stackup, padstack_defs, outline_extent, pitch, pin_map, signals, differential_signals): + self.voids = [] + self.variables = [{"name": "pitch", "value": pitch, "description": ""}] + + self.stackup = stackup + self.padstack_defs = padstack_defs + self.outline_extent = outline_extent + + self.pin_map = pin_map + self.signals = self.parser_signals(signals) if signals is not False else [] + self.differential_signals = ( + self.parser_differential_signals(differential_signals) if differential_signals is not False else [] + ) + + def get_signal_location(self, signal_name): + pin_map = pd.DataFrame(self.pin_map) + temp = (pin_map == signal_name).stack() + xy = [[i[1], i[0]] for i in temp[temp].index.tolist()] + return xy + + def parser_signals(self, data): + signals = [] + + for name, signal_data in data.items(): + fanout = signal_data["fanout_trace"] + stacked_vias = signal_data["stacked_vias"] + for f in fanout: + idx = f["via_index"] + stacked_vias[idx]["fanout_trace"].append(f) + + stacked_vias_reversed = list(reversed(stacked_vias)) + for x, y in self.get_signal_location(name): + s = Signal( + p_board=self, + signal_name=name if name != "GND" else f"{name}_{x}{y}", + name_suffix=None, + base_x=f"{x}*pitch", + base_y=f"{y}*pitch", + stacked_vias=stacked_vias_reversed, + flip_x=False, + flip_y=False, + ) + signals.append(s) + return signals + + def parser_differential_signals(self, data): + diff_signals = [] + for name, temp in data.items(): + signals = temp["signals"] + fanout_trace = temp["fanout_trace"] + stacked_vias = temp["stacked_vias"] + diff_signal = DiffSignal(self, name, signals, fanout_trace, stacked_vias) + diff_signals.append(diff_signal) + return diff_signals + + def populate_config(self, cfg): + cfg["variables"].extend(self.variables) + + cfg["stackup"]["layers"] = self.stackup + for p in self.padstack_defs: + regular_pad = [] + for layer in self.conductor_layers: + regular_pad.append( + { + "layer_name": layer, + "shape": "circle", + "diameter": p["pad_diameter"], + } + ) + pdef = copy(p) + pdef["material"] = "copper" + pdef["hole_range"] = "upper_pad_to_lower_pad" + pdef["pad_parameters"] = {"regular_pad": regular_pad} + pdef["hole_parameters"] = { + "shape": "circle", + "diameter": p["hole_diameter"], + } + + cfg["modeler"]["padstack_definitions"].append(pdef) + + # voids = [] + for signal in self.signals: + signal.populate_config(cfg) + # voids.extend(signal.voids) + + for diff_signal in self.differential_signals: + diff_signal.populate_config(cfg) + # voids.extend(diff_signal.voids) + + matrix = np.array(self.pin_map) + y_size_count, x_size_count = matrix.shape + x_lower_left = f"-1*({self.outline_extent})" + x_upper_right = f"({self.outline_extent})+({x_size_count}-1)*pitch" + y_lower_left = f"-1*({self.outline_extent})" + y_upper_right = f"({self.outline_extent})+({y_size_count}-1)*pitch" + for l in self.conductor_layers: + p = { + "type": "rectangle", + "name": f"GND_{l}", + "layer": l, + "net_name": "GND", + "lower_left_point": [x_lower_left, y_lower_left], + "upper_right_point": [x_upper_right, y_upper_right], + "voids": [], + } + for v in self.voids: + if v["layer"] == l: + p["voids"].append(v["name"]) + cfg["modeler"]["planes"].append(p) + + +class ViaDesignBackend: + _OUTPUT_DIR = None + + @property + def output_dir(self): + if self._OUTPUT_DIR is None: + output_dir = self.cfg["general"]["output_dir"] + if output_dir == "": + self._OUTPUT_DIR = Path(tempfile.TemporaryDirectory(suffix=".ansys").name) + else: + self._OUTPUT_DIR = Path(output_dir) + return self._OUTPUT_DIR + + def __init__(self, cfg): + cfg_json = { + "stackup": {"layers": [], "materials": []}, + "variables": [], + "ports": [], + "modeler": {"traces": [], "planes": [], "padstack_definitions": [], "padstack_instances": []}, + } + + if isinstance(cfg, str): + self.cfg = toml.load(cfg) if cfg.endswith(".toml") else json.load(cfg) + else: + self.cfg = cfg + self.version = self.cfg["general"]["version"] + outline_extent = self.cfg["general"]["outline_extent"] + pitch = self.cfg["general"]["pitch"] + + board = Board( + stackup=self.cfg["stackup"], + padstack_defs=self.cfg["padstack_defs"], + outline_extent=outline_extent, + pitch=pitch, + pin_map=self.cfg["pin_map"], + signals=self.cfg["signals"], + differential_signals=self.cfg["differential_signals"], + ) + board.populate_config(cfg_json) + + self.app = Edb( + edbpath=str((Path(self.output_dir) / self.cfg["title"]).with_suffix(".aedb")), edbversion=self.version + ) + self.app.configuration.load(cfg_json, apply_file=True) + self.app.save_edb() + self.app.close_edb() diff --git a/tests/legacy/system/test_edb_configuration_2p0.py b/tests/legacy/system/test_edb_configuration_2p0.py index 79972da6c4..382c3244ac 100644 --- a/tests/legacy/system/test_edb_configuration_2p0.py +++ b/tests/legacy/system/test_edb_configuration_2p0.py @@ -544,7 +544,7 @@ def test_05h_diff_wave_port(self, edb_examples): "negative_terminal": {"primitive_name": prim_2.aedt_name, "point_on_edge": ["1mm", "1mm"]}, "horizontal_extent_factor": 6, "vertical_extent_factor": 4, - "pec_launch_width": "0,2mm", + "pec_launch_width": "0.2mm", } ] } @@ -1237,7 +1237,12 @@ def test_18_modeler(self, edb_examples): "end_cap_style": "flat", "corner_style": "round", }, - {"name": "trace_1_void", "layer": "TOP", "width": "0.3mm", "path": [[0, 0], [0, "10mm"]]}, + { + "name": "trace_1_void", + "layer": "TOP", + "width": "0.3mm", + "incremental_path": [[0, 0], [0, "10mm"]], + }, ], "padstack_definitions": [ { diff --git a/tests/legacy/system/test_extensions.py b/tests/legacy/system/test_extensions.py index eec7e0bb8b..dc9036b319 100644 --- a/tests/legacy/system/test_extensions.py +++ b/tests/legacy/system/test_extensions.py @@ -19,41 +19,17 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from copy import deepcopy as copy -import json from pathlib import Path import pytest -from pyedb.extensions.pre_layout_design_toolkit.via_design import ViaDesignConfig -from pyedb.generic.general_methods import is_linux +from pyedb.extensions.via_design_backend import ViaDesignBackend from tests.conftest import desktop_version pytestmark = [pytest.mark.unit, pytest.mark.legacy] -pcb_stackup = [ - {"name": "PCB_TOP", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "50um"}, - {"name": "PCB_DE0", "type": "dielectric", "material": "fr4", "thickness": "100um"}, - {"name": "PCB_L2", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE1", "type": "dielectric", "material": "fr4", "thickness": "125um"}, - {"name": "PCB_L3", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE2", "type": "dielectric", "material": "fr4", "thickness": "100um"}, - {"name": "PCB_L4", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE3", "type": "dielectric", "material": "fr4", "thickness": "125um"}, - {"name": "PCB_L5", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE4", "type": "dielectric", "material": "fr4", "thickness": "100um"}, - {"name": "PCB_L6", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE5", "type": "dielectric", "material": "fr4", "thickness": "125um"}, - {"name": "PCB_L7", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE6", "type": "dielectric", "material": "fr4", "thickness": "100um"}, - {"name": "PCB_L8", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE7", "type": "dielectric", "material": "fr4", "thickness": "125um"}, - {"name": "PCB_L9", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, - {"name": "PCB_DE8", "type": "dielectric", "material": "fr4", "thickness": "100um"}, - {"name": "PCB_BOT", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "50um"}, -] -pkg_stackup = [ - {"name": "PKG_TOP", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "22um"}, +STACKUP = [ + {"name": "PKG_L1", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "22um"}, {"name": "PKG_DE0", "type": "dielectric", "material": "fr4", "thickness": "30um"}, {"name": "PKG_L2", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "15um"}, {"name": "PKG_DE1", "type": "dielectric", "material": "fr4", "thickness": "30um"}, @@ -71,212 +47,58 @@ {"name": "PKG_DE7", "type": "dielectric", "material": "fr4", "thickness": "30um"}, {"name": "PKG_L9", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "15um"}, {"name": "PKG_DE8", "type": "dielectric", "material": "fr4", "thickness": "30um"}, - {"name": "PKG_BOT", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "22um"}, + {"name": "PKG_L10", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "22um"}, + {"name": "AIR", "type": "dielectric", "material": "air", "thickness": "400um"}, + {"name": "PCB_L1", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "50um"}, + {"name": "PCB_DE0", "type": "dielectric", "material": "fr4", "thickness": "100um"}, + {"name": "PCB_L2", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE1", "type": "dielectric", "material": "fr4", "thickness": "125um"}, + {"name": "PCB_L3", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE2", "type": "dielectric", "material": "fr4", "thickness": "100um"}, + {"name": "PCB_L4", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE3", "type": "dielectric", "material": "fr4", "thickness": "125um"}, + {"name": "PCB_L5", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE4", "type": "dielectric", "material": "fr4", "thickness": "100um"}, + {"name": "PCB_L6", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE5", "type": "dielectric", "material": "fr4", "thickness": "125um"}, + {"name": "PCB_L7", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE6", "type": "dielectric", "material": "fr4", "thickness": "100um"}, + {"name": "PCB_L8", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE7", "type": "dielectric", "material": "fr4", "thickness": "125um"}, + {"name": "PCB_L9", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "17um"}, + {"name": "PCB_DE8", "type": "dielectric", "material": "fr4", "thickness": "100um"}, + {"name": "PCB_L10", "type": "signal", "material": "copper", "fill_material": "fr4", "thickness": "50um"}, ] -padstacks = [ +PADSTACK_DEFS = [ { - "name": "pcb_via", + "name": "CORE_VIA", "shape": "circle", - "pad_diameter": "0.5mm", - "hole_diameter": "0.3mm", - "anti_pad_diameter": "0.6mm", + "pad_diameter": "0.25mm", + "hole_diameter": "0.1mm", "hole_range": "upper_pad_to_lower_pad", }, { - "name": "micro_via", + "name": "MICRO_VIA", "shape": "circle", "pad_diameter": "0.1mm", - "x_size": "0.1mm", - "y_size": "0.15mm", - "anti_pad_diameter": "0.3mm", "hole_diameter": "0.05mm", "hole_range": "upper_pad_to_lower_pad", }, { - "name": "bga", - "shape": "rectangle", - "pad_diameter": "0.25mm", - "x_size": "0.4mm", - "y_size": "0.4mm", - "anti_pad_diameter": "0.6mm", - }, - { - "name": "blind_via", + "name": "BGA", "shape": "circle", - "pad_diameter": "0.25mm", - "anti_pad_diameter": "0.6mm", - "hole_diameter": "0.1mm", + "pad_diameter": "0.5mm", + "hole_diameter": "0.4mm", "hole_range": "upper_pad_to_lower_pad", - "is_core": True, + "solder_ball_parameters": { + "shape": "spheroid", + "diameter": "0.4mm", + "mid_diameter": "0.5mm", + "placement": "above_padstack", + "material": "solder", + }, }, ] -materials = [ - {"name": "copper", "conductivity": 58000000.0}, - {"name": "fr4", "permittivity": 4.4, "dielectric_loss_tangent": 0.02}, -] -technology = { - "plane_extend": "1mm", - "pitch": "1mm", - "bga_component": { - "enabled": False, - "solder_ball_shape": "spheroid", - "solder_ball_diameter": "300um", - "solder_ball_mid_diameter": "400um", - "solder_ball_height": "200um", - "fanout_dx": "0.4mm", - "fanout_dy": "0.4mm", - "fanout_width": "0.3mm", - "fanout_clearance": "0.15mm", - }, - "pkg_ground_via": {"distance": "0.2mm", "core_via_start_layer": "PKG_L2", "core_via_stop_layer": "PKG_L3"}, -} -setup = { - "name": "hfss_1", - "type": "hfss", - "f_adapt": "5GHz", - "max_num_passes": 10, - "max_mag_delta_s": 0.02, - "freq_sweep": [ - { - "name": "Sweep1", - "type": "interpolation", - "frequencies": [{"distribution": "log_scale", "start": 1000000.0, "stop": 1000000000.0, "increment": 20}], - } - ], -} -pin_map = { - "signal_pairs": {"S0": ["S0_P", "S0_N"], "S1": ["S1_P", "S1_N"]}, - "locations": [ - ["GND", "S0_P", "S0_N", "GND", "GND"], - ["GND", "GND", "S1_P", "S1_N", "GND"], - ], -} -main = { - "version": desktop_version, - "working_directory": None, - "design_type": "pcb", - "materials": "materials.json", - "pcb_stackup": "pcb_stackup.json", - "padstacks": "padstacks.json", - "pin_map": "pin_map.json", - "technology": "technology.json", - "setup": "setup.json", -} - -main_pkg_w_pcb = copy(main) -main_pkg_w_pcb["design_type"] = "pkg" -main_pkg_w_pcb["include_pcb"] = True -main_pkg_w_pcb["pkg_stackup"] = "pkg_stackup.json" - -S0 = { - "pcb_trace": [ - { - "width": "0.075mm", - "gap": "0.1mm", - "length": "0.5mm", - "clearance": "0.1mm", - "shift": "0.5mm", - "layer": "PCB_L3", - "trace_out_direction": "backward", - } - ], - "pcb_signal_via": [ - { - "padstack_definition": "pcb_via", - "start_layer": "PCB_TOP", - "stop_layer": "PCB_BOT", - "trace": True, - "trace_width": "0.4mm", - "trace_clearance": "0.15mm", - "dx": "0mm", - "dy": "0mm", - "backdrill_parameters": { - "from_bottom": { - "drill_to_layer": "PCB_L3", - "diameter": "0.5mm", - "stub_length": "0.05mm", - } - }, - } - ], -} -S0_pcb = copy(S0) -S0_pcb["pcb_trace"].append( - { - "width": "0.075mm", - "gap": "0.1mm", - "length": "0.5mm", - "clearance": "0.1mm", - "shift": "0.5mm", - "layer": "PCB_TOP", - "trace_out_direction": "forward", - } -) -S0_pkg_w_pcb = copy(S0) -S0_pkg_w_pcb["pkg_signal_via"] = [ - { - "padstack_definition": "micro_via", - "start_layer": "PKG_L7", - "stop_layer": "PKG_BOT", - "trace": True, - "trace_width": "0.05mm", - "trace_clearance": "0.05mm", - "dx": "0.05mm", - "dy": "0mm", - }, - { - "padstack_definition": "micro_via", - "start_layer": "PKG_L6", - "stop_layer": "PKG_L7", - "trace": True, - "trace_width": "0.05mm", - "trace_clearance": "0.05mm", - "dx": "0.05mm", - "dy": "0mm", - }, - { - "padstack_definition": "blind_via", - "start_layer": "PKG_L5", - "stop_layer": "PKG_L6", - "trace": True, - "trace_width": "0.05mm", - "trace_clearance": "0.05mm", - "dx": "0.05mm", - "dy": "0mm", - }, - { - "padstack_definition": "micro_via", - "start_layer": "PKG_L3", - "stop_layer": "PKG_L5", - "trace": True, - "trace_width": "0.05mm", - "trace_clearance": "0.05mm", - "dx": "0.1mm", - "dy": "0mm", - }, - { - "padstack_definition": "micro_via", - "start_layer": "PKG_TOP", - "stop_layer": "PKG_L3", - "trace": True, - "trace_width": "0.05mm", - "trace_clearance": "0.05mm", - "dx": "0.05mm", - "dy": "0.05mm", - }, -] -S0_pkg_w_pcb["pkg_trace"] = [ - { - "width": "0.05mm", - "gap": "0.05mm", - "length": "0.2mm", - "clearance": "0.05mm", - "stitching_via_dy": "0.4mm", - "shift": "0.2mm", - "layer": "PKG_TOP", - "trace_out_direction": "forward", - } -] class TestClass: @@ -285,75 +107,502 @@ def init(self, local_scratch): working_dir = Path(local_scratch.path) self.working_dir = working_dir - with open(working_dir / "materials.json", "w") as f: - json.dump(materials, f, indent=4, ensure_ascii=False) - - with open(working_dir / "padstacks.json", "w") as f: - json.dump(padstacks, f, indent=4, ensure_ascii=False) - - with open(working_dir / "pcb_stackup.json", "w") as f: - json.dump(pcb_stackup, f, indent=4, ensure_ascii=False) - - with open(working_dir / "pkg_stackup.json", "w") as f: - json.dump(pkg_stackup, f, indent=4, ensure_ascii=False) - - with open(self.working_dir / "setup.json", "w") as f: - json.dump(setup, f, indent=4, ensure_ascii=False) - - with open(self.working_dir / "pin_map.json", "w") as f: - json.dump(pin_map, f, indent=4, ensure_ascii=False) - - with open(self.working_dir / "main.json", "w") as f: - json.dump(main, f, indent=4, ensure_ascii=False) - - with open(self.working_dir / "main_pkg_w_pcb.json", "w") as f: - json.dump(main_pkg_w_pcb, f, indent=4, ensure_ascii=False) - - @pytest.mark.skipif(is_linux, reason="Failing on linux") - def test_01_pre_layout_design_toolkit_pcb_diff_via(self): - signal_pair = { - "S0": S0_pcb, - "S1": S0_pcb, - } - pcb_ground_via = { - "distance": "0.2mm", - "core_via_start_layer": "PCB_TOP", - "core_via_stop_layer": "PCB_BOT", + self.cfg = { + "stackup": {"layers": [], "materials": []}, + "variables": [], + "ports": [], + "modeler": {"traces": [], "planes": [], "padstack_definitions": [], "padstack_instances": []}, } - local_tech = technology.copy() - local_tech["signal_pair"] = signal_pair - local_tech["pcb_ground_via"] = pcb_ground_via - with open(self.working_dir / "technology.json", "w") as f: - json.dump(local_tech, f, indent=4, ensure_ascii=False) - - config_file_path = self.working_dir / "main.json" - app = ViaDesignConfig(config_file_path, desktop_version) - data = app.create_design() - app.save_cfg_to_file(data) - edb_path = app.create_edb(data) - - assert edb_path - @pytest.mark.skipif(is_linux, reason="Failing on linux") - def test_02_pre_layout_design_toolkit_pcb_pkg_diff_via(self): - signal_pair = { - "S0": S0_pkg_w_pcb, - "S1": S0_pkg_w_pcb, + def test_backend_single(self): + cfg = { + "title": "Test Design", + "general": { + "version": desktop_version, + "output_dir": "", + "outline_extent": "1mm", + "pitch": "1mm", + }, + "stackup": STACKUP, + "padstack_defs": PADSTACK_DEFS, + "pin_map": [ + ["GND", "SIG", "GND"], + ], + "signals": { + "SIG": { + "fanout_trace": [ + { + "via_index": 0, + "layer": "PKG_L1", + "width": "0.05mm", + "clearance": "0.05mm", + "flip_dx": False, + "flip_dy": False, + "incremental_path": [[0, "0.5mm"]], + "end_cap_style": "flat", + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + { + "via_index": 3, + "layer": "PCB_L6", + "width": "0.1mm", + "clearance": "0.2mm", + "flip_dx": False, + "flip_dy": True, + "incremental_path": [[0, "1mm"]], + "end_cap_style": "flat", + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + ], + "stacked_vias": [ + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L1", + "stop_layer": "PKG_L5", + "dx": "0.05mm", + "dy": "0.05mm", + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": {"width": "0.1mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PKG_L5", + "stop_layer": "PKG_L6", + "dx": "0.2mm", + "dy": "0mm", + "anti_pad_diameter": "0.5mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.1mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": {"start_angle": 0, "step_angle": 45, "number_of_vias": 6, "distance": 0}, + }, + { + "padstack_def": "BGA", + "start_layer": "PKG_L10", + "stop_layer": "PCB_L1", + "dx": "pitch/2", + "dy": "pitch/2", + "anti_pad_diameter": "0.8mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.3mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + "GND": { + "fanout_trace": {}, + "stacked_vias": [ + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L1", + "stop_layer": "PKG_L5", + "dx": 0, + "dy": 0, + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PKG_L5", + "stop_layer": "PKG_L6", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.5mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L6", + "stop_layer": "PKG_L10", + "dx": 0, + "dy": 0, + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "BGA", + "start_layer": "PKG_L10", + "stop_layer": "PCB_L1", + "dx": "pitch/2", + "dy": "pitch/2", + "anti_pad_diameter": "0.8mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.3mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": True, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + }, + "differential_signals": {}, } - pcb_ground_via = { - "distance": "0.2mm", - "core_via_start_layer": "PCB_TOP", - "core_via_stop_layer": "PCB_BOT", + app = ViaDesignBackend(cfg) + + def test_backend_diff(self): + cfg = { + "title": "Test Design", + "general": { + "version": desktop_version, + "output_dir": "", + "outline_extent": "1mm", + "pitch": "1mm", + }, + "stackup": STACKUP, + "padstack_defs": PADSTACK_DEFS, + "pin_map": [ + ["GND", "SIG_1_P", "SIG_1_N", "GND"], + ], + "signals": { + "GND": { + "fanout_trace": {}, + "stacked_vias": [ + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L1", + "stop_layer": "PKG_L5", + "dx": 0, + "dy": 0, + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PKG_L5", + "stop_layer": "PKG_L6", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.5mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L6", + "stop_layer": "PKG_L10", + "dx": 0, + "dy": 0, + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "BGA", + "start_layer": "PKG_L10", + "stop_layer": "PCB_L1", + "dx": "pitch/2", + "dy": "pitch/2", + "anti_pad_diameter": "0.8mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.3mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": True, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + }, + "differential_signals": { + "SIG_1": { + "signals": ["SIG_1_P", "SIG_1_N"], + "fanout_trace": [ + { + "via_index": 0, + "layer": "PKG_L1", + "width": "0.05mm", + "separation": "0.05mm", + "clearance": "0.05mm", + "incremental_path_dy": ["0.3mm", "0.3mm"], + "end_cap_style": "flat", + "flip_dx": False, + "flip_dy": False, + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + { + "via_index": 4, + "layer": "PCB_L6", + "width": "0.1mm", + "separation": "0.15mm", + "clearance": "0.2mm", + "incremental_path_dy": ["0.1mm", "0.5mm"], + "flip_dx": False, + "flip_dy": True, + "end_cap_style": "flat", + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + ], + "stacked_vias": [ + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L1", + "stop_layer": "PKG_L5", + "dx": "0.05mm", + "dy": "0.05mm", + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": {"width": "0.1mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PKG_L5", + "stop_layer": "PKG_L6", + "dx": "0.2mm", + "dy": "0mm", + "anti_pad_diameter": "1mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.1mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": { + "start_angle": 90, + "step_angle": 45, + "number_of_vias": 5, + "distance": "0.125mm", + }, + }, + { + "padstack_def": "MICRO_VIA", + "start_layer": "PKG_L6", + "stop_layer": "PKG_L10", + "dx": "0.05mm", + "dy": "0.05mm", + "flip_dx": False, + "flip_dy": False, + "anti_pad_diameter": "0.5mm", + "connection_trace": {"width": "0.1mm", "clearance": "0.15mm"}, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "BGA", + "start_layer": "PKG_L10", + "stop_layer": "PCB_L1", + "dx": "pitch/2", + "dy": "pitch/2", + "anti_pad_diameter": "0.8mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": {"width": "0.3mm", "clearance": "0.15mm"}, + "with_solder_ball": True, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + }, } - local_tech = technology.copy() - local_tech["signal_pair"] = signal_pair - local_tech["pcb_ground_via"] = pcb_ground_via - with open(self.working_dir / "technology.json", "w") as f: - json.dump(local_tech, f, indent=4, ensure_ascii=False) + app = ViaDesignBackend(cfg) - config_file_path = self.working_dir / "main_pkg_w_pcb.json" - app = ViaDesignConfig(config_file_path, desktop_version) - data = app.create_design() - app.save_cfg_to_file(data) - edb_path = app.create_edb(data) - assert edb_path + def test_backend_diff_pcb(self): + cfg = { + "title": "Test Design", + "general": { + "version": desktop_version, + "output_dir": "", + "outline_extent": "1mm", + "pitch": "1mm", + }, + "stackup": STACKUP, + "padstack_defs": PADSTACK_DEFS, + "pin_map": [ + ["GND", "SIG_1_P", "SIG_1_N", "GND"], + ], + "signals": { + "GND": { + "fanout_trace": {}, + "stacked_vias": [ + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": True, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": False, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + }, + "differential_signals": { + "SIG_1": { + "signals": ["SIG_1_P", "SIG_1_N"], + "fanout_trace": [ + { + "via_index": 0, + "layer": "PCB_L1", + "width": "0.05mm", + "separation": "0.05mm", + "clearance": "0.05mm", + "incremental_path_dy": ["0.3mm", "0.3mm"], + "end_cap_style": "flat", + "flip_dx": False, + "flip_dy": False, + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + { + "via_index": 0, + "layer": "PCB_L6", + "width": "0.1mm", + "separation": "0.15mm", + "clearance": "0.2mm", + "incremental_path_dy": ["0.1mm", "0.5mm"], + "flip_dx": False, + "flip_dy": True, + "end_cap_style": "flat", + "port": {"horizontal_extent_factor": 6, "vertical_extent_factor": 4}, + }, + ], + "stacked_vias": [ + { + "padstack_def": "CORE_VIA", + "start_layer": "PCB_L1", + "stop_layer": "PCB_L10", + "dx": 0, + "dy": 0, + "anti_pad_diameter": "0.7mm", + "flip_dx": False, + "flip_dy": False, + "connection_trace": False, + "with_solder_ball": False, + "backdrill_parameters": { + "from_bottom": { + "drill_to_layer": "PCB_L6", + "diameter": "1.2mm", + "stub_length": "0.15mm", + }, + }, + "fanout_trace": list(), + "stitching_vias": False, + }, + ], + }, + }, + } + app = ViaDesignBackend(cfg)