From b9d993c88b6cfa1b152c3145c8cd9b010b7a0154 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jul 2019 12:57:12 -0700 Subject: [PATCH] Add dummy bitcell module. Modify bitcell logic to guess if bitcell is not "bitcell" No longer need to specify replica (and dummy) bitcell explicitly Add support for 1 or 2 port replica array. --- compiler/bitcells/dummy_bitcell_1rw_1r.py | 53 +++++++++++++ compiler/bitcells/dummy_bitcell_1w_1r.py | 51 ++++++++++++ compiler/globals.py | 36 +++++---- compiler/modules/bank.py | 38 ++++----- compiler/modules/replica_bitcell_array.py | 77 +++++++++++-------- compiler/modules/replica_column.py | 5 +- compiler/options.py | 1 + compiler/sram/sram_base.py | 3 - .../tests/05_bitcell_1rw_1r_array_test.py | 1 - .../tests/05_replica_bitcell_array_test.py | 11 ++- compiler/tests/05_replica_column_test.py | 6 +- .../14_replica_bitline_multiport_test.py | 2 + 12 files changed, 208 insertions(+), 76 deletions(-) create mode 100644 compiler/bitcells/dummy_bitcell_1rw_1r.py create mode 100644 compiler/bitcells/dummy_bitcell_1w_1r.py diff --git a/compiler/bitcells/dummy_bitcell_1rw_1r.py b/compiler/bitcells/dummy_bitcell_1rw_1r.py new file mode 100644 index 00000000..189c4894 --- /dev/null +++ b/compiler/bitcells/dummy_bitcell_1rw_1r.py @@ -0,0 +1,53 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +import debug +import utils +from tech import GDS,layer,drc,parameter + +class dummy_bitcell_1rw_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] + (width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + design.design.__init__(self, "dummy_cell_1rw_1r") + debug.info(2, "Create dummy bitcell 1rw+1r object") + + self.width = dummy_bitcell_1rw_1r.width + self.height = dummy_bitcell_1rw_1r.height + self.pin_map = dummy_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for both ports. + # Port 0 edges + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"]) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"]) + # Port 1 edges + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"]) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"]) diff --git a/compiler/bitcells/dummy_bitcell_1w_1r.py b/compiler/bitcells/dummy_bitcell_1w_1r.py new file mode 100644 index 00000000..95d514f8 --- /dev/null +++ b/compiler/bitcells/dummy_bitcell_1w_1r.py @@ -0,0 +1,51 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +import debug +import utils +from tech import GDS,layer,drc,parameter + +class dummy_bitcell_1w_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] + (width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + design.design.__init__(self, "dummy_cell_1w_1r") + debug.info(2, "Create dummy bitcell 1w+1r object") + + self.width = dummy_bitcell_1w_1r.width + self.height = dummy_bitcell_1w_1r.height + self.pin_map = dummy_bitcell_1w_1r.pin_map + self.add_pin_types(self.type_list) + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for both ports. + # Port 0 edges + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"]) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"]) + # Port 1 is a write port, so its timing is not considered here. diff --git a/compiler/globals.py b/compiler/globals.py index 40daddce..c29cd081 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -169,11 +169,12 @@ def setup_bitcell(): # If we have non-1rw ports, # and the user didn't over-ride the bitcell manually, # figure out the right bitcell to use - if (OPTS.bitcell=="bitcell" and OPTS.replica_bitcell=="replica_bitcell"): + if (OPTS.bitcell=="bitcell"): if (OPTS.num_rw_ports==1 and OPTS.num_w_ports==0 and OPTS.num_r_ports==0): OPTS.bitcell = "bitcell" OPTS.replica_bitcell = "replica_bitcell" + OPTS.dummy_bitcell = "dummy_bitcell" else: ports = "" if OPTS.num_rw_ports>0: @@ -185,21 +186,26 @@ def setup_bitcell(): OPTS.bitcell = "bitcell_"+ports OPTS.replica_bitcell = "replica_bitcell_"+ports - - # See if a custom bitcell exists - from importlib import find_loader - try: - __import__(OPTS.bitcell) - __import__(OPTS.replica_bitcell) - except ImportError: - # Use the pbitcell if we couldn't find a custom bitcell - # or its custom replica bitcell - # Use the pbitcell (and give a warning if not in unit test mode) - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - if not OPTS.is_unit_test: - debug.warning("Using the parameterized bitcell which may have suboptimal density.") + OPTS.dummy_bitcell = "dummy_bitcell_"+ports + else: + OPTS.replica_bitcell = "replica_" + OPTS.bitcell + OPTS.replica_bitcell = "dummy_" + OPTS.bitcell + # See if bitcell exists + from importlib import find_loader + try: + __import__(OPTS.bitcell) + __import__(OPTS.replica_bitcell) + __import__(OPTS.dummy_bitcell) + except ImportError: + # Use the pbitcell if we couldn't find a custom bitcell + # or its custom replica bitcell + # Use the pbitcell (and give a warning if not in unit test mode) + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.replica_bitcell = "dummy_pbitcell" + if not OPTS.is_unit_test: + debug.warning("Using the parameterized bitcell which may have suboptimal density.") debug.info(1,"Using bitcell: {}".format(OPTS.bitcell)) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 523f34df..841f99b1 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -148,11 +148,12 @@ class bank(design.design): # The center point for these cells are the upper-right corner of # the bitcell array. - # The decoder/driver logic is placed on the right and mirrored on Y-axis. - # The write/sense/precharge/mux is placed on the top and mirrored on the X-axis. - + # The port address decoder/driver logic is placed on the right and mirrored on X- and Y-axis. + # The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis. self.bitcell_array_top = self.bitcell_array.height - self.bitcell_array_right = self.bitcell_array.width + self.m1_width + self.m2_gap + self.bitcell_array_right = self.bitcell_array.width + self.m1_width + self.m2_gap + # Offset past the dummy/RBL column + self.bitcell_array_left = 2*self.bitcell.width self.compute_instance_port0_offsets() if len(self.all_ports)==2: @@ -161,7 +162,7 @@ class bank(design.design): def compute_instance_port0_offsets(self): """ - Compute the instance offsets for port0. + Compute the instance offsets for port0 on the left/bottom of the bank. """ port = 0 @@ -172,17 +173,15 @@ class bank(design.design): # LOWER RIGHT QUADRANT # Below the bitcell array - self.port_data_offsets[port] = vector(0,0) + self.port_data_offsets[port] = vector(self.bitcell_array_left,0) # UPPER LEFT QUADRANT # To the left of the bitcell array - # The wordline driver is placed to the right of the main decoder width. x_offset = self.m2_gap + self.port_address.width self.port_address_offsets[port] = vector(-x_offset,0) # LOWER LEFT QUADRANT - # Place the col decoder left aligned with wordline driver plus halfway under row decoder - # Place the col decoder left aligned with row decoder (x_offset doesn't change) + # Place the col decoder left aligned with wordline driver # Below the bitcell array with well spacing x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: @@ -204,7 +203,7 @@ class bank(design.design): def compute_instance_port1_offsets(self): """ - Compute the instance offsets for port1 on the top of the bank. + Compute the instance offsets for port1 on the right/top of the bank. """ port=1 @@ -218,12 +217,11 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the left of the bitcell array - # The wordline driver is placed to the right of the main decoder width. x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap self.port_address_offsets[port] = vector(x_offset,0) # UPPER RIGHT QUADRANT - # Place the col decoder right aligned with wordline driver plus halfway under row decoder + # Place the col decoder right aligned with wordline driver # Above the bitcell array with a well spacing x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: @@ -247,16 +245,12 @@ class bank(design.design): self.compute_instance_offsets() - # UPPER RIGHT QUADRANT self.place_bitcell_array(self.bitcell_array_offset) - # LOWER RIGHT QUADRANT self.place_port_data(self.port_data_offsets) - # UPPER LEFT QUADRANT self.place_port_address(self.port_address_offsets) - # LOWER LEFT QUADRANT self.place_column_decoder(self.column_decoder_offsets) self.place_bank_select(self.bank_select_offsets) @@ -274,11 +268,6 @@ class bank(design.design): debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") - # Width for the vdd/gnd rails - self.supply_rail_width = 4*self.m2_width - # FIXME: This spacing should be width dependent... - self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space - # The order of the control signals on the control bus: self.input_control_signals = [] port_num = 0 @@ -328,7 +317,7 @@ class bank(design.design): self.wl_names = self.bitcell.list_all_wl_names() self.bitline_names = self.bitcell.list_all_bitline_names() - self.bitcell_array = factory.create(module_type="bitcell_array", + self.bitcell_array = factory.create(module_type="replica_bitcell_array", cols=self.num_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) @@ -358,11 +347,14 @@ class bank(design.design): self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array) - + print(self.bitcell_array.pins) temp = [] for col in range(self.num_cols): for bitline in self.bitline_names: temp.append(bitline+"_{0}".format(col)) + for col in range(2): + for bitline in self.bitline_names: + temp.append("replica_"+bitline+"_{0}".format(col)) for row in range(self.num_rows): for wordline in self.wl_names: temp.append(wordline+"_{0}".format(row)) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 0bf635ca..feab1cce 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -22,13 +22,18 @@ class replica_bitcell_array(design.design): Dummy are the outside columns/rows with WL and BL tied to gnd. Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected). """ - def __init__(self, cols, rows, name): + def __init__(self, cols, rows, num_ports, name): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.column_size = cols self.row_size = rows + self.num_ports = num_ports + + # Two dummy rows/cols plus replica for each port + self.extra_rows = 2 + self.num_ports + self.extra_cols = 2 + self.num_ports self.create_netlist() if not OPTS.netlist_only: @@ -80,19 +85,20 @@ class replica_bitcell_array(design.design): # Replica bitline self.replica_column = factory.create(module_type="replica_column", - rows=self.row_size + 4) + rows=self.row_size + self.extra_rows, + num_ports = self.num_ports) self.add_mod(self.replica_column) # Dummy row self.dummy_row = factory.create(module_type="dummy_array", - rows=1, - cols=self.column_size) + cols=self.column_size, + rows=1) self.add_mod(self.dummy_row) # Dummy col self.dummy_col = factory.create(module_type="dummy_array", cols=1, - rows=self.row_size + 4) + rows=self.row_size + self.extra_rows) self.add_mod(self.dummy_col) @@ -103,27 +109,33 @@ class replica_bitcell_array(design.design): self.bl_names = [x for x in self.bitcell_array.pins if x.startswith("b")] # top/bottom rows (in the middle) - self.replica_wl_names = ["replica_"+x for x in self.cell.pins if x.startswith("w")] - self.dummy_wl_names = ["dummy_"+x for x in self.cell.pins if x.startswith("w")] - self.dummy_bl_names = ["dummy_"+x for x in self.cell.pins if x.startswith("b")] + self.replica_wl_names = ["replica_"+x for x in self.cell.list_all_wl_names()] + self.dummy_wl_names = ["dummy_"+x for x in self.cell.list_all_wl_names()] + self.dummy_bl_names = ["dummy_"+x for x in self.cell.list_all_bitline_names()] self.dummy_row_bl_names = self.bl_names # dummy row and replica on each side of the bitcell rows self.replica_col_wl_names = [x+"_0" for x in self.dummy_wl_names] \ + ["replica_"+x+"_0" for x in self.cell.list_all_wl_names()] \ - + self.wl_names \ - + ["replica_"+x+"_1" for x in self.cell.list_all_wl_names()] \ - + [x+"_1" for x in self.dummy_wl_names] - self.replica_bl_names = ["replica_"+x for x in self.cell.pins if x.startswith("b")] + + self.wl_names + if self.num_ports==2: + self.replica_col_wl_names.extend(["replica_"+x+"_1" for x in self.cell.list_all_wl_names()]) + self.replica_col_wl_names.extend([x+"_1" for x in self.dummy_wl_names]) + self.replica_bl_names = ["replica_"+x for x in self.cell.list_all_bitline_names()] + self.replica_bl0_names = [x+"_0" for x in self.replica_bl_names] + self.replica_bl1_names = [x+"_1" for x in self.replica_bl_names] # left/right rows self.dummy_col_wl_names = self.replica_col_wl_names self.add_pin_list(self.bl_names) - self.add_pin_list([x+"_0" for x in self.replica_bl_names]) - self.add_pin_list([x+"_1" for x in self.replica_bl_names]) - self.add_pin_list([x for x in self.replica_col_wl_names if not x.startswith("dummy")]) + self.add_pin_list(self.replica_bl0_names) + if self.num_ports==2: + self.add_pin_list(self.replica_bl1_names) + self.add_pin_list(self.wl_names) + self.add_pin_list(self.replica_wl_names) + #self.add_pin_list([x for x in self.replica_col_wl_names if not x.startswith("dummy")]) self.add_pin("vdd") self.add_pin("gnd") @@ -142,10 +154,11 @@ class replica_bitcell_array(design.design): self.replica_col_left_inst=self.add_inst(name="replica_col_left", mod=self.replica_column) self.connect_inst([x+"_0" for x in self.replica_bl_names] + self.replica_col_wl_names + supplies) - - self.replica_col_right_inst=self.add_inst(name="replica_col_right", - mod=self.replica_column) - self.connect_inst([x+"_1" for x in self.replica_bl_names] + self.replica_col_wl_names[::-1] + supplies) + + if self.num_ports==2: + self.replica_col_right_inst=self.add_inst(name="replica_col_right", + mod=self.replica_column) + self.connect_inst([x+"_1" for x in self.replica_bl_names] + self.replica_col_wl_names[::-1] + supplies) # Replica rows with replica bitcell self.dummy_row_bottop_inst=self.add_inst(name="dummy_row_bottop", @@ -177,16 +190,17 @@ class replica_bitcell_array(design.design): def create_layout(self): - self.height = (self.row_size+4)*self.dummy_row.height - self.width = (self.column_size+4)*self.replica_column.width + self.height = (self.row_size+self.extra_rows)*self.dummy_row.height + self.width = (self.column_size+self.extra_cols)*self.replica_column.width # This is a bitcell x bitcell offset to scale offset = vector(self.replica_column.width, self.dummy_row.height) self.bitcell_array_inst.place(offset=[0,0]) self.replica_col_left_inst.place(offset=offset.scale(-1,-2)) - self.replica_col_right_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ur(), - mirror="MX") + if self.num_ports==2: + self.replica_col_right_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ur(), + mirror="MX") self.dummy_row_toptop_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ul(), mirror="MX") @@ -196,7 +210,11 @@ class replica_bitcell_array(design.design): self.dummy_row_botbot_inst.place(offset=offset.scale(0,-2)) self.dummy_col_left_inst.place(offset=offset.scale(-2,-2)) - self.dummy_col_right_inst.place(offset=offset.scale(1,-2)+self.bitcell_array_inst.lr()) + if self.num_ports==2: + self.dummy_col_right_inst.place(offset=offset.scale(1,-2)+self.bitcell_array_inst.lr()) + else: + self.dummy_col_right_inst.place(offset=offset.scale(0,-2)+self.bitcell_array_inst.lr()) + self.translate_all(offset.scale(-2,-2)) @@ -246,7 +264,10 @@ class replica_bitcell_array(design.design): height=pin.height()) # Replica columns - for index,side in enumerate(["left","right"]): + replica_sides = ["left"] + if self.num_ports==2: + replica_sides.append("right") + for index,side in enumerate(replica_sides): inst = getattr(self, "replica_col_{}_inst".format(side)) pin_names = inst.mod.get_pin_names() for pin_name in pin_names: @@ -262,11 +283,7 @@ class replica_bitcell_array(design.design): for pin_name in ["vdd","gnd"]: - for inst in [self.bitcell_array_inst, - self.replica_col_left_inst, self.replica_col_right_inst, - self.dummy_col_left_inst, self.dummy_col_right_inst, - self.dummy_row_toptop_inst, self.dummy_row_topbot_inst, - self.dummy_row_bottop_inst, self.dummy_row_botbot_inst]: + for inst in self.insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 0de0aace..d692db78 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -16,10 +16,11 @@ class replica_column(design.design): Generate a replica bitline column for the replica array. """ - def __init__(self, name, rows): + def __init__(self, name, rows, num_ports): design.design.__init__(self, name) self.row_size = rows + self.num_ports = num_ports self.create_netlist() if not OPTS.netlist_only: @@ -64,7 +65,7 @@ class replica_column(design.design): self.cell_inst = {} for row in range(self.row_size): name="rbc_{0}".format(row) - if row>0 and row0 and row