diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 58a3d1fe..60519bdf 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -39,6 +39,7 @@ class layout(lef.lef): shift the origin in the lowest left corner """ offset = self.find_lowest_coords() self.translate_all(offset) + return offset def get_gate_offset(self, x_offset, height, inv_num): """Gets the base offset and y orientation of stacked rows of gates diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index db58441a..9f6ed008 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -66,7 +66,8 @@ class bank(design.design): # Can remove the following, but it helps for debug! self.add_lvs_correspondence_points() - self.offset_all_coordinates() + # Remember the bank center for further placement + self.bank_center=self.offset_all_coordinates() self.DRC_LVS() @@ -75,7 +76,7 @@ class bank(design.design): 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") + self.add_pin("BANK_DIN[{0}]".format(i),"IN") for i in range(self.addr_size): self.add_pin("A[{0}]".format(i),"INPUT") @@ -95,8 +96,9 @@ class bank(design.design): self.route_precharge_to_bitcell_array() self.route_col_mux_to_bitcell_array() self.route_sense_amp_to_col_mux_or_bitcell_array() - self.route_sense_amp_to_trigate() - self.route_tri_gate_out() + #self.route_sense_amp_to_trigate() + #self.route_tri_gate_out() + self.route_sense_amp_out() self.route_wordline_driver() self.route_write_driver() self.route_row_decoder() @@ -119,7 +121,8 @@ class bank(design.design): self.add_column_mux_array() self.add_sense_amp_array() self.add_write_driver_array() - self.add_tri_gate_array() + # Not needed for single bank + #self.add_tri_gate_array() # To the left of the bitcell array self.add_row_decoder() @@ -294,14 +297,15 @@ class bank(design.design): 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 + 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)) temp = [] for i in range(self.word_size): - temp.append("DIN[{0}]".format(i)) + temp.append("BANK_DIN[{0}]".format(i)) for i in range(self.word_size): if (self.words_per_row == 1): temp.append("bl[{0}]".format(i)) @@ -315,7 +319,7 @@ class bank(design.design): def add_tri_gate_array(self): """ data tri gate to drive the data bus """ y_offset = self.sense_amp_array.height+self.column_mux_height \ - + self.write_driver_array.height + self.m2_gap + self.tri_gate_array.height + + self.m2_gap + self.tri_gate_array.height self.tri_gate_array_inst=self.add_inst(name="tri_gate_array", mod=self.tri_gate_array, offset=vector(0,y_offset).scale(-1,-1)) @@ -443,7 +447,7 @@ class bank(design.design): self.precharge_array_inst, self.sense_amp_array_inst, self.write_driver_array_inst, - self.tri_gate_array_inst, +# self.tri_gate_array_inst, self.row_decoder_inst, self.wordline_driver_inst] # Add these if we use the part... @@ -492,7 +496,8 @@ class bank(design.design): #the column decoder (if there is one) or the tristate output #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 + #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() if self.col_addr_size > 0: col_decoder_min_y_offset = self.col_decoder_inst.by() @@ -502,10 +507,10 @@ class bank(design.design): if self.num_banks>1: # The control gating logic is below the decoder # Min of the control gating logic and tri gate. - self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, tri_gate_min_y_offset) + self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset) else: # Just the min of the decoder logic logic and tri gate. - self.min_y_offset = min(col_decoder_min_y_offset, tri_gate_min_y_offset) + self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset) # The max point is always the top of the precharge bitlines # Add a vdd and gnd power rail above the array @@ -620,6 +625,16 @@ class bank(design.design): offset=sa_data_out) self.add_path("metal3",[sa_data_out,tri_gate_in]) + 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), + layer="metal2", + offset=data_pin.center(), + height=data_pin.height(), + width=data_pin.width()), + def route_tri_gate_out(self): """ Metal 3 routing of tri_gate output data """ for i in range(self.word_size): @@ -647,7 +662,7 @@ class bank(design.design): for i in range(self.word_size): data_name = "data[{}]".format(i) - din_name = "DIN[{}]".format(i) + din_name = "BANK_DIN[{}]".format(i) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) @@ -749,13 +764,13 @@ class bank(design.design): layer="metal2", offset=br_pin.center()) - # Add the data output names to the sense amp output - for i in range(self.word_size): - data_name = "data[{}]".format(i) - data_pin = self.sense_amp_array_inst.get_pin(data_name) - self.add_label(text="sa_out[{}]".format(i), - layer="metal2", - offset=data_pin.center()) + # # Add the data output names to the sense amp output + # for i in range(self.word_size): + # data_name = "data[{}]".format(i) + # data_pin = self.sense_amp_array_inst.get_pin(data_name) + # self.add_label(text="sa_out[{}]".format(i), + # layer="metal2", + # offset=data_pin.center()) # Add labels on the decoder for i in range(self.word_size): @@ -775,8 +790,8 @@ class bank(design.design): # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 connection = [] - connection.append((self.prefix+"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+"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())) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 18330353..06b1521a 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -18,7 +18,7 @@ class dff_array(design.design): if name=="": name = "dff_array_{0}x{1}".format(rows, columns) design.design.__init__(self, name) - debug.info(1, "Creating {}".format(self.name)) + debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) from importlib import reload c = reload(__import__(OPTS.dff)) @@ -38,33 +38,33 @@ class dff_array(design.design): self.DRC_LVS() def add_pins(self): - for y in range(self.rows): - for x in range(self.columns): - self.add_pin(self.get_din_name(y,x)) - for y in range(self.rows): - for x in range(self.columns): - self.add_pin(self.get_dout_name(y,x)) + for row in range(self.rows): + for col in range(self.columns): + self.add_pin(self.get_din_name(row,col)) + for row in range(self.rows): + for col in range(self.columns): + self.add_pin(self.get_dout_name(row,col)) self.add_pin("clk") self.add_pin("vdd") self.add_pin("gnd") def create_dff_array(self): self.dff_insts={} - for y in range(self.rows): - for x in range(self.columns): - name = "Xdff_r{0}_c{1}".format(y,x) - if (y % 2 == 0): - base = vector(x*self.dff.width,y*self.dff.height) + 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(x*self.dff.width,(y+1)*self.dff.height) + base = vector(col*self.dff.width,(row+1)*self.dff.height) mirror = "MX" - self.dff_insts[x,y]=self.add_inst(name=name, + self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff, offset=base, mirror=mirror) - self.connect_inst([self.get_din_name(y,x), - self.get_dout_name(y,x), + self.connect_inst([self.get_din_name(row,col), + self.get_dout_name(row,col), "clk", "vdd", "gnd"]) @@ -91,10 +91,9 @@ class dff_array(design.design): def add_layout_pins(self): - - for y in range(self.rows): + for row in range(self.rows): # Continous vdd rail along with label. - vdd_pin=self.dff_insts[0,y].get_pin("vdd") + vdd_pin=self.dff_insts[row,0].get_pin("vdd") self.add_layout_pin(text="vdd", layer="metal1", offset=vdd_pin.ll(), @@ -102,7 +101,7 @@ class dff_array(design.design): height=self.m1_width) # Continous gnd rail along with label. - gnd_pin=self.dff_insts[0,y].get_pin("gnd") + gnd_pin=self.dff_insts[row,0].get_pin("gnd") self.add_layout_pin(text="gnd", layer="metal1", offset=gnd_pin.ll(), @@ -110,19 +109,19 @@ class dff_array(design.design): height=self.m1_width) - for y in range(self.rows): - for x in range(self.columns): - din_pin = self.dff_insts[x,y].get_pin("D") + for row in range(self.rows): + for col in range(self.columns): + din_pin = self.dff_insts[row,col].get_pin("D") debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") - self.add_layout_pin(text=self.get_din_name(y,x), + self.add_layout_pin(text=self.get_din_name(row,col), layer=din_pin.layer, offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) - dout_pin = self.dff_insts[x,y].get_pin("Q") + dout_pin = self.dff_insts[row,col].get_pin("Q") debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") - self.add_layout_pin(text=self.get_dout_name(y,x), + self.add_layout_pin(text=self.get_dout_name(row,col), layer=dout_pin.layer, offset=dout_pin.ll(), width=dout_pin.width(), @@ -145,8 +144,8 @@ class dff_array(design.design): offset=vector(0,0), width=self.width, height=self.m3_width) - for x in range(self.columns): - clk_pin = self.dff_insts[x,0].get_pin("clk") + for col in range(self.columns): + clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column self.add_layout_pin(text="clk", layer="metal2", diff --git a/compiler/sram.py b/compiler/sram.py index 910d1d2e..56448b05 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -23,6 +23,8 @@ class sram(): num_words)) start_time = datetime.datetime.now() + self.name = name + if num_banks == 1: from sram_1bank import sram_1bank self.s=sram_1bank(word_size, num_words, name) @@ -53,8 +55,6 @@ class sram(): if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) - def get_name(self): - return self.s.name def sp_write(self,name): self.s.sp_write(name) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 605c3011..b97283ca 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -37,14 +37,18 @@ class sram_1bank(sram_base): self.add_control_logic(position=control_pos) # Leave room for the control routes to the left of the flops - addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch, + row_addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch, control_pos.y + self.control_logic.height + self.m1_pitch) - self.add_addr_dff(addr_pos) + self.add_row_addr_dff(row_addr_pos) - # Leave room for the control routes to the left of the flops - data_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch, - control_pos.y + self.control_logic.height + self.m1_pitch) - self.add_addr_dff(addr_pos) + # Add the column address below the bank under the control + if self.col_addr_dff: + col_addr_pos = vector(-self.col_addr_dff.width, -1.5*self.col_addr_dff.height) + self.add_col_addr_dff(col_addr_pos) + + # Add the data flops below the bank + data_pos = vector(-self.bank_inst.mod.bank_center.x, -1.5*self.data_dff.height) + self.add_data_dff(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 @@ -54,15 +58,22 @@ class sram_1bank(sram_base): """ Add the top-level pins for a single bank SRAM with control. """ + # Connect the control pins as inputs + for n in self.control_logic_inputs + ["clk"]: + self.copy_layout_pin(self.control_logic_inst, n) for i in range(self.word_size): self.copy_layout_pin(self.bank_inst, "DOUT[{}]".format(i)) - - for i in range(self.addr_size): - self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i)) + + # Lower address bits + for i in range(self.col_addr_size): + self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i)) + # Upper address bits + for i in range(self.row_addr_size): + self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i+self.col_addr_size)) for i in range(self.word_size): - self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i)) + self.copy_layout_pin(self.data_dff_inst, "din[{}]".format(i),"DIN[{}]".format(i)) def route(self): """ Route a single bank SRAM """ @@ -79,14 +90,28 @@ class sram_1bank(sram_base): rotate=90) - # Connect the output of the flops to the bank pins - for i in range(self.addr_size): + # 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 = "A[{}]".format(i) - flop_pin = self.addr_dff_inst.get_pin(flop_name) + bank_name = "A[{}]".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() - bank_pos = vector(bank_pin.cx(),flop_pos.y) + bank_pos = bank_pin.center() + mid_pos = vector(bank_pos.x,flop_pos.y) + self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=flop_pos, + rotate=90) + + # Connect the output of the row flops to the bank pins + for i in range(self.col_addr_size): + flop_name = "dout[{}]".format(i) + bank_name = "A[{}]".format(i) + flop_pin = self.col_addr_dff_inst.get_pin(flop_name) + bank_pin = self.bank_inst.get_pin(bank_name) + flop_pos = flop_pin.center() + bank_pos = bank_pin.center() self.add_path("metal3",[flop_pos, bank_pos]) self.add_via_center(layers=("metal2","via2","metal3"), offset=flop_pos, @@ -95,17 +120,28 @@ class sram_1bank(sram_base): offset=bank_pos, rotate=90) - # Connect the control pins as inputs - for n in self.control_logic_inputs + ["clk"]: - self.copy_layout_pin(self.control_logic_inst, n) + # Connect the output of the row flops to the bank pins + for i in range(self.word_size): + flop_name = "dout[{}]".format(i) + bank_name = "BANK_DIN[{}]".format(i) + flop_pin = self.data_dff_inst.get_pin(flop_name) + bank_pin = self.bank_inst.get_pin(bank_name) + flop_pos = flop_pin.center() + bank_pos = bank_pin.center() + mid_pos = vector(bank_pos.x,flop_pos.y) + self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=flop_pos, + rotate=90) + - # Connect the clock between the flops and control module - flop_pin = self.addr_dff_inst.get_pin("clk") - ctrl_pin = self.control_logic_inst.get_pin("clk_buf") - flop_pos = flop_pin.uc() - ctrl_pos = ctrl_pin.bc() - mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y) - mid1_pos = vector(flop_pos.x, mid_ypos) - mid2_pos = vector(ctrl_pos.x, mid_ypos) - self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()]) + # # Connect the clock between the flops and control module + # flop_pin = self.addr_dff_inst.get_pin("clk") + # ctrl_pin = self.control_logic_inst.get_pin("clk_buf") + # flop_pos = flop_pin.uc() + # ctrl_pos = ctrl_pin.bc() + # mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y) + # mid1_pos = vector(flop_pos.x, mid_ypos) + # mid2_pos = vector(ctrl_pos.x, mid_ypos) + # self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()]) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 6bc8d0d6..bfc8941c 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -32,9 +32,6 @@ class sram_base(design): self.num_words = num_words self.num_banks = num_banks - def whoami(): - print("abstract") - def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" @@ -258,10 +255,18 @@ class sram_base(design): self.add_mod(self.control_logic) # Create the address and control flops (but not the clk) - dff_size = self.addr_size from dff_array import dff_array - self.addr_dff = dff_array(name="dff_array", rows=dff_size, columns=1) - self.add_mod(self.addr_dff) + self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1) + self.add_mod(self.row_addr_dff) + + if self.col_addr_size > 0: + self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size) + self.add_mod(self.col_addr_dff) + else: + self.col_addr_dff = None + + self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) + self.add_mod(self.data_dff) # Create the bank module (up to four are instantiated) from bank import bank @@ -329,21 +334,49 @@ class sram_base(design): return bank_inst - - def add_addr_dff(self, position): - """ Add and place address and control flops """ - self.addr_dff_inst = self.add_inst(name="address", - mod=self.addr_dff, - offset=position) + 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) # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.addr_size): + for i in range(self.row_addr_size): + inputs.append("ADDR[{}]".format(i+self.col_addr_size)) + outputs.append("A[{}]".format(i+self.col_addr_size)) + + self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) + + + def add_col_addr_dff(self, position): + """ 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) + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for i in range(self.col_addr_size): inputs.append("ADDR[{}]".format(i)) outputs.append("A[{}]".format(i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) - + + def add_data_dff(self, position): + """ Add and place all data flops """ + self.data_dff_inst = self.add_inst(name="data_dff", + mod=self.data_dff, + offset=position) + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for i in range(self.word_size): + inputs.append("DIN[{}]".format(i)) + outputs.append("BANK_DIN[{}]".format(i)) + + self.connect_inst(inputs + outputs + ["clk_buf_bar", "vdd", "gnd"]) + def add_control_logic(self, position): """ Add and place control logic """ inputs = [] diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index d6c7dbe1..6c7feddd 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -32,14 +32,14 @@ class openram_test(unittest.TestCase): a.gds_write(tempgds) import verify - result=verify.run_drc(a.get_name(), tempgds) + result=verify.run_drc(a.name, tempgds) if result != 0: - self.fail("DRC failed: {}".format(a.get_name())) + self.fail("DRC failed: {}".format(a.name)) - result=verify.run_lvs(a.get_name(), tempgds, tempspice, final_verification) + result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) if result != 0: - self.fail("LVS mismatch: {}".format(a.get_name())) + self.fail("LVS mismatch: {}".format(a.name)) if OPTS.purge_temp: self.cleanup()