From 4fe635a05fa5c4aa2ca688dff7942f28a66bc17f Mon Sep 17 00:00:00 2001 From: FriedrichWu Date: Wed, 20 Nov 2024 16:24:26 +0100 Subject: [PATCH] add route_outside, remove unused methods --- compiler/base/hierarchy_layout.py | 77 ++-- compiler/modules/sram_1bank.py | 33 +- compiler/router/router.py | 6 +- compiler/router/router_tech.py | 2 + compiler/router/supply_placer.py | 591 +++++++++++++++++++++++++++--- compiler/sram.py | 4 +- 6 files changed, 611 insertions(+), 102 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index bdb7258b..7562bb69 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1913,8 +1913,8 @@ class layout(): # Just use the power pin function for now to save code self.add_power_pin(new_name, pin.center(), start_layer=start_layer, directions=directions) - def add_power_pin_no_via(self, name, loc, directions=None, start_layer="m1"): - # same function like normal one, but do not add via to the gird layer + def add_power_pin_m2(self, name, loc, directions=None, start_layer="m1"): + # same function like normal one, but add power pin at m2 # Hack for min area if OPTS.tech_name == "sky130": min_area = drc["minarea_{}".format(self.pwr_grid_layers[1])] @@ -1925,7 +1925,7 @@ class layout(): height = None pin = None - if start_layer in self.pwr_grid_layers: + if start_layer == "m2": pin = self.add_layout_pin_rect_center(text=name, layer=start_layer, offset=loc, @@ -1933,7 +1933,7 @@ class layout(): height=height) else: via = self.add_via_stack_center(from_layer=start_layer, - to_layer=start_layer,# so only enclosure shape will be added + to_layer="m2", offset=loc, directions=directions) @@ -1942,7 +1942,7 @@ class layout(): if not height: height = via.height pin = self.add_layout_pin_rect_center(text=name, - layer=start_layer, + layer="m2", offset=loc, width=width, height=height) @@ -2063,7 +2063,7 @@ class layout(): layer=layer, offset=peri_pin_loc) - def add_dnwell(self, bbox=None, inflate=1, add_vias=True): + def add_dnwell(self, bbox=None, inflate=1, route_option="classic"): """ Create a dnwell, along with nwell moat at border. """ if "dnwell" not in tech_layer: @@ -2108,9 +2108,9 @@ class layout(): tap_spacing = 2 nwell_offset = vector(self.nwell_width, self.nwell_width) - # Every nth tap is connected to gnd + # Every nth tap is connected to vdd period = 5 - + moat_pins = [] # BOTTOM count = 0 loc = ll + nwell_offset.scale(tap_spacing, 0) @@ -2125,14 +2125,10 @@ class layout(): to_layer="m1", offset=loc) else: - if add_vias: - self.add_power_pin(name="vdd", - loc=loc, - start_layer="li") - else: - self.add_power_pin_no_via(name="vdd", - loc=loc, - start_layer="li") + pin = self.add_power_pin(name="vdd", + loc=loc, + start_layer="li") + moat_pins.append(pin) count += 1 loc += nwell_offset.scale(tap_spacing, 0) @@ -2150,14 +2146,10 @@ class layout(): to_layer="m1", offset=loc) else: - if add_vias: - self.add_power_pin(name="vdd", - loc=loc, - start_layer="li") - else: - self.add_power_pin_no_via(name="vdd", - loc=loc, - start_layer="li") + pin = self.add_power_pin(name="vdd", + loc=loc, + start_layer="li") + moat_pins.append(pin) count += 1 loc += nwell_offset.scale(tap_spacing, 0) @@ -2175,14 +2167,15 @@ class layout(): to_layer="m2", offset=loc) else: - if add_vias: - self.add_power_pin(name="vdd", - loc=loc, - start_layer="li") - else: - self.add_power_pin_no_via(name="vdd", - loc=loc, - start_layer="li") + if route_option == "classic": + pin = self.add_power_pin(name="vdd", + loc=loc, + start_layer="li") + elif route_option == "fast": + pin = self.add_power_pin_m2(name="vdd", + loc=loc, + start_layer="li") + moat_pins.append(pin) count += 1 loc += nwell_offset.scale(0, tap_spacing) @@ -2200,19 +2193,21 @@ class layout(): to_layer="m2", offset=loc) else: - if add_vias: - self.add_power_pin(name="vdd", - loc=loc, - start_layer="li") - else: - self.add_power_pin_no_via(name="vdd", - loc=loc, - start_layer="li") + if route_option == "classic": + pin = self.add_power_pin(name="vdd", + loc=loc, + start_layer="li") + elif route_option == "fast": + pin = self.add_power_pin_m2(name="vdd", + loc=loc, + start_layer="li") + moat_pins.append(pin) count += 1 loc += nwell_offset.scale(0, tap_spacing) - # Add the gnd ring + # Add the vdd ring self.add_ring([ll, ur]) + return moat_pins def add_ring(self, bbox=None, width_mult=8, offset=0): """ diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 88349dcf..0c5056d0 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -53,8 +53,10 @@ class sram_1bank(design, verilog, lef): # delay control logic does not have RBLs self.has_rbl = OPTS.control_logic != "control_logic_delay" - # IO pins, except power, list of pin names + # IO pins, except power, list of pin names self.pins_to_route = [] + # vdd pins on moat, list of pins + self.moat_pins = [] def add_pins(self): """ Add pins for entire SRAM. """ @@ -248,6 +250,7 @@ class sram_1bank(design, verilog, lef): debug.error("Must override pure virtual function.", -1) def route_supplies_constructive(self, bbox=None): + """ Corresponding supply router for io_pin_placer """ # prepare the "router" from openram.router.supply_placer import supply_placer as router rtr = router(layers=self.supply_stack, @@ -255,22 +258,32 @@ class sram_1bank(design, verilog, lef): bbox=bbox, pin_type=OPTS.supply_pin_type, ext_vdd_name=self.vdd_name, - ext_gnd_name=self.gnd_name) + ext_gnd_name=self.gnd_name, + moat_pins=self.moat_pins) + # add power rings / side pins if OPTS.supply_pin_type in ["top", "bottom", "right", "left"]: - rtr.add_side_pin(self.vdd_name) - rtr.add_side_pin(self.gnd_name) + rtr.add_side_pin("vdd") + rtr.add_side_pin("gnd")# noraml gnd name elif OPTS.supply_pin_type == "ring": - rtr.add_ring_pin(self.vdd_name)# ring vdd name - rtr.add_ring_pin(self.gnd_name) + rtr.add_ring_pin("vdd") + rtr.add_ring_pin("gnd")# normal gnd name else: debug.warning("Side supply pins aren't created.") - # maze router the bank power pins + # Prepare the inside power pins (all power pins of submodules), at m3 for pin_name in ["vdd", "gnd"]: - for inst in self.bank_insts: + for inst in self.insts: self.copy_power_pins(inst, pin_name) - rtr.route_bank() + # Route all the power pins + #rtr.route_inside() # only connecting the power pins of inside submodules with each other, moat pins will connect to the ring + rtr.route_outside(io_pin_names=self.pins_to_route) + # route moat vdds + #rtr.route_moat(self.pins_to_route) + + # route to the outside + #rtr.prepare_escape_pins() + def route_supplies(self, bbox=None): """ Route the supply grid and connect the pins to them. """ @@ -1114,7 +1127,7 @@ class sram_1bank(design, verilog, lef): self.add_layout_pins() # Some technologies have an isolation - self.add_dnwell(inflate=2.5, add_vias=False) + self.moat_pins = self.add_dnwell(inflate=2.5, route_option=route_option) init_bbox = self.get_bbox() # Route the supplies together and/or to the ring/stripes. diff --git a/compiler/router/router.py b/compiler/router/router.py index 4b58a51e..cf3c17ff 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -112,10 +112,10 @@ class router(router_tech): def find_pins_inside(self, pin_name): - # find pins except moat, power ring, the moat pins will be store as list and return + # find pins except moat, power ring, the moat pins will be store as set and return """ Find the pins with the given name. """ debug.info(4, "Finding all pins for {}".format(pin_name)) - moat_pins = [] + shape_list = self.layout.getAllPinShapes(str(pin_name)) pin_set = set() for shape in shape_list: @@ -130,7 +130,6 @@ class router(router_tech): continue # skip the moat pin if self.check_pin_on_moat(new_pin): - moat_pins.append(new_pin) continue # Merge previous pins into this one if possible self.merge_shapes(new_pin, pin_set) @@ -138,7 +137,6 @@ class router(router_tech): # Add these pins to the 'pins' dict self.pins[pin_name] = pin_set self.all_pins.update(pin_set) - return moat_pins def check_pin_on_moat(self, pin_shape): diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 7a9fffeb..97cefe09 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -89,6 +89,8 @@ class router_tech: # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) self.layer_widths = [self.track_wire, 1, self.track_wire] + # via2 to via3 distance requirements + self.via2_via3_pitch = 0.5 * drc("minwidth_{}".format("via2")) + 0.5 * drc("minwidth_{}".format("via3")) + drc["via3_to_via2"] def same_lpp(self, lpp1, lpp2): """ diff --git a/compiler/router/supply_placer.py b/compiler/router/supply_placer.py index bec2b07c..e782b788 100644 --- a/compiler/router/supply_placer.py +++ b/compiler/router/supply_placer.py @@ -12,7 +12,7 @@ from .router import router class supply_placer(router): - def __init__(self, layers, design, bbox=None, pin_type=None, ext_vdd_name="vccd1", ext_gnd_name="vssd1"): + def __init__(self, layers, design, bbox=None, pin_type=None, ext_vdd_name="vccd1", ext_gnd_name="vssd1", moat_pins=None): # `router` is the base router class router.__init__(self, layers, design, bbox) @@ -28,14 +28,23 @@ class supply_placer(router): # instances self.insts = self.design.insts # moat pins - self.moat_pins = [] + self.moat_pins = moat_pins + # store a graphshape of intermediate points(if shift)/source points(if no shift) when connecting moat_pins to outside + # trick: since in the creation of dnwell, these pins are "ordered" added, so they'are also ordered here + # order inside list: left -> right or bottom -> up + self.moat_pins_left = [] + self.moat_pins_right = [] + self.moat_pins_top = [] + self.moat_pins_bottom = [] # io pins self.io_pins_left = [] self.io_pins_right = [] self.io_pins_top = [] self.io_pins_bottom = [] - def route_bank(self, vdd_name="vdd", gnd_name="gnd"): + + def route_outside(self, vdd_name="vdd", gnd_name="gnd", io_pin_names=None): + # only connect supply with inside submodules, not connecting to the power ring debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name)) # Save pin names @@ -46,7 +55,75 @@ class supply_placer(router): self.prepare_gds_reader() # Find vdd/gnd pins of bank, to be routed - self.moat_pins = self.find_pins_inside(vdd_name) + self.find_pins_inside(vdd_name) + self.find_pins_inside(gnd_name) + self.route_moat(io_pin_names) + # Find blockages and vias + self.find_blockages() + self.find_vias() + + # Convert blockages and vias if they overlap a pin + self.convert_vias() + self.convert_blockages() + + # Add vdd and gnd pins as blockages as well + # NOTE: This is done to make vdd and gnd pins DRC-safe + for pin in self.all_pins: + self.blockages.append(self.inflate_shape(pin)) + + # Prepare the selected moat pins + selected_moat_pins = self.prepare_selected_moat_pins() + # Route vdd and gnd + routed_count = 0 + routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name]) + for pin_name in [vdd_name, gnd_name]: + if pin_name == gnd_name: # otherwise will not recognaize the moat blocakge + self.prepare_gds_reader() + # Find blockages and vias + self.find_blockages() + self.find_vias() + + # Convert blockages and vias if they overlap a pin + self.convert_vias() + self.convert_blockages() + + pins = self.pins[pin_name] + # Route closest pins according to the minimum spanning tree + for source, target in self.get_mst_with_ring(list(pins), selected_moat_pins, pin_name): + # Create the graph + g = graph(self) + g.create_graph(source, target) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If no path is found, throw an error + if path is None: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Create the path shapes on layout + new_wires, new_vias = self.add_path(path) + # Find the recently added shapes + self.find_blockages(pin_name, new_wires) + self.find_vias(new_vias) + # Report routed count + routed_count += 1 + debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max)) + # finsih + self.replace_layout_pins() + + + def route_inside(self, vdd_name="vdd", gnd_name="gnd"): + # only connect supply with inside submodules, not connecting to the power ring + debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name)) + + # Save pin names + self.vdd_name = vdd_name + self.gnd_name = gnd_name + + # Prepare gdsMill to find pins and blockages + self.prepare_gds_reader() + + # Find vdd/gnd pins of bank, to be routed + self.find_pins_inside(vdd_name) self.find_pins_inside(gnd_name) # Find blockages and vias @@ -86,14 +163,152 @@ class supply_placer(router): # Report routed count routed_count += 1 debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max)) + # finsih + self.replace_layout_pins() - #def route_moat(self): + def route_moat(self, io_pin_names): + # route the vdd pins at the moat + # io_pin_names is a list + # the moat vdd shape will also be created and stored in the list + for moat_pin in self.moat_pins: + self.check_overlap(moat_pin, io_pin_names) + + + def replace_layout_pins(self): + # clear all the inside "vdd " "gnd" at sram module level + # Copy the pin shape(s) to rectangles + for pin_name in ["vdd", "gnd"]: + # Copy the pin shape(s) to rectangles + for pin in self.design.get_pins(pin_name): + self.design.add_rect(pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + # Remove the pin shape(s) + self.design.remove_layout_pin(pin_name) - #def route_other(self): + # Get new pins, change the name of ring to extern supply name + # vccd1 ring + pins = self.get_new_pins("vdd") + for pin in pins: + self.design.add_layout_pin(self.ext_vdd_name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + # vssd1 ring + pins = self.get_new_pins("gnd") + for pin in pins: + self.design.add_layout_pin(self.ext_gnd_name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + + def prepare_selected_moat_pins(self): + """ Selcet the possibe moat pins, feed into the MST, where will decide which of these pin should be connected to which pin """ + if len(self.design.all_ports) > 1: + # in order to save runtime + # top -> moat pins all + # bottom -> moat pins all + # left -> moat pins near control logic + # right -> moat pins near control logic + # expected connection for control logic + + # for port 0 -> left + start_y = self.design.control_logic_insts[0].by()# bottom edge y value + end_y = self.design.control_logic_insts[0].uy()# up edge y value + # filter the pin in the range + filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y] + + # for port 1 -> right + start_y = self.design.control_logic_insts[1].by()# bottom edge y value + end_y = self.design.control_logic_insts[1].uy()# up edge y value + # filter the pin in the range + filtered_moat_pins_right = [pin for pin in self.moat_pins_right if start_y <= pin.center().y <= end_y] + # return the selected moat pins + selected_moat_pins = [] + selected_moat_pins.extend(filtered_moat_pins_left) + selected_moat_pins.extend(self.moat_pins_bottom) + selected_moat_pins.extend(filtered_moat_pins_right) + selected_moat_pins.extend(self.moat_pins_top) + return selected_moat_pins + + else: # only 1 port + # in order to save runtime + # top -> moat pins all + # bottom -> moat pins all + # left -> moat pins near control logic + # right -> moat pins all + start_y = self.design.control_logic_insts[0].by()# bottom edge y value + end_y = self.design.control_logic_insts[0].uy()# up edge y value + # filter the pin in the range + filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y] + # return the selected moat pins + selected_moat_pins = [] + selected_moat_pins.extend(filtered_moat_pins_left) + selected_moat_pins.extend(self.moat_pins_bottom) + selected_moat_pins.extend(self.moat_pins_right) + selected_moat_pins.extend(self.moat_pins_top) + return selected_moat_pins + + + def prepare_escape_pins(self): + # clear all the inside "vdd " "gnd" at sram module level + # Copy the pin shape(s) to rectangles + for pin_name in ["vdd", "gnd"]: + # Copy the pin shape(s) to rectangles + for pin in self.design.get_pins(pin_name): + self.design.add_rect(pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + # Remove the pin shape(s) + self.design.remove_layout_pin(pin_name) + + # prepare pins for every instances + for pin_name in ["vdd"]: + count =0 + for pin in self.design.control_logic_insts[0].get_pins(pin_name): + debug.warning("vdd pin shape -> ll:{0} ur:{1}".format(pin.ll(), pin.ur())) + """ + count = count + 1 + new_pin = graph_shape("gnd", pin.rect, pin.layer) + source = new_pin + source_center_x = pin.center().x + target_ur_y = self.new_pins[self.ext_gnd_name][1].center().y + 0.5 * self.new_pins[self.ext_gnd_name][1].height() + ll = vector(source_center_x - 2.6 - 0.5 * self.track_wire, pin.center().y - 0.5 * self.track_wire)#target_ur_y - self.track_wire) + ur = vector(source_center_x - 2.6 + 0.5 * self.track_wire, pin.center().y + 0.5 * self.track_wire)#target_ur_y) + target = graph_shape("fake", [ll,ur], pin.layer) + # Create the graph + g = graph(self) + g.create_graph(source, target) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If no path is found, throw an error + if path is None: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Create the path shapes on layout + new_wires, new_vias = self.add_path(path) + # Find the recently added shapes + self.find_blockages(pin_name, new_wires) + self.find_vias(new_vias) + """ + debug.warning("vdd of wmask number -> {0}".format(count)) + debug.warning("instance postion -> {0} {1}".format(self.design.control_logic_insts[0].lx(), self.design.control_logic_insts[0].by())) + + # print pins_all + for pin in self.all_pins: + debug.warning("all_pins -> {0}".format(pin)) + def check_overlap(self, moat_pin, io_pin_names): - # use all the IO pins(at correspoding edge) to check overlap, check 1 moat vdd pin, give the corresponding target/source position as list, and pull the source up to m3 + # use all the IO pins(at correspoding edge) to check overlap, check 1 moat vdd pin, give the corresponding target/source position as list, and connect them add_distance = 0 direction = 1 self.prepare_io_pins(io_pin_names) @@ -101,36 +316,294 @@ class supply_placer(router): edge = self.get_closest_edge(moat_pin) source_center = moat_pin.center() if edge == "bottom": + add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only pin_too_close = any(abs(io_pin.center().x - source_center.x) < self.track_width for io_pin in self.io_pins_bottom) - tmp_center = source_center + tmp_center = vector(source_center.x, source_center.y) while pin_too_close: - tmp_center = source_center + tmp_center = vector(source_center.x, source_center.y) add_distance = add_distance + 0.1 if direction == 1: # right shift - tmp_center = tmp_center + add_distance + tmp_center = vector((tmp_center.x + add_distance), tmp_center.y) else: # left shift - tmp_center = tmp_center - add_distance + tmp_center = vector((tmp_center.x - add_distance), tmp_center.y) pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < self.track_width for io_pin in self.io_pins_bottom) + direction = - direction + # the nearst vdd ring + vdd_ring = self.new_pins["vdd"][1] # order in list -> "top", "bottom", "right", "left"] + # bottom ring's y position at it's top + target_egde_y = vdd_ring.center().y + 0.5 * vdd_ring.height() if tmp_center == source_center: # no overlap - # no jog, direct pull to m3 - self.design.copy_power_pin(moat_pin, loc=None, directions=None, new_name="") + # no jog, direct return the source/target center position + # the target center position, should consider enought space for via + target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire)) + source_point = vector(tmp_center.x, tmp_center.y) + point_list = [source_point, target_point] + self.add_wire(point_list, vertical=True) + # store the shape of moat pins, need for route later + ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire) + ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m4") + self.moat_pins_bottom.append(moat_pin_route) else: # need jog # shift the center # add rectangle at same layer (original) - self.design.add + intermediate_point = vector(tmp_center.x, tmp_center.y) + source_point = vector(source_center.x, source_center.y) + target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire)) + point_list = [source_point, intermediate_point, target_point] + self.add_wire(point_list, vertical=True) + # store the shape of moat pins, need for route later + ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire) + ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m4") + self.moat_pins_bottom.append(moat_pin_route) elif edge == "top": - pass + add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only + pin_too_close = any(abs(io_pin.center().x - source_center.x) < self.track_width for io_pin in self.io_pins_top) + tmp_center = vector(source_center.x, source_center.y) + while pin_too_close: + tmp_center = vector(source_center.x, source_center.y) + add_distance = add_distance + 0.1 + if direction == 1: # right shift + tmp_center = vector((tmp_center.x + add_distance), tmp_center.y) + else: # left shift + tmp_center = vector((tmp_center.x - add_distance), tmp_center.y) + pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < self.track_width for io_pin in self.io_pins_top) + direction = - direction + # the nearst vdd ring + vdd_ring = self.new_pins["vdd"][0] # order in list -> "top", "bottom", "right", "left"] + # top ring's y position at it's bottom + target_egde_y = vdd_ring.center().y - 0.5 * vdd_ring.height() + if tmp_center == source_center: # no overlap + # no jog, direct return the source/target center position + # the target center position, should consider enought space for via + target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire)) + source_point = vector(tmp_center.x, tmp_center.y) + point_list = [source_point, target_point] + self.add_wire(point_list, vertical=True) + # store the shape of moat pins, need for route later + ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire) + ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m4") + self.moat_pins_top.append(moat_pin_route) + else: # need jog + # shift the center + # add rectangle at same layer (original) + intermediate_point = vector(tmp_center.x, tmp_center.y) + source_point = vector(source_center.x, source_center.y) + target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire)) + point_list = [source_point, intermediate_point, target_point] + self.add_wire(point_list, vertical=True) + # store the shape of moat pins, need for route later + ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire) + ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m4") + self.moat_pins_top.append(moat_pin_route) elif edge == "left": - pass + pin_too_close = any(abs(io_pin.center().y - source_center.y) < self.track_width for io_pin in self.io_pins_left) + tmp_center = vector(source_center.x, source_center.y) + while pin_too_close: + tmp_center = vector(source_center.x, source_center.y) + add_distance = add_distance + 0.1 + if direction == 1: # up shift + tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance)) + else: # down shift + tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance)) + pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < self.track_width for io_pin in self.io_pins_left) + direction = - direction + # the nearst vdd ring + vdd_ring = self.new_pins["vdd"][3] # order in list -> "top", "bottom", "right", "left"] + # left ring's x position at it's right + target_egde_x = vdd_ring.center().x + 0.5 * vdd_ring.width() + if tmp_center == source_center: # no overlap + # no jog, direct return the source/target center position + # the target center position, should consider enought space for via + target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y) + source_point = vector(tmp_center.x, tmp_center.y) + point_list = [source_point, target_point] + self.add_wire(point_list, vertical=False) + # store the shape of moat pins, need for route later + ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire) + ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m3") + self.moat_pins_left.append(moat_pin_route) + else: # need jog + # shift the center + # add rectangle at same layer (original) + intermediate_point = vector(tmp_center.x, tmp_center.y) + source_point = vector(source_center.x, source_center.y) + target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y) + point_list = [source_point, intermediate_point, target_point] + self.add_wire(point_list, vertical=False) + # store the shape of moat pins, need for route later + ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire) + ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m3") + self.moat_pins_left.append(moat_pin_route) else: #right - pass + pin_too_close = any(abs(io_pin.center().y - source_center.y) < self.track_width for io_pin in self.io_pins_right) + tmp_center = vector(source_center.x, source_center.y) + while pin_too_close: + tmp_center = vector(source_center.x, source_center.y) + add_distance = add_distance + 0.1 + if direction == 1: # up shift + tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance)) + else: # down shift + tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance)) + pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < self.track_width for io_pin in self.io_pins_right) + direction = - direction + # the nearst vdd ring + vdd_ring = self.new_pins["vdd"][2] # order in list -> "top", "bottom", "right", "left"] + # right ring's y position at it's left + target_egde_x = vdd_ring.center().x - 0.5 * vdd_ring.width() + if tmp_center == source_center: # no overlap + # no jog, direct return the source/target center position + # the target center position, should consider enought space for via + target_point = vector((target_egde_x + 0.5 * self.track_wire), tmp_center.y) + source_point = vector(tmp_center.x, tmp_center.y) + point_list = [source_point, target_point] + self.add_wire(point_list, vertical=False) + # store the shape of moat pins, need for route later + ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire) + ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m3") + self.moat_pins_right.append(moat_pin_route) + else: # need jog + # shift the center + # add rectangle at same layer (original) + intermediate_point = vector(tmp_center.x, tmp_center.y) + source_point = vector(source_center.x, source_center.y) + target_point = vector((target_egde_x + 0.5 * self.track_wire) ,tmp_center.y) + point_list = [source_point, intermediate_point, target_point] + self.add_wire(point_list, vertical=False) + # store the shape of moat pins, need for route later + ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire) + ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire) + rect = [ll, ur] + moat_pin_route = graph_shape("vdd", rect, "m3") + self.moat_pins_right.append(moat_pin_route) + + + def add_wire(self, point_list, vertical=False): + if vertical == True: # m4 line, need start via3(m3 -> m4), end via3(m3 -> m4) + if len(point_list) == 2: # direct connect + # start via + self.add_via(point=point_list[0], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[0], + from_layer="m4", + to_layer="m4") # shape + # connection + self.add_line(point_1=point_list[0], + point_2=point_list[1], + layer="m4") + # end via + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[1], + from_layer="m4", + to_layer="m4") # shape + elif len(point_list) == 3: # need intermediate point + # jog + self.add_line(point_1=point_list[0], + point_2=point_list[1], + layer="m3") + # start_via + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m3") # shape + # connection + self.add_line(point_1=point_list[1], + point_2=point_list[2], + layer="m4") + # end via + self.add_via(point=point_list[2], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[2], + from_layer="m4", + to_layer="m4") # shape + else: # m3 line, need start via2(m2 -> m3), end via3(m3 -> m4) + if len(point_list) == 2: # direct connect + # start via + self.add_via(point=point_list[0], + from_layer="m2", + to_layer="m3") + self.add_via(point=point_list[0], + from_layer="m3", + to_layer="m3") # shape + # connection + self.add_line(point_1=point_list[0], + point_2=point_list[1], + layer="m3") + # end via + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m3") # shape + elif len(point_list) == 3: # need intermediate point + # jog + self.add_line(point_1=point_list[0], + point_2=point_list[1], + layer="m2") + # start_via + self.add_via(point=point_list[1], + from_layer="m2", + to_layer="m3") + self.add_via(point=point_list[1], + from_layer="m3", + to_layer="m3") # shape + # connection + self.add_line(point_1=point_list[1], + point_2=point_list[2], + layer="m3") + # end via + self.add_via(point=point_list[2], + from_layer="m3", + to_layer="m4") + self.add_via(point=point_list[2], + from_layer="m3", + to_layer="m3") # shape + + + def add_line(self, point_1, point_2, layer="m3"): # "m2", "m3", "m4" + self.design.add_path(layer, [point_1, point_2], self.track_wire) + + + def add_via(self, point, from_layer="m3", to_layer="m4"): + # via could be via2(m2 -> m3), via3(m3 -> m4) + # or a shape at same layer + if from_layer == to_layer: + self.design.add_rect_center(layer=from_layer, + offset=point, + width=self.track_wire, + height=self.track_wire) + else: + self.design.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=point) + def prepare_io_pins(self, io_pin_names): # io_pin_names is a list # find all the io pins for pin_name in io_pin_names: self.find_pins(pin_name)# pin now in self.pins - io_pin = self.pins[pin_name] + io_pin = next(iter(self.pins[pin_name])) self.find_closest_edge(io_pin) @@ -139,6 +612,7 @@ class supply_placer(router): ll, ur = self.bbox point = pin.center() + debug.warning("moat pin center -> {0}".format(point)) # Snap the pin to the perimeter and break the iteration ll_diff_x = abs(point.x - ll.x) ll_diff_y = abs(point.y - ll.y) @@ -159,6 +633,7 @@ class supply_placer(router): """ Use to find the edge, where the io pin locats """ ll, ur = self.bbox + #debug.warning("pin -> {0}".format(pin)) point = pin.center() # Snap the pin to the perimeter and break the iteration ll_diff_x = abs(point.x - ll.x) @@ -182,7 +657,7 @@ class supply_placer(router): ll, ur = self.bbox vertical = side in ["left", "right"] - inner = pin_name == self.ext_vdd_name + inner = pin_name == "vdd" # Calculate wires' wideness wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1) @@ -225,29 +700,7 @@ class supply_placer(router): width=shape_width, height=shape_height) - # Add fake pins on this new pin evenly - fake_pins = [] - if vertical: - space = (shape_height - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1) - start_offset = vector(offset.x, offset.y + wideness) - else: - space = (shape_width - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1) - start_offset = vector(offset.x + wideness, offset.y) - for i in range(1, num_fake_pins + 1): - if vertical: - offset = vector(start_offset.x, start_offset.y + i * (space + self.track_wire)) - ll = vector(offset.x, offset.y - self.track_wire) - ur = vector(offset.x + wideness, offset.y) - else: - offset = vector(start_offset.x + i * (space + self.track_wire), start_offset.y) - ll = vector(offset.x - self.track_wire, offset.y) - ur = vector(offset.x, offset.y + wideness) - rect = [ll, ur] - fake_pin = graph_shape(name=pin_name, - rect=rect, - layer_name_pp=layer) - fake_pins.append(fake_pin) - return pin, fake_pins + return pin def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4): @@ -256,7 +709,7 @@ class supply_placer(router): # Add side pins new_pins = [] for side in ["top", "bottom", "right", "left"]: - new_shape, fake_pins = self.add_side_pin(pin_name, side, num_vias, num_fake_pins) + new_shape = self.add_side_pin(pin_name, side, num_vias, num_fake_pins) ll, ur = new_shape.rect rect = [ll, ur] layer = self.get_layer(side in ["left", "right"]) @@ -264,8 +717,6 @@ class supply_placer(router): rect=rect, layer_name_pp=layer) new_pins.append(new_pin) - #self.pins[pin_name].update(fake_pins) - self.fake_pins.extend(fake_pins) # Add vias to the corners shift = self.track_wire + self.track_space @@ -338,6 +789,56 @@ class supply_placer(router): return mst_pairs + def get_mst_with_ring(self, pins, ring_pins, pin_name="vdd"): + """ + Extend the MST logic to connect internal pins to the nearest external ring pins. + """ + # Prepare the pins that are allowed to connect to the moat pins. + # Specical handle gnd ring + candidate_pins = [] + max_distance = 13 + if pin_name == "gnd": + ring_pins = [] + ring_pins = self.new_pins[pin_name] + for pin in pins: + for ring_pin in ring_pins: + dist = pin.distance(ring_pin) + if max_distance is None or dist <= max_distance: + candidate_pins.append(pin) + break + + # Compute the MST for internal pins + mst_pairs = self.get_mst_pairs(pins) + + # Connect each internal pin to the nearest external ring pin + used_ring_pins = set() + internal_to_ring_pairs = [] + for pin in candidate_pins: + min_distance = float("inf") + nearest_ring_pin = None + + for ring_pin in ring_pins: + if pin_name == "vdd" and ring_pin in used_ring_pins: + continue + + dist = pin.distance(ring_pin) + if dist < min_distance: + min_distance = dist + nearest_ring_pin = ring_pin + + # Add the connection to the nearest ring pin + if nearest_ring_pin: + internal_to_ring_pairs.append((pin, nearest_ring_pin)) + # Mark the ring pin as used if the pin is VDD + if pin_name == "vdd": + used_ring_pins.add(nearest_ring_pin) + + # Combine internal MST pairs and external connections + full_connections = mst_pairs + internal_to_ring_pairs + + return full_connections + + def get_new_pins(self, name): """ Return the new supply pins added by this router. """ diff --git a/compiler/sram.py b/compiler/sram.py index 997f5746..f7e9b46d 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -169,7 +169,7 @@ class sram(): spname = OPTS.output_path + self.s.name + ".sp" debug.print_raw("SP: Writing to {0}".format(spname)) self.sp_write(spname) - #''' #comment the following state when generating big sram, and then disable drc/lvs, because maigc_ext stuck + ''' #comment the following state when generating big sram, and then disable drc/lvs, because maigc_ext stuck # Save a functional simulation file with default period functional(self.s, spname, @@ -191,7 +191,7 @@ class sram(): d.targ_write_ports = [self.s.write_ports[0]] d.write_delay_stimulus() print_time("DELAY", datetime.datetime.now(), start_time) - #''' #comment the above when generating big sram, and then disable drc/lvs, bevause magic_ext stuck + ''' #comment the above when generating big sram, and then disable drc/lvs, bevause magic_ext stuck # Save trimmed spice file temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path) self.sp_write(temp_trim_sp, lvs=False, trim=True)