diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 1e2add8d..e603bdc3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1161,6 +1161,8 @@ class layout(): height=ur.y - ll.y, width=ur.x - ll.x) + self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()] + def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None): """ Add a layer that surrounds the given instances. Useful @@ -1341,7 +1343,182 @@ class layout(): layer=layer, offset=peri_pin_loc) - def add_power_ring(self, bbox): + def add_dnwell(self, bbox=None, inflate=1): + """ Create a dnwell, along with nwell moat at border. """ + + if "dnwell" not in techlayer: + return + + if not bbox: + bbox = [self.find_lowest_coords(), + self.find_highest_coords()] + + # Find the corners + [ll, ur] = bbox + + # Possibly inflate the bbox + nwell_offset = vector(self.nwell_width, self.nwell_width) + ll -= nwell_offset.scale(inflate, inflate) + ur += nwell_offset.scale(inflate, inflate) + + # Other corners + ul = vector(ll.x, ur.y) + lr = vector(ur.x, ll.y) + + # Add the dnwell + self.add_rect("dnwell", + offset=ll, + height=ur.y - ll.y, + width=ur.x - ll.x) + + # Add the moat + self.add_path("nwell", [ll, lr, ur, ul, ll - vector(0, 0.5 * self.nwell_width)]) + + # Add the taps + layer_stack = self.active_stack + tap_spacing = 2 + nwell_offset = vector(self.nwell_width, self.nwell_width) + + # Every nth tap is connected to gnd + period = 5 + + # BOTTOM + count = 0 + loc = ll + nwell_offset.scale(tap_spacing, 0) + end_loc = lr - nwell_offset.scale(tap_spacing, 0) + while loc.x < end_loc.x: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + if count % period: + self.add_via_stack_center(from_layer="li", + to_layer="m1", + offset=loc) + else: + self.add_power_pin(name="gnd", + loc=loc, + start_layer="li") + count += 1 + loc += nwell_offset.scale(tap_spacing, 0) + + # TOP + count = 0 + loc = ul + nwell_offset.scale(tap_spacing, 0) + end_loc = ur - nwell_offset.scale(tap_spacing, 0) + while loc.x < end_loc.x: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + if count % period: + self.add_via_stack_center(from_layer="li", + to_layer="m1", + offset=loc) + else: + self.add_power_pin(name="gnd", + loc=loc, + start_layer="li") + count += 1 + loc += nwell_offset.scale(tap_spacing, 0) + + # LEFT + count = 0 + loc = ll + nwell_offset.scale(0, tap_spacing) + end_loc = ul - nwell_offset.scale(0, tap_spacing) + while loc.y < end_loc.y: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + if count % period: + self.add_via_stack_center(from_layer="li", + to_layer="m2", + offset=loc) + else: + self.add_power_pin(name="gnd", + loc=loc, + start_layer="li") + count += 1 + loc += nwell_offset.scale(0, tap_spacing) + + # RIGHT + count = 0 + loc = lr + nwell_offset.scale(0, tap_spacing) + end_loc = ur - nwell_offset.scale(0, tap_spacing) + while loc.y < end_loc.y: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + if count % period: + self.add_via_stack_center(from_layer="li", + to_layer="m2", + offset=loc) + else: + self.add_power_pin(name="gnd", + loc=loc, + start_layer="li") + count += 1 + loc += nwell_offset.scale(0, tap_spacing) + + # Add the gnd ring + self.add_ring([ll, ur]) + + def add_ring(self, bbox=None, width_mult=8, offset=0): + """ + Add a ring around the bbox + """ + # Ring size/space/pitch + wire_width = self.m2_width * width_mult + half_width = 0.5 * wire_width + wire_space = self.m2_space + wire_pitch = wire_width + wire_space + + # Find the corners + if not bbox: + bbox = [self.find_lowest_coords(), + self.find_highest_coords()] + + [ll, ur] = bbox + ul = vector(ll.x, ur.y) + lr = vector(ur.x, ll.y) + ll += vector(-offset * wire_pitch, + -offset * wire_pitch) + lr += vector(offset * wire_pitch, + -offset * wire_pitch) + ur += vector(offset * wire_pitch, + offset * wire_pitch) + ul += vector(-offset * wire_pitch, + offset * wire_pitch) + + half_offset = vector(half_width, half_width) + self.add_path("m1", [ll - half_offset.scale(1, 0), lr + half_offset.scale(1, 0)], width=wire_width) + self.add_path("m1", [ul - half_offset.scale(1, 0), ur + half_offset.scale(1, 0)], width=wire_width) + self.add_path("m2", [ll - half_offset.scale(0, 1), ul + half_offset.scale(0, 1)], width=wire_width) + self.add_path("m2", [lr - half_offset.scale(0, 1), ur + half_offset.scale(0, 1)], width=wire_width) + + # Find the number of vias for this pitch + supply_vias = 1 + from sram_factory import factory + while True: + c = factory.create(module_type="contact", + layer_stack=self.m1_stack, + dimensions=(supply_vias, supply_vias)) + if c.second_layer_width < wire_width and c.second_layer_height < wire_width: + supply_vias += 1 + else: + supply_vias -= 1 + break + + via_points = [ll, lr, ur, ul] + for pt in via_points: + self.add_via_center(layers=self.m1_stack, + offset=pt, + size=(supply_vias, + supply_vias)) + + def add_power_ring(self): """ Create vdd and gnd power rings around an area of the bounding box argument. Must have a supply_rail_width and supply_rail_pitch @@ -1350,7 +1527,7 @@ class layout(): modules.. """ - [ll, ur] = bbox + [ll, ur] = self.bbox supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 9db18ab1..ce1eef1c 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -110,24 +110,25 @@ class lef: # For each pin, remove the blockage and add the pin for pin_name in self.pins: - pin = self.get_pin(pin_name) - inflated_pin = pin.inflated_pin(multiple=1) - another_iteration_needed = True - while another_iteration_needed: - another_iteration_needed = False - old_blockages = list(self.blockages[pin.layer]) - for blockage in old_blockages: - if blockage.overlaps(inflated_pin): - intersection_shape = blockage.intersection(inflated_pin) - # If it is zero area, don't add the pin - if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: - continue - another_iteration_needed = True - # Remove the old blockage and add the new ones - self.blockages[pin.layer].remove(blockage) - intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) - new_blockages = blockage.cut(intersection_pin) - self.blockages[pin.layer].extend(new_blockages) + pins = self.get_pins(pin_name) + for pin in pins: + inflated_pin = pin.inflated_pin(multiple=1) + another_iteration_needed = True + while another_iteration_needed: + another_iteration_needed = False + old_blockages = list(self.blockages[pin.layer]) + for blockage in old_blockages: + if blockage.overlaps(inflated_pin): + intersection_shape = blockage.intersection(inflated_pin) + # If it is zero area, don't add the pin + if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: + continue + another_iteration_needed = True + # Remove the old blockage and add the new ones + self.blockages[pin.layer].remove(blockage) + intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) + new_blockages = blockage.cut(intersection_pin) + self.blockages[pin.layer].extend(new_blockages) def lef_write_header(self): """ Header of LEF file """ diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 7886615f..c2f9833a 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -6,6 +6,7 @@ # All rights reserved. # import math +from tech import spice class verilog: @@ -28,10 +29,19 @@ class verilog: else: self.vf.write("\n") + try: + self.vdd_name = spice["power"] + except KeyError: + self.vdd_name = "vdd" + try: + self.gnd_name = spice["ground"] + except KeyError: + self.gnd_name = "gnd" + self.vf.write("module {0}(\n".format(self.name)) self.vf.write("`ifdef USE_POWER_PINS\n") - self.vf.write(" vdd,\n") - self.vf.write(" gnd,\n") + self.vf.write(" {},\n".format(self.vdd_name)) + self.vf.write(" {},\n".format(self.gnd_name)) self.vf.write("`endif\n") for port in self.all_ports: @@ -71,8 +81,8 @@ class verilog: self.vf.write("\n") self.vf.write("`ifdef USE_POWER_PINS\n") - self.vf.write(" inout vdd;\n") - self.vf.write(" inout gnd;\n") + self.vf.write(" inout {};\n".format(self.vdd_name)) + self.vf.write(" inout {};\n".format(self.gnd_name)) self.vf.write("`endif\n") for port in self.all_ports: diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index a700f57f..f7936413 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -15,6 +15,7 @@ from .charutils import * import tech import numpy as np from globals import OPTS +from tech import spice class lib: @@ -22,6 +23,15 @@ class lib: def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay): + try: + self.vdd_name = spice["power"] + except KeyError: + self.vdd_name = "vdd" + try: + self.gnd_name = spice["ground"] + except KeyError: + self.gnd_name = "gnd" + self.out_dir = out_dir self.sram = sram self.sp_file = sp_file @@ -274,8 +284,8 @@ class lib: self.lib.write(" default_max_fanout : 4.0 ;\n") self.lib.write(" default_connection_class : universal ;\n\n") - self.lib.write(" voltage_map ( VDD, {} );\n".format(self.voltage)) - self.lib.write(" voltage_map ( GND, 0 );\n\n") + self.lib.write(" voltage_map ( {0}, {1} );\n".format(self.vdd_name.upper(), self.voltage)) + self.lib.write(" voltage_map ( {0}, 0 );\n\n".format(self.gnd_name.upper())) def create_list(self,values): """ Helper function to create quoted, line wrapped list """ @@ -607,12 +617,12 @@ class lib: self.lib.write(" }\n") def write_pg_pin(self): - self.lib.write(" pg_pin(vdd) {\n") - self.lib.write(" voltage_name : VDD;\n") + self.lib.write(" pg_pin({0}) ".format(self.vdd_name) + "{\n") + self.lib.write(" voltage_name : {};\n".format(self.vdd_name.upper())) self.lib.write(" pg_type : primary_power;\n") self.lib.write(" }\n\n") - self.lib.write(" pg_pin(gnd) {\n") - self.lib.write(" voltage_name : GND;\n") + self.lib.write(" pg_pin({0}) ".format(self.gnd_name) + "{\n") + self.lib.write(" voltage_name : {};\n".format(self.gnd_name.upper())) self.lib.write(" pg_type : primary_ground;\n") self.lib.write(" }\n\n") diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 49fbc97c..f5b5967f 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -22,7 +22,7 @@ from globals import OPTS class stimuli(): """ Class for providing stimuli functions """ - def __init__(self, stim_file, corner): + def __init__(self, stim_file, corner): self.vdd_name = "vdd" self.gnd_name = "gnd" self.pmos_name = tech.spice["pmos"] diff --git a/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py b/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py index 5d86dff6..6462032e 100644 --- a/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py +++ b/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py @@ -8,7 +8,7 @@ num_words = 1024 human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) # Allow byte writes -write_size = 8 # Bits +#write_size = 8 # Bits # Dual port num_rw_ports = 0 diff --git a/compiler/example_configs/sky130_sram_common.py b/compiler/example_configs/sky130_sram_common.py index 0e3443b1..8efc8f10 100644 --- a/compiler/example_configs/sky130_sram_common.py +++ b/compiler/example_configs/sky130_sram_common.py @@ -9,7 +9,8 @@ nominal_corner_only = True # Local wordlines have issues with met3 power routing for now #local_array_size = 16 -#route_supplies = False +#route_supplies = "ring" +route_supplies = "left" check_lvsdrc = True #perimeter_pins = False #netlist_only = True diff --git a/compiler/globals.py b/compiler/globals.py index d64c727f..1b272b98 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -613,8 +613,8 @@ def report_status(): # If write size is more than half of the word size, # then it doesn't need a write mask. It would be writing # the whole word. - if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size/2): - debug.error("Write size needs to be between 1 bit and {0} bits/2.".format(OPTS.word_size)) + if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size / 2): + debug.error("Write size needs to be between 1 bit and {0} bits.".format(int(OPTS.word_size / 2))) if not OPTS.tech_name: debug.error("Tech name must be specified in config file.") diff --git a/compiler/router/grid.py b/compiler/router/grid.py index ea59c80f..404a716f 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -132,25 +132,25 @@ class grid: # Add the left/right columns if side=="all" or side=="left": for x in range(self.ll.x + offset, self.ll.x + width + offset, 1): - for y in range(self.ll.y + margin, self.ur.y - margin, 1): + for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1): for layer in layers: perimeter_list.append(vector3d(x, y, layer)) if side=="all" or side=="right": for x in range(self.ur.x - width - offset, self.ur.x - offset, 1): - for y in range(self.ll.y + margin, self.ur.y - margin, 1): + for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1): for layer in layers: perimeter_list.append(vector3d(x, y, layer)) if side=="all" or side=="bottom": for y in range(self.ll.y + offset, self.ll.y + width + offset, 1): - for x in range(self.ll.x + margin, self.ur.x - margin, 1): + for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1): for layer in layers: perimeter_list.append(vector3d(x, y, layer)) if side=="all" or side=="top": for y in range(self.ur.y - width - offset, self.ur.y - offset, 1): - for x in range(self.ll.x + margin, self.ur.x - margin, 1): + for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1): for layer in layers: perimeter_list.append(vector3d(x, y, layer)) diff --git a/compiler/router/router.py b/compiler/router/router.py index ca077572..dcfde6cf 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -75,6 +75,9 @@ class router(router_tech): self.margin = margin self.init_bbox(bbox, margin) + # New pins if we create a ring or side pins or etc. + self.new_pins = {} + def init_bbox(self, bbox=None, margin=0): """ Initialize the ll,ur values with the paramter or using the layout boundary. @@ -895,20 +898,89 @@ class router(router_tech): Adds a supply pin to the perimeter and resizes the bounding box. """ pg = pin_group(name, [], self) - if name == "vdd": - offset = width + if name == "gnd": + offset = width + 1 else: - offset = 0 - + offset = 1 + if side in ["left", "right"]: + layers = [1] + else: + layers = [0] pg.grids = set(self.rg.get_perimeter_list(side=side, width=width, margin=self.margin, offset=offset, - layers=[1])) + layers=layers)) pg.enclosures = pg.compute_enclosures() pg.pins = set(pg.enclosures) + debug.check(len(pg.pins)==1, "Too many pins for a side supply.") + self.cell.pin_map[name].update(pg.pins) self.pin_groups[name].append(pg) + + self.new_pins[name] = pg.pins + + def add_ring_supply_pin(self, name, width=2): + """ + Adds a ring supply pin that goes inside the given bbox. + """ + pg = pin_group(name, [], self) + # Offset the vdd inside one ring width + # Units are in routing grids + if name == "gnd": + offset = width + 1 + else: + offset = 1 + + # LEFT + left_grids = set(self.rg.get_perimeter_list(side="left", + width=width, + margin=self.margin, + offset=offset, + layers=[1])) + + # RIGHT + right_grids = set(self.rg.get_perimeter_list(side="right", + width=width, + margin=self.margin, + offset=offset, + layers=[1])) + # TOP + top_grids = set(self.rg.get_perimeter_list(side="top", + width=width, + margin=self.margin, + offset=offset, + layers=[0])) + # BOTTOM + bottom_grids = set(self.rg.get_perimeter_list(side="bottom", + width=width, + margin=self.margin, + offset=offset, + layers=[0])) + + horizontal_layer_grids = left_grids | right_grids + + # Must move to the same layer to find layer 1 corner grids + vertical_layer_grids = set() + for x in top_grids | bottom_grids: + vertical_layer_grids.add(vector3d(x.x, x.y, 1)) + + # Add vias in the overlap points + horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids + for g in horizontal_corner_grids: + self.add_via(g) + + # The big pin group, but exclude the corners from the pins + pg.grids = (left_grids | right_grids | top_grids | bottom_grids) + pg.enclosures = pg.compute_enclosures() + pg.pins = set(pg.enclosures) + + self.cell.pin_map[name].update(pg.pins) + self.pin_groups[name].append(pg) + self.new_pins[name] = pg.pins + + def get_new_pins(self, name): + return self.new_pins[name] def add_perimeter_target(self, side="all"): """ diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 97ba87d5..e95cdee1 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -21,7 +21,7 @@ class supply_tree_router(router): routes a grid to connect the supply on the two layers. """ - def __init__(self, layers, design, bbox=None, side_pin=None): + def __init__(self, layers, design, bbox=None, pin_type=None): """ This will route on layers in design. It will get the blockages from either the gds file name or the design itself (by saving to a gds file). @@ -33,7 +33,10 @@ class supply_tree_router(router): # The pin escape router already made the bounding box big enough, # so we can use the regular bbox here. - self.side_pin = side_pin + if pin_type: + debug.check(pin_type in ["left", "right", "top", "bottom", "tree", "ring"], + "Invalid pin type {}".format(pin_type)) + self.pin_type = pin_type router.__init__(self, layers, design, @@ -65,10 +68,13 @@ class supply_tree_router(router): print_time("Finding pins and blockages", datetime.now(), start_time, 3) # Add side pins if enabled - if self.side_pin: - self.add_side_supply_pin(self.vdd_name) - self.add_side_supply_pin(self.gnd_name) - + if self.pin_type in ["left", "right", "top", "bottom"]: + self.add_side_supply_pin(self.vdd_name, side=self.pin_type) + self.add_side_supply_pin(self.gnd_name, side=self.pin_type) + elif self.pin_type == "ring": + self.add_ring_supply_pin(self.vdd_name) + self.add_ring_supply_pin(self.gnd_name) + # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 8830fc36..71709837 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -5,9 +5,9 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug import math + class vector3d(): """ This is the vector3d class to represent a 3D coordinate. @@ -22,20 +22,20 @@ class vector3d(): self.x = x[0] self.y = x[1] self.z = x[2] - #will take inputs as the values of a coordinate + # will take inputs as the values of a coordinate else: self.x = x self.y = y self.z = z - self._hash = hash((self.x,self.y,self.z)) + self._hash = hash((self.x, self.y, self.z)) def __str__(self): """ override print function output """ - return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d[" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + "]" def __repr__(self): """ override print function output """ - return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d[" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + "]" def __setitem__(self, index, value): """ @@ -74,7 +74,6 @@ class vector3d(): """ return vector3d(self.x + other[0], self.y + other[1], self.z + other[2]) - def __radd__(self, other): """ Override + function (right add) @@ -98,7 +97,6 @@ class vector3d(): """ return self._hash - def __rsub__(self, other): """ Override - function (right) @@ -107,7 +105,7 @@ class vector3d(): def rotate(self): """ pass a copy of rotated vector3d, without altering the vector3d! """ - return vector3d(self.y,self.x,self.z) + return vector3d(self.y, self.x, self.z) def scale(self, x_factor, y_factor=None,z_factor=None): """ pass a copy of scaled vector3d, without altering the vector3d! """ @@ -115,7 +113,7 @@ class vector3d(): z_factor=x_factor[2] y_factor=x_factor[1] x_factor=x_factor[0] - return vector3d(self.x*x_factor,self.y*y_factor,self.z*z_factor) + return vector3d(self.x * x_factor, self.y * y_factor, self.z * z_factor) def rotate_scale(self, x_factor, y_factor=None, z_factor=None): """ pass a copy of scaled vector3d, without altering the vector3d! """ @@ -123,25 +121,25 @@ class vector3d(): z_factor=x_factor[2] y_factor=x_factor[1] x_factor=x_factor[0] - return vector3d(self.y*x_factor,self.x*y_factor,self.z*z_factor) + return vector3d(self.y * x_factor, self.x * y_factor, self.z * z_factor) def floor(self): """ Override floor function """ - return vector3d(int(math.floor(self.x)),int(math.floor(self.y)), self.z) + return vector3d(int(math.floor(self.x)), int(math.floor(self.y)), self.z) def ceil(self): """ Override ceil function """ - return vector3d(int(math.ceil(self.x)),int(math.ceil(self.y)), self.z) + return vector3d(int(math.ceil(self.x)), int(math.ceil(self.y)), self.z) def round(self): """ Override round function """ - return vector3d(int(round(self.x)),int(round(self.y)), self.z) + return vector3d(int(round(self.x)), int(round(self.y)), self.z) def __eq__(self, other): """Override the default Equals behavior""" @@ -164,30 +162,29 @@ class vector3d(): def max(self, other): """ Max of both values """ - return vector3d(max(self.x,other.x),max(self.y,other.y),max(self.z,other.z)) + return vector3d(max(self.x, other.x), max(self.y, other.y), max(self.z, other.z)) def min(self, other): """ Min of both values """ - return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z)) + return vector3d(min(self.x, other.x), min(self.y, other.y), min(self.z, other.z)) def distance(self, other): """ Return the manhattan distance between two values """ - return abs(self.x-other.x)+abs(self.y-other.y) + return abs(self.x - other.x) + abs(self.y - other.y) def euclidean_distance(self, other): """ Return the euclidean distance between two values """ - return math.sqrt((self.x-other.x)**2+(self.y-other.y)**2) - + return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2) def adjacent(self, other): """ Is the one grid adjacent in any planar direction to the other """ - if self == other + vector3d(1,0,0): + if self == other + vector3d(1, 0, 0): return True - elif self == other + vector3d(-1,0,0): + elif self == other + vector3d(-1, 0, 0): return True - elif self == other + vector3d(0,1,0): + elif self == other + vector3d(0, 1, 0): return True - elif self == other + vector3d(0,-1,0): + elif self == other + vector3d(0, -1, 0): return True else: return False diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a6eb9b71..327ce209 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -326,6 +326,9 @@ class sram_1bank(sram_base): # they might create some blockages self.add_layout_pins() + # Some technologies have an isolation + self.add_dnwell(inflate=2) + # Route the pins to the perimeter if OPTS.perimeter_pins: self.route_escape_pins() diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 8b6d6a31..a7530e0b 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -15,6 +15,7 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory +from tech import spice, layer class sram_base(design, verilog, lef): @@ -81,8 +82,20 @@ class sram_base(design, verilog, lef): for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") + # Standard supply and ground names + try: + self.vdd_name = spice["power"] + except KeyError: + self.vdd_name = "vdd" + try: + self.gnd_name = spice["ground"] + except KeyError: + self.gnd_name = "gnd" + + self.add_pin(self.vdd_name, "POWER") + self.add_pin(self.gnd_name, "GROUND") + self.ext_supplies = [self.vdd_name, self.gnd_name] + self.ext_supply = {"vdd" : self.vdd_name, "gnd" : self.gnd_name} def add_global_pex_labels(self): """ @@ -224,7 +237,7 @@ class sram_base(design, verilog, lef): # This will either be used to route or left unconnected. for pin_name in ["vdd", "gnd"]: for inst in self.insts: - self.copy_power_pins(inst, pin_name) + self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) try: from tech import power_grid @@ -234,42 +247,21 @@ class sram_base(design, verilog, lef): # Route a M3/M4 grid grid_stack = self.m3_stack - # lowest_coord = self.find_lowest_coords() - # highest_coord = self.find_highest_coords() - - # # Add two rails to the side - # if OPTS.route_supplies == "side": - # supply_pins = {} - # # Find the lowest leftest pin for vdd and gnd - # for (pin_name, pin_index) in [("vdd", 0), ("gnd", 1)]: - # pin_width = 8 * getattr(self, "{}_width".format(grid_stack[2])) - # pin_space = 2 * getattr(self, "{}_space".format(grid_stack[2])) - # supply_pitch = pin_width + pin_space - - # # Add side power rails on left from bottom to top - # # These have a temporary name and will be connected later. - # # They are here to reserve space now and ensure other pins go beyond - # # their perimeter. - # supply_height = highest_coord.y - lowest_coord.y - - # supply_pins[pin_name] = self.add_layout_pin(text=pin_name, - # layer=grid_stack[2], - # offset=lowest_coord + vector(pin_index * supply_pitch, 0), - # width=pin_width, - # height=supply_height) - if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) return elif OPTS.route_supplies == "grid": from supply_grid_router import supply_grid_router as router + rtr=router(grid_stack, self) else: from supply_tree_router import supply_tree_router as router + rtr=router(grid_stack, + self, + pin_type=OPTS.route_supplies) - rtr=router(grid_stack, self, side_pin=(OPTS.route_supplies == "side")) rtr.route() - if OPTS.route_supplies == "side": + if OPTS.route_supplies in ["left", "right", "top", "bottom", "ring"]: # Find the lowest leftest pin for vdd and gnd for pin_name in ["vdd", "gnd"]: # Copy the pin shape(s) to rectangles @@ -282,13 +274,14 @@ class sram_base(design, verilog, lef): # Remove the pin shape(s) self.remove_layout_pin(pin_name) - # Get the lowest, leftest pin - pin = rtr.get_ll_pin(pin_name) - self.add_layout_pin(pin_name, - pin.layer, - pin.ll(), - pin.width(), - pin.height()) + # Get new pins + pins = rtr.get_new_pins(pin_name) + for pin in pins: + self.add_layout_pin(self.ext_supply[pin_name], + pin.layer, + pin.ll(), + pin.width(), + pin.height()) elif OPTS.route_supplies: # Update these as we may have routed outside the region (perimeter pins) @@ -319,7 +312,7 @@ class sram_base(design, verilog, lef): route_width, pin.height()) - self.add_layout_pin(pin_name, + self.add_layout_pin(self.ext_supply[pin_name], pin.layer, pin_offset, pin_width, @@ -571,7 +564,7 @@ class sram_base(design, verilog, lef): temp.append("bank_spare_wen{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) - temp.extend(["vdd", "gnd"]) + temp.extend(self.ext_supplies) self.connect_inst(temp) return self.bank_insts[-1] @@ -620,7 +613,7 @@ class sram_base(design, verilog, lef): inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size)) outputs.append("a{}[{}]".format(port, bit + self.col_addr_size)) - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) return insts @@ -638,7 +631,7 @@ class sram_base(design, verilog, lef): inputs.append("addr{}[{}]".format(port, bit)) outputs.append("a{}[{}]".format(port, bit)) - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) return insts @@ -660,7 +653,7 @@ class sram_base(design, verilog, lef): inputs.append("din{}[{}]".format(port, bit)) outputs.append("bank_din{}[{}]".format(port, bit)) - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) return insts @@ -682,7 +675,7 @@ class sram_base(design, verilog, lef): inputs.append("wmask{}[{}]".format(port, bit)) outputs.append("bank_wmask{}[{}]".format(port, bit)) - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) return insts @@ -704,7 +697,7 @@ class sram_base(design, verilog, lef): inputs.append("spare_wen{}[{}]".format(port, bit)) outputs.append("bank_spare_wen{}[{}]".format(port, bit)) - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) return insts @@ -735,7 +728,7 @@ class sram_base(design, verilog, lef): if port in self.write_ports: temp.append("w_en{}".format(port)) temp.append("p_en_bar{}".format(port)) - temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) + temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port)] + self.ext_supplies) self.connect_inst(temp) return insts