From 19d46f5954deedf04268c203f43d4e2ea4a08f01 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 27 Aug 2018 14:18:32 -0700 Subject: [PATCH] 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 +