diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 7860e83c..d316e713 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -49,6 +49,10 @@ class geometry: ll = vector(min(first[0],second[0]),min(first[1],second[1])) ur = vector(max(first[0],second[0]),max(first[1],second[1])) self.boundary=[ll,ur] + + def update_boundary(self): + """ Update the boundary with a new placement. """ + self.compute_boundary(self.offset,self.mirror,self.rotate) def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0): """ Transform with offset, mirror and rotation to get the absolute pin location. @@ -124,7 +128,7 @@ class instance(geometry): An instance of an instance/module with a specified location and rotation """ - def __init__(self, name, mod, offset, mirror, rotate): + def __init__(self, name, mod, offset=[0,0], mirror="R0", rotate=0): """Initializes an instance to represent a module""" geometry.__init__(self) debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.") diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 6bf59ea2..3aa21794 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -118,6 +118,16 @@ class layout(lef.lef): for pin in pin_list: pin.rect = [pin.ll() - offset, pin.ur() - offset] + def place_inst(self, name, offset, mirror="R0", rotate=0): + """ This updates the placement of an instance. """ + inst = self.get_inst(name) + debug.info(3, "placing instance {}".format(inst)) + # Update the placement of an already added instance + inst.offset = offset + inst.mirror = mirror + inst.rotate = rotate + inst.update_boundary() + return inst def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): """Adds an instance of a mod to this module""" diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index f3bf5da4..0152a6c8 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -91,19 +91,26 @@ class spice(verilog.verilog): group of modules are generated.""" if (check and (len(self.insts[-1].mod.pins) != len(args))): - debug.error("Connections: {}".format(self.insts[-1].mod.pins)) - debug.error("Connections: {}".format(args)) + import pprint + modpins_string=pprint.pformat(self.insts[-1].mod.pins) + argpins_string=pprint.pformat(args) + debug.error("Connections: {}".format(modpins_string)) + debug.error("Connections: {}".format(argpins_string)) debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins), len(args)), 1) self.conns.append(args) if check and (len(self.insts)!=len(self.conns)): + import pprint + insts_string=pprint.pformat(self.insts) + conns_string=pprint.pformat(self.conns) + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, len(self.insts), len(self.conns))) - debug.error("Instances: \n"+str(self.insts)) + debug.error("Instances: \n"+str(insts_string)) debug.error("-----") - debug.error("Connections: \n"+str(self.conns),1) + debug.error("Connections: \n"+str(conns_string),1) diff --git a/compiler/globals.py b/compiler/globals.py index 9d088418..02168e37 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -204,6 +204,11 @@ def read_config(config_file, is_unit_test=True): OPTS.is_unit_test=is_unit_test + # If we are only generating a netlist, we can't do DRC/LVS + if OPTS.netlist_only: + OPTS.check_lvsdrc=False + + # If config didn't set output name, make a reasonable default. if (OPTS.output_name == ""): OPTS.output_name = "sram_{0}rw_{1}b_{2}w_{3}bank_{4}".format(OPTS.rw_ports, @@ -372,6 +377,9 @@ def report_status(): print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size, OPTS.num_words, OPTS.num_banks)) + if OPTS.netlist_only: + print("Netlist only mode (no physical design is being done).") + if not OPTS.check_lvsdrc: print("DRC/LVS/PEX checking is disabled.") diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index ea7881ed..cadb0b7e 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -42,6 +42,10 @@ class bank(design.design): self.num_words = num_words self.words_per_row = words_per_row self.num_banks = num_banks + + self.total_write = OPTS.rw_ports + OPTS.w_ports + self.total_read = OPTS.rw_ports + OPTS.r_ports + self.total_ports = OPTS.rw_ports + OPTS.w_ports + OPTS.r_ports # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create @@ -50,41 +54,51 @@ class bank(design.design): self.prefix="gated_" else: self.prefix="" - + + self.create_netlist() + self.create_layout() + + + def create_netlist(self): self.compute_sizes() self.add_pins() - self.create_modules() self.add_modules() - self.setup_layout_constraints() + self.create_modules() - # FIXME: Move this to the add modules function - self.add_bank_select() - + def create_layout(self): + self.place_modules() + self.setup_routing_constraints() self.route_layout() - # Can remove the following, but it helps for debug! - self.add_lvs_correspondence_points() + #self.add_lvs_correspondence_points() # Remember the bank center for further placement self.bank_center=self.offset_all_coordinates().scale(-1,-1) self.DRC_LVS() - + def add_pins(self): """ Adding pins for Bank module""" - for i in range(self.word_size): - self.add_pin("dout[{0}]".format(i),"OUT") - for i in range(self.word_size): - self.add_pin("din[{0}]".format(i),"IN") - for i in range(self.addr_size): - self.add_pin("addr[{0}]".format(i),"INPUT") + for k in range(self.total_read): + for i in range(self.word_size): + self.add_pin("dout{0}[{1}]".format(k,i),"OUT") + for k in range(self.total_write): + for i in range(self.word_size): + self.add_pin("din{0}[{1}]".format(k,i),"IN") + for k in range(self.total_ports): + for i in range(self.addr_size): + self.add_pin("addr{0}[{1}]".format(k,i),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: self.add_pin("bank_sel","INPUT") - for pin in ["s_en","w_en","clk_buf_bar","clk_buf"]: + for k in range(self.total_read): + self.add_pin("s_en{0}".format(k), "INPUT") + for k in range(self.total_write): + self.add_pin("w_en{0}".format(k), "INPUT") + for pin in ["clk_buf_bar","clk_buf"]: self.add_pin(pin,"INPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -106,25 +120,46 @@ class bank(design.design): self.route_vdd_gnd() - def add_modules(self): + def create_modules(self): """ Add modules. The order should not matter! """ # Above the bitcell array - self.add_bitcell_array() - self.add_precharge_array() + self.create_bitcell_array() + self.create_precharge_array() # Below the bitcell array - self.add_column_mux_array() - self.add_sense_amp_array() - self.add_write_driver_array() + self.create_column_mux_array() + self.create_sense_amp_array() + self.create_write_driver_array() # To the left of the bitcell array - self.add_row_decoder() - self.add_wordline_driver() - self.add_column_decoder() + self.create_row_decoder() + self.create_wordline_driver() + self.create_column_decoder() + self.create_bank_select() + def place_modules(self): + """ Add modules. The order should not matter! """ + + # Above the bitcell array + self.place_bitcell_array() + self.place_precharge_array() + + # Below the bitcell array + self.place_column_mux_array() + self.place_sense_amp_array() + self.place_write_driver_array() + + # To the left of the bitcell array + self.place_row_decoder() + self.place_wordline_driver() + self.place_column_decoder() + + + self.place_bank_select() + def compute_sizes(self): """ Computes the required sizes to create the bank """ @@ -146,7 +181,8 @@ class bank(design.design): # Number of control lines in the bus self.num_control_lines = 4 # The order of the control signals on the control bus: - self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en"] + self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] + # These will be outputs of the gaters if this is multibank, if not, normal signals. if self.num_banks > 1: self.control_signals = ["gated_"+str for str in self.input_control_signals] @@ -167,17 +203,31 @@ class bank(design.design): 2*self.m2_pitch) - - def create_modules(self): + def add_modules(self): """ Create all the modules using the class loader """ self.bitcell = self.mod_bitcell() self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) + + # create arrays of bitline and bitline_bar names for read, write, or all ports + self.read_bl_list = self.bitcell.list_read_bl_names() + self.read_br_list = self.bitcell.list_read_br_names() + + self.write_bl_list = self.bitcell.list_write_bl_names() + self.write_br_list = self.bitcell.list_write_br_names() + + self.total_bl_list = self.bitcell.list_all_bl_names() + self.total_br_list = self.bitcell.list_all_br_names() + + self.total_wl_list = self.bitcell.list_all_wl_names() + self.total_bitline_list = self.bitcell.list_all_bitline_names() - self.precharge_array = self.mod_precharge_array(columns=self.num_cols) - self.add_mod(self.precharge_array) + self.precharge_array = [] + for k in range(self.total_read): + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[k], bitcell_br=self.read_br_list[k])) + self.add_mod(self.precharge_array[k]) if self.col_addr_size > 0: self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, @@ -207,171 +257,219 @@ class bank(design.design): self.add_mod(self.bank_select) - def add_bitcell_array(self): - """ Adding Bitcell Array """ + def create_bitcell_array(self): + """ Creating Bitcell Array """ self.bitcell_array_inst=self.add_inst(name="bitcell_array", - mod=self.bitcell_array, - offset=vector(0,0)) + mod=self.bitcell_array) + + temp = [] - for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) - for j in range(self.num_rows): - temp.append("wl[{0}]".format(j)) - temp.extend(["vdd", "gnd"]) + for col in range(self.num_cols): + for bitline in self.total_bitline_list: + temp.append(bitline+"[{0}]".format(col)) + for row in range(self.num_rows): + for wordline in self.total_wl_list: + temp.append(wordline+"[{0}]".format(row)) + temp.append("vdd") + temp.append("gnd") self.connect_inst(temp) + def place_bitcell_array(self): + """ Placing Bitcell Array """ + self.place_inst(name="bitcell_array", + offset=vector(0,0)) - def add_precharge_array(self): - """ Adding Precharge """ + + def create_precharge_array(self): + """ Creating Precharge """ - # The wells must be far enough apart - # The enclosure is for the well and the spacing is to the bitcell wells - y_offset = self.bitcell_array.height + self.m2_gap - self.precharge_array_inst=self.add_inst(name="precharge_array", - mod=self.precharge_array, - offset=vector(0,y_offset)) - temp = [] - for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) - temp.extend([self.prefix+"clk_buf_bar", "vdd"]) - self.connect_inst(temp) + self.precharge_array_inst = [] + for k in range(self.total_read): + self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(k), + mod=self.precharge_array[k])) + temp = [] + for i in range(self.num_cols): + temp.append(self.read_bl_list[k]+"[{0}]".format(i)) + temp.append(self.read_br_list[k]+"[{0}]".format(i)) + temp.extend([self.prefix+"clk_buf_bar", "vdd"]) + self.connect_inst(temp) - def add_column_mux_array(self): - """ Adding Column Mux when words_per_row > 1 . """ + def place_precharge_array(self): + """ Placing Precharge """ + + # FIXME: place for multiport + for k in range(self.total_read): + # The wells must be far enough apart + # The enclosure is for the well and the spacing is to the bitcell wells + y_offset = self.bitcell_array.height + self.m2_gap + self.place_inst(name=self.precharge_array_inst[k].name, + offset=vector(0,y_offset)) + + def create_column_mux_array(self): + """ Creating Column Mux when words_per_row > 1 . """ + if self.col_addr_size == 0: + return + + self.col_mux_array_inst = [] + for k in range(self.total_ports): + self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(k), + mod=self.column_mux_array)) + + temp = [] + for i in range(self.num_cols): + temp.append(self.total_bl_list[k]+"[{0}]".format(i)) + temp.append(self.total_br_list[k]+"[{0}]".format(i)) + for h in range(self.words_per_row): + temp.append("sel{0}[{1}]".format(k,h)) + for j in range(self.word_size): + temp.append(self.total_bl_list[k]+"_out[{0}]".format(j)) + temp.append(self.total_br_list[k]+"_out[{0}]".format(j)) + temp.append("gnd") + self.connect_inst(temp) + + def place_column_mux_array(self): + """ Placing Column Mux when words_per_row > 1 . """ if self.col_addr_size > 0: self.column_mux_height = self.column_mux_array.height + self.m2_gap else: self.column_mux_height = 0 return - y_offset = self.column_mux_height - self.col_mux_array_inst=self.add_inst(name="column_mux_array", - mod=self.column_mux_array, - offset=vector(0,y_offset).scale(-1,-1)) - temp = [] - for i in range(self.num_cols): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) - for k in range(self.words_per_row): - temp.append("sel[{0}]".format(k)) - for j in range(self.word_size): - temp.append("bl_out[{0}]".format(j)) - temp.append("br_out[{0}]".format(j)) - temp.append("gnd") - self.connect_inst(temp) + for k in range(self.total_ports): + y_offset = self.column_mux_height + self.place_inst(name=self.col_mux_array_inst[k].name, + offset=vector(0,y_offset).scale(-1,-1)) + + def create_sense_amp_array(self): + """ Creating Sense amp """ - def add_sense_amp_array(self): - """ Adding Sense amp """ + self.sense_amp_array_inst = [] + for k in range(self.total_read): + self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(k), + mod=self.sense_amp_array)) - y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst=self.add_inst(name="sense_amp_array", - mod=self.sense_amp_array, - offset=vector(0,y_offset).scale(-1,-1)) - temp = [] - for i in range(self.word_size): - temp.append("dout[{0}]".format(i)) - if self.words_per_row == 1: - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) - else: - temp.append("bl_out[{0}]".format(i)) - temp.append("br_out[{0}]".format(i)) - - temp.extend([self.prefix+"s_en", "vdd", "gnd"]) - self.connect_inst(temp) + temp = [] + for i in range(self.word_size): + temp.append("dout{0}[{1}]".format(k,i)) + if self.words_per_row == 1: + temp.append(self.read_bl_list[k]+"[{0}]".format(i)) + temp.append(self.read_br_list[k]+"[{0}]".format(i)) + else: + temp.append(self.read_bl_list[k]+"_out[{0}]".format(i)) + temp.append(self.read_br_list[k]+"_out[{0}]".format(i)) + + temp.extend([self.prefix+"s_en{0}".format(k), "vdd", "gnd"]) + self.connect_inst(temp) - def add_write_driver_array(self): - """ Adding Write Driver """ + def place_sense_amp_array(self): + """ Placing Sense amp """ - y_offset = self.sense_amp_array.height + self.column_mux_height \ - + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst=self.add_inst(name="write_driver_array", - mod=self.write_driver_array, - offset=vector(0,y_offset).scale(-1,-1)) + # FIXME: place for multiport + for k in range(self.total_read): + y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap + self.place_inst(name=self.sense_amp_array_inst[k].name, + offset=vector(0,y_offset).scale(-1,-1)) + + def create_write_driver_array(self): + """ Creating Write Driver """ - temp = [] - for i in range(self.word_size): - temp.append("din[{0}]".format(i)) - for i in range(self.word_size): - if (self.words_per_row == 1): - temp.append("bl[{0}]".format(i)) - temp.append("br[{0}]".format(i)) - else: - temp.append("bl_out[{0}]".format(i)) - temp.append("br_out[{0}]".format(i)) - temp.extend([self.prefix+"w_en", "vdd", "gnd"]) - self.connect_inst(temp) + self.write_driver_array_inst = [] + for k in range(self.total_write): + self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k), + mod=self.write_driver_array)) - def add_row_decoder(self): - """ Add the hierarchical row decoder """ + temp = [] + for i in range(self.word_size): + temp.append("din{0}[{1}]".format(k,i)) + for i in range(self.word_size): + if (self.words_per_row == 1): + temp.append(self.write_bl_list[k]+"[{0}]".format(i)) + temp.append(self.write_br_list[k]+"[{0}]".format(i)) + else: + temp.append(self.write_bl_list[k]+"_out[{0}]".format(i)) + temp.append(self.write_br_list[k]+"_out[{0}]".format(i)) + temp.extend([self.prefix+"w_en{0}".format(k), "vdd", "gnd"]) + self.connect_inst(temp) + def place_write_driver_array(self): + """ Placing Write Driver """ + + # FIXME: place for multiport + for k in range(self.total_write): + y_offset = self.sense_amp_array.height + self.column_mux_height \ + + self.m2_gap + self.write_driver_array.height + self.place_inst(name=self.write_driver_array_inst[k].name, + offset=vector(0,y_offset).scale(-1,-1)) + + + + def create_row_decoder(self): + """ Create the hierarchical row decoder """ + self.row_decoder_inst = [] + for k in range(self.total_ports): + self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(k), + mod=self.row_decoder)) + + temp = [] + for i in range(self.row_addr_size): + temp.append("addr{0}[{1}]".format(k,i+self.col_addr_size)) + for j in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(k,j)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def place_row_decoder(self): + """ Place the hierarchical row decoder """ + # The address and control bus will be in between decoder and the main memory array # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. - x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst=self.add_inst(name="row_decoder", - mod=self.row_decoder, - offset=vector(x_offset,0)) + # FIXME: place for multiport + for k in range(self.total_ports): + x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) + self.place_inst(name=self.row_decoder_inst[k].name, + offset=vector(x_offset,0)) - temp = [] - for i in range(self.row_addr_size): - temp.append("addr[{0}]".format(i+self.col_addr_size)) - for j in range(self.num_rows): - temp.append("dec_out[{0}]".format(j)) - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) + + def create_wordline_driver(self): + """ Create the Wordline Driver """ - def add_wordline_driver(self): - """ Wordline Driver """ + self.wordline_driver_inst = [] + for k in range(self.total_ports): + self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(k), + mod=self.wordline_driver)) - # The wordline driver is placed to the right of the main decoder width. - x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst=self.add_inst(name="wordline_driver", - mod=self.wordline_driver, - offset=vector(x_offset,0)) + temp = [] + for i in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(k,i)) + for i in range(self.num_rows): + temp.append(self.total_wl_list[k]+"[{0}]".format(i)) + temp.append(self.prefix+"clk_buf") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) - temp = [] - for i in range(self.num_rows): - temp.append("dec_out[{0}]".format(i)) - for i in range(self.num_rows): - temp.append("wl[{0}]".format(i)) - temp.append(self.prefix+"clk_buf") - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) + def place_wordline_driver(self): + """ Place the Wordline Driver """ + + # FIXME: place for multiport + for k in range(self.total_ports): + # The wordline driver is placed to the right of the main decoder width. + x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch + self.place_inst(name=self.wordline_driver_inst[k].name, + offset=vector(x_offset,0)) - def add_column_decoder_module(self): + def create_column_decoder(self): """ Create a 2:4 or 3:8 column address decoder. """ - # Place the col decoder right aligned with row decoder - x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.col_decoder_inst=self.add_inst(name="col_address_decoder", - mod=self.col_decoder, - offset=vector(x_off,y_off)) - - temp = [] - for i in range(self.col_addr_size): - temp.append("addr[{0}]".format(i)) - for j in range(self.num_col_addr_lines): - temp.append("sel[{0}]".format(j)) - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) - def add_column_decoder(self): - """ - Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2, - 2:4 decoder, or 3:8 decoder. - """ if self.col_addr_size == 0: return elif self.col_addr_size == 1: @@ -384,59 +482,100 @@ class bank(design.design): else: # No error checking before? debug.error("Invalid column decoder?",-1) + + self.col_decoder_inst = [] + for k in range(self.total_ports): + self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(k), + mod=self.col_decoder)) + + temp = [] + for i in range(self.col_addr_size): + temp.append("addr{0}[{1}]".format(k,i)) + for j in range(self.num_col_addr_lines): + temp.append("sel{0}[{1}]".format(k,j)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def place_column_decoder(self): + """ + Place a 2:4 or 3:8 column address decoder. + """ + if self.col_addr_size == 0: + return - self.add_column_decoder_module() + # FIXME: place for multiport + for k in range(self.total_ports): + # Place the col decoder right aligned with row decoder + x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) + y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + self.place_inst(name=self.col_decoder_inst[k].name, + offset=vector(x_off,y_off)) + + def create_bank_select(self): + """ Create the bank select logic. """ - def add_bank_select(self): - """ Instantiate the bank select logic. """ + if not self.num_banks > 1: + return + + self.bank_select_inst = [] + for k in range(self.total_ports): + self.bank_select_inst.append(self.add_inst(name="bank_select", + mod=self.bank_select)) + + temp = [] + temp.extend(self.input_control_signals) + temp.append("bank_sel") + temp.extend(self.control_signals) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def place_bank_select(self): + """ Place the bank select logic. """ if not self.num_banks > 1: return - x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - if self.col_addr_size > 0: - y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) - else: - y_off = self.row_decoder_inst.by() - y_off -= (self.bank_select.height + drc["well_to_well"]) - self.bank_select_pos = vector(x_off,y_off) - self.bank_select_inst = self.add_inst(name="bank_select", - mod=self.bank_select, - offset=self.bank_select_pos) - - temp = [] - temp.extend(self.input_control_signals) - temp.append("bank_sel") - temp.extend(self.control_signals) - temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) + # FIXME: place for multiport + for k in range(self.total_ports): + x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) + if self.col_addr_size > 0: + y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by()) + else: + y_off = self.row_decoder_inst[0].by() + y_off -= (self.bank_select.height + drc["well_to_well"]) + self.bank_select_pos = vector(x_off,y_off) + self.place_inst(name=self.bank_select_inst[k].name, + offset=self.bank_select_pos) + def route_vdd_gnd(self): """ Propagate all vdd/gnd pins up to this level for all modules """ # These are the instances that every bank has - top_instances = [self.bitcell_array_inst, - self.precharge_array_inst, - self.sense_amp_array_inst, - self.write_driver_array_inst, - self.row_decoder_inst, - self.wordline_driver_inst] - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst) - top_instances.append(self.col_mux_array_inst) + top_instances = [self.bitcell_array_inst] + + for k in range(self.total_ports): + top_instances.extend([self.precharge_array_inst[k], + self.sense_amp_array_inst[k], + self.write_driver_array_inst[k], + self.row_decoder_inst[k], + self.wordline_driver_inst[k]]) + # Add these if we use the part... + if self.col_addr_size > 0: + top_instances.append(self.col_decoder_inst[k]) + top_instances.append(self.col_mux_array_inst[k]) - if self.num_banks > 1: - top_instances.append(self.bank_select_inst) + if self.num_banks > 1: + top_instances.append(self.bank_select_inst[k]) for inst in top_instances: # Column mux has no vdd - if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): + if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst[0]): self.copy_layout_pin(inst, "vdd") # Precharge has no gnd - if inst != self.precharge_array_inst: + if inst != self.precharge_array_inst[0]: self.copy_layout_pin(inst, "gnd") def route_bank_select(self): @@ -446,7 +585,7 @@ class bank(design.design): for gated_name in self.control_signals: # Connect the inverter output to the central bus - out_pos = self.bank_select_inst.get_pin(gated_name).rc() + out_pos = self.bank_select_inst[0].get_pin(gated_name).rc() bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -460,16 +599,19 @@ class bank(design.design): rotate=90) - def setup_layout_constraints(self): - """ After the modules are instantiated, find the dimensions for the - control bus, power ring, etc. """ + def setup_routing_constraints(self): + """ + After the modules are instantiated, find the dimensions for the + control bus, power ring, etc. + """ #The minimum point is either the bottom of the address flops, #the column decoder (if there is one). - write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch - row_decoder_min_y_offset = self.row_decoder_inst.by() + write_driver_min_y_offset = self.write_driver_array_inst[0].by() - 3*self.m2_pitch + row_decoder_min_y_offset = self.row_decoder_inst[0].by() + if self.col_addr_size > 0: - col_decoder_min_y_offset = self.col_decoder_inst.by() + col_decoder_min_y_offset = self.col_decoder_inst[0].by() else: col_decoder_min_y_offset = row_decoder_min_y_offset @@ -483,10 +625,11 @@ class bank(design.design): # The max point is always the top of the precharge bitlines # Add a vdd and gnd power rail above the array - self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width + # FIXME: Update multiport + self.max_y_offset = self.precharge_array_inst[0].uy() + 3*self.m1_width self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width - self.min_x_offset = self.row_decoder_inst.lx() - + self.min_x_offset = self.row_decoder_inst[0].lx() + # # Create the core bbox for the power rings ur = vector(self.max_x_offset, self.max_y_offset) ll = vector(self.min_x_offset, self.min_y_offset) @@ -519,17 +662,19 @@ class bank(design.design): def route_precharge_to_bitcell_array(self): """ Routing of BL and BR between pre-charge and bitcell array """ - for i in range(self.num_cols): - precharge_bl = self.precharge_array_inst.get_pin("bl[{}]".format(i)).bc() - precharge_br = self.precharge_array_inst.get_pin("br[{}]".format(i)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).uc() - bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).uc() + # FIXME: Update for multiport + for k in range(self.total_read): + for i in range(self.num_cols): + precharge_bl = self.precharge_array_inst[k].get_pin("bl[{}]".format(i)).bc() + precharge_br = self.precharge_array_inst[k].get_pin("br[{}]".format(i)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).uc() + bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).uc() - yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) - self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), - vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), - vector(bitcell_br.x,yoffset), bitcell_br]) + yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) + self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), + vector(bitcell_bl.x,yoffset), bitcell_bl]) + self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), + vector(bitcell_br.x,yoffset), bitcell_br]) def route_col_mux_to_bitcell_array(self): @@ -539,51 +684,56 @@ class bank(design.design): if self.col_addr_size==0: return - for i in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst.get_pin("bl[{}]".format(i)).uc() - col_mux_br = self.col_mux_array_inst.get_pin("br[{}]".format(i)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() - bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() + # FIXME: Update for multiport + for k in range(self.total_ports): + for i in range(self.num_cols): + col_mux_bl = self.col_mux_array_inst[k].get_pin("bl[{}]".format(i)).uc() + col_mux_br = self.col_mux_array_inst[k].get_pin("br[{}]".format(i)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[k]+"[{}]".format(i)).bc() + bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[k]+"[{}]".format(i)).bc() - yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) - self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), - vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), - vector(bitcell_br.x,yoffset), bitcell_br]) + yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) + self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), + vector(bitcell_bl.x,yoffset), bitcell_bl]) + self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), + vector(bitcell_br.x,yoffset), bitcell_br]) def route_sense_amp_to_col_mux_or_bitcell_array(self): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ - for i in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst.get_pin("bl[{}]".format(i)).uc() - sense_amp_br = self.sense_amp_array_inst.get_pin("br[{}]".format(i)).uc() + for k in range(self.total_read): + for i in range(self.word_size): + sense_amp_bl = self.sense_amp_array_inst[k].get_pin("bl[{}]".format(i)).uc() + sense_amp_br = self.sense_amp_array_inst[k].get_pin("br[{}]".format(i)).uc() - if self.col_addr_size>0: - # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst.get_pin("bl_out[{}]".format(i)).bc() - connect_br = self.col_mux_array_inst.get_pin("br_out[{}]".format(i)).bc() - else: - # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() - connect_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() - + if self.col_addr_size>0: + # Sense amp is connected to the col mux + connect_bl = self.col_mux_array_inst[k].get_pin("bl_out[{}]".format(i)).bc() + connect_br = self.col_mux_array_inst[k].get_pin("br_out[{}]".format(i)).bc() + else: + # Sense amp is directly connected to the bitcell array + connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc() + connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).bc() - yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) - self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), - vector(connect_bl.x,yoffset), connect_bl]) - self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), - vector(connect_br.x,yoffset), connect_br]) + + yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) + self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), + vector(connect_bl.x,yoffset), connect_bl]) + self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), + vector(connect_br.x,yoffset), connect_br]) + def route_sense_amp_out(self): """ Add pins for the sense amp output """ for i in range(self.word_size): - data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i)) - self.add_layout_pin_rect_center(text="dout[{}]".format(i), + data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i)) + self.add_layout_pin_rect_center(text="dout0[{}]".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), - width=data_pin.width()), + width=data_pin.width()) + def route_row_decoder(self): """ Routes the row decoder inputs and supplies """ @@ -591,8 +741,8 @@ class bank(design.design): for i in range(self.row_addr_size): addr_idx = i + self.col_addr_size decoder_name = "addr[{}]".format(i) - addr_name = "addr[{}]".format(addr_idx) - self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) + addr_name = "addr0[{}]".format(addr_idx) + self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name) def route_write_driver(self): @@ -600,26 +750,25 @@ class bank(design.design): for i in range(self.word_size): data_name = "data[{}]".format(i) - din_name = "din[{}]".format(i) - self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) + din_name = "din0[{}]".format(i) + self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name) def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - - # we don't care about bends after connecting to the input pin, so let the path code decide. + for i in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() - driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(i)).rc() + driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(i)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc() + driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(i)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(i)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) @@ -639,7 +788,7 @@ class bank(design.design): decode_names = ["Zb", "Z"] # The Address LSB - self.copy_layout_pin(self.col_decoder_inst, "A", "addr[0]") + self.copy_layout_pin(self.col_decoder_inst[0], "A", "addr0[0]") elif self.col_addr_size > 1: decode_names = [] @@ -648,22 +797,22 @@ class bank(design.design): for i in range(self.col_addr_size): decoder_name = "in[{}]".format(i) - addr_name = "addr[{}]".format(i) - self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) + addr_name = "addr0[{}]".format(i) + self.copy_layout_pin(self.col_decoder_inst[0], decoder_name, addr_name) # This will do a quick "river route" on two layers. # When above the top select line it will offset "inward" again to prevent conflicts. # This could be done on a single layer, but we follow preferred direction rules for later routing. - top_y_offset = self.col_mux_array_inst.get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy() + top_y_offset = self.col_mux_array_inst[0].get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy() for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): mux_name = "sel[{}]".format(i) - mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc() + mux_addr_pos = self.col_mux_array_inst[0].get_pin(mux_name).lc() - decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center() + decode_out_pos = self.col_decoder_inst[0].get_pin(decode_name).center() # To get to the edge of the decoder and one track out - delta_offset = self.col_decoder_inst.rx() - decode_out_pos.x + self.m2_pitch + delta_offset = self.col_decoder_inst[0].rx() - decode_out_pos.x + self.m2_pitch if decode_out_pos.y > top_y_offset: mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y) else: @@ -714,7 +863,7 @@ class bank(design.design): for i in range(self.word_size): data_name = "dec_out[{}]".format(i) pin_name = "in[{}]".format(i) - data_pin = self.wordline_driver_inst.get_pin(pin_name) + data_pin = self.wordline_driver_inst[0].get_pin(pin_name) self.add_label(text=data_name, layer="metal1", offset=data_pin.center()) @@ -728,9 +877,9 @@ class bank(design.design): # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 connection = [] - connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc())) - connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc())) - connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) + connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst[0].get_pin("en").lc())) + connection.append((self.prefix+"w_en0", self.write_driver_array_inst[0].get_pin("en").lc())) + connection.append((self.prefix+"s_en0", self.sense_amp_array_inst[0].get_pin("en").lc())) for (control_signal, pin_pos) in connection: control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) @@ -741,7 +890,7 @@ class bank(design.design): # clk to wordline_driver control_signal = self.prefix+"clk_buf" - pin_pos = self.wordline_driver_inst.get_pin("en").uc() + pin_pos = self.wordline_driver_inst[0].get_pin("en").uc() mid_pos = pin_pos + vector(0,self.m1_pitch) control_x_offset = self.bus_xoffset[control_signal].x control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index f89f16be..511e2e7c 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -21,7 +21,7 @@ class bank_select(design.design): # Number of control lines in the bus self.num_control_lines = 4 # The order of the control signals on the control bus: - self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en"] + self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] # These will be outputs of the gaters if this is multibank self.control_signals = ["gated_"+str for str in self.input_control_signals] diff --git a/compiler/modules/bitcell.py b/compiler/modules/bitcell.py index 1a8e853e..38fd8823 100644 --- a/compiler/modules/bitcell.py +++ b/compiler/modules/bitcell.py @@ -44,56 +44,54 @@ class bitcell(design.design): "vdd", "gnd"] return bitcell_pins - - def list_row_pins(self): - """ Creates a list of all row pins (except for gnd and vdd) """ + def list_all_wl_names(self): + """ Creates a list of all wordline pin names """ row_pins = ["wl"] return row_pins - def list_read_row_pins(self): - """ Creates a list of row pins associated with read ports """ + def list_read_wl_names(self): + """ Creates a list of wordline pin names associated with read ports """ row_pins = ["wl"] return row_pins - def list_write_row_pins(self): - """ Creates a list of row pins associated with write ports """ + def list_write_wl_names(self): + """ Creates a list of wordline pin names associated with write ports """ row_pins = ["wl"] return row_pins - - def list_all_column_pins(self): - """ Creates a list of all column pins (except for gnd and vdd) """ + def list_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ column_pins = ["bl", "br"] return column_pins - def list_column_pins(self): - """ Creates a list of all column pins (except for gnd and vdd) """ + def list_all_bl_names(self): + """ Creates a list of all bl pins names """ column_pins = ["bl"] return column_pins - def list_column_bar_pins(self): - """ Creates a list of all column pins (except for gnd and vdd) """ + def list_all_br_names(self): + """ Creates a list of all br pins names """ column_pins = ["br"] return column_pins - def list_read_column_pins(self): - """ Creates a list of column pins associated with read ports """ + def list_read_bl_names(self): + """ Creates a list of bl pin names associated with read ports """ column_pins = ["bl"] return column_pins - def list_read_bar_column_pins(self): - """ Creates a list of column pins associated with read_bar ports """ + def list_read_br_names(self): + """ Creates a list of br pin names associated with read ports """ column_pins = ["br"] return column_pins - def list_write_column_pins(self): - """ Creates a list of column pins associated with write ports """ + def list_write_bl_names(self): + """ Creates a list of bl pin names associated with write ports """ column_pins = ["bl"] return column_pins - def list_write_bar_column_pins(self): - """ Creates a list of column pins asscociated with write_bar ports""" + def list_write_br_names(self): + """ Creates a list of br pin names asscociated with write ports""" column_pins = ["br"] return column_pins diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 8804a955..4d3e0b20 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -31,19 +31,18 @@ class bitcell_array(design.design): self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width self.width = self.column_size*self.cell.width + self.m1_width - self.add_pins() + self.create_netlist() + self.create_layout() - self.add_layout_pins() # We don't offset this because we need to align # the replica bitcell in the control logic #self.offset_all_coordinates() - self.DRC_LVS() def add_pins(self): - row_list = self.cell.list_row_pins() - column_list = self.cell.list_all_column_pins() + row_list = self.cell.list_all_wl_names() + column_list = self.cell.list_all_bitline_names() for col in range(self.column_size): for cell_column in column_list: self.add_pin(cell_column+"[{0}]".format(col)) @@ -55,7 +54,6 @@ class bitcell_array(design.design): def create_layout(self): xoffset = 0.0 - self.cell_inst = {} for col in range(self.column_size): yoffset = 0.0 for row in range(self.row_size): @@ -68,21 +66,34 @@ class bitcell_array(design.design): tempy = yoffset dir_key = "" - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.cell, - offset=[xoffset, tempy], - mirror=dir_key) - self.connect_inst(self.cell.list_bitcell_pins(col, row)) - + self.place_inst(name=name, + offset=[xoffset, tempy], + mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width + self.add_layout_pins() + self.DRC_LVS() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_pins() + + self.cell_inst = {} + for col in range(self.column_size): + for row in range(self.row_size): + name = "bit_r{0}_c{1}".format(row, col) + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.cell) + self.connect_inst(self.cell.list_bitcell_pins(col, row)) + + def add_layout_pins(self): """ Add the layout pins """ - row_list = self.cell.list_row_pins() - column_list = self.cell.list_all_column_pins() + row_list = self.cell.list_all_wl_names() + column_list = self.cell.list_all_bitline_names() offset = vector(0.0, 0.0) for col in range(self.column_size): diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 5e744bfa..26e5e717 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -100,7 +100,7 @@ class control_logic(design.design): # 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", "w_en", "clk_buf_bar", "clk_buf"] + self.output_list = ["s_en0", "w_en0", "clk_buf_bar", "clk_buf"] self.supply_list = ["vdd", "gnd"] @@ -231,7 +231,7 @@ class control_logic(design.design): mod=self.inv8, offset=self.s_en_offset, mirror=mirror) - self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) + self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"]) self.row_end_inst.append(self.s_en_inst) @@ -313,7 +313,7 @@ class control_logic(design.design): mod=self.inv8, offset=w_en_offset, mirror=mirror) - self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"]) + self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"]) x_off += self.inv8.width self.row_end_inst.append(self.w_en_inst) @@ -406,7 +406,7 @@ class control_logic(design.design): 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, "Z", "w_en") + self.connect_output(self.w_en_inst, "Z", "w_en0") def route_sen(self): rbl_out_pos = self.rbl_inst.get_pin("out").bc() @@ -417,7 +417,7 @@ class control_logic(design.design): self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()]) - self.connect_output(self.s_en_inst, "Z", "s_en") + self.connect_output(self.s_en_inst, "Z", "s_en0") def route_clk(self): """ Route the clk and clk_buf_bar signal internally """ diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index ef59f8a6..fd21aa98 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -29,11 +29,15 @@ class dff_array(design.design): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height + self.create_netlist() self.create_layout() - def create_layout(self): + def create_netlist(self): self.add_pins() self.create_dff_array() + + def create_layout(self): + self.place_dff_array() self.add_layout_pins() self.DRC_LVS() @@ -53,22 +57,28 @@ class dff_array(design.design): for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) - if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) - mirror = "R0" - else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) - mirror = "MX" self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff, - offset=base, - mirror=mirror) + mod=self.dff) self.connect_inst([self.get_din_name(row,col), self.get_dout_name(row,col), "clk", "vdd", "gnd"]) + def place_dff_array(self): + for row in range(self.rows): + for col in range(self.columns): + name = "Xdff_r{0}_c{1}".format(row,col) + if (row % 2 == 0): + base = vector(col*self.dff.width,row*self.dff.height) + mirror = "R0" + else: + base = vector(col*self.dff.width,(row+1)*self.dff.height) + mirror = "MX" + self.place_inst(name=name, + offset=base, + mirror=mirror) + def get_din_name(self, row, col): if self.columns == 1: din_name = "din[{0}]".format(row) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 43885af0..f6b72ae8 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -27,11 +27,15 @@ class dff_buf_array(design.design): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height + self.create_netlist() self.create_layout() - def create_layout(self): + def create_netlist(self): self.add_pins() self.create_dff_array() + + def create_layout(self): + self.place_dff_array() self.add_layout_pins() self.DRC_LVS() @@ -52,16 +56,8 @@ class dff_buf_array(design.design): for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) - if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) - mirror = "R0" - else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) - mirror = "MX" self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff, - offset=base, - mirror=mirror) + mod=self.dff) self.connect_inst([self.get_din_name(row,col), self.get_dout_name(row,col), self.get_dout_bar_name(row,col), @@ -69,6 +65,20 @@ class dff_buf_array(design.design): "vdd", "gnd"]) + def place_dff_array(self): + for row in range(self.rows): + for col in range(self.columns): + name = "Xdff_r{0}_c{1}".format(row,col) + if (row % 2 == 0): + base = vector(col*self.dff.width,row*self.dff.height) + mirror = "R0" + else: + base = vector(col*self.dff.width,(row+1)*self.dff.height) + mirror = "MX" + self.place_inst(name=name, + offset=base, + mirror=mirror) + def get_din_name(self, row, col): if self.columns == 1: din_name = "din[{0}]".format(row) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index fce07a41..b74af940 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -26,6 +26,9 @@ class hierarchical_decoder(design.design): self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell_height = self.mod_bitcell.height + self.NAND_FORMAT = "DEC_NAND[{0}]" + self.INV_FORMAT = "DEC_INV_[{0}]" + self.pre2x4_inst = [] self.pre3x8_inst = [] @@ -33,22 +36,27 @@ class hierarchical_decoder(design.design): self.num_inputs = int(math.log(self.rows, 2)) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) + self.create_netlist() self.create_layout() - self.offset_all_coordinates() - - self.DRC_LVS() - def create_layout(self): + def create_netlist(self): self.add_modules() - self.setup_layout_constants() + self.setup_netlist_constants() self.add_pins() self.create_pre_decoder() self.create_row_decoder() - self.create_input_rail() - self.create_predecode_rail() - self.route_vdd_gnd() + def create_layout(self): + self.setup_layout_constants() + self.place_pre_decoder() + self.place_row_decoder() + self.route_input_rails() + self.route_predecode_rails() + self.route_vdd_gnd() + self.offset_all_coordinates() + self.DRC_LVS() + def add_modules(self): self.inv = pinv() self.add_mod(self.inv) @@ -89,7 +97,7 @@ class hierarchical_decoder(design.design): else: debug.error("Invalid number of inputs for hierarchical decoder",-1) - def setup_layout_constants(self): + def setup_netlist_constants(self): self.predec_groups = [] # This array is a 2D array. # Distributing vertical rails to different groups. One group belongs to one pre-decoder. @@ -112,92 +120,9 @@ class hierarchical_decoder(design.design): index = index + 1 self.predec_groups.append(lines) - self.calculate_dimensions() - def create_input_rail(self): - """ Create input rails for the predecoders """ - # inputs should be as high as the decoders - input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height - - # Find the left-most predecoder - min_x = 0 - if self.no_of_pre2x4 > 0: - min_x = min(min_x, -self.pre2_4.width) - if self.no_of_pre3x8 > 0: - min_x = min(min_x, -self.pre3_8.width) - input_offset=vector(min_x - self.input_routing_width,0) - - input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)] - self.input_rails = self.create_vertical_pin_bus(layer="metal2", - pitch=self.m2_pitch, - offset=input_offset, - names=input_bus_names, - length=input_height) - - self.connect_input_to_predecodes() - - - def connect_input_to_predecodes(self): - """ Connect the vertical input rail to the predecoders """ - for pre_num in range(self.no_of_pre2x4): - for i in range(2): - index = pre_num * 2 + i - - input_pos = self.input_rails["addr[{}]".format(index)] - - in_name = "in[{}]".format(i) - decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) - - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) - input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) - - self.connect_input_rail(decoder_offset, input_offset) - - - for pre_num in range(self.no_of_pre3x8): - for i in range(3): - index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - - input_pos = self.input_rails["addr[{}]".format(index)] - - in_name = "in[{}]".format(i) - decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) - - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) - input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) - - self.connect_input_rail(decoder_offset, input_offset) - - - def connect_input_rail(self, input_offset, output_offset): - """ Connect a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ - - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=input_offset, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=output_offset, - rotate=90) - self.add_path(("metal3"), [input_offset, output_offset]) - - - def add_pins(self): - """ Add the module pins """ - - for i in range(self.num_inputs): - self.add_pin("addr[{0}]".format(i)) - - for j in range(self.rows): - self.add_pin("decode[{0}]".format(j)) - self.add_pin("vdd") - self.add_pin("gnd") - - def calculate_dimensions(self): - """ Calculate the overal dimensions of the hierarchical decoder """ + def setup_layout_constants(self): + """ Calculate the overall dimensions of the hierarchical decoder """ # If we have 4 or fewer rows, the predecoder is the decoder itself if self.num_inputs>=4: @@ -227,24 +152,105 @@ class hierarchical_decoder(design.design): self.height = self.row_decoder_height self.width = self.input_routing_width + self.predecoder_width \ + self.internal_routing_width + nand_width + self.inv.width + + def route_input_rails(self): + """ Create input rails for the predecoders """ + # inputs should be as high as the decoders + input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height + + # Find the left-most predecoder + min_x = 0 + if self.no_of_pre2x4 > 0: + min_x = min(min_x, -self.pre2_4.width) + if self.no_of_pre3x8 > 0: + min_x = min(min_x, -self.pre3_8.width) + input_offset=vector(min_x - self.input_routing_width,0) + + input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)] + self.input_rails = self.create_vertical_pin_bus(layer="metal2", + pitch=self.m2_pitch, + offset=input_offset, + names=input_bus_names, + length=input_height) + + self.route_input_to_predecodes() + + + def route_input_to_predecodes(self): + """ Route the vertical input rail to the predecoders """ + for pre_num in range(self.no_of_pre2x4): + for i in range(2): + index = pre_num * 2 + i + + input_pos = self.input_rails["addr[{}]".format(index)] + + in_name = "in[{}]".format(i) + decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) + + # To prevent conflicts, we will offset each input connect so + # that it aligns with the vdd/gnd rails + decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) + input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) + + self.route_input_rail(decoder_offset, input_offset) + + + for pre_num in range(self.no_of_pre3x8): + for i in range(3): + index = pre_num * 3 + i + self.no_of_pre2x4 * 2 + + input_pos = self.input_rails["addr[{}]".format(index)] + + in_name = "in[{}]".format(i) + decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) + + # To prevent conflicts, we will offset each input connect so + # that it aligns with the vdd/gnd rails + decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) + input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) + + self.route_input_rail(decoder_offset, input_offset) + + + def route_input_rail(self, input_offset, output_offset): + """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ + + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=input_offset, + rotate=90) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=output_offset, + rotate=90) + self.add_path(("metal3"), [input_offset, output_offset]) + + + def add_pins(self): + """ Add the module pins """ + + for i in range(self.num_inputs): + self.add_pin("addr[{0}]".format(i)) + + for j in range(self.rows): + self.add_pin("decode[{0}]".format(j)) + self.add_pin("vdd") + self.add_pin("gnd") + def create_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ for i in range(self.no_of_pre2x4): - self.add_pre2x4(i) + self.create_pre2x4(i) for i in range(self.no_of_pre3x8): - self.add_pre3x8(i) + self.create_pre3x8(i) - def add_pre2x4(self,num): + def create_pre2x4(self,num): """ Add a 2x4 predecoder to the left of the origin """ if (self.num_inputs == 2): - base = vector(-self.pre2_4.width,0) index_off1 = index_off2 = 0 else: - base= vector(-self.pre2_4.width, num * self.pre2_4.height) index_off1 = num * 2 index_off2 = num * 4 @@ -256,20 +262,12 @@ class hierarchical_decoder(design.design): pins.extend(["vdd", "gnd"]) self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num), - mod=self.pre2_4, - offset=base)) + mod=self.pre2_4)) self.connect_inst(pins) - def add_pre3x8(self,num): + def create_pre3x8(self,num): """ Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """ - if (self.num_inputs == 3): - offset = vector(-self.pre_3_8.width,0) - mirror ="R0" - else: - height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height - offset = vector(-self.pre3_8.width, height) - # If we had 2x4 predecodes, those are used as the lower # decode output bits in_index_offset = num * 3 + self.no_of_pre2x4 * 2 @@ -283,79 +281,112 @@ class hierarchical_decoder(design.design): pins.extend(["vdd", "gnd"]) self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num), - mod=self.pre3_8, - offset=offset)) + mod=self.pre3_8)) self.connect_inst(pins) + def place_pre_decoder(self): + """ Creates pre-decoder and places labels input address [A] """ + + for i in range(self.no_of_pre2x4): + self.place_pre2x4(i) + + for i in range(self.no_of_pre3x8): + self.place_pre3x8(i) + + def place_pre2x4(self,num): + """ Place 2x4 predecoder to the left of the origin """ + + if (self.num_inputs == 2): + base = vector(-self.pre2_4.width,0) + else: + base= vector(-self.pre2_4.width, num * self.pre2_4.height) + + self.place_inst(name="pre[{0}]".format(num), + offset=base) + + + def place_pre3x8(self,num): + """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ + if (self.num_inputs == 3): + offset = vector(-self.pre_3_8.width,0) + mirror ="R0" + else: + height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height + offset = vector(-self.pre3_8.width, height) + + self.place_inst(name="pre3x8[{0}]".format(num), + offset=offset) def create_row_decoder(self): """ Create the row-decoder by placing NAND2/NAND3 and Inverters and add the primary decoder output pins. """ if (self.num_inputs >= 4): - self.add_decoder_nand_array() - self.add_decoder_inv_array() - self.route_decoder() + self.create_decoder_nand_array() + self.create_decoder_inv_array() - def add_decoder_nand_array(self): + def create_decoder_nand_array(self): """ Add a column of NAND gates for final decode """ + + self.nand_inst = [] # Row Decoder NAND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): - self.add_nand_array(nand_mod=self.nand2) - # FIXME: Can we convert this to the connect_inst with checks? for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): + row = len(self.predec_groups[1])*i + j + name = self.NAND_FORMAT.format(row) + self.nand_inst.append(self.add_inst(name=name, + mod=self.nand2)) pins =["out[{0}]".format(i), "out[{0}]".format(j + len(self.predec_groups[0])), - "Z[{0}]".format(len(self.predec_groups[1])*i + j), + "Z[{0}]".format(row), "vdd", "gnd"] - self.connect_inst(args=pins, check=False) + self.connect_inst(pins) + # Row Decoder NAND GATE array for address inputs >5. elif (self.num_inputs > 5): - self.add_nand_array(nand_mod=self.nand3, - correct=drc["minwidth_metal1"]) - # This will not check that the inst connections match. for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): for k in range(len(self.predec_groups[2])): - Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ - + len(self.predec_groups[2])*j + k + row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ + + len(self.predec_groups[2])*j + k + + name = self.NAND_FORMAT.format(row) + self.nand_inst.append(self.add_inst(name=name, + mod=self.nand3)) + pins = ["out[{0}]".format(i), "out[{0}]".format(j + len(self.predec_groups[0])), "out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), - "Z[{0}]".format(Z_index), + "Z[{0}]".format(row), "vdd", "gnd"] - self.connect_inst(args=pins, check=False) + self.connect_inst(pins) - def add_nand_array(self, nand_mod, correct=0): - """ Add a column of NAND gates for the decoder above the predecoders.""" + + def create_decoder_inv_array(self): + """ + Add a column of INV gates for the decoder. + """ - self.nand_inst = [] + self.inv_inst = [] for row in range(self.rows): - name = "DEC_NAND[{0}]".format(row) - if ((row % 2) == 0): - y_off = nand_mod.height*row - y_dir = 1 - mirror = "R0" - else: - y_off = nand_mod.height*(row + 1) - y_dir = -1 - mirror = "MX" + name = self.INV_FORMAT.format(row) + self.inv_inst.append(self.add_inst(name=name, + mod=self.inv)) + self.connect_inst(args=["Z[{0}]".format(row), + "decode[{0}]".format(row), + "vdd", "gnd"]) - self.nand_inst.append(self.add_inst(name=name, - mod=nand_mod, - offset=[self.internal_routing_width, y_off], - mirror=mirror)) - - - def add_decoder_inv_array(self): - """Add a column of INV gates for the decoder above the predecoders - and to the right of the NAND decoders.""" + def place_decoder_inv_array(self): + """ + Place the column of INV gates for the decoder above the predecoders + and to the right of the NAND decoders. + """ z_pin = self.inv.get_pin("Z") @@ -364,9 +395,8 @@ class hierarchical_decoder(design.design): else: x_off = self.internal_routing_width + self.nand3.width - self.inv_inst = [] for row in range(self.rows): - name = "DEC_INV_[{0}]".format(row) + name = self.INV_FORMAT.format(row) if (row % 2 == 0): inv_row_height = self.inv.height * row mirror = "R0" @@ -377,17 +407,52 @@ class hierarchical_decoder(design.design): y_dir = -1 y_off = inv_row_height offset = vector(x_off,y_off) - - self.inv_inst.append(self.add_inst(name=name, - mod=self.inv, - offset=offset, - mirror=mirror)) + self.place_inst(name=name, + offset=offset, + mirror=mirror) - # This will not check that the inst connections match. - self.connect_inst(args=["Z[{0}]".format(row), - "decode[{0}]".format(row), - "vdd", "gnd"], - check=False) + def place_row_decoder(self): + """ + Place the row-decoder by placing NAND2/NAND3 and Inverters + and add the primary decoder output pins. + """ + if (self.num_inputs >= 4): + self.place_decoder_nand_array() + self.place_decoder_inv_array() + self.route_decoder() + + + def place_decoder_nand_array(self): + """ Add a column of NAND gates for final decode """ + + # Row Decoder NAND GATE array for address inputs <5. + if (self.num_inputs == 4 or self.num_inputs == 5): + self.place_nand_array(nand_mod=self.nand2) + + # Row Decoder NAND GATE array for address inputs >5. + # FIXME: why this correct offset?) + elif (self.num_inputs > 5): + self.place_nand_array(nand_mod=self.nand3) + + def place_nand_array(self, nand_mod): + """ Add a column of NAND gates for the decoder above the predecoders.""" + + for row in range(self.rows): + name = self.NAND_FORMAT.format(row) + if ((row % 2) == 0): + y_off = nand_mod.height*row + y_dir = 1 + mirror = "R0" + else: + y_off = nand_mod.height*(row + 1) + y_dir = -1 + mirror = "MX" + + self.place_inst(name=name, + offset=[self.internal_routing_width, y_off], + mirror=mirror) + + def route_decoder(self): @@ -412,7 +477,7 @@ class hierarchical_decoder(design.design): - def create_predecode_rail(self): + def route_predecode_rails(self): """ Creates vertical metal 2 rails to connect predecoder and decoder stages.""" # This is not needed for inputs <4 since they have no pre/decode stages. @@ -426,10 +491,10 @@ class hierarchical_decoder(design.design): length=self.height) - self.connect_rails_to_predecodes() - self.connect_rails_to_decoder() + self.route_rails_to_predecodes() + self.route_rails_to_decoder() - def connect_rails_to_predecodes(self): + def route_rails_to_predecodes(self): """ Iterates through all of the predecodes and connects to the rails including the offsets """ # FIXME: convert to connect_bus @@ -438,7 +503,7 @@ class hierarchical_decoder(design.design): predecode_name = "predecode[{}]".format(pre_num * 4 + i) out_name = "out[{}]".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - self.connect_predecode_rail_m3(predecode_name, pin) + self.route_predecode_rail_m3(predecode_name, pin) # FIXME: convert to connect_bus @@ -447,11 +512,11 @@ class hierarchical_decoder(design.design): predecode_name = "predecode[{}]".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) out_name = "out[{}]".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) - self.connect_predecode_rail_m3(predecode_name, pin) + self.route_predecode_rail_m3(predecode_name, pin) - def connect_rails_to_decoder(self): + def route_rails_to_decoder(self): """ Use the self.predec_groups to determine the connections to the decoder NAND gates. Inputs of NAND2/NAND3 gates come from different groups. For example for these groups [ [0,1,2,3] ,[4,5,6,7], @@ -465,9 +530,9 @@ class hierarchical_decoder(design.design): for index_B in self.predec_groups[1]: # FIXME: convert to connect_bus? predecode_name = "predecode[{}]".format(index_A) - self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) + self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) predecode_name = "predecode[{}]".format(index_B) - self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) + self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) row_index = row_index + 1 elif (self.num_inputs > 5): @@ -476,11 +541,11 @@ class hierarchical_decoder(design.design): for index_C in self.predec_groups[2]: # FIXME: convert to connect_bus? predecode_name = "predecode[{}]".format(index_A) - self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) + self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) predecode_name = "predecode[{}]".format(index_B) - self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) + self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) predecode_name = "predecode[{}]".format(index_C) - self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) + self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) row_index = row_index + 1 def route_vdd_gnd(self): @@ -516,7 +581,7 @@ class hierarchical_decoder(design.design): self.copy_layout_pin(pre, "gnd") - def connect_predecode_rail(self, rail_name, pin): + def route_predecode_rail(self, rail_name, pin): """ Connect the routing rail to the given metal1 pin """ rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) self.add_path("metal1", [rail_pos, pin.lc()]) @@ -525,7 +590,7 @@ class hierarchical_decoder(design.design): rotate=90) - def connect_predecode_rail_m3(self, rail_name, pin): + def route_predecode_rail_m3(self, rail_name, pin): """ Connect the routing rail to the given metal1 pin """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 74ad15ea..6522e233 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -37,17 +37,17 @@ class hierarchical_predecode(design.design): self.inv = pinv() self.add_mod(self.inv) - self.create_nand(self.number_of_inputs) + self.add_nand(self.number_of_inputs) self.add_mod(self.nand) - def create_nand(self,inputs): + def add_nand(self,inputs): """ Create the NAND for the predecode input stage """ if inputs==2: self.nand = pnand2() elif inputs==3: self.nand = pnand3() else: - debug.error("Invalid number of predecode inputs.",-1) + debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) def setup_constraints(self): @@ -65,7 +65,7 @@ class hierarchical_predecode(design.design): # Height width are computed self.width = self.x_off_inv_2 + self.inv.width - def create_rails(self): + def route_rails(self): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)] offset = vector(0.5*self.m2_width,2*self.m1_width) @@ -86,10 +86,21 @@ class hierarchical_predecode(design.design): length=self.height - 2*self.m1_width) - def add_input_inverters(self): + def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ self.in_inst = [] + for inv_num in range(self.number_of_inputs): + name = "Xpre_inv[{0}]".format(inv_num) + self.in_inst.append(self.add_inst(name=name, + mod=self.inv)) + self.connect_inst(["in[{0}]".format(inv_num), + "inbar[{0}]".format(inv_num), + "vdd", "gnd"]) + + def place_input_inverters(self): + """ Place the input inverters to invert input signals for the decode stage. """ + for inv_num in range(self.number_of_inputs): name = "Xpre_inv[{0}]".format(inv_num) if (inv_num % 2 == 0): @@ -99,18 +110,26 @@ class hierarchical_predecode(design.design): y_off = (inv_num + 1) * (self.inv.height) mirror="MX" offset = vector(self.x_off_inv_1, y_off) - self.in_inst.append(self.add_inst(name=name, - mod=self.inv, - offset=offset, - mirror=mirror)) - self.connect_inst(["in[{0}]".format(inv_num), - "inbar[{0}]".format(inv_num), - "vdd", "gnd"]) + self.place_inst(name=name, + offset=offset, + mirror=mirror) - def add_output_inverters(self): + def create_output_inverters(self): """ Create inverters for the inverted output decode signals. """ self.inv_inst = [] + for inv_num in range(self.number_of_outputs): + name = "Xpre_nand_inv[{}]".format(inv_num) + self.inv_inst.append(self.add_inst(name=name, + mod=self.inv)) + self.connect_inst(["Z[{}]".format(inv_num), + "out[{}]".format(inv_num), + "vdd", "gnd"]) + + + def place_output_inverters(self): + """ Place inverters for the inverted output decode signals. """ + for inv_num in range(self.number_of_outputs): name = "Xpre_nand_inv[{}]".format(inv_num) if (inv_num % 2 == 0): @@ -120,19 +139,23 @@ class hierarchical_predecode(design.design): y_off =(inv_num + 1)*self.inv.height mirror = "MX" offset = vector(self.x_off_inv_2, y_off) - self.inv_inst.append(self.add_inst(name=name, - mod=self.inv, - offset=offset, - mirror=mirror)) - self.connect_inst(["Z[{}]".format(inv_num), - "out[{}]".format(inv_num), - "vdd", "gnd"]) - - + self.place_inst(name=name, + offset=offset, + mirror=mirror) - def add_nand(self,connections): + def create_nand_array(self,connections): """ Create the NAND stage for the decodes """ self.nand_inst = [] + for nand_input in range(self.number_of_outputs): + inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) + name = "Xpre{0}_nand[{1}]".format(inout,nand_input) + self.nand_inst.append(self.add_inst(name=name, + mod=self.nand)) + self.connect_inst(connections[nand_input]) + + + def place_nand_array(self): + """ Place the NAND stage for the decodes """ for nand_input in range(self.number_of_outputs): inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) name = "Xpre{0}_nand[{1}]".format(inout,nand_input) @@ -143,11 +166,9 @@ class hierarchical_predecode(design.design): y_off = (nand_input + 1) * self.inv.height mirror = "MX" offset = vector(self.x_off_nand, y_off) - self.nand_inst.append(self.add_inst(name=name, - mod=self.nand, - offset=offset, - mirror=mirror)) - self.connect_inst(connections[nand_input]) + self.place_inst(name=name, + offset=offset, + mirror=mirror) def route(self): diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 5dbacf0e..ce994a82 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -14,8 +14,17 @@ class hierarchical_predecode2x4(hierarchical_predecode): self.add_pins() self.create_modules() self.setup_constraints() + self.create_netlist() self.create_layout() - self.DRC_LVS() + + def create_netlist(self): + self.create_input_inverters() + self.create_output_inverters() + connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], + ["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"], + ["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"], + ["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]] + self.create_nand_array(connections) def create_layout(self): """ The general organization is from left to right: @@ -24,15 +33,12 @@ class hierarchical_predecode2x4(hierarchical_predecode): 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs 4) a set of NAND gates for inversion """ - self.create_rails() - self.add_input_inverters() - self.add_output_inverters() - connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], - ["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"], - ["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"], - ["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]] - self.add_nand(connections) + self.route_rails() + self.place_input_inverters() + self.place_output_inverters() + self.place_nand_array() self.route() + self.DRC_LVS() def get_nand_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B pins """ diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index ded0c612..78462077 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -14,19 +14,12 @@ class hierarchical_predecode3x8(hierarchical_predecode): self.add_pins() self.create_modules() self.setup_constraints() + self.create_netlist() self.create_layout() - self.DRC_LVS() - def create_layout(self): - """ The general organization is from left to right: - 1) a set of M2 rails for input signals - 2) a set of inverters to invert input signals - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of NAND gates for inversion - """ - self.create_rails() - self.add_input_inverters() - self.add_output_inverters() + def create_netlist(self): + self.create_input_inverters() + self.create_output_inverters() connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"], ["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"], ["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"], @@ -35,9 +28,23 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"], ["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"], ["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]] - self.add_nand(connections) - self.route() + self.create_nand_array(connections) + def create_layout(self): + """ + The general organization is from left to right: + 1) a set of M2 rails for input signals + 2) a set of inverters to invert input signals + 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs + 4) a set of NAND gates for inversion + """ + self.route_rails() + self.place_input_inverters() + self.place_output_inverters() + self.place_nand_array() + self.route() + self.DRC_LVS() + def get_nand_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B,C pins """ combination = [["Abar[0]", "Abar[1]", "Abar[2]"], diff --git a/compiler/modules/ms_flop_array.py b/compiler/modules/ms_flop_array.py index 72174952..653c0c38 100644 --- a/compiler/modules/ms_flop_array.py +++ b/compiler/modules/ms_flop_array.py @@ -30,14 +30,18 @@ class ms_flop_array(design.design): self.height = self.ms.height self.words_per_row = int(self.columns / self.word_size) + self.create_netlist() self.create_layout() - def create_layout(self): + def create_netlist(self): self.add_pins() self.create_ms_flop_array() + + def create_layout(self): + self.place_ms_flop_array() self.add_layout_pins() self.DRC_LVS() - + def add_pins(self): for i in range(self.word_size): self.add_pin("din[{0}]".format(i)) @@ -52,25 +56,28 @@ class ms_flop_array(design.design): self.ms_inst={} for i in range(0,self.columns,self.words_per_row): name = "Xdff{0}".format(i) - if (i % 2 == 0 or self.words_per_row>1): - base = vector(i*self.ms.width,0) - mirror = "R0" - else: - base = vector((i+1)*self.ms.width,0) - mirror = "MY" - index = int(i/self.words_per_row) - self.ms_inst[index]=self.add_inst(name=name, - mod=self.ms, - offset=base, - mirror=mirror) + mod=self.ms) self.connect_inst(["din[{0}]".format(index), "dout[{0}]".format(index), "dout_bar[{0}]".format(index), "clk", "vdd", "gnd"]) + def place_ms_flop_array(self): + for i in range(0,self.columns,self.words_per_row): + name = "Xdff{0}".format(i) + if (i % 2 == 0 or self.words_per_row>1): + base = vector(i*self.ms.width,0) + mirror = "R0" + else: + base = vector((i+1)*self.ms.width,0) + mirror = "MY" + self.place_inst(name=name, + offset=base, + mirror=mirror) + def add_layout_pins(self): for i in range(self.word_size): diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index cc8aa40a..eed25978 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -11,21 +11,24 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, columns, size=1): - design.design.__init__(self, "precharge_array") + unique_id = 1 + + def __init__(self, columns, size=1, bitcell_bl="bl", bitcell_br="br"): + name = "precharge_array_{}".format(precharge_array.unique_id) + precharge_array.unique_id += 1 + design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.columns = columns - self.pc_cell = precharge(name="precharge", size=size) + self.pc_cell = precharge(name="precharge", size=size, bitcell_bl=bitcell_bl, bitcell_br=bitcell_br) self.add_mod(self.pc_cell) self.width = self.columns * self.pc_cell.width self.height = self.pc_cell.height - self.add_pins() + self.create_netlist() self.create_layout() - self.DRC_LVS() def add_pins(self): """Adds pins for spice file""" @@ -35,9 +38,14 @@ class precharge_array(design.design): self.add_pin("en") self.add_pin("vdd") + def create_netlist(self): + self.add_pins() + self.create_insts() + def create_layout(self): - self.add_insts() + self.place_insts() self.add_layout_pins() + self.DRC_LVS() def add_layout_pins(self): @@ -51,20 +59,8 @@ class precharge_array(design.design): for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") - - - def add_insts(self): - """Creates a precharge array by horizontally tiling the precharge cell""" - self.local_insts = [] - for i in range(self.columns): - name = "pre_column_{0}".format(i) - offset = vector(self.pc_cell.width * i, 0) - inst = self.add_inst(name=name, - mod=self.pc_cell, - offset=offset) - self.local_insts.append(inst) - - self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"]) + for i in range(len(self.local_insts)): + inst = self.local_insts[i] bl_pin = inst.get_pin("bl") self.add_layout_pin(text="bl[{0}]".format(i), layer="metal2", @@ -77,4 +73,25 @@ class precharge_array(design.design): offset=br_pin.ll(), width=drc["minwidth_metal2"], height=bl_pin.height()) + + def create_insts(self): + """Creates a precharge array by horizontally tiling the precharge cell""" + self.local_insts = [] + for i in range(self.columns): + name = "pre_column_{0}".format(i) + offset = vector(self.pc_cell.width * i, 0) + inst = self.add_inst(name=name, + mod=self.pc_cell, + offset=offset) + self.local_insts.append(inst) + self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"]) + + + def place_insts(self): + """ Places precharge array by horizontally tiling the precharge cell""" + for i in range(self.columns): + name = "pre_column_{0}".format(i) + offset = vector(self.pc_cell.width * i, 0) + inst = self.place_inst(name=name, + offset=offset) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 5d16b323..5a513583 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -27,7 +27,7 @@ class sense_amp_array(design.design): self.height = self.amp.height self.width = self.amp.width * self.word_size * self.words_per_row - self.add_pins() + self.create_netlist() self.create_layout() self.DRC_LVS() @@ -42,36 +42,42 @@ class sense_amp_array(design.design): self.add_pin("vdd") self.add_pin("gnd") + def create_netlist(self): + self.add_pins() + self.create_sense_amp_array() + def create_layout(self): - - self.add_sense_amp() - self.connect_rails() + self.place_sense_amp_array() + self.add_layout_pins() + self.route_rails() - def add_sense_amp(self): - - bl_pin = self.amp.get_pin("bl") - br_pin = self.amp.get_pin("br") - dout_pin = self.amp.get_pin("dout") - - amp_spacing = self.amp.width * self.words_per_row + def create_sense_amp_array(self): + self.local_insts = [] for i in range(0,self.word_size): name = "sa_d{0}".format(i) - amp_position = vector(amp_spacing * i, 0) - - bl_offset = amp_position + bl_pin.ll().scale(1,0) - br_offset = amp_position + br_pin.ll().scale(1,0) - dout_offset = amp_position + dout_pin.ll() - - inst = self.add_inst(name=name, - mod=self.amp, - offset=amp_position) + self.local_insts.append(self.add_inst(name=name, + mod=self.amp)) self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "data[{0}]".format(i), "en", "vdd", "gnd"]) + def place_sense_amp_array(self): + + amp_spacing = self.amp.width * self.words_per_row + for i in range(0,self.word_size): + + name = "sa_d{0}".format(i) + amp_position = vector(amp_spacing * i, 0) + self.place_inst(name=name, + offset=amp_position) + + + def add_layout_pins(self): + for i in range(len(self.local_insts)): + inst = self.local_insts[i] gnd_pos = inst.get_pin("gnd").center() self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -79,33 +85,36 @@ class sense_amp_array(design.design): self.add_layout_pin_rect_center(text="gnd", layer="metal3", offset=gnd_pos) - vdd_pos = inst.get_pin("vdd").center() self.add_via_center(layers=("metal2", "via2", "metal3"), offset=vdd_pos) self.add_layout_pin_rect_center(text="vdd", layer="metal3", offset=vdd_pos) + + bl_pin = inst.get_pin("bl") + br_pin = inst.get_pin("br") + dout_pin = inst.get_pin("dout") self.add_layout_pin(text="bl[{0}]".format(i), layer="metal2", - offset=bl_offset, + offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) self.add_layout_pin(text="br[{0}]".format(i), layer="metal2", - offset=br_offset, + offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) self.add_layout_pin(text="data[{0}]".format(i), layer="metal2", - offset=dout_offset, + offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) - - def connect_rails(self): + + def route_rails(self): # add sclk rail across entire array sclk_offset = self.amp.get_pin("en").ll().scale(0,1) self.add_layout_pin(text="en", diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 777958a5..4913bfb7 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -20,9 +20,8 @@ class single_level_column_mux_array(design.design): self.columns = columns self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) - self.add_pins() + self.create_netlist() self.create_layout() - self.DRC_LVS() def add_pins(self): for i in range(self.columns): @@ -35,10 +34,14 @@ class single_level_column_mux_array(design.design): self.add_pin("br_out[{}]".format(i)) self.add_pin("gnd") - def create_layout(self): + def create_netlist(self): + self.add_pins() self.add_modules() - self.setup_layout_constants() self.create_array() + + def create_layout(self): + self.setup_layout_constants() + self.place_array() self.add_routing() # Find the highest shapes to determine height before adding well highest = self.find_highest_coords() @@ -46,6 +49,7 @@ class single_level_column_mux_array(design.design): self.add_layout_pins() self.add_enclosure(self.mux_inst, "pwell") + self.DRC_LVS() def add_modules(self): @@ -65,14 +69,11 @@ class single_level_column_mux_array(design.design): def create_array(self): self.mux_inst = [] - # For every column, add a pass gate for col_num in range(self.columns): name = "XMUX{0}".format(col_num) - x_off = vector(col_num * self.mux.width, self.route_height) self.mux_inst.append(self.add_inst(name=name, - mod=self.mux, - offset=x_off)) + mod=self.mux)) self.connect_inst(["bl[{}]".format(col_num), "br[{}]".format(col_num), @@ -81,6 +82,14 @@ class single_level_column_mux_array(design.design): "sel[{}]".format(col_num % self.words_per_row), "gnd"]) + def place_array(self): + # For every column, add a pass gate + for col_num in range(self.columns): + name = "XMUX{0}".format(col_num) + x_off = vector(col_num * self.mux.width, self.route_height) + self.place_inst(name=name, + offset=x_off) + def add_layout_pins(self): """ Add the pins after we determine the height. """ diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index b18461cc..22004a9f 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -27,14 +27,17 @@ class tri_gate_array(design.design): self.width = (self.columns / self.words_per_row) * self.tri.width self.height = self.tri.height + self.create_netlist() self.create_layout() - self.DRC_LVS() - def create_layout(self): - """generate layout """ + def create_netlist(self): self.add_pins() self.create_array() + + def create_layout(self): + self.place_array() self.add_layout_pins() + self.DRC_LVS() def add_pins(self): """create the name of pins depend on the word size""" @@ -50,15 +53,21 @@ class tri_gate_array(design.design): self.tri_inst = {} for i in range(0,self.columns,self.words_per_row): name = "Xtri_gate{0}".format(i) - base = vector(i*self.tri.width, 0) self.tri_inst[i]=self.add_inst(name=name, - mod=self.tri, - offset=base) + mod=self.tri) index = int(i/self.words_per_row) self.connect_inst(["in[{0}]".format(index), "out[{0}]".format(index), "en", "en_bar", "vdd", "gnd"]) + def place_array(self): + """ Place the tri gate to the array """ + for i in range(0,self.columns,self.words_per_row): + name = "Xtri_gate{0}".format(i) + base = vector(i*self.tri.width, 0) + self.place_inst(name=name, + offset=base) + def add_layout_pins(self): diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 2ddda761..4aa7526b 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -20,8 +20,8 @@ class wordline_driver(design.design): design.design.__init__(self, "wordline_driver") self.rows = rows - self.add_pins() - self.design_layout() + self.create_netlist() + self.create_layout() self.offset_all_coordinates() self.DRC_LVS() @@ -36,14 +36,18 @@ class wordline_driver(design.design): self.add_pin("vdd") self.add_pin("gnd") - def design_layout(self): - self.create_modules() + def create_netlist(self): + self.add_pins() self.add_modules() + self.create_drivers() + + def create_layout(self): + self.place_drivers() self.route_layout() self.route_vdd_gnd() - def create_modules(self): + def add_modules(self): self.inv = pinv() self.add_mod(self.inv) @@ -84,7 +88,37 @@ class wordline_driver(design.design): - def add_modules(self): + def create_drivers(self): + self.inv1_inst = [] + self.nand_inst = [] + self.inv2_inst = [] + for row in range(self.rows): + name_inv1 = "wl_driver_inv_en{}".format(row) + name_nand = "wl_driver_nand{}".format(row) + name_inv2 = "wl_driver_inv{}".format(row) + + # add inv1 based on the info above + self.inv1_inst.append(self.add_inst(name=name_inv1, + mod=self.inv_no_output)) + self.connect_inst(["en", + "en_bar[{0}]".format(row), + "vdd", "gnd"]) + # add nand 2 + self.nand_inst.append(self.add_inst(name=name_nand, + mod=self.nand2)) + self.connect_inst(["en_bar[{0}]".format(row), + "in[{0}]".format(row), + "wl_bar[{0}]".format(row), + "vdd", "gnd"]) + # add inv2 + self.inv2_inst.append(self.add_inst(name=name_inv2, + mod=self.inv)) + self.connect_inst(["wl_bar[{0}]".format(row), + "wl[{0}]".format(row), + "vdd", "gnd"]) + + + def place_drivers(self): inv1_xoffset = 2*self.m1_width + 5*self.m1_space nand2_xoffset = inv1_xoffset + self.inv.width inv2_xoffset = nand2_xoffset + self.nand2.width @@ -93,9 +127,6 @@ class wordline_driver(design.design): self.height = self.inv.height * self.rows - self.inv1_inst = [] - self.nand_inst = [] - self.inv2_inst = [] for row in range(self.rows): name_inv1 = "wl_driver_inv_en{}".format(row) name_nand = "wl_driver_nand{}".format(row) @@ -113,30 +144,17 @@ class wordline_driver(design.design): inv2_offset=[inv2_xoffset, y_offset] # add inv1 based on the info above - self.inv1_inst.append(self.add_inst(name=name_inv1, - mod=self.inv_no_output, - offset=inv1_offset, - mirror=inst_mirror)) - self.connect_inst(["en", - "en_bar[{0}]".format(row), - "vdd", "gnd"]) + self.place_inst(name=name_inv1, + offset=inv1_offset, + mirror=inst_mirror) # add nand 2 - self.nand_inst.append(self.add_inst(name=name_nand, - mod=self.nand2, - offset=nand2_offset, - mirror=inst_mirror)) - self.connect_inst(["en_bar[{0}]".format(row), - "in[{0}]".format(row), - "wl_bar[{0}]".format(row), - "vdd", "gnd"]) + self.place_inst(name=name_nand, + offset=nand2_offset, + mirror=inst_mirror) # add inv2 - self.inv2_inst.append(self.add_inst(name=name_inv2, - mod=self.inv, - offset=inv2_offset, - mirror=inst_mirror)) - self.connect_inst(["wl_bar[{0}]".format(row), - "wl[{0}]".format(row), - "vdd", "gnd"]) + self.place_inst(name=name_inv2, + offset=inv2_offset, + mirror=inst_mirror) def route_layout(self): diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 650361eb..2d5bea87 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -28,9 +28,8 @@ class write_driver_array(design.design): self.width = self.columns * self.driver.width self.height = self.height = self.driver.height - self.add_pins() + self.create_netlist() self.create_layout() - self.DRC_LVS() def add_pins(self): for i in range(self.word_size): @@ -42,20 +41,22 @@ class write_driver_array(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_layout(self): + def create_netlist(self): + self.add_pins() self.create_write_array() + + def create_layout(self): + self.place_write_array() self.add_layout_pins() + self.DRC_LVS() def create_write_array(self): self.driver_insts = {} for i in range(0,self.columns,self.words_per_row): name = "Xwrite_driver{}".format(i) - base = vector(i * self.driver.width,0) - index = int(i/self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, - mod=self.driver, - offset=base) + mod=self.driver) self.connect_inst(["data[{0}]".format(index), "bl[{0}]".format(index), @@ -63,6 +64,15 @@ class write_driver_array(design.design): "en", "vdd", "gnd"]) + def place_write_array(self): + for i in range(0,self.columns,self.words_per_row): + name = "Xwrite_driver{}".format(i) + base = vector(i * self.driver.width,0) + + self.place_inst(name=name, + offset=base) + + def add_layout_pins(self): for i in range(self.word_size): din_pin = self.driver_insts[i].get_pin("din") diff --git a/compiler/openram.py b/compiler/openram.py index 354c3cee..9045e8e7 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -40,7 +40,10 @@ report_status() import verify import sram -output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in ["sp","gds","v","lib","lef"]] +output_extensions = ["sp","v","lib"] +if not OPTS.netlist_only: + output_extensions.extend(["gds","lef"]) +output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions] print("Output files are: ") print(*output_files,sep="\n") diff --git a/compiler/options.py b/compiler/options.py index 72006f1c..5360515d 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -18,6 +18,8 @@ class options(optparse.Values): # This is the verbosity level to control debug information. 0 is none, 1 # is minimal, etc. debug_level = 0 + # When enabled, layout is not generated (and no DRC or LVS are performed) + netlist_only = False # This determines whether LVS and DRC is checked for each submodule. check_lvsdrc = True # Variable to select the variant of spice diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 0f9755e1..a2b7e26c 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -28,9 +28,8 @@ class pbitcell(pgate.pgate): self.num_write = num_write self.num_read = num_read - self.add_pins() + self.create_netlist() self.create_layout() - self.DRC_LVS() pbitcell.width = self.width pbitcell.height = self.height @@ -61,6 +60,9 @@ class pbitcell(pgate.pgate): self.add_pin("gnd") + def create_netlist(self): + self.add_pins() + def create_layout(self): self.create_ptx() self.calculate_spacing() @@ -75,6 +77,7 @@ class pbitcell(pgate.pgate): self.add_read_ports() self.extend_well() self.offset_all_coordinates() + self.DRC_LVS() def create_ptx(self): @@ -1055,10 +1058,9 @@ class pbitcell(pgate.pgate): bitcell_pins.append("gnd") return bitcell_pins - - def list_row_pins(self): - """ Creates a list of all row pins (except for gnd and vdd) """ + def list_all_wl_names(self): + """ Creates a list of all wordline pin names """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -1069,8 +1071,8 @@ class pbitcell(pgate.pgate): return row_pins - def list_read_row_pins(self): - """ Creates a list of row pins associated with read ports """ + def list_read_wl_names(self): + """ Creates a list of wordline pin names associated with read ports """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -1079,8 +1081,8 @@ class pbitcell(pgate.pgate): return row_pins - def list_write_row_pins(self): - """ Creates a list of row pins associated with write ports """ + def list_write_wl_names(self): + """ Creates a list of wordline pin names associated with write ports """ row_pins = [] for k in range(self.num_readwrite): row_pins.append("rwwl{0}".format(k)) @@ -1090,8 +1092,8 @@ class pbitcell(pgate.pgate): return row_pins - def list_all_column_pins(self): - """ Creates a list of all bitline pins """ + def list_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1105,8 +1107,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_column_pins(self): - """ Creates a list of all bitline bar pins """ + def list_all_bl_names(self): + """ Creates a list of all bl pins names """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1117,8 +1119,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_column_bar_pins(self): - """ Creates a list of all bitline bar pins """ + def list_all_br_names(self): + """ Creates a list of all br pins names """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl_bar{0}".format(k)) @@ -1129,8 +1131,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_read_column_pins(self): - """ Creates a list of column pins associated with read ports """ + def list_read_bl_names(self): + """ Creates a list of bl pin names associated with read ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1139,8 +1141,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_read_bar_column_pins(self): - """ Creates a list of column pins associated with read_bar ports """ + def list_read_br_names(self): + """ Creates a list of br pin names associated with read ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl_bar{0}".format(k)) @@ -1149,8 +1151,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_write_column_pins(self): - """ Creates a list of column pins associated with write ports """ + def list_write_bl_names(self): + """ Creates a list of bl pin names associated with write ports """ column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl{0}".format(k)) @@ -1159,8 +1161,8 @@ class pbitcell(pgate.pgate): return column_pins - def list_write_bar_column_pins(self): - """ Creates a list of column pins asscociated with write_bar ports""" + def list_write_br_names(self): + """ Creates a list of br pin names asscociated with write ports""" column_pins = [] for k in range(self.num_readwrite): column_pins.append("rwbl_bar{0}".format(k)) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 48644b7a..ef67180b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -39,7 +39,8 @@ class pinv(pgate.pgate): self.height = height # Maybe minimize height if not defined in future? self.route_output = False - self.add_pins() + + self.create_netlist() self.create_layout() # for run-time, we won't check every transitor DRC/LVS independently @@ -50,6 +51,10 @@ class pinv(pgate.pgate): """ Adds pins for spice netlist """ self.add_pin_list(["A", "Z", "vdd", "gnd"]) + def create_netlist(self): + """ Calls all functions related to the generation of the netlist """ + self.add_pins() + def create_layout(self): """ Calls all functions related to the generation of the layout """ diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 6347c157..b7238474 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -19,47 +19,43 @@ class pinvbuf(design.design): def __init__(self, driver_size=4, height=bitcell.height, name=""): - stage_effort = 4 + self.stage_effort = 4 + self.row_height = height # FIXME: Change the number of stages to support high drives. # stage effort of 4 or less # The pinvbuf has a FO of 2 for the first stage, so the second stage # should be sized "half" to prevent loading of the first stage - predriver_size = max(int(driver_size/(stage_effort/2)),1) - + self.driver_size = driver_size + self.predriver_size = max(int(self.driver_size/(self.stage_effort/2)),1) if name=="": - name = "pinvbuf_{0}_{1}_{2}".format(predriver_size, driver_size, pinvbuf.unique_id) + name = "pinvbuf_{0}_{1}_{2}".format(self.predriver_size, self.driver_size, pinvbuf.unique_id) pinvbuf.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) - - # Shield the cap, but have at least a stage effort of 4 - input_size = max(1,int(predriver_size/stage_effort)) - self.inv = pinv(size=input_size, height=height) - self.add_mod(self.inv) - - self.inv1 = pinv(size=predriver_size, height=height) - self.add_mod(self.inv1) + self.create_netlist() + self.create_layout() - self.inv2 = pinv(size=driver_size, height=height) - self.add_mod(self.inv2) + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_insts() + + def create_layout(self): self.width = 2*self.inv1.width + self.inv2.width self.height = 2*self.inv1.height - - self.create_layout() - + + self.place_insts() + self.route_wires() + self.add_layout_pins() + self.offset_all_coordinates() self.DRC_LVS() - - def create_layout(self): - self.add_pins() - self.add_insts() - self.add_wires() - self.add_layout_pins() def add_pins(self): self.add_pin("A") @@ -68,35 +64,58 @@ class pinvbuf(design.design): self.add_pin("vdd") self.add_pin("gnd") - def add_insts(self): - # Add INV1 to the right (capacitance shield) + def add_modules(self): + + # Shield the cap, but have at least a stage effort of 4 + input_size = max(1,int(self.predriver_size/self.stage_effort)) + self.inv = pinv(size=input_size, height=self.row_height) + self.add_mod(self.inv) + + self.inv1 = pinv(size=self.predriver_size, height=self.row_height) + self.add_mod(self.inv1) + + self.inv2 = pinv(size=self.driver_size, height=self.row_height) + self.add_mod(self.inv2) + + def create_insts(self): + # Create INV1 (capacitance shield) self.inv1_inst=self.add_inst(name="buf_inv1", - mod=self.inv, - offset=vector(0,0)) + mod=self.inv) self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - # Add INV2 to the right self.inv2_inst=self.add_inst(name="buf_inv2", - mod=self.inv1, - offset=vector(self.inv1_inst.rx(),0)) + mod=self.inv1) self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) - # Add INV3 to the right self.inv3_inst=self.add_inst(name="buf_inv3", - mod=self.inv2, - offset=vector(self.inv2_inst.rx(),0)) + mod=self.inv2) self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) - # Add INV4 to the bottom self.inv4_inst=self.add_inst(name="buf_inv4", - mod=self.inv2, - offset=vector(self.inv2_inst.rx(),2*self.inv2.height), - mirror = "MX") + mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add INV1 to the right (capacitance shield) + self.place_inst(name="buf_inv1", + offset=vector(0,0)) + + # Add INV2 to the right + self.place_inst(name="buf_inv2", + offset=vector(self.inv1_inst.rx(),0)) + + # Add INV3 to the right + self.place_inst(name="buf_inv3", + offset=vector(self.inv2_inst.rx(),0)) + + # Add INV4 to the bottom + self.place_inst(name="buf_inv4", + offset=vector(self.inv2_inst.rx(),2*self.inv2.height), + mirror = "MX") - def add_wires(self): + def route_wires(self): # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index a4ca41fe..fd684b63 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -35,7 +35,7 @@ class pnand2(pgate.pgate): debug.check(size==1,"Size 1 pnand2 is only supported now.") self.tx_mults = 1 - self.add_pins() + self.create_netlist() self.create_layout() #self.DRC_LVS() @@ -44,6 +44,10 @@ class pnand2(pgate.pgate): """ Adds pins for spice netlist """ self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + + def create_netlist(self): + self.add_pins() + def create_layout(self): """ Calls all functions related to the generation of the layout """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index b95cee3c..3df9628d 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -14,7 +14,8 @@ class pnand3(pgate.pgate): from importlib import reload c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) + mod_bitcell = getattr(c, OPTS.bitcell) + bitcell = mod_bitcell() unique_id = 1 @@ -37,7 +38,7 @@ class pnand3(pgate.pgate): debug.check(size==1,"Size 1 pnand3 is only supported now.") self.tx_mults = 1 - self.add_pins() + self.create_netlist() self.create_layout() #self.DRC_LVS() @@ -46,6 +47,9 @@ class pnand3(pgate.pgate): """ Adds pins for spice netlist """ self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"]) + def create_netlist(self): + self.add_pins() + def create_layout(self): """ Calls all functions related to the generation of the layout """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 677fc81d..e19f03cf 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -36,7 +36,7 @@ class pnor2(pgate.pgate): debug.check(size==1,"Size 1 pnor2 is only supported now.") self.tx_mults = 1 - self.add_pins() + self.create_netlist() self.create_layout() #self.DRC_LVS() @@ -45,6 +45,9 @@ class pnor2(pgate.pgate): """ Adds pins for spice netlist """ self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + def create_netlist(self): + self.add_pins() + def create_layout(self): """ Calls all functions related to the generation of the layout """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 186e5ece..3d06f889 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -12,7 +12,11 @@ class precharge(pgate.pgate): This module implements the precharge bitline cell used in the design. """ - def __init__(self, name, size=1, BL="bl", BR="br"): + unique_id = 1 + + def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): + name = name+"_{}".format(precharge.unique_id) + precharge.unique_id += 1 pgate.pgate.__init__(self, name) debug.info(2, "create single precharge cell: {0}".format(name)) @@ -24,16 +28,18 @@ class precharge(pgate.pgate): self.beta = parameter["beta"] self.ptx_width = self.beta*parameter["min_tx_size"] self.width = self.bitcell.width - self.BL = BL - self.BR = BR + self.bitcell_bl = bitcell_bl + self.bitcell_br = bitcell_br - self.add_pins() + self.create_netlist() self.create_layout() - self.DRC_LVS() def add_pins(self): - self.add_pin_list([self.BL, self.BR, "en", "vdd"]) + self.add_pin_list(["bl", "br", "en", "vdd"]) + def create_netlist(self): + self.add_pins() + def create_layout(self): self.create_ptx() self.add_ptx() @@ -43,6 +49,7 @@ class precharge(pgate.pgate): self.add_vdd_rail() self.add_bitlines() self.connect_to_bitlines() + self.DRC_LVS() def create_ptx(self): """Initializes the upper and lower pmos""" @@ -84,12 +91,12 @@ class precharge(pgate.pgate): """Adds both the upper_pmos and lower_pmos to the module""" # adds the lower pmos to layout #base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) - self.lower_pmos_position = vector(self.bitcell.get_pin(self.BL).lx(), + self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), self.pmos.active_offset.y) self.lower_pmos_inst=self.add_inst(name="lower_pmos", mod=self.pmos, offset=self.lower_pmos_position) - self.connect_inst([self.BL, "en", self.BR, "vdd"]) + self.connect_inst(["bl", "en", "br", "vdd"]) # adds the upper pmos(s) to layout ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width @@ -97,13 +104,13 @@ class precharge(pgate.pgate): self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", mod=self.pmos, offset=self.upper_pmos1_pos) - self.connect_inst([self.BL, "en", "vdd", "vdd"]) + self.connect_inst(["bl", "en", "vdd", "vdd"]) upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", mod=self.pmos, offset=upper_pmos2_pos) - self.connect_inst([self.BR, "en", "vdd", "vdd"]) + self.connect_inst(["br", "en", "vdd", "vdd"]) def connect_poly(self): """Connects the upper and lower pmos together""" @@ -161,16 +168,16 @@ class precharge(pgate.pgate): def add_bitlines(self): """Adds both bit-line and bit-line-bar to the module""" # adds the BL on metal 2 - offset = vector(self.bitcell.get_pin(self.BL).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text=self.BL, + offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) + self.add_layout_pin(text="bl", layer="metal2", offset=offset, width=drc['minwidth_metal2'], height=self.height) # adds the BR on metal 2 - offset = vector(self.bitcell.get_pin(self.BR).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text=self.BR, + offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0) + self.add_layout_pin(text="br", layer="metal2", offset=offset, width=drc['minwidth_metal2'], @@ -178,10 +185,10 @@ class precharge(pgate.pgate): def connect_to_bitlines(self): self.add_bitline_contacts() - self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin(self.BL)) - self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin(self.BR)) - self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin(self.BL)) - self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin(self.BR)) + self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) + self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) def add_bitline_contacts(self): diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 643f8feb..57451ee4 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -39,7 +39,7 @@ class ptx(design.design): self.connect_poly = connect_poly self.num_contacts = num_contacts - self.create_spice() + self.create_netlist() self.create_layout() self.translate_all(self.active_offset) @@ -57,7 +57,7 @@ class ptx(design.design): self.add_poly() self.add_active_contacts() - def create_spice(self): + def create_netlist(self): self.add_pin_list(["D", "G", "S", "B"]) # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 6b7d7988..c4deedb8 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -23,12 +23,15 @@ class single_level_column_mux(design.design): self.bitcell = self.mod_bitcell() self.ptx_width = tx_size * drc["minwidth_tx"] - self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) + + self.create_netlist() self.create_layout() - def create_layout(self): - + def create_netlist(self): + self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) self.add_ptx() + + def create_layout(self): self.pin_height = 2*self.m2_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height diff --git a/compiler/sram.py b/compiler/sram.py index cecf7cea..1f1adb79 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -37,9 +37,7 @@ class sram(): else: debug.error("Invalid number of banks.",-1) - self.s.compute_sizes() - self.s.create_modules() - self.s.add_pins() + self.s.create_netlist() self.s.create_layout() # Can remove the following, but it helps for debug! @@ -101,19 +99,20 @@ class sram(): lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) print_time("Characterization", datetime.datetime.now(), start_time) - # Write the layout - start_time = datetime.datetime.now() - gdsname = OPTS.output_path + self.s.name + ".gds" - print("GDS: Writing to {0}".format(gdsname)) - self.s.gds_write(gdsname) - print_time("GDS", datetime.datetime.now(), start_time) + if not OPTS.netlist_only: + # Write the layout + start_time = datetime.datetime.now() + gdsname = OPTS.output_path + self.s.name + ".gds" + print("GDS: Writing to {0}".format(gdsname)) + self.s.gds_write(gdsname) + print_time("GDS", datetime.datetime.now(), start_time) - # Create a LEF physical model - start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - print("LEF: Writing to {0}".format(lefname)) - self.s.lef_write(lefname) - print_time("LEF", datetime.datetime.now(), start_time) + # Create a LEF physical model + start_time = datetime.datetime.now() + lefname = OPTS.output_path + self.s.name + ".lef" + print("LEF: Writing to {0}".format(lefname)) + self.s.lef_write(lefname) + print_time("LEF", datetime.datetime.now(), start_time) # Write a verilog model start_time = datetime.datetime.now() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 4d2d72eb..fe51d94f 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -20,14 +20,38 @@ class sram_1bank(sram_base): def __init__(self, word_size, num_words, name): sram_base.__init__(self, word_size, num_words, 1, name) - def add_modules(self): + def create_netlist(self): + self.compute_sizes() + self.add_modules() + # Must run this after add modules to get control pin names + self.add_pins() + self.create_modules() + + def create_modules(self): """ - This adds the moduels for a single bank SRAM with control + This adds the modules for a single bank SRAM with control + logic. + """ + + self.bank_inst = self.create_bank() + + self.control_logic_inst = self.create_control_logic() + + self.row_addr_dff_inst = self.create_row_addr_dff() + + if self.col_addr_dff: + self.col_addr_dff_inst = self.create_col_addr_dff() + + self.data_dff_inst = self.create_data_dff() + + def place_modules(self): + """ + This places the modules for a single bank SRAM with control logic. """ # No orientation or offset - self.bank_inst = self.add_bank(0, [0, 0], 1, 1) + self.place_bank(self.bank_inst, [0, 0], 1, 1) # The control logic is placed such that the vertical center (between the delay/RBL and # the actual control logic is aligned with the vertical center of the bank (between @@ -36,12 +60,14 @@ class sram_1bank(sram_base): # up to the row address DFFs. control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, self.bank.bank_center.y - self.control_logic.control_logic_center.y) - self.add_control_logic(position=control_pos) + self.place_inst(name=self.control_logic_inst.name, + offset=control_pos) # The row address bits are placed above the control logic aligned on the right. row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width, self.control_logic_inst.uy()) - self.add_row_addr_dff(row_addr_pos) + self.place_inst(name=self.row_addr_dff_inst.name, + offset=row_addr_pos) # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk data_gap = -self.m2_pitch*(self.word_size+1) @@ -51,7 +77,8 @@ class sram_1bank(sram_base): if self.col_addr_dff: col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, data_gap - self.col_addr_dff.height) - self.add_col_addr_dff(col_addr_pos) + self.place_inst(name=self.col_addr_dff_inst.name, + offset=col_addr_pos) # Add the data flops below the bank to the right of the center of bank: # This relies on the center point of the bank: @@ -60,12 +87,13 @@ class sram_1bank(sram_base): # sense amps. data_pos = vector(self.bank.bank_center.x, data_gap - self.data_dff.height) - self.add_data_dff(data_pos) + self.place_inst(self.data_dff.name, + offset=data_pos) # two supply rails are already included in the bank, so just 2 here. self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch self.height = self.bank.height - + def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. @@ -75,8 +103,8 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.control_logic_inst, n) for i in range(self.word_size): - dout_name = "dout[{}]".format(i) - self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper()) + dout_name = "dout0[{}]".format(i) + self.copy_layout_pin(self.bank_inst, dout_name, "DOUT[{}]".format(i)) # Lower address bits for i in range(self.col_addr_size): @@ -179,7 +207,7 @@ class sram_1bank(sram_base): """ Connect the output of the row flops to the bank pins """ for i in range(self.row_addr_size): flop_name = "dout[{}]".format(i) - bank_name = "addr[{}]".format(i+self.col_addr_size) + bank_name = "addr0[{}]".format(i+self.col_addr_size) flop_pin = self.row_addr_dff_inst.get_pin(flop_name) bank_pin = self.bank_inst.get_pin(bank_name) flop_pos = flop_pin.center() @@ -204,7 +232,7 @@ class sram_1bank(sram_base): data_dff_map = zip(dff_names, bus_names) self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets) - bank_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)] + bank_names = ["addr0[{}]".format(x) for x in range(self.col_addr_size)] data_bank_map = zip(bank_names, bus_names) self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) @@ -215,7 +243,7 @@ class sram_1bank(sram_base): offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch) dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] - bank_names = ["din[{}]".format(x) for x in range(self.word_size)] + bank_names = ["din0[{}]".format(x) for x in range(self.word_size)] route_map = list(zip(bank_names, dff_names)) dff_pins = {key: self.data_dff_inst.get_pin(key) for key in dff_names } diff --git a/compiler/sram_base.py b/compiler/sram_base.py index a11b0984..7023407d 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -32,6 +32,8 @@ class sram_base(design): self.num_words = num_words self.num_banks = num_banks + self.bank_insts = [] + def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" @@ -114,12 +116,17 @@ class sram_base(design): self.add_pin("gnd","GROUND") + def create_netlist(self): + """ Netlist creation """ + self.add_modules() + self.add_pins() + def create_layout(self): """ Layout creation """ - self.add_modules() + self.place_modules() self.route() self.add_lvs_correspondence_points() - + def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -150,7 +157,7 @@ class sram_base(design): """ Add the horizontal and vertical busses """ # Vertical bus # The order of the control signals on the control bus: - self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en", "s_en"] + self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"] self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", pitch=self.m2_pitch, offset=self.vertical_bus_offset, @@ -235,7 +242,7 @@ class sram_base(design): self.copy_layout_pin(inst, "gnd") - def create_multi_bank_modules(self): + def add_multi_bank_modules(self): """ Create the multibank address flops and bank decoder """ from dff_buf_array import dff_buf_array self.msb_address = dff_buf_array(name="msb_address", @@ -247,7 +254,7 @@ class sram_base(design): self.msb_decoder = self.bank.decoder.pre2_4 self.add_mod(self.msb_decoder) - def create_modules(self): + def add_modules(self): """ Create all the modules that will be used """ from control_logic import control_logic @@ -280,7 +287,7 @@ class sram_base(design): # Create bank decoder if(self.num_banks > 1): - self.create_multi_bank_modules() + self.add_multi_bank_modules() self.bank_count = 0 @@ -289,7 +296,28 @@ class sram_base(design): - def add_bank(self, bank_num, position, x_flip, y_flip): + def create_bank(self): + """ Create a bank """ + bank_num = len(self.bank_insts) + self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), + mod=self.bank)) + + temp = [] + for i in range(self.word_size): + temp.append("DOUT[{0}]".format(i)) + for i in range(self.word_size): + temp.append("BANK_DIN[{0}]".format(i)) + for i in range(self.bank_addr_size): + temp.append("A[{0}]".format(i)) + if(self.num_banks > 1): + temp.append("bank_sel[{0}]".format(bank_num)) + temp.extend(["s_en0", "w_en0", "clk_buf_bar","clk_buf" , "vdd", "gnd"]) + self.connect_inst(temp) + + return self.bank_insts[-1] + + + def place_bank(self, bank_inst, position, x_flip, y_flip): """ Place a bank at the given position with orientations """ # x_flip == 1 --> no flip in x_axis @@ -313,32 +341,19 @@ class sram_base(design): else: bank_mirror = "R0" - bank_inst=self.add_inst(name="bank{0}".format(bank_num), - mod=self.bank, - offset=position, - mirror=bank_mirror, - rotate=bank_rotation) - - temp = [] - for i in range(self.word_size): - temp.append("DOUT[{0}]".format(i)) - for i in range(self.word_size): - temp.append("BANK_DIN[{0}]".format(i)) - for i in range(self.bank_addr_size): - temp.append("A[{0}]".format(i)) - if(self.num_banks > 1): - temp.append("bank_sel[{0}]".format(bank_num)) - temp.extend(["s_en", "w_en", "clk_buf_bar","clk_buf" , "vdd", "gnd"]) - self.connect_inst(temp) + self.place_inst(name=bank_inst.name, + offset=position, + mirror=bank_mirror, + rotate=bank_rotation) return bank_inst - - def add_row_addr_dff(self, position): - """ Add and place all address flops for the main decoder """ - self.row_addr_dff_inst = self.add_inst(name="row_address", - mod=self.row_addr_dff, - offset=position) + + def create_row_addr_dff(self): + """ Add all address flops for the main decoder """ + inst = self.add_inst(name="row_address", + mod=self.row_addr_dff) + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -347,13 +362,13 @@ class sram_base(design): outputs.append("A[{}]".format(i+self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - + return inst - def add_col_addr_dff(self, position): + def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ - self.col_addr_dff_inst = self.add_inst(name="row_address", - mod=self.col_addr_dff, - offset=position) + inst = self.add_inst(name="col_address", + mod=self.col_addr_dff) + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -362,12 +377,13 @@ class sram_base(design): outputs.append("A[{}]".format(i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - - def add_data_dff(self, position): + return inst + + def create_data_dff(self): """ Add and place all data flops """ - self.data_dff_inst = self.add_inst(name="data_dff", - mod=self.data_dff, - offset=position) + inst = self.add_inst(name="data_dff", + mod=self.data_dff) + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -376,15 +392,16 @@ class sram_base(design): outputs.append("BANK_DIN[{}]".format(i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) + return inst - def add_control_logic(self, position): + def create_control_logic(self): """ Add and place control logic """ - self.control_logic_inst=self.add_inst(name="control", - mod=self.control_logic, - offset=position) + inst = self.add_inst(name="control", + mod=self.control_logic) + self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) - - + return inst + diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index b46fb206..7ed46e50 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -69,9 +69,9 @@ def check_file_format_tab(file_name): if len(key_positions)>10: line_numbers = key_positions[:10] + [" ..."] else: - line_numbers = key_positoins + line_numbers = key_positions debug.info(0, '\nFound ' + str(len(key_positions)) + ' tabs in ' + - str(file_name) + ' (lines ' + ",".join(str(x) for x in line_positions) + ')') + str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')') f.close() return len(key_positions) diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py old mode 100755 new mode 100644 index 4e473a25..54f3dabb --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -30,16 +30,16 @@ class precharge_test(openram_test): OPTS.rw_ports = 2 OPTS.r_ports = 2 OPTS.w_ports = 2 - tx = precharge.precharge(name="precharge_driver", size=1, BL="rwbl0", BR="rwbl_bar0") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") self.local_check(tx) - tx = precharge.precharge(name="precharge_driver", size=1, BL="wbl0", BR="wbl_bar0") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="wbl0", bitcell_br="wbl_bar0") self.local_check(tx) - tx = precharge.precharge(name="precharge_driver", size=1, BL="rbl0", BR="rbl_bar0") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rbl0", bitcell_br="rbl_bar0") self.local_check(tx) - #globals.end_openram() + globals.end_openram() # instantiate a copy of the class to actually run the test if __name__ == "__main__": diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py old mode 100755 new mode 100644 index d4a3190f..236d8cf1 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -24,9 +24,23 @@ class precharge_test(openram_test): debug.info(2, "Checking 3 column precharge") pc = precharge_array.precharge_array(columns=3) self.local_check(pc) + + debug.info(2, "Checking precharge for pbitcell") + OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 2 + OPTS.r_ports = 2 + OPTS.w_ports = 2 + + pc = precharge_array.precharge_array(columns=3, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") + self.local_check(pc) + + pc = precharge_array.precharge_array(columns=3, bitcell_bl="wbl0", bitcell_br="wbl_bar0") + self.local_check(pc) + + pc = precharge_array.precharge_array(columns=3, bitcell_bl="rbl0", bitcell_br="rbl_bar0") + self.local_check(pc) globals.end_openram() - # instantiate a copy of the class to actually run the test if __name__ == "__main__": diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py new file mode 100644 index 00000000..e7ce8460 --- /dev/null +++ b/compiler/tests/19_psingle_bank_test.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +@unittest.skip("Multiported Bank not working yet") +class single_bank_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + + from bank import bank + OPTS.bitcell = "pbitcell" + + # testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry + OPTS.rw_ports = 2 + OPTS.w_ports = 2 + OPTS.r_ports = 2 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_2r_single") + self.local_check(a) + """ + OPTS.rw_ports = 0 + OPTS.w_ports = 2 + OPTS.r_ports = 2 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_0rw_2w_2r_single") + self.local_check(a) + + OPTS.rw_ports = 2 + OPTS.w_ports = 0 + OPTS.r_ports = 2 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_2r_single") + self.local_check(a) + + OPTS.rw_ports = 2 + OPTS.w_ports = 2 + OPTS.r_ports = 0 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_0r_single") + self.local_check(a) + + OPTS.rw_ports = 2 + OPTS.w_ports = 0 + OPTS.r_ports = 0 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_0r_single") + self.local_check(a) + + # testing with various column muxes + OPTS.rw_ports = 2 + OPTS.w_ports = 2 + OPTS.r_ports = 2 + + debug.info(1, "Two way column mux") + a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") + self.local_check(a) + + debug.info(1, "Four way column mux") + a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") + self.local_check(a) + + # Eight way has a short circuit of one column mux select to gnd rail + debug.info(1, "Eight way column mux") + a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") + self.local_check(a) + """ + + globals.end_openram() + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main()