diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 3d3434ef..be0a6246 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -18,14 +18,18 @@ class control_logic(design.design): Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows): + def __init__(self, num_rows, port="rw"): """ Constructor """ design.design.__init__(self, "control_logic") debug.info(1, "Creating {}".format(self.name)) - + self.num_rows = num_rows - self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports - self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports + self.port = port + + if self.port == "r": + self.num_control_signals = 1 + else: + self.num_control_signals = 2 self.create_netlist() if not OPTS.netlist_only: @@ -43,7 +47,7 @@ class control_logic(design.design): self.place_modules() self.route_all() - self.add_lvs_correspondence_points() + #self.add_lvs_correspondence_points() self.DRC_LVS() @@ -63,7 +67,7 @@ class control_logic(design.design): dff = dff_inv() dff_height = dff.height - self.ctrl_dff_array = dff_inv_array(rows=1+self.total_write,columns=1) + self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1) self.add_mod(self.ctrl_dff_array) self.nand2 = pnand2(height=dff_height) @@ -83,39 +87,47 @@ class control_logic(design.design): self.inv8 = pinv(size=16, height=dff_height) self.add_mod(self.inv8) - from importlib import reload - c = reload(__import__(OPTS.replica_bitline)) - replica_bitline = getattr(c, OPTS.replica_bitline) - # FIXME: These should be tuned according to the size! - delay_stages = 4 # Must be non-inverting - delay_fanout = 3 # This can be anything >=2 - bitcell_loads = int(math.ceil(self.num_rows / 5.0)) - self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads) - self.add_mod(self.replica_bitline) + if (self.port == "rw") or (self.port == "r"): + from importlib import reload + c = reload(__import__(OPTS.replica_bitline)) + replica_bitline = getattr(c, OPTS.replica_bitline) + # FIXME: These should be tuned according to the size! + delay_stages = 4 # Must be non-inverting + delay_fanout = 3 # This can be anything >=2 + bitcell_loads = int(math.ceil(self.num_rows / 5.0)) + self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads) + self.add_mod(self.replica_bitline) def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ # List of input control signals - self.input_list =["csb"] - for port in range(self.total_write): - self.input_list.append("web{}".format(port)) - - self.dff_output_list =["cs_bar", "cs"] - for port in range(self.total_write): - self.dff_output_list.append("we_bar{}".format(port)) - self.dff_output_list.append("we{}".format(port)) + if self.port == "r": + self.input_list =["csb"] + else: + self.input_list =["csb", "web"] + + if self.port == "r": + self.dff_output_list =["cs_bar", "cs"] + else: + self.dff_output_list =["cs_bar", "cs", "we_bar", "we"] # list of output control signals (for making a vertical bus) - self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"] + if self.port == "r": + self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"] + else: + self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"] # leave space for the bus plus one extra space self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch # Outputs to the bank - self.output_list = ["s_en"] - for port in range(self.total_write): - self.output_list.append("w_en{}".format(port)) + if self.port == "r": + self.output_list = ["s_en"] + elif self.port == "w": + self.output_list = ["w_en"] + else: + self.output_list = ["s_en", "w_en"] self.output_list.append("clk_buf_bar") self.output_list.append("clk_buf") @@ -133,14 +145,13 @@ class control_logic(design.design): def create_modules(self): """ Create all the modules """ self.create_dffs() - self.create_clk_row() - self.create_we_row() - # self.create_trien_row() - # self.create_trien_bar_row() - self.create_rbl_in_row() - self.create_sen_row() - self.create_rbl() - + self.create_clk_row() + if (self.port == "rw") or (self.port == "w"): + self.create_we_row() + if (self.port == "rw") or (self.port == "r"): + self.create_rbl_in_row() + self.create_sen_row() + self.create_rbl() def place_modules(self): @@ -149,38 +160,44 @@ class control_logic(design.design): # and add the vdd/gnd pins self.row_end_inst = [] - # Add the control flops on the left of the bus self.place_dffs() + row = 0 # Add the logic on the right of the bus - self.place_clk_row(row=0) # clk is a double-high cell - self.place_we_row(row=2) - # self.place_trien_row(row=3) - # self.place_trien_bar_row(row=4) - self.place_rbl_in_row(row=3) - self.place_sen_row(row=4) - self.place_rbl(row=5) - + self.place_clk_row(row=row) # clk is a double-high cell + row += 2 + if (self.port == "rw") or (self.port == "w"): + self.place_we_row(row=row) + pre_height = self.w_en_inst.uy() + control_center_y = self.w_en_inst.by() + row += 1 + if (self.port == "rw") or (self.port == "r"): + self.place_rbl_in_row(row=row) + self.place_sen_row(row=row+1) + self.place_rbl(row=row+2) + pre_height = self.rbl_inst.uy() + control_center_y = self.rbl_inst.by() - # This offset is used for placement of the control logic in - # the SRAM level. - self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by()) + # 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) # Extra pitch on top and right - self.height = self.rbl_inst.uy() + self.m3_pitch + self.height = pre_height + self.m3_pitch # Max of modules or logic rows - self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch - + if (self.port == "rw") or (self.port == "r"): + self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch + else: + self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch def route_all(self): """ Routing between modules """ self.route_dffs() - #self.route_trien() - #self.route_trien_bar() - self.route_rbl_in() - self.route_wen() - self.route_sen() + if (self.port == "rw") or (self.port == "w"): + self.route_wen() + if (self.port == "rw") or (self.port == "r"): + self.route_rbl_in() + self.route_sen() self.route_clk() self.route_supply() @@ -217,7 +234,7 @@ class control_logic(design.design): def create_rbl_in_row(self): - self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar", + self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar", mod=self.nand2) self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"]) @@ -277,7 +294,11 @@ class control_logic(design.design): def route_dffs(self): """ Route the input inverters """ - dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(3)], ["cs", "we"]) + if self.port == "r": + control_inputs = ["cs"] + else: + control_inputs = ["cs", "we"] + dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs) self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets) # Connect the clock rail to the other clock rail @@ -290,7 +311,8 @@ class control_logic(design.design): rotate=90) self.copy_layout_pin(self.ctrl_dff_inst, "din[0]", "csb") - self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web0") + if (self.port == "rw") or (self.port == "w"): + self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web") def create_dffs(self): @@ -317,31 +339,23 @@ class control_logic(design.design): def create_we_row(self): # input: WE, CS output: w_en_bar - self.w_en_bar_inst = [] - for port in range(self.total_write): - self.w_en_bar_inst.append(self.add_inst(name="nand3_w_en_bar{}".format(port), - mod=self.nand3)) - self.connect_inst(["clk_buf_bar", "cs", "we{}".format(port), "w_en_bar{}".format(port), "vdd", "gnd"]) + self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar", + mod=self.nand3) + self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) # input: w_en_bar, output: pre_w_en - self.pre_w_en_inst = [] - for port in range(self.total_write): - self.pre_w_en_inst.append(self.add_inst(name="inv_pre_w_en{}".format(port), - mod=self.inv1)) - self.connect_inst(["w_en_bar{}".format(port), "pre_w_en{}".format(port), "vdd", "gnd"]) + self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en", + mod=self.inv1) + self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) # BUFFER INVERTERS FOR W_EN - self.pre_w_en_bar_inst = [] - for port in range(self.total_write): - self.pre_w_en_bar_inst.append(self.add_inst(name="inv_pre_w_en_bar{}".format(port), - mod=self.inv2)) - self.connect_inst(["pre_w_en{}".format(port), "pre_w_en_bar{}".format(port), "vdd", "gnd"]) + self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar", + mod=self.inv2) + self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) - self.w_en_inst = [] - for port in range(self.total_write): - self.w_en_inst.append(self.add_inst(name="inv_w_en2_{}".format(port), - mod=self.inv8)) - self.connect_inst(["pre_w_en_bar{}".format(port), "w_en{}".format(port), "vdd", "gnd"]) + self.w_en_inst = self.add_inst(name="inv_w_en2", + mod=self.inv8) + self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"]) def place_we_row(self,row): @@ -349,26 +363,26 @@ class control_logic(design.design): (y_off,mirror)=self.get_offset(row) w_en_bar_offset = vector(x_off, y_off) - self.w_en_bar_inst[0].place(offset=w_en_bar_offset, + self.w_en_bar_inst.place(offset=w_en_bar_offset, mirror=mirror) x_off += self.nand3.width pre_w_en_offset = vector(x_off, y_off) - self.pre_w_en_inst[0].place(offset=pre_w_en_offset, + self.pre_w_en_inst.place(offset=pre_w_en_offset, mirror=mirror) x_off += self.inv1.width pre_w_en_bar_offset = vector(x_off, y_off) - self.pre_w_en_bar_inst[0].place(offset=pre_w_en_bar_offset, + self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset, mirror=mirror) x_off += self.inv2.width w_en_offset = vector(x_off, y_off) - self.w_en_inst[0].place(offset=w_en_offset, + self.w_en_inst.place(offset=w_en_offset, mirror=mirror) x_off += self.inv8.width - self.row_end_inst.append(self.w_en_inst[0]) + self.row_end_inst.append(self.w_en_inst) def route_rbl_in(self): @@ -446,19 +460,19 @@ class control_logic(design.design): def route_wen(self): wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"]) - self.connect_vertical_bus(wen_map, self.w_en_bar_inst[0], self.rail_offsets) + self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets) # Connect the NAND3 output to the inverter # The pins are assumed to extend all the way to the cell edge - w_en_bar_pos = self.w_en_bar_inst[0].get_pin("Z").center() - inv_in_pos = self.pre_w_en_inst[0].get_pin("A").center() + w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center() + inv_in_pos = self.pre_w_en_inst.get_pin("A").center() mid1 = vector(inv_in_pos.x,w_en_bar_pos.y) self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos]) - self.add_path("metal1",[self.pre_w_en_inst[0].get_pin("Z").center(), self.pre_w_en_bar_inst[0].get_pin("A").center()]) - self.add_path("metal1",[self.pre_w_en_bar_inst[0].get_pin("Z").center(), self.w_en_inst[0].get_pin("A").center()]) + self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()]) + self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()]) - self.connect_output(self.w_en_inst[0], "Z", "w_en0") + self.connect_output(self.w_en_inst, "Z", "w_en") def route_sen(self): rbl_out_pos = self.rbl_inst.get_pin("out").bc() @@ -521,9 +535,9 @@ class control_logic(design.design): self.add_power_pin("gnd", pin_loc) self.add_path("metal1", [row_loc, pin_loc]) - - self.copy_layout_pin(self.rbl_inst,"gnd") - self.copy_layout_pin(self.rbl_inst,"vdd") + if (self.port == "rw") or (self.port == "r"): + self.copy_layout_pin(self.rbl_inst,"gnd") + self.copy_layout_pin(self.rbl_inst,"vdd") self.copy_layout_pin(self.ctrl_dff_inst,"gnd") self.copy_layout_pin(self.ctrl_dff_inst,"vdd") diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 644ce9f4..b9d9cf54 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -107,7 +107,7 @@ class replica_bitline(design.design): def create_modules(self): """ Create all of the module instances in the logical netlist """ - total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports # This is the threshold detect inverter on the output of the RBL self.rbl_inv_inst=self.add_inst(name="rbl_inv", @@ -127,10 +127,10 @@ class replica_bitline(design.design): self.rbc_inst=self.add_inst(name="bitcell", mod=self.replica_bitcell) temp = [] - for port in range(total_ports): + for port in range(self.total_ports): temp.append("bl{}[0]".format(port)) temp.append("br{}[0]".format(port)) - for port in range(total_ports): + for port in range(self.total_ports): temp.append("delayed_en") temp.append("vdd") temp.append("gnd") @@ -141,11 +141,11 @@ class replica_bitline(design.design): mod=self.rbl) temp = [] - for port in range(total_ports): + for port in range(self.total_ports): temp.append("bl{}[0]".format(port)) temp.append("br{}[0]".format(port)) for wl in range(self.bitcell_loads): - for port in range(total_ports): + for port in range(self.total_ports): temp.append("gnd") temp.append("vdd") temp.append("gnd") @@ -169,9 +169,6 @@ class replica_bitline(design.design): mirror="MX") self.rbl_inst.place(self.rbl_offset) - - - def route(self): @@ -201,7 +198,13 @@ class replica_bitline(design.design): continue self.add_path("metal1", [pin_right, pin_extension1, pin_extension2]) self.add_power_pin("gnd", pin_extension2) - + + # for multiport, need to short wordlines to each other so they all connect to gnd + wl_last = self.wl_list[self.total_ports-1]+"[{}]".format(row) + pin_last = self.rbl_inst.get_pin(wl_last) + + correct = vector(0.5*drc["minwidth_metal1"], 0) + self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct]) def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ @@ -265,6 +268,15 @@ class replica_bitline(design.design): #wl_mid1 = vector(xmid_point,contact_offset.y) #wl_mid2 = vector(xmid_point,wl_offset.y) self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset]) + + # 4. Short wodlines if multiport + wl = self.wl_list[0] + wl_last = self.wl_list[self.total_ports-1] + pin = self.rbc_inst.get_pin(wl) + pin_last = self.rbc_inst.get_pin(wl_last) + + correct = vector(0.5*drc["minwidth_metal1"], 0) + self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) # DRAIN ROUTE # Route the drain to the vdd rail diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py index 0eaedc15..6797bc65 100644 --- a/compiler/tests/14_replica_bitline_test.py +++ b/compiler/tests/14_replica_bitline_test.py @@ -51,6 +51,23 @@ class replica_bitline_test(openram_test): a = replica_bitline.replica_bitline(stages,fanout,rows) self.local_check(a) + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + stages=4 + fanout=4 + rows=13 + debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages,fanout,rows) + self.local_check(a) + + stages=8 + rows=100 + debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages,fanout,rows) + self.local_check(a) + globals.end_openram() # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index f6aa8dce..5e6a7747 100644 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -24,7 +24,6 @@ class control_logic_test(openram_test): self.local_check(a) # check control logic for multi-port - # only layout for 1RW is supported at the moment OPTS.bitcell = "pbitcell" OPTS.replica_bitcell = "replica_pbitcell" OPTS.num_rw_ports = 1 @@ -34,6 +33,19 @@ class control_logic_test(openram_test): debug.info(1, "Testing sample for control_logic for multiport") a = control_logic.control_logic(num_rows=128) self.local_check(a) + + # Check write-only and read-only control logic + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(1, "Testing sample for control_logic for multiport, only write control logic") + a = control_logic.control_logic(num_rows=128, port="w") + self.local_check(a) + + debug.info(1, "Testing sample for control_logic for multiport, only read control logic") + a = control_logic.control_logic(num_rows=128, port="r") + self.local_check(a) globals.end_openram()