From 8d97862f6e481c6c79dd91d45969e1cecc1323d8 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 00:55:23 -0700 Subject: [PATCH 01/16] altered precharge array and precharge unit tests to accommodate multiport --- compiler/modules/precharge_array.py | 20 +++++++++++--------- compiler/tests/04_precharge_test.py | 2 +- compiler/tests/08_precharge_array_test.py | 17 ++++++++++++++++- 3 files changed, 28 insertions(+), 11 deletions(-) mode change 100755 => 100644 compiler/tests/08_precharge_array_test.py diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index cc8aa40a..3fc14237 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -11,13 +11,15 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, columns, size=1): + def __init__(self, columns, size=1, BL="bl", BR="br"): design.design.__init__(self, "precharge_array") debug.info(1, "Creating {0}".format(self.name)) self.columns = columns + self.BL = BL + self.BR = BR - self.pc_cell = precharge(name="precharge", size=size) + self.pc_cell = precharge(name="precharge", size=size, BL=self.BL, BR=self.BR) self.add_mod(self.pc_cell) self.width = self.columns * self.pc_cell.width @@ -30,8 +32,8 @@ class precharge_array(design.design): def add_pins(self): """Adds pins for spice file""" for i in range(self.columns): - self.add_pin("bl[{0}]".format(i)) - self.add_pin("br[{0}]".format(i)) + self.add_pin(self.BL+"[{0}]".format(i)) + self.add_pin(self.BR+"[{0}]".format(i)) self.add_pin("en") self.add_pin("vdd") @@ -64,15 +66,15 @@ class precharge_array(design.design): offset=offset) self.local_insts.append(inst) - self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"]) - bl_pin = inst.get_pin("bl") - self.add_layout_pin(text="bl[{0}]".format(i), + self.connect_inst([self.BL+"[{0}]".format(i), self.BR+"[{0}]".format(i), "en", "vdd"]) + bl_pin = inst.get_pin(self.BL) + self.add_layout_pin(text=self.BL+"[{0}]".format(i), layer="metal2", offset=bl_pin.ll(), width=drc["minwidth_metal2"], height=bl_pin.height()) - br_pin = inst.get_pin("br") - self.add_layout_pin(text="br[{0}]".format(i), + br_pin = inst.get_pin(self.BR) + self.add_layout_pin(text=self.BR+"[{0}]".format(i), layer="metal2", offset=br_pin.ll(), width=drc["minwidth_metal2"], diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 4e473a25..8a501009 100644 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -39,7 +39,7 @@ class precharge_test(openram_test): tx = precharge.precharge(name="precharge_driver", size=1, BL="rbl0", 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..94db51f1 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -24,8 +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, BL="rwbl0", BR="rwbl_bar0") + self.local_check(pc) + + pc = precharge_array.precharge_array(columns=3, BL="wbl0", BR="wbl_bar0") + self.local_check(pc) + + pc = precharge_array.precharge_array(columns=3, BL="rbl0", BR="rbl_bar0") + self.local_check(pc) - globals.end_openram() + #globals.end_openram() # instantiate a copy of the class to actually run the test From 040340b49f88063050530a5919f7cb9b7a1d1b55 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 02:14:45 -0700 Subject: [PATCH 02/16] editted naming convention on precharge to accommodate multiport --- compiler/modules/precharge_array.py | 6 +++++- compiler/pgates/precharge.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 3fc14237..6caed238 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -11,8 +11,12 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ + unique_id = 1 + def __init__(self, columns, size=1, BL="bl", BR="br"): - design.design.__init__(self, "precharge_array") + 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 diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 186e5ece..0a20770c 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. """ + unique_id = 1 + def __init__(self, name, size=1, BL="bl", 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)) From af43fb6276f1bc74939d16934fe1469b90c0cafe Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 02:19:36 -0700 Subject: [PATCH 03/16] called bitcell function before reading the height of the bitcell because pbitcell's dynamic height can only be determined after the module is called --- compiler/pgates/pnand3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index b95cee3c..4a1587ad 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 From a5af4a2b9ca419dd3df67ca23df650f907281061 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 03:33:33 -0700 Subject: [PATCH 04/16] resolved variable name error in 00_code_format test --- compiler/tests/00_code_format_check_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) From e592d95146e27283318098f96ffc894fd5707249 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 03:36:40 -0700 Subject: [PATCH 05/16] Altered bank to accommodate multiport. Single port still works, though some of the control signal names have been changes to have a following 0 (e.g. s_en to s_en0). Multiport does not pass drc yet, but can generate an accurate spice netlist. --- compiler/modules/bank.py | 469 ++++++++++++---------- compiler/tests/08_precharge_array_test.py | 2 +- 2 files changed, 263 insertions(+), 208 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 74730f8b..5ec3587f 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 @@ -64,7 +68,7 @@ class bank(design.design): # 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) @@ -73,18 +77,25 @@ class bank(design.design): 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","tri_en_bar","tri_en", + 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 ["tri_en_bar","tri_en", "clk_buf_bar","clk_buf"]: self.add_pin(pin,"INPUT") self.add_pin("vdd","POWER") @@ -152,7 +163,7 @@ class bank(design.design): # Number of control lines in the bus self.num_control_lines = 6 # The order of the control signals on the control bus: - self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"] + self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "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] @@ -173,7 +184,6 @@ class bank(design.design): 2*self.m2_pitch) - def create_modules(self): """ Create all the modules using the class loader """ self.tri = self.mod_tri_gate() @@ -182,9 +192,23 @@ class bank(design.design): 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_column_pins() + self.read_br_list = self.bitcell.list_read_bar_column_pins() + + self.write_bl_list = self.bitcell.list_write_column_pins() + self.write_br_list = self.bitcell.list_write_bar_column_pins() + + self.total_bl_list = self.bitcell.list_column_pins() + self.total_br_list = self.bitcell.list_column_bar_pins() + + self.total_wl_list = self.bitcell.list_row_pins() - self.precharge_array = self.mod_precharge_array(columns=self.num_cols) - self.add_mod(self.precharge_array) + self.precharge_array = [None] * self.total_read + for k in range(self.total_read): + self.precharge_array[k] = self.mod_precharge_array(columns=self.num_cols, BL=self.read_bl_list[k], 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, @@ -224,97 +248,115 @@ class bank(design.design): self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array, offset=vector(0,0)) + 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"]) + bl_list = self.bitcell.list_all_column_pins() + wl_list = self.bitcell.list_row_pins() + for col in range(self.num_cols): + for bitline in bl_list: + temp.append(bitline+"[{0}]".format(col)) + for row in range(self.num_rows): + for wordline in wl_list: + temp.append(wordline+"[{0}]".format(row)) + temp.append("vdd") + temp.append("gnd") self.connect_inst(temp) def add_precharge_array(self): """ Adding 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 = [None] * self.total_read + + 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.precharge_array_inst[k]=self.add_inst(name="precharge_array{}".format(k), + mod=self.precharge_array[k], + offset=vector(0,y_offset)) + 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 . """ + 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) + self.col_mux_array_inst = [None] * self.total_ports + + for k in range(self.total_ports): + y_offset = self.column_mux_height + self.col_mux_array_inst[k]=self.add_inst(name="column_mux_array{}".format(k), + mod=self.column_mux_array, + offset=vector(0,y_offset).scale(-1,-1)) + 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 add_sense_amp_array(self): """ Adding Sense amp """ - 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) + self.sense_amp_array_inst = [None] * self.total_read + + for k in range(self.total_read): + y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap + self.sense_amp_array_inst[k]=self.add_inst(name="sense_amp_array{}".format(k), + 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}[{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 """ - 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)) + self.write_driver_array_inst = [None] * self.total_write + + 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.write_driver_array_inst[k]=self.add_inst(name="write_driver_array{}".format(k), + mod=self.write_driver_array, + offset=vector(0,y_offset).scale(-1,-1)) - 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) + 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 add_tri_gate_array(self): """ data tri gate to drive the data bus """ @@ -328,71 +370,80 @@ class bank(design.design): for i in range(self.word_size): temp.append("sa_out[{0}]".format(i)) for i in range(self.word_size): - temp.append("dout[{0}]".format(i)) + temp.append("dout0[{0}]".format(i)) temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"]) self.connect_inst(temp) def add_row_decoder(self): """ Add 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)) + self.row_decoder_inst = [None] * self.total_ports + + for k in range(self.total_ports): + x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) + self.row_decoder_inst[k]=self.add_inst(name="row_decoder{}".format(k), + mod=self.row_decoder, + 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) + 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 add_wordline_driver(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)) + self.wordline_driver_inst = [None] * self.total_ports + + 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.wordline_driver_inst[k]=self.add_inst(name="wordline_driver{}".format(k), + mod=self.wordline_driver, + offset=vector(x_offset,0)) - 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) + 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) def add_column_decoder_module(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)) + + self.col_decoder_inst = [None] * self.total_ports + + 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.col_decoder_inst[k]=self.add_inst(name="col_address_decoder{}".format(k), + 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) + 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 add_column_decoder(self): """ @@ -423,9 +474,9 @@ class bank(design.design): 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()) + y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by()) else: - y_off = self.row_decoder_inst.by() + 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.bank_select_inst = self.add_inst(name="bank_select", @@ -444,16 +495,16 @@ class bank(design.design): # 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.precharge_array_inst[0], + self.sense_amp_array_inst[0], + self.write_driver_array_inst[0], # self.tri_gate_array_inst, - self.row_decoder_inst, - self.wordline_driver_inst] + self.row_decoder_inst[0], + self.wordline_driver_inst[0]] # 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.append(self.col_decoder_inst[0]) + top_instances.append(self.col_mux_array_inst[0]) if self.num_banks > 1: top_instances.append(self.bank_select_inst) @@ -461,10 +512,10 @@ class bank(design.design): 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): @@ -497,10 +548,10 @@ class bank(design.design): #driver. # Leave room for the output below the tri gate. #tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch - 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 @@ -514,9 +565,9 @@ 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 + 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) @@ -547,18 +598,19 @@ class bank(design.design): def route_precharge_to_bitcell_array(self): """ Routing of BL and BR between pre-charge and bitcell array """ + + for k in range(self.total_read): + for i in range(self.num_cols): + precharge_bl = self.precharge_array_inst[k].get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc() + precharge_br = self.precharge_array_inst[k].get_pin(self.read_br_list[k]+"[{}]".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() - 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() - - 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): @@ -568,40 +620,42 @@ 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() + 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_to_trigate(self): """ Routing of sense amp output to tri_gate input """ @@ -609,7 +663,7 @@ class bank(design.design): for i in range(self.word_size): # Connection of data_out of sense amp to data_in tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).lc() - sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).bc() + sa_data_out = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i)).bc() self.add_via_center(layers=("metal2", "via2", "metal3"), offset=tri_gate_in) @@ -620,8 +674,8 @@ class bank(design.design): 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(), @@ -631,7 +685,7 @@ class bank(design.design): """ Metal 3 routing of tri_gate output data """ for i in range(self.word_size): data_pin = self.tri_gate_array_inst.get_pin("out[{}]".format(i)) - self.add_layout_pin_rect_center(text="dout[{}]".format(i), + self.add_layout_pin_rect_center(text="dout0[{}]".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -645,8 +699,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): @@ -654,29 +708,30 @@ 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() - 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() - 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]) + for k in range(self.total_read): + # 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[k].get_pin("decode[{}]".format(i)).rc() + driver_in_pos = self.wordline_driver_inst[k].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[k].get_pin("wl[{}]".format(i)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[k]+"[{}]".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]) @@ -693,7 +748,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 = [] @@ -702,22 +757,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: @@ -768,7 +823,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()) @@ -784,9 +839,9 @@ class bank(design.design): connection = [] #connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc())) #connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc())) - 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) @@ -797,7 +852,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/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 94db51f1..6c39d6e8 100644 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -40,7 +40,7 @@ class precharge_test(openram_test): pc = precharge_array.precharge_array(columns=3, BL="rbl0", BR="rbl_bar0") self.local_check(pc) - #globals.end_openram() + globals.end_openram() # instantiate a copy of the class to actually run the test From e147f807a59c5a9a0af61e4221c5f75caabf0e80 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 15 Aug 2018 04:32:56 -0700 Subject: [PATCH 06/16] adding a unit test for multiported bank, this test will skip in the regression testing because multiported bank does not pass drc yet --- compiler/tests/19_psingle_bank_test.py | 92 ++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 compiler/tests/19_psingle_bank_test.py 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() From 0f8da1510e31c4ab61a519f24692312c194f50e6 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Sat, 18 Aug 2018 15:27:07 -0700 Subject: [PATCH 07/16] Reverting pin name changes of precharge cell and array back to 'bl' and 'br'. Also clarifying bl and br init parameters to reflect that they refer to the bitcell lines. --- compiler/modules/precharge_array.py | 20 +++++++------- compiler/pgates/precharge.py | 32 +++++++++++------------ compiler/tests/04_precharge_test.py | 6 ++--- compiler/tests/08_precharge_array_test.py | 7 +++-- 4 files changed, 31 insertions(+), 34 deletions(-) mode change 100755 => 100644 compiler/tests/04_precharge_test.py diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 6caed238..23f8b361 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -13,17 +13,15 @@ class precharge_array(design.design): unique_id = 1 - def __init__(self, columns, size=1, BL="bl", BR="br"): + 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.BL = BL - self.BR = BR - self.pc_cell = precharge(name="precharge", size=size, BL=self.BL, BR=self.BR) + 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 @@ -36,8 +34,8 @@ class precharge_array(design.design): def add_pins(self): """Adds pins for spice file""" for i in range(self.columns): - self.add_pin(self.BL+"[{0}]".format(i)) - self.add_pin(self.BR+"[{0}]".format(i)) + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) self.add_pin("en") self.add_pin("vdd") @@ -70,15 +68,15 @@ class precharge_array(design.design): offset=offset) self.local_insts.append(inst) - self.connect_inst([self.BL+"[{0}]".format(i), self.BR+"[{0}]".format(i), "en", "vdd"]) - bl_pin = inst.get_pin(self.BL) - self.add_layout_pin(text=self.BL+"[{0}]".format(i), + self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"]) + bl_pin = inst.get_pin("bl") + self.add_layout_pin(text="bl[{0}]".format(i), layer="metal2", offset=bl_pin.ll(), width=drc["minwidth_metal2"], height=bl_pin.height()) - br_pin = inst.get_pin(self.BR) - self.add_layout_pin(text=self.BR+"[{0}]".format(i), + br_pin = inst.get_pin("br") + self.add_layout_pin(text="br[{0}]".format(i), layer="metal2", offset=br_pin.ll(), width=drc["minwidth_metal2"], diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 0a20770c..8866e107 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -14,7 +14,7 @@ class precharge(pgate.pgate): unique_id = 1 - def __init__(self, name, size=1, BL="bl", BR="br"): + 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) @@ -28,15 +28,15 @@ 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_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_layout(self): self.create_ptx() @@ -88,12 +88,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 @@ -101,13 +101,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""" @@ -165,16 +165,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'], @@ -182,10 +182,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/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py old mode 100755 new mode 100644 index 8a501009..54f3dabb --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -30,13 +30,13 @@ 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() diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 6c39d6e8..236d8cf1 100644 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -31,17 +31,16 @@ class precharge_test(openram_test): OPTS.r_ports = 2 OPTS.w_ports = 2 - pc = precharge_array.precharge_array(columns=3, BL="rwbl0", BR="rwbl_bar0") + 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, BL="wbl0", BR="wbl_bar0") + 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, BL="rbl0", BR="rbl_bar0") + 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__": From 19ca0d6c2a830b7d076faafa92fbc86f9bb151d6 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Sat, 18 Aug 2018 16:51:21 -0700 Subject: [PATCH 08/16] Changing control logic names to match naming scheme for multi-port. din[0] to din0[0], s_en to s_en0, addr[0] to addr0[0], etc. Sram level should pass unit tests for single port but will not currently pass for multi-port --- compiler/modules/bank.py | 11 +++++------ compiler/modules/control_logic.py | 10 +++++----- compiler/sram_1bank.py | 8 ++++---- compiler/sram_base.py | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 0f236ddc..02030b46 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -133,7 +133,6 @@ class bank(design.design): self.add_row_decoder() self.add_wordline_driver() self.add_column_decoder() - def compute_sizes(self): @@ -201,7 +200,7 @@ class bank(design.design): self.precharge_array = [None] * self.total_read for k in range(self.total_read): - self.precharge_array[k] = self.mod_precharge_array(columns=self.num_cols, BL=self.read_bl_list[k], BR=self.read_br_list[k]) + self.precharge_array[k] = 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: @@ -521,7 +520,7 @@ class bank(design.design): #the column decoder (if there is one). 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[0].by() else: @@ -575,8 +574,8 @@ class bank(design.design): for k in range(self.total_read): for i in range(self.num_cols): - precharge_bl = self.precharge_array_inst[k].get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc() - precharge_br = self.precharge_array_inst[k].get_pin(self.read_br_list[k]+"[{}]".format(i)).bc() + 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() @@ -631,7 +630,7 @@ class bank(design.design): 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): 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/sram_1bank.py b/compiler/sram_1bank.py index 4d2d72eb..d36c4be1 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -75,7 +75,7 @@ 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) + dout_name = "dout0[{}]".format(i) self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper()) # Lower address bits @@ -179,7 +179,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 +204,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 +215,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..d0f08d32 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -150,7 +150,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, @@ -328,7 +328,7 @@ class sram_base(design): 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"]) + temp.extend(["s_en0", "w_en0", "clk_buf_bar","clk_buf" , "vdd", "gnd"]) self.connect_inst(temp) return bank_inst From 8e3dc332f3b8a1464e4be4b608b9f672940bc386 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Sun, 19 Aug 2018 00:00:42 -0700 Subject: [PATCH 09/16] changed control signal names in bank select to accommodate multi-port changes in bank --- compiler/modules/bank.py | 2 +- compiler/modules/bank_select.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 02030b46..dbfeebda 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -639,7 +639,7 @@ class bank(design.design): 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): 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] From b8ae21a52bbe070e53c6aab324b574b4d4f07ab9 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 20 Aug 2018 22:11:24 -0700 Subject: [PATCH 10/16] made multi-port changes to sram. This commit will allow all levels of openram to pass unit tests --- compiler/sram_1bank.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index d36c4be1..477e4a2a 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -76,7 +76,7 @@ class sram_1bank(sram_base): for i in range(self.word_size): dout_name = "dout0[{}]".format(i) - self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper()) + self.copy_layout_pin(self.bank_inst, dout_name, "DOUT[{}]".format(i)) # Lower address bits for i in range(self.col_addr_size): From 8c73a26daa6581affa9c75ac40014675eede4660 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Sun, 26 Aug 2018 14:37:17 -0700 Subject: [PATCH 11/16] Changing function names in bitcell and pbitcell to better reflect what values they're returning. Editting function calls in bitcell_array and bank accordingly. --- compiler/modules/bank.py | 21 ++++++++-------- compiler/modules/bitcell.py | 42 +++++++++++++++---------------- compiler/modules/bitcell_array.py | 8 +++--- compiler/pgates/pbitcell.py | 41 +++++++++++++++--------------- 4 files changed, 54 insertions(+), 58 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index dbfeebda..6bfb5d2a 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -187,16 +187,17 @@ class bank(design.design): 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_column_pins() - self.read_br_list = self.bitcell.list_read_bar_column_pins() + 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_column_pins() - self.write_br_list = self.bitcell.list_write_bar_column_pins() + 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_column_pins() - self.total_br_list = self.bitcell.list_column_bar_pins() + 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_row_pins() + self.total_wl_list = self.bitcell.list_all_wl_names() + self.total_bitline_list = self.bitcell.list_all_bitline_names() self.precharge_array = [None] * self.total_read for k in range(self.total_read): @@ -239,13 +240,11 @@ class bank(design.design): offset=vector(0,0)) temp = [] - bl_list = self.bitcell.list_all_column_pins() - wl_list = self.bitcell.list_row_pins() for col in range(self.num_cols): - for bitline in bl_list: + for bitline in self.total_bitline_list: temp.append(bitline+"[{0}]".format(col)) for row in range(self.num_rows): - for wordline in wl_list: + for wordline in self.total_wl_list: temp.append(wordline+"[{0}]".format(row)) temp.append("vdd") temp.append("gnd") 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..04f81e1a 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -42,8 +42,8 @@ class bitcell_array(design.design): 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)) @@ -81,8 +81,8 @@ class bitcell_array(design.design): 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/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 0f9755e1..c2731d49 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -1055,10 +1055,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 +1068,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 +1078,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 +1089,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 +1104,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 +1116,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 +1128,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 +1138,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 +1148,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 +1158,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)) From 138a70fc23931da0713f0ea47bff79b401b295df Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 10:42:40 -0700 Subject: [PATCH 12/16] Add place_inst routine. Separate create netlist and layout in some modules. --- compiler/base/geometry.py | 6 +- compiler/base/hierarchy_layout.py | 9 + compiler/base/hierarchy_spice.py | 15 +- compiler/modules/bitcell_array.py | 31 +- compiler/modules/dff_array.py | 30 +- compiler/modules/dff_buf_array.py | 30 +- compiler/modules/hierarchical_decoder.py | 409 ++++++++++-------- compiler/modules/hierarchical_predecode.py | 71 +-- compiler/modules/hierarchical_predecode2x4.py | 24 +- compiler/modules/hierarchical_predecode3x8.py | 33 +- 10 files changed, 403 insertions(+), 255 deletions(-) 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..f39cfba8 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -118,6 +118,15 @@ 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() 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/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 04f81e1a..4d3e0b20 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -31,15 +31,14 @@ 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_all_wl_names() @@ -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,16 +66,29 @@ 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 """ 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..fcf3d42a 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,25 @@ 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() + def add_modules(self): self.inv = pinv() self.add_mod(self.inv) @@ -89,7 +95,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 +118,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 +150,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 +260,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 +279,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 +393,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 +405,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 +475,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 +489,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 +501,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 +510,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 +528,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 +539,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 +579,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 +588,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..1063dc15 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -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..c2eb564b 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(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() + 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]"], From 87f539f3a81fdd5f53c5838be7f46bff7fde83c7 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 10:54:21 -0700 Subject: [PATCH 13/16] Separate netlist/layout for flop and precharge array. --- compiler/modules/ms_flop_array.py | 33 ++++++++++++-------- compiler/modules/precharge_array.py | 47 ++++++++++++++++++----------- 2 files changed, 50 insertions(+), 30 deletions(-) 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 23f8b361..eed25978 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -27,9 +27,8 @@ class precharge_array(design.design): 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""" @@ -39,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): @@ -55,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", @@ -81,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) From 0daad338e47f1c0d2aec410f2edb4646f48aa7b6 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 11:13:34 -0700 Subject: [PATCH 14/16] All modules have split netlist/layout. --- compiler/base/hierarchy_layout.py | 1 + compiler/modules/sense_amp_array.py | 61 ++++++++------ .../modules/single_level_column_mux_array.py | 25 ++++-- compiler/modules/tri_gate_array.py | 21 +++-- compiler/modules/wordline_driver.py | 80 ++++++++++++------- compiler/modules/write_driver_array.py | 24 ++++-- 6 files changed, 134 insertions(+), 78 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index f39cfba8..3aa21794 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -127,6 +127,7 @@ class layout(lef.lef): 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/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") From 19d46f5954deedf04268c203f43d4e2ea4a08f01 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 14:18:32 -0700 Subject: [PATCH 15/16] Finalized separation of netlist/layout creation. --- compiler/modules/bank.py | 415 +++++++++++------- compiler/modules/hierarchical_decoder.py | 4 +- compiler/modules/hierarchical_predecode.py | 6 +- compiler/modules/hierarchical_predecode3x8.py | 4 +- compiler/pgates/pbitcell.py | 7 +- compiler/pgates/pinv.py | 7 +- compiler/pgates/pinvbuf.py | 95 ++-- compiler/pgates/pnand2.py | 6 +- compiler/pgates/pnand3.py | 5 +- compiler/pgates/pnor2.py | 5 +- compiler/pgates/precharge.py | 7 +- compiler/pgates/ptx.py | 4 +- compiler/pgates/single_level_column_mux.py | 9 +- compiler/sram.py | 4 +- compiler/sram_1bank.py | 44 +- compiler/sram_base.py | 107 +++-- 16 files changed, 454 insertions(+), 275 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 6bfb5d2a..cadb0b7e 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -54,19 +54,22 @@ 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() @@ -74,7 +77,7 @@ class bank(design.design): self.bank_center=self.offset_all_coordinates().scale(-1,-1) self.DRC_LVS() - + def add_pins(self): """ Adding pins for Bank module""" for k in range(self.total_read): @@ -117,24 +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 """ @@ -178,7 +203,7 @@ 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() @@ -199,9 +224,9 @@ class bank(design.design): self.total_wl_list = self.bitcell.list_all_wl_names() self.total_bitline_list = self.bitcell.list_all_bitline_names() - self.precharge_array = [None] * self.total_read + self.precharge_array = [] for k in range(self.total_read): - self.precharge_array[k] = self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[k], bitcell_br=self.read_br_list[k]) + 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: @@ -232,12 +257,12 @@ 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 col in range(self.num_cols): @@ -250,19 +275,19 @@ class bank(design.design): 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 """ - - self.precharge_array_inst = [None] * self.total_read + def create_precharge_array(self): + """ Creating Precharge """ + + self.precharge_array_inst = [] 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.precharge_array_inst[k]=self.add_inst(name="precharge_array{}".format(k), - mod=self.precharge_array[k], - offset=vector(0,y_offset)) + 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)) @@ -270,22 +295,27 @@ class bank(design.design): 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 """ - if self.col_addr_size > 0: - self.column_mux_height = self.column_mux_array.height + self.m2_gap - else: - self.column_mux_height = 0 + # 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 = [None] * self.total_ports - + self.col_mux_array_inst = [] for k in range(self.total_ports): - y_offset = self.column_mux_height - self.col_mux_array_inst[k]=self.add_inst(name="column_mux_array{}".format(k), - mod=self.column_mux_array, - offset=vector(0,y_offset).scale(-1,-1)) + 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)) @@ -298,16 +328,27 @@ class bank(design.design): temp.append("gnd") self.connect_inst(temp) - def add_sense_amp_array(self): - """ Adding Sense amp """ + 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 - self.sense_amp_array_inst = [None] * self.total_read - + 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 """ + + self.sense_amp_array_inst = [] for k in range(self.total_read): - y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst[k]=self.add_inst(name="sense_amp_array{}".format(k), - mod=self.sense_amp_array, - offset=vector(0,y_offset).scale(-1,-1)) + self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(k), + mod=self.sense_amp_array)) + temp = [] for i in range(self.word_size): temp.append("dout{0}[{1}]".format(k,i)) @@ -321,17 +362,22 @@ class bank(design.design): 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 """ - self.write_driver_array_inst = [None] * self.total_write - + # 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 """ + + self.write_driver_array_inst = [] 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.write_driver_array_inst[k]=self.add_inst(name="write_driver_array{}".format(k), - mod=self.write_driver_array, - offset=vector(0,y_offset).scale(-1,-1)) + self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k), + mod=self.write_driver_array)) temp = [] for i in range(self.word_size): @@ -346,23 +392,25 @@ class bank(design.design): temp.extend([self.prefix+"w_en{0}".format(k), "vdd", "gnd"]) self.connect_inst(temp) + def place_write_driver_array(self): + """ Placing Write Driver """ - def add_row_decoder(self): - """ Add the hierarchical row decoder """ + # 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)) - # 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. - - self.row_decoder_inst = [None] * self.total_ports + + + def create_row_decoder(self): + """ Create the hierarchical row decoder """ + self.row_decoder_inst = [] for k in range(self.total_ports): - x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst[k]=self.add_inst(name="row_decoder{}".format(k), - mod=self.row_decoder, - offset=vector(x_offset,0)) + 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): @@ -372,17 +420,29 @@ class bank(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def add_wordline_driver(self): - """ Wordline Driver """ + def place_row_decoder(self): + """ Place the hierarchical row decoder """ - self.wordline_driver_inst = [None] * self.total_ports + # 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. + # 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.wordline_driver_inst[k]=self.add_inst(name="wordline_driver{}".format(k), - mod=self.wordline_driver, - offset=vector(x_offset,0)) + 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)) + + + def create_wordline_driver(self): + """ Create the 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)) temp = [] for i in range(self.num_rows): @@ -394,35 +454,22 @@ class bank(design.design): 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. """ - self.col_decoder_inst = [None] * self.total_ports - - 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.col_decoder_inst[k]=self.add_inst(name="col_address_decoder{}".format(k), - mod=self.col_decoder, - offset=vector(x_off,y_off)) - - 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 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: @@ -435,51 +482,92 @@ 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[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.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[0], - self.sense_amp_array_inst[0], - self.write_driver_array_inst[0], - self.row_decoder_inst[0], - self.wordline_driver_inst[0]] - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst[0]) - top_instances.append(self.col_mux_array_inst[0]) + 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: @@ -497,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"), @@ -511,9 +599,11 @@ 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). @@ -535,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 + # 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[0].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) @@ -570,7 +661,8 @@ class bank(design.design): def route_precharge_to_bitcell_array(self): """ Routing of BL and BR between pre-charge and bitcell array """ - + + # 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() @@ -592,6 +684,7 @@ class bank(design.design): if self.col_addr_size==0: return + # 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() @@ -665,22 +758,20 @@ class bank(design.design): def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - for k in range(self.total_read): - # 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[k].get_pin("decode[{}]".format(i)).rc() - driver_in_pos = self.wordline_driver_inst[k].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]) + 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[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[k].get_pin("wl[{}]".format(i)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[k]+"[{}]".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]) + # The mid guarantees we exit the input cell to the right. + 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]) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index fcf3d42a..b74af940 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -54,7 +54,9 @@ class hierarchical_decoder(design.design): 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) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 1063dc15..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): diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index c2eb564b..78462077 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -28,7 +28,7 @@ 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.create_nand(connections) + self.create_nand_array(connections) def create_layout(self): """ @@ -41,7 +41,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): self.route_rails() self.place_input_inverters() self.place_output_inverters() - self.place_nand() + self.place_nand_array() self.route() self.DRC_LVS() diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index c2731d49..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): 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 4a1587ad..3df9628d 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -38,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() @@ -47,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 8866e107..3d06f889 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -31,13 +31,15 @@ class precharge(pgate.pgate): 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(["bl", "br", "en", "vdd"]) + def create_netlist(self): + self.add_pins() + def create_layout(self): self.create_ptx() self.add_ptx() @@ -47,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""" 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..2933f325 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! diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 477e4a2a..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. diff --git a/compiler/sram_base.py b/compiler/sram_base.py index d0f08d32..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 """ @@ -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_en0", "w_en0", "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 + From 9f051df18d6b9056f5ce5cf07caa21bd40e88f4d Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 14:33:02 -0700 Subject: [PATCH 16/16] Added netlist only configuration option. --- compiler/globals.py | 8 ++++++++ compiler/openram.py | 5 ++++- compiler/options.py | 2 ++ compiler/sram.py | 25 +++++++++++++------------ 4 files changed, 27 insertions(+), 13 deletions(-) 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/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/sram.py b/compiler/sram.py index 2933f325..1f1adb79 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -99,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()