Merge branch 'dev' into laptop_checkpoint

This commit is contained in:
Jesse Cirimelli-Low 2021-05-07 19:06:17 -07:00
commit 0ba229afe5
19 changed files with 316 additions and 148 deletions

View File

@ -112,18 +112,22 @@ class lef:
for pin_name in self.pins: for pin_name in self.pins:
pin = self.get_pin(pin_name) pin = self.get_pin(pin_name)
inflated_pin = pin.inflated_pin(multiple=1) inflated_pin = pin.inflated_pin(multiple=1)
for blockage in self.blockages[pin.layer]: another_iteration_needed = True
if blockage.overlaps(inflated_pin): while another_iteration_needed:
intersection_shape = blockage.intersection(inflated_pin) another_iteration_needed = False
# If it is zero area, don't add the pin old_blockages = list(self.blockages[pin.layer])
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: for blockage in old_blockages:
continue if blockage.overlaps(inflated_pin):
# Remove the old blockage and add the new ones intersection_shape = blockage.intersection(inflated_pin)
self.blockages[pin.layer].remove(blockage) # If it is zero area, don't add the pin
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
new_blockages = blockage.cut(intersection_pin) continue
self.blockages[pin.layer].extend(new_blockages) 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): def lef_write_header(self):
""" Header of LEF file """ """ Header of LEF file """

View File

@ -29,6 +29,11 @@ class verilog:
self.vf.write("\n") self.vf.write("\n")
self.vf.write("module {0}(\n".format(self.name)) 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("`endif\n")
for port in self.all_ports: for port in self.all_ports:
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write("// Port {0}: RW\n".format(port)) self.vf.write("// Port {0}: RW\n".format(port))
@ -65,6 +70,11 @@ class verilog:
self.vf.write(" parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary\n") self.vf.write(" parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary\n")
self.vf.write("\n") 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("`endif\n")
for port in self.all_ports: for port in self.all_ports:
self.add_inputs_outputs(port) self.add_inputs_outputs(port)

View File

@ -11,7 +11,7 @@ process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]
route_supplies = True route_supplies = "side"
check_lvsdrc = True check_lvsdrc = True
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,

View File

@ -426,7 +426,8 @@ class VlsiLayout:
self.structures[self.rootStructureName].texts.append(textToAdd) self.structures[self.rootStructureName].texts.append(textToAdd)
def padText(self, text): def padText(self, text):
if(len(text)%2 == 1): debug.check(len(text) > 0, "Cannot have zero length text string.")
if(len(text) % 2 == 1):
return text + '\x00' return text + '\x00'
else: else:
return text return text
@ -700,7 +701,6 @@ class VlsiLayout:
return max_pins return max_pins
def getAllPinShapes(self, pin_name): def getAllPinShapes(self, pin_name):
""" """
Search for a pin label and return ALL the enclosing rectangles on the same layer Search for a pin label and return ALL the enclosing rectangles on the same layer

View File

@ -75,6 +75,11 @@ class bank(design.design):
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1) self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
self.bank_array_ur = self.bitcell_array_inst.ur() self.bank_array_ur = self.bitcell_array_inst.ur()
self.bank_array_ul = self.bitcell_array_inst.ul() self.bank_array_ul = self.bitcell_array_inst.ul()
# These are used for other placements (e.g. address flops)
self.predecoder_top = self.port_address[0].predecoder_height + self.port_address_inst[0].by()
self.predecoder_bottom = self.port_address_inst[0].by()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
@ -227,7 +232,6 @@ class bank(design.design):
x_offset = self.m2_gap + self.port_address[port].width x_offset = self.m2_gap + self.port_address[port].width
self.port_address_offsets[port] = vector(-x_offset, self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
self.predecoder_height = self.port_address[port].predecoder_height + self.port_address_offsets[port].y
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver

View File

@ -346,9 +346,12 @@ class control_logic(design.design):
row += 1 row += 1
self.place_wlen_row(row) self.place_wlen_row(row)
row += 1 row += 1
self.place_delay(row)
control_center_y = self.wl_en_inst.uy() + self.m3_pitch
# Delay chain always gets placed at row 4
self.place_delay(4)
height = self.delay_inst.uy() height = self.delay_inst.uy()
control_center_y = self.delay_inst.by()
# This offset is used for placement of the control logic in the SRAM level. # This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
@ -387,19 +390,22 @@ class control_logic(design.design):
def place_delay(self, row): def place_delay(self, row):
""" Place the replica bitline """ """ Place the replica bitline """
y_off = row * self.and2.height + 2 * self.m1_pitch debug.check(row % 2 == 0, "Must place delay chain at even row for supply alignment.")
# It is flipped on X axis
y_off = row * self.and2.height + self.delay_chain.height
# Add the RBL above the rows # Add the RBL above the rows
# Add to the right of the control rows and routing channel # Add to the right of the control rows and routing channel
offset = vector(self.delay_chain.width, y_off) offset = vector(0, y_off)
self.delay_inst.place(offset, mirror="MY") self.delay_inst.place(offset, mirror="MX")
def route_delay(self): def route_delay(self):
out_pos = self.delay_inst.get_pin("out").bc() out_pos = self.delay_inst.get_pin("out").center()
# Connect to the rail level with the vdd rail # Connect to the rail level with the vdd rail
# Use pen since it is in every type of control logic # Use gated clock since it is in every type of control logic
vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() vdd_ypos = self.gated_clk_buf_inst.get_pin("vdd").cy() + self.m1_pitch
in_pos = vector(self.input_bus["rbl_bl_delay"].cx(), vdd_ypos) in_pos = vector(self.input_bus["rbl_bl_delay"].cx(), vdd_ypos)
mid1 = vector(out_pos.x, in_pos.y) mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
@ -676,7 +682,7 @@ class control_logic(design.design):
# Connect the clock rail to the other clock rail # Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts # by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc() in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = in_pos + vector(0, self.and2.height) mid_pos = vector(in_pos.x, self.gated_clk_buf_inst.get_pin("vdd").cy() - self.m1_pitch)
rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y) rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos]) self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
@ -794,3 +800,8 @@ class control_logic(design.design):
to_layer="m2", to_layer="m2",
offset=out_pos) offset=out_pos)
def get_left_pins(self, name):
"""
Return the left side supply pins to connect to a vertical stripe.
"""
return(self.cntrl_dff_inst.get_pins(name) + self.delay_inst.get_pins(name))

View File

@ -31,6 +31,7 @@ class delay_chain(design.design):
# number of inverters including any fanout loads. # number of inverters including any fanout loads.
self.fanout_list = fanout_list self.fanout_list = fanout_list
self.rows = len(self.fanout_list)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -43,7 +44,7 @@ class delay_chain(design.design):
def create_layout(self): def create_layout(self):
# Each stage is a a row # Each stage is a a row
self.height = len(self.fanout_list) * self.inv.height self.height = self.rows * self.inv.height
# The width is determined by the largest fanout plus the driver # The width is determined by the largest fanout plus the driver
self.width = (max(self.fanout_list) + 1) * self.inv.width self.width = (max(self.fanout_list) + 1) * self.inv.width
@ -62,14 +63,19 @@ class delay_chain(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
self.inv = factory.create(module_type="pinv")
self.dff = factory.create(module_type="dff_buf")
dff_height = self.dff.height
self.inv = factory.create(module_type="pinv",
height=dff_height)
self.add_mod(self.inv) self.add_mod(self.inv)
def create_inverters(self): def create_inverters(self):
""" Create the inverters and connect them based on the stage list """ """ Create the inverters and connect them based on the stage list """
self.driver_inst_list = [] self.driver_inst_list = []
self.load_inst_map = {} self.load_inst_map = {}
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list): for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
# Add the inverter # Add the inverter
cur_driver=self.add_inst(name="dinv{}".format(stage_num), cur_driver=self.add_inst(name="dinv{}".format(stage_num),
mod=self.inv) mod=self.inv)
@ -77,7 +83,7 @@ class delay_chain(design.design):
self.driver_inst_list.append(cur_driver) self.driver_inst_list.append(cur_driver)
# Hook up the driver # Hook up the driver
if stage_num + 1 == len(self.fanout_list): if stage_num + 1 == self.rows:
stageout_name = "out" stageout_name = "out"
else: else:
stageout_name = "dout_{}".format(stage_num + 1) stageout_name = "dout_{}".format(stage_num + 1)
@ -101,7 +107,7 @@ class delay_chain(design.design):
def place_inverters(self): def place_inverters(self):
""" Place the inverters and connect them based on the stage list """ """ Place the inverters and connect them based on the stage list """
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list): for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
if stage_num % 2: if stage_num % 2:
inv_mirror = "MX" inv_mirror = "MX"
inv_offset = vector(0, (stage_num + 1) * self.inv.height) inv_offset = vector(0, (stage_num + 1) * self.inv.height)
@ -185,24 +191,26 @@ class delay_chain(design.design):
def add_layout_pins(self): def add_layout_pins(self):
# input is A pin of first inverter # input is A pin of first inverter
# It gets routed to the left a bit to prevent pin access errors
# due to the output pin when going up to M3
a_pin = self.driver_inst_list[0].get_pin("A") a_pin = self.driver_inst_list[0].get_pin("A")
mid_loc = vector(a_pin.cx() - self.m3_pitch, a_pin.cy())
self.add_via_stack_center(from_layer=a_pin.layer, self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m2", to_layer="m2",
offset=a_pin.center()) offset=mid_loc)
self.add_layout_pin(text="in", self.add_path(a_pin.layer, [a_pin.center(), mid_loc])
layer="m2",
offset=a_pin.ll().scale(1, 0),
height=a_pin.cy())
# output is A pin of last load inverter self.add_layout_pin_rect_center(text="in",
layer="m2",
offset=mid_loc)
# output is A pin of last load/fanout inverter
last_driver_inst = self.driver_inst_list[-1] last_driver_inst = self.driver_inst_list[-1]
a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A") a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A")
self.add_via_stack_center(from_layer=a_pin.layer, self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m2", to_layer="m1",
offset=a_pin.center()) offset=a_pin.center())
mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy()) self.add_layout_pin_rect_center(text="out",
self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)]) layer="m1",
self.add_layout_pin_segment_center(text="out", offset=a_pin.center())
layer="m2",
start=mid_point,
end=mid_point.scale(1, 0))

View File

@ -122,34 +122,44 @@ class grid:
self.set_target(n) self.set_target(n)
# self.set_blocked(n, False) # self.set_blocked(n, False)
def add_perimeter_target(self, side="all"): def get_perimeter_list(self, side="left", layers=[0, 1], width=1, margin=0, offset=0):
debug.info(3, "Adding perimeter target") """
Side specifies which side.
Layer specifies horizontal (0) or vertical (1)
Width specifies how wide the perimter "stripe" should be.
"""
perimeter_list = [] perimeter_list = []
# Add the left/right columns # Add the left/right columns
if side=="all" or side=="left": if side=="all" or side=="left":
x = self.ll.x for x in range(self.ll.x + offset, self.ll.x + width + offset, 1):
for y in range(self.ll.y, self.ur.y, 1): for y in range(self.ll.y + margin, self.ur.y - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="right": if side=="all" or side=="right":
x = self.ur.x for x in range(self.ur.x - width - offset, self.ur.x - offset, 1):
for y in range(self.ll.y, self.ur.y, 1): for y in range(self.ll.y + margin, self.ur.y - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="bottom": if side=="all" or side=="bottom":
y = self.ll.y for y in range(self.ll.y + offset, self.ll.y + width + offset, 1):
for x in range(self.ll.x, self.ur.x, 1): for x in range(self.ll.x + margin, self.ur.x - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="top": if side=="all" or side=="top":
y = self.ur.y for y in range(self.ur.y - width - offset, self.ur.y - offset, 1):
for x in range(self.ll.x, self.ur.x, 1): for x in range(self.ll.x + margin, self.ur.x - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
return perimeter_list
def add_perimeter_target(self, side="all", layers=[0, 1]):
debug.info(3, "Adding perimeter target")
perimeter_list = self.get_perimeter_list(side, layers)
self.set_target(perimeter_list) self.set_target(perimeter_list)

View File

@ -155,6 +155,10 @@ class pin_group:
# Now simplify the enclosure list # Now simplify the enclosure list
new_pin_list = self.remove_redundant_shapes(pin_list) new_pin_list = self.remove_redundant_shapes(pin_list)
# Now add the right name
for pin in new_pin_list:
pin.name = self.name
debug.check(len(new_pin_list) > 0, debug.check(len(new_pin_list) > 0,
"Did not find any enclosures.") "Did not find any enclosures.")

View File

@ -28,7 +28,7 @@ class router(router_tech):
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
It populates blockages on a grid class. It populates blockages on a grid class.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None, margin=0, route_track_width=1): def __init__(self, layers, design, bbox=None, margin=0, route_track_width=1):
""" """
This will instantiate a copy of the gds file or the module at (0,0) and This will instantiate a copy of the gds file or the module at (0,0) and
route on top of this. The blockages from the gds/module will be route on top of this. The blockages from the gds/module will be
@ -39,19 +39,7 @@ class router(router_tech):
self.cell = design self.cell = design
# If didn't specify a gds blockage file, write it out to read the gds self.gds_filename = OPTS.openram_temp + "temp.gds"
# This isn't efficient, but easy for now
# start_time = datetime.now()
if not gds_filename:
gds_filename = OPTS.openram_temp+"temp.gds"
self.cell.gds_write(gds_filename)
# Load the gds file and read in all the shapes
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName
# print_time("GDS read",datetime.now(), start_time)
# The pin data structures # The pin data structures
# A map of pin names to a set of pin_layout structures # A map of pin names to a set of pin_layout structures
@ -91,6 +79,16 @@ class router(router_tech):
""" """
Initialize the ll,ur values with the paramter or using the layout boundary. Initialize the ll,ur values with the paramter or using the layout boundary.
""" """
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.cell.gds_write(self.gds_filename)
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(self.gds_filename)
self.top_name = self.layout.rootStructureName
if not bbox: if not bbox:
# The boundary will determine the limits to the size # The boundary will determine the limits to the size
# of the routing grid # of the routing grid
@ -178,6 +176,17 @@ class router(router_tech):
""" """
Find the pins and blockages in the design Find the pins and blockages in the design
""" """
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.cell.gds_write(self.gds_filename)
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(self.gds_filename)
self.top_name = self.layout.rootStructureName
# print_time("GDS read",datetime.now(), start_time)
# This finds the pin shapes and sorts them into "groups" that # This finds the pin shapes and sorts them into "groups" that
# are connected. This must come before the blockages, so we # are connected. This must come before the blockages, so we
# can not count the pins themselves # can not count the pins themselves
@ -881,6 +890,26 @@ class router(router_tech):
# Clearing the blockage of this pin requires the inflated pins # Clearing the blockage of this pin requires the inflated pins
self.clear_blockages(pin_name) self.clear_blockages(pin_name)
def add_side_supply_pin(self, name, side="left", width=2):
"""
Adds a supply pin to the perimeter and resizes the bounding box.
"""
pg = pin_group(name, [], self)
if name == "vdd":
offset = width
else:
offset = 0
pg.grids = set(self.rg.get_perimeter_list(side=side,
width=width,
margin=self.margin,
offset=offset,
layers=[1]))
pg.enclosures = pg.compute_enclosures()
pg.pins = set(pg.enclosures)
self.cell.pin_map[name].update(pg.pins)
self.pin_groups[name].append(pg)
def add_perimeter_target(self, side="all"): def add_perimeter_target(self, side="all"):
""" """
This will mark all the cells on the perimeter of the original layout as a target. This will mark all the cells on the perimeter of the original layout as a target.
@ -1219,7 +1248,7 @@ class router(router_tech):
""" Return the lowest, leftest pin group """ """ Return the lowest, leftest pin group """
keep_pin = None keep_pin = None
for index,pg in enumerate(self.pin_groups[pin_name]): for index, pg in enumerate(self.pin_groups[pin_name]):
for pin in pg.enclosures: for pin in pg.enclosures:
if not keep_pin: if not keep_pin:
keep_pin = pin keep_pin = pin

View File

@ -123,7 +123,7 @@ class router_tech:
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
min_width = drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf) min_width = self.route_track_width * drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf) min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf)
return (min_width, min_spacing) return (min_width, min_spacing)

View File

@ -17,7 +17,7 @@ class signal_escape_router(router):
A router that routes signals to perimeter and makes pins. A router that routes signals to perimeter and makes pins.
""" """
def __init__(self, layers, design, bbox=None, margin=0, gds_filename=None): def __init__(self, layers, design, bbox=None, margin=0):
""" """
This will route on layers in design. It will get the blockages from 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). either the gds file name or the design itself (by saving to a gds file).
@ -25,7 +25,6 @@ class signal_escape_router(router):
router.__init__(self, router.__init__(self,
layers=layers, layers=layers,
design=design, design=design,
gds_filename=gds_filename,
bbox=bbox, bbox=bbox,
margin=margin) margin=margin)

View File

@ -15,12 +15,12 @@ class signal_router(router):
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, bbox=None):
""" """
This will route on layers in design. It will get the blockages from 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). either the gds file name or the design itself (by saving to a gds file).
""" """
router.__init__(self, layers, design, gds_filename, bbox) router.__init__(self, layers, design, bbox)
def route(self, src, dest, detour_scale=5): def route(self, src, dest, detour_scale=5):
""" """

View File

@ -21,7 +21,7 @@ class supply_grid_router(router):
routes a grid to connect the supply on the two layers. routes a grid to connect the supply on the two layers.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, margin=0, bbox=None):
""" """
This will route on layers in design. It will get the blockages from 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). either the gds file name or the design itself (by saving to a gds file).
@ -29,9 +29,9 @@ class supply_grid_router(router):
start_time = datetime.now() start_time = datetime.now()
# Power rail width in minimum wire widths # Power rail width in minimum wire widths
self.route_track_width = 2 self.route_track_width = 1
router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) router.__init__(self, layers, design, bbox=bbox, margin=margin, route_track_width=self.route_track_width)
# The list of supply rails (grid sets) that may be routed # The list of supply rails (grid sets) that may be routed
self.supply_rails = {} self.supply_rails = {}
@ -357,7 +357,8 @@ class supply_grid_router(router):
# This is inefficient since it is non-incremental, but it was # This is inefficient since it is non-incremental, but it was
# easier to debug. # easier to debug.
self.prepare_blockages(pin_name) self.prepare_blockages()
self.clear_blockages(self.vdd_name)
# Add the single component of the pin as the source # Add the single component of the pin as the source
# which unmarks it as a blockage too # which unmarks it as a blockage too
@ -369,7 +370,7 @@ class supply_grid_router(router):
# Actually run the A* router # Actually run the A* router
if not self.run_router(detour_scale=5): if not self.run_router(detour_scale=5):
self.write_debug_gds("debug_route.gds", False) self.write_debug_gds("debug_route.gds")
# if index==3 and pin_name=="vdd": # if index==3 and pin_name=="vdd":
# self.write_debug_gds("route.gds",False) # self.write_debug_gds("route.gds",False)

View File

@ -21,7 +21,7 @@ class supply_tree_router(router):
routes a grid to connect the supply on the two layers. routes a grid to connect the supply on the two layers.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, bbox=None, side_pin=None):
""" """
This will route on layers in design. It will get the blockages from 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). either the gds file name or the design itself (by saving to a gds file).
@ -31,11 +31,19 @@ class supply_tree_router(router):
# for prettier routes. # for prettier routes.
self.route_track_width = 1 self.route_track_width = 1
router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) # The pin escape router already made the bounding box big enough,
# so we can use the regular bbox here.
self.side_pin = side_pin
router.__init__(self,
layers,
design,
bbox=bbox,
route_track_width=self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"): def route(self, vdd_name="vdd", gnd_name="gnd"):
""" """
Route the two nets in a single layer) Route the two nets in a single layer.
Setting pin stripe will make a power rail on the left side.
""" """
debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
self.vdd_name = vdd_name self.vdd_name = vdd_name
@ -50,11 +58,17 @@ class supply_tree_router(router):
# but this is simplest for now. # but this is simplest for now.
self.create_routing_grid(signal_grid) self.create_routing_grid(signal_grid)
# Get the pin shapes
start_time = datetime.now() start_time = datetime.now()
# Get the pin shapes
self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
print_time("Finding pins and blockages", datetime.now(), start_time, 3) 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)
# Route the supply pins to the supply rails # Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter # Route vdd first since we want it to be shorter
start_time = datetime.now() start_time = datetime.now()
@ -87,16 +101,21 @@ class supply_tree_router(router):
pin_size = len(self.pin_groups[pin_name]) pin_size = len(self.pin_groups[pin_name])
adj_matrix = [[0] * pin_size for i in range(pin_size)] adj_matrix = [[0] * pin_size for i in range(pin_size)]
for index1,pg1 in enumerate(self.pin_groups[pin_name]): for index1, pg1 in enumerate(self.pin_groups[pin_name]):
for index2,pg2 in enumerate(self.pin_groups[pin_name]): for index2, pg2 in enumerate(self.pin_groups[pin_name]):
if index1>=index2: if index1>=index2:
continue continue
dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids)) dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids))
adj_matrix[index1][index2] = dist adj_matrix[index1][index2] = dist
# Find MST # Find MST
debug.info(2, "Finding MinimumSpanning Tree") debug.info(2, "Finding Minimum Spanning Tree")
X = csr_matrix(adj_matrix) X = csr_matrix(adj_matrix)
from scipy.sparse import save_npz
#print("Saving {}.npz".format(self.cell.name))
#save_npz("{}.npz".format(self.cell.name), X)
#exit(1)
Tcsr = minimum_spanning_tree(X) Tcsr = minimum_spanning_tree(X)
mst = Tcsr.toarray().astype(int) mst = Tcsr.toarray().astype(int)
connections = [] connections = []

View File

@ -120,8 +120,9 @@ class sram_1bank(sram_base):
port = 0 port = 0
# The row address bits are placed above the control logic aligned on the right. # The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is above the control logic but below the top of the bitcell array # It is above the control logic and the predecoder array
y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height) y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_top)
self.row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port]) self.row_addr_dff_insts[port].place(self.row_addr_pos[port])
@ -130,7 +131,7 @@ class sram_1bank(sram_base):
# The row address bits are placed above the control logic aligned on the left. # The row address bits are placed above the control logic aligned on the left.
x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# If it can be placed above the predecoder and below the control logic, do it # If it can be placed above the predecoder and below the control logic, do it
y_offset = self.bank.bank_array_ll.y y_offset = self.bank.predecoder_bottom
self.row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY") self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY")
@ -419,11 +420,11 @@ class sram_1bank(sram_base):
if len(route_map) > 0: if len(route_map) > 0:
# The write masks will have blockages on M1 # The write masks will have blockages on M1
if self.num_wmasks > 0 and port in self.write_ports: # if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack # layer_stack = self.m3_stack
else: # else:
layer_stack = self.m1_stack # layer_stack = self.m1_stack
layer_stack = self.m3_stack
if port == 0: if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width, offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m3_pitch) - self.data_bus_size[port] + 2 * self.m3_pitch)
@ -527,13 +528,13 @@ class sram_1bank(sram_base):
# Only input (besides pins) is the replica bitline # Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl") src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
dest_pin = self.bank_inst.get_pin("rbl_bl_{0}_{0}".format(port)) dest_pin = self.bank_inst.get_pin("rbl_bl_{0}_{0}".format(port))
self.add_wire(self.m2_stack[::-1], self.add_wire(self.m3_stack,
[src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()]) [src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()])
self.add_via_stack_center(from_layer=src_pin.layer, self.add_via_stack_center(from_layer=src_pin.layer,
to_layer="m2", to_layer="m4",
offset=src_pin.center()) offset=src_pin.center())
self.add_via_stack_center(from_layer=dest_pin.layer, self.add_via_stack_center(from_layer=dest_pin.layer,
to_layer="m2", to_layer="m3",
offset=dest_pin.center()) offset=dest_pin.center())
def route_row_addr_dff(self): def route_row_addr_dff(self):

View File

@ -226,10 +226,6 @@ class sram_base(design, verilog, lef):
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst, pin_name) self.copy_power_pins(inst, pin_name)
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
try: try:
from tech import power_grid from tech import power_grid
grid_stack = power_grid grid_stack = power_grid
@ -238,43 +234,99 @@ class sram_base(design, verilog, lef):
# Route a M3/M4 grid # Route a M3/M4 grid
grid_stack = self.m3_stack grid_stack = self.m3_stack
if OPTS.route_supplies == "grid": # 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 from supply_grid_router import supply_grid_router as router
elif OPTS.route_supplies: else:
from supply_tree_router import supply_tree_router as router from supply_tree_router import supply_tree_router as router
rtr=router(grid_stack, self) rtr=router(grid_stack, self, side_pin=(OPTS.route_supplies == "side"))
rtr.route() rtr.route()
# Find the lowest leftest pin for vdd and gnd if OPTS.route_supplies == "side":
for pin_name in ["vdd", "gnd"]: # Find the lowest leftest pin for vdd and gnd
# Copy the pin shape(s) to rectangles for pin_name in ["vdd", "gnd"]:
for pin in self.get_pins(pin_name): # Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# 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())
elif OPTS.route_supplies:
# Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords()
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get the lowest, leftest pin
pin = rtr.get_ll_pin(pin_name)
pin_width = 2 * getattr(self, "{}_width".format(pin.layer))
# Add it as an IO pin to the perimeter
route_width = pin.rx() - lowest_coord.x
pin_offset = vector(lowest_coord.x, pin.by())
self.add_rect(pin.layer, self.add_rect(pin.layer,
pin.ll(), pin_offset,
pin.width(), route_width,
pin.height()) pin.height())
# Remove the pin shape(s) self.add_layout_pin(pin_name,
self.remove_layout_pin(pin_name) pin.layer,
pin_offset,
# Get the lowest, leftest pin pin_width,
pin = rtr.get_ll_pin(pin_name) pin.height())
else:
# Add it as an IO pin to the perimeter # Grid is left with many top level pins
lowest_coord = self.find_lowest_coords() pass
route_width = pin.rx() - lowest_coord.x
pin_width = 2 * getattr(self, "{}_width".format(pin.layer))
pin_offset = vector(lowest_coord.x, pin.by())
self.add_layout_pin(pin_name,
pin.layer,
pin_offset,
pin_width,
pin.height())
self.add_rect(pin.layer,
pin_offset,
route_width,
pin.height())
def route_escape_pins(self): def route_escape_pins(self):
""" """
@ -319,7 +371,7 @@ class sram_base(design, verilog, lef):
from signal_escape_router import signal_escape_router as router from signal_escape_router import signal_escape_router as router
rtr=router(layers=self.m3_stack, rtr=router(layers=self.m3_stack,
design=self, design=self,
margin=4 * self.m3_pitch) margin=8 * self.m3_pitch)
rtr.escape_route(pins_to_route) rtr.escape_route(pins_to_route)
def compute_bus_sizes(self): def compute_bus_sizes(self):

View File

@ -3,6 +3,10 @@
// Word size: 2 // Word size: 2
module sram_2_16_1_freepdk45( module sram_2_16_1_freepdk45(
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
// Port 0: RW // Port 0: RW
clk0,csb0,web0,addr0,din0,dout0 clk0,csb0,web0,addr0,din0,dout0
); );
@ -15,6 +19,10 @@ module sram_2_16_1_freepdk45(
parameter VERBOSE = 1 ; //Set to 0 to only display warnings parameter VERBOSE = 1 ; //Set to 0 to only display warnings
parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0; // clock input clk0; // clock
input csb0; // active low chip select input csb0; // active low chip select
input web0; // active low write control input web0; // active low write control

View File

@ -3,6 +3,10 @@
// Word size: 2 // Word size: 2
module sram_2_16_1_scn4m_subm( module sram_2_16_1_scn4m_subm(
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
// Port 0: RW // Port 0: RW
clk0,csb0,web0,addr0,din0,dout0 clk0,csb0,web0,addr0,din0,dout0
); );
@ -15,6 +19,10 @@ module sram_2_16_1_scn4m_subm(
parameter VERBOSE = 1 ; //Set to 0 to only display warnings parameter VERBOSE = 1 ; //Set to 0 to only display warnings
parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0; // clock input clk0; // clock
input csb0; // active low chip select input csb0; // active low chip select
input web0; // active low write control input web0; // active low write control