From aa779a7f82bca4d568a2593f0ea8b86cae726fb3 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 13 Nov 2018 16:05:22 -0800 Subject: [PATCH] Initial two port bank in SCMOS --- compiler/modules/bank.py | 854 +++++++++++------- compiler/modules/bank_select.py | 12 +- compiler/modules/bitcell_array.py | 4 +- compiler/modules/control_logic.py | 12 +- compiler/modules/dff_buf.py | 8 +- compiler/modules/hierarchical_predecode.py | 4 +- compiler/modules/hierarchical_predecode2x4.py | 2 +- compiler/modules/hierarchical_predecode3x8.py | 2 +- compiler/modules/multibank.py | 8 +- compiler/modules/replica_bitline.py | 8 +- compiler/modules/wordline_driver.py | 7 +- compiler/sram_1bank.py | 60 +- compiler/sram_base.py | 30 +- 13 files changed, 620 insertions(+), 391 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index cd1b83e4..80816601 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -19,6 +19,7 @@ class bank(design.design): Dynamically generated a single bank including bitcell array, hierarchical_decoder, precharge, (optional column_mux and column decoder), write driver and sense amplifiers. + This can create up to two ports in any combination: rw, w, r. """ def __init__(self, sram_config, name=""): @@ -30,6 +31,7 @@ class bank(design.design): design.design.__init__(self, name) debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) + debug.check(len(self.all_ports)<=2,"Bank cannot handle more than two 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 @@ -48,11 +50,11 @@ class bank(design.design): self.compute_sizes() self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() def create_layout(self): - self.place_modules() + self.place_instances() self.setup_routing_constraints() self.route_layout() @@ -96,44 +98,107 @@ class bank(design.design): def route_layout(self): """ Create routing amoung the modules """ self.route_central_bus() - self.route_precharge_to_bitcell_array() - self.route_col_mux_to_precharge_array() - self.route_sense_amp_to_col_mux_or_precharge_array() - self.route_write_driver_to_sense_amp() - self.route_sense_amp_out() - self.route_wordline_driver() - self.route_write_driver() - self.route_row_decoder() - self.route_column_address_lines() - self.route_control_lines() - if self.num_banks > 1: - self.route_bank_select() + + for port in self.all_ports: + self.route_bitlines(port) + self.route_wordline_driver(port) + self.route_row_decoder(port) + self.route_column_address_lines(port) + self.route_control_lines(port) + if self.num_banks > 1: + self.route_bank_select(port) self.route_supplies() - - def create_modules(self): - """ Add modules. The order should not matter! """ - # Above the bitcell array - self.create_bitcell_array() - self.create_precharge_array() + def route_bitlines(self, port): + """ Route the bitlines depending on the port type rw, w, or r. """ - # Below the bitcell array + if port in self.readwrite_ports: + # write_driver -> sense_amp -> (column_mux) -> precharge -> bitcell_array + self.route_write_driver_in(port) + self.route_sense_amp_out(port) + self.route_write_driver_to_sense_amp(port) + self.route_sense_amp_to_column_mux_or_precharge_array(port) + self.route_column_mux_to_precharge_array(port) + self.route_precharge_to_bitcell_array(port) + elif port in self.read_ports: + # sense_amp -> (column_mux) -> precharge -> bitcell_array + self.route_sense_amp_out(port) + self.route_sense_amp_to_column_mux_or_precharge_array(port) + self.route_column_mux_to_precharge_array(port) + self.route_precharge_to_bitcell_array(port) + else: + # write_driver -> (column_mux) -> bitcell_array + self.route_write_driver_in(port) + self.route_write_driver_to_column_mux_or_bitcell_array(port) + self.route_column_mux_to_bitcell_array(port) + + def create_instances(self): + """ Create the instances of the netlist. """ + + self.create_bitcell_array() + + self.create_precharge_array() self.create_column_mux_array() self.create_sense_amp_array() self.create_write_driver_array() - # To the left of the bitcell array self.create_row_decoder() self.create_wordline_driver() self.create_column_decoder() self.create_bank_select() - def compute_module_offsets(self): + def compute_instance_offsets(self): """ - Compute the module offsets. + Compute the empty instance offsets for port0 and port1 (if needed) """ + + # These are created even if the port type (e.g. read only) + # doesn't need the instance (e.g. write driver). + + # Create the bottom-up and left to right order of components in each port + # which deepends on the port type rw, w, r + self.vertical_port_order = [] + self.vertical_port_offsets = [] + for port in self.all_ports: + self.vertical_port_order.append([]) + self.vertical_port_offsets.append([None]*4) + + # For later placement, these are fixed in the order: write driver, + # sense amp, clumn mux, precharge, even if the item is not used + # in a given port (it will be None then) + self.vertical_port_order[port].append(self.write_driver_array_inst[port]) + self.vertical_port_order[port].append(self.sense_amp_array_inst[port]) + self.vertical_port_order[port].append(self.column_mux_array_inst[port]) + self.vertical_port_order[port].append(self.precharge_array_inst[port]) + + # For the odd ones they will go on top, so reverse in place + if port%2: + self.vertical_port_order[port]=self.vertical_port_order[port][::-1] + + self.write_driver_offsets = [None]*len(self.all_ports) + self.sense_amp_offsets = [None]*len(self.all_ports) + self.column_mux_offsets = [None]*len(self.all_ports) + self.precharge_offsets = [None]*len(self.all_ports) + + self.wordline_driver_offsets = [None]*len(self.all_ports) + self.row_decoder_offsets = [None]*len(self.all_ports) + + self.column_decoder_offsets = [None]*len(self.all_ports) + self.bank_select_offsets = [None]*len(self.all_ports) + + self.compute_instance_port0_offsets() + if len(self.all_ports)==2: + self.compute_instance_port1_offsets() + + + def compute_instance_port0_offsets(self): + """ + Compute the instance offsets for port0. + """ + + port = 0 # UPPER RIGHT QUADRANT # Bitcell array is placed at (0,0) @@ -141,64 +206,127 @@ class bank(design.design): # LOWER RIGHT QUADRANT # Below the bitcell array - y_offset = self.precharge_array[0].height + self.m2_gap - self.precharge_offset = vector(0,-y_offset) - if self.col_addr_size > 0: - y_offset += self.column_mux_array[0].height + self.m2_gap - self.column_mux_offset = vector(0,-y_offset) - y_offset += self.sense_amp_array.height + self.m2_gap - self.sense_amp_offset = vector(0,-y_offset) - y_offset += self.write_driver_array.height + self.m2_gap - self.write_driver_offset = vector(0,-y_offset) + y_height = 0 + for p in self.vertical_port_order[port]: + if p==None: + continue + y_height += p.height + self.m2_gap + + y_offset = -y_height + for i,p in enumerate(self.vertical_port_order[port]): + if p==None: + continue + self.vertical_port_offsets[port][i]=vector(0,y_offset) + y_offset += (p.height + self.m2_gap) + + self.write_driver_offsets[port] = self.vertical_port_offsets[port][0] + self.sense_amp_offsets[port] = self.vertical_port_offsets[port][1] + self.column_mux_offsets[port] = self.vertical_port_offsets[port][2] + self.precharge_offsets[port] = self.vertical_port_offsets[port][3] # UPPER LEFT QUADRANT # To the left of the bitcell array # 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_offset = vector(-x_offset,0) - x_offset += self.row_decoder.width + self.m2_pitch - self.row_decoder_offset = vector(-x_offset,0) + x_offset = self.central_bus_width + self.wordline_driver.width + self.wordline_driver_offsets[port] = vector(-x_offset,0) + x_offset += self.row_decoder.width + self.m2_gap + self.row_decoder_offsets[port] = vector(-x_offset,0) # LOWER LEFT QUADRANT # Place the col decoder right aligned with row decoder (x_offset doesn't change) # Below the bitcell array if self.col_addr_size > 0: - y_offset = self.col_decoder.height + y_offset = self.column_decoder.height else: y_offset = 0 y_offset += 2*drc("well_to_well") - self.column_decoder_offset = vector(-x_offset,-y_offset) + self.column_decoder_offsets[port] = vector(-x_offset,-y_offset) # Bank select gets placed below the column decoder (x_offset doesn't change) if self.col_addr_size > 0: - y_offset = min(self.column_decoder_offset.y, self.column_mux_offset.y) + y_offset = min(self.column_decoder_offsets[port].y, self.column_mux_offsets[port].y) else: - y_offset = self.row_decoder_offset.y + y_offset = self.row_decoder_offsets[port].y if self.num_banks > 1: y_offset += self.bank_select.height + drc("well_to_well") - self.bank_select_offset = vector(-x_offset,-y_offset) - - def place_modules(self): - """ Place the modules. """ + self.bank_select_offsets[port] = vector(-x_offset,-y_offset) - self.compute_module_offsets() + def compute_instance_port1_offsets(self): + """ + Compute the instance offsets for port1 on the top of the bank. + """ + + port=1 + + # The center point for these cells are the upper-right corner of + # the bitcell array. + # The decoder/driver logic is placed on the right and mirrored on Y-axis. + # The write/sense/precharge/mux is placed on the top and mirrored on the X-axis. + + # LOWER LEFT QUADRANT + # Bitcell array is placed at (0,0) + + # UPPER LEFT QUADRANT + # Above the bitcell array + y_offset = self.bitcell_array.height + self.m2_gap + for i,p in enumerate(self.vertical_port_order[port]): + if p==None: + continue + y_offset += (p.height + self.m2_gap) + self.vertical_port_offsets[port][i]=vector(0,y_offset) + + # Reversed order + self.write_driver_offsets[port] = self.vertical_port_offsets[port][3] + self.sense_amp_offsets[port] = self.vertical_port_offsets[port][2] + self.column_mux_offsets[port] = self.vertical_port_offsets[port][1] + self.precharge_offsets[port] = self.vertical_port_offsets[port][0] + + # LOWER RIGHT QUADRANT + # To the left of the bitcell array + # The wordline driver is placed to the right of the main decoder width. + x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width + self.wordline_driver_offsets[port] = vector(x_offset,0) + x_offset += self.row_decoder.width + self.m2_gap + self.row_decoder_offsets[port] = vector(x_offset,0) + + # UPPER RIGHT QUADRANT + # Place the col decoder right aligned with row decoder (x_offset doesn't change) + # Below the bitcell array + y_offset = self.bitcell_array.height + self.m2_gap + y_offset += 2*drc("well_to_well") + self.column_decoder_offsets[port] = vector(x_offset,y_offset) + + # Bank select gets placed above the column decoder (x_offset doesn't change) + if self.col_addr_size > 0: + y_offset = max(self.column_decoder_offsets[port].y + self.column_decoder.height, + self.column_mux_offsets[port].y + self.column_mux_array[port].height) + else: + y_offset = self.row_decoder_offsets[port].y + self.bank_select_offsets[port] = vector(x_offset,y_offset) + + def place_instances(self): + """ Place the instances. """ + + self.compute_instance_offsets() # UPPER RIGHT QUADRANT self.place_bitcell_array(self.bitcell_array_offset) # LOWER RIGHT QUADRANT - self.place_precharge_array([self.precharge_offset]*len(self.read_ports)) - self.place_column_mux_array([self.column_mux_offset]*len(self.all_ports)) - self.place_sense_amp_array([self.sense_amp_offset]*len(self.read_ports)) - self.place_write_driver_array([self.write_driver_offset]*len(self.write_ports)) + # These are fixed in the order: write driver, sense amp, clumn mux, precharge, + # even if the item is not used in a given port (it will be None then) + self.place_write_driver_array(self.write_driver_offsets) + self.place_sense_amp_array(self.sense_amp_offsets) + self.place_column_mux_array(self.column_mux_offsets) + self.place_precharge_array(self.precharge_offsets) # UPPER LEFT QUADRANT - self.place_row_decoder([self.row_decoder_offset]*len(self.all_ports)) - self.place_wordline_driver([self.wordline_driver_offset]*len(self.all_ports)) + self.place_row_decoder(self.row_decoder_offsets) + self.place_wordline_driver(self.wordline_driver_offsets) # LOWER LEFT QUADRANT - self.place_column_decoder([self.column_decoder_offset]*len(self.all_ports)) - self.place_bank_select([self.bank_select_offset]*len(self.all_ports)) + self.place_column_decoder(self.column_decoder_offsets) + self.place_bank_select(self.bank_select_offsets) def compute_sizes(self): @@ -249,7 +377,7 @@ class bank(design.design): # The width of this bus is needed to place other modules (e.g. decoder) # A width on each side too - self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width + self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width # A space for wells or jogging m2 self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), @@ -257,7 +385,7 @@ class bank(design.design): def add_modules(self): - """ Create all the modules using the class loader """ + """ Add all the modules using the class loader """ mod_list = ["bitcell", "decoder", "wordline_driver", "bitcell_array", "sense_amp_array", "precharge_array", @@ -351,10 +479,10 @@ class bank(design.design): def create_precharge_array(self): """ Creating Precharge """ - self.precharge_array_inst = [] + self.precharge_array_inst = [None]*len(self.all_ports) for port in self.read_ports: - self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), - mod=self.precharge_array[port])) + self.precharge_array_inst[port]=self.add_inst(name="precharge_array{}".format(port), + mod=self.precharge_array[port]) temp = [] for i in range(self.num_cols): temp.append(self.bl_names[port]+"_{0}".format(i)) @@ -368,21 +496,24 @@ class bank(design.design): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.") - # FIXME: place for multiport for port in self.read_ports: - self.precharge_array_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.precharge_array_inst[port].place(offset=offsets[port], mirror=mirror) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ - self.col_mux_array_inst = [] + self.column_mux_array_inst = [None]*len(self.all_ports) if self.col_addr_size == 0: return for port in self.all_ports: - self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), - mod=self.column_mux_array[port])) + self.column_mux_array_inst[port] = self.add_inst(name="column_mux_array{}".format(port), + mod=self.column_mux_array[port]) temp = [] for col in range(self.num_cols): @@ -406,16 +537,20 @@ class bank(design.design): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.") for port in self.all_ports: - self.col_mux_array_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.column_mux_array_inst[port].place(offset=offsets[port], mirror=mirror) def create_sense_amp_array(self): """ Creating Sense amp """ - self.sense_amp_array_inst = [] + self.sense_amp_array_inst = [None]*len(self.all_ports) for port in self.read_ports: - self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), - mod=self.sense_amp_array)) + self.sense_amp_array_inst[port] = self.add_inst(name="sense_amp_array{}".format(port), + mod=self.sense_amp_array) temp = [] for bit in range(self.word_size): @@ -435,23 +570,21 @@ class bank(design.design): """ Placing Sense amp """ debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.") - - # FIXME: place for multiport for port in self.read_ports: - self.sense_amp_array_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.sense_amp_array_inst[port].place(offset=offsets[port], mirror=mirror) def create_write_driver_array(self): """ Creating Write Driver """ - self.write_driver_array_inst = [] - for port in self.all_ports: - if port in self.write_ports: - self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), - mod=self.write_driver_array)) - else: - self.write_driver_array_inst.append(None) - continue + self.write_driver_array_inst = [None]*len(self.all_ports) + for port in self.write_ports: + self.write_driver_array_inst[port] = self.add_inst(name="write_driver_array{}".format(port), + mod=self.write_driver_array) temp = [] for bit in range(self.word_size): @@ -473,16 +606,20 @@ class bank(design.design): debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.") for port in self.write_ports: - self.write_driver_array_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MX" + else: + mirror = "R0" + self.write_driver_array_inst[port].place(offset=offsets[port], mirror=mirror) def create_row_decoder(self): """ Create the hierarchical row decoder """ - self.row_decoder_inst = [] + self.row_decoder_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), - mod=self.row_decoder)) + self.row_decoder_inst[port] = self.add_inst(name="row_decoder{}".format(port), + mod=self.row_decoder) temp = [] for bit in range(self.row_addr_size): @@ -504,18 +641,21 @@ class bank(design.design): # 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 port in self.all_ports: - self.row_decoder_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MY" + else: + mirror = "R0" + self.row_decoder_inst[port].place(offset=offsets[port], mirror=mirror) def create_wordline_driver(self): """ Create the Wordline Driver """ - self.wordline_driver_inst = [] + self.wordline_driver_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), - mod=self.wordline_driver)) + self.wordline_driver_inst[port] = self.add_inst(name="wordline_driver{}".format(port), + mod=self.wordline_driver) temp = [] for row in range(self.num_rows): @@ -534,7 +674,11 @@ class bank(design.design): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.") for port in self.all_ports: - self.wordline_driver_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MY" + else: + mirror = "R0" + self.wordline_driver_inst[port].place(offset=offsets[port], mirror=mirror) def create_column_decoder(self): @@ -545,20 +689,20 @@ class bank(design.design): if self.col_addr_size == 0: return elif self.col_addr_size == 1: - self.col_decoder = pinvbuf(height=self.mod_dff.height) - self.add_mod(self.col_decoder) + self.column_decoder = pinvbuf(height=self.mod_dff.height) + self.add_mod(self.column_decoder) elif self.col_addr_size == 2: - self.col_decoder = self.row_decoder.pre2_4 + self.column_decoder = self.row_decoder.pre2_4 elif self.col_addr_size == 3: - self.col_decoder = self.row_decoder.pre3_8 + self.column_decoder = self.row_decoder.pre3_8 else: # No error checking before? debug.error("Invalid column decoder?",-1) - self.col_decoder_inst = [] + self.column_decoder_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), - mod=self.col_decoder)) + self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), + mod=self.column_decoder) temp = [] for bit in range(self.col_addr_size): @@ -579,7 +723,11 @@ class bank(design.design): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") for port in self.all_ports: - self.col_decoder_inst[port].place(offsets[port]) + if port%2 == 1: + mirror = "MY" + else: + mirror = "R0" + self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) @@ -589,10 +737,10 @@ class bank(design.design): if not self.num_banks > 1: return - self.bank_select_inst = [] + self.bank_select_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), - mod=self.bank_select)) + self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), + mod=self.bank_select) temp = [] temp.extend(self.input_control_signals[port]) @@ -620,39 +768,38 @@ class bank(design.design): self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst,"gnd") - def route_bank_select(self): + def route_bank_select(self, port): """ Route the bank select logic. """ - for port in self.all_ports: - if self.port_id[port] == "rw": - bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] - elif self.port_id[port] == "w": - bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"] - else: - bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"] + if self.port_id[port] == "rw": + bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] + elif self.port_id[port] == "w": + bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"] + else: + bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"] - copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] - for signal in range(len(copy_control_signals)): - self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) + copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] + for signal in range(len(copy_control_signals)): + self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) - for signal in range(len(gated_bank_sel_signals)): - # Connect the inverter output to the central bus - out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() - name = self.control_signals[port][signal] - bus_pos = vector(self.bus_xoffset[name].x, out_pos.y) - self.add_path("metal3",[out_pos, bus_pos]) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=bus_pos, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=out_pos, - rotate=90) + for signal in range(len(gated_bank_sel_signals)): + # Connect the inverter output to the central bus + out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() + name = self.control_signals[port][signal] + bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) + self.add_path("metal3",[out_pos, bus_pos]) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=bus_pos, + rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=out_pos, + rotate=90) def setup_routing_constraints(self): @@ -660,32 +807,12 @@ class bank(design.design): After the modules are instantiated, find the dimensions for the control bus, power ring, etc. """ - # FIXME: calculate for multiport - - #The minimum point is either the bottom of the address flops, - #the column decoder (if there is one). - write_driver_min_y_offset = self.write_driver_array_inst[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: - col_decoder_min_y_offset = row_decoder_min_y_offset - - if self.num_banks>1: - # The control gating logic is below the decoder - # Min of the control gating logic and write driver. - 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 write driver. - 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 - # FIXME: Update multiport - self.max_y_offset = self.bitcell_array_inst.ur().y + 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.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width + self.min_y_offset = min([x.by() for x in self.insts]) + + self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width + self.min_x_offset = min([x.lx() for x in self.insts]) # # Create the core bbox for the power rings ur = vector(self.max_x_offset, self.max_y_offset) @@ -701,126 +828,163 @@ class bank(design.design): # Overall central bus width. It includes all the column mux lines, # and control lines. + + self.bus_xoffset = [None]*len(self.all_ports) + # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - for port in self.all_ports: - control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) - control_bus_length = self.max_y_offset - self.min_y_offset - self.bus_xoffset = self.create_bus(layer="metal2", - pitch=self.m2_pitch, - offset=control_bus_offset, - names=self.control_signals[port], - length=control_bus_length, - vertical=True, - make_pins=(self.num_banks==1)) + control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) + control_bus_length = self.max_y_offset - self.min_y_offset + self.bus_xoffset[0] = self.create_bus(layer="metal2", + pitch=self.m2_pitch, + offset=control_bus_offset, + names=self.control_signals[0], + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) + + # Port 1 + if len(self.all_ports)==2: + control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset) + self.bus_xoffset[1] = self.create_bus(layer="metal2", + pitch=self.m2_pitch, + offset=control_bus_offset, + names=self.control_signals[1], + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) + - - def route_precharge_to_bitcell_array(self): + def route_precharge_to_bitcell_array(self, port): """ Routing of BL and BR between pre-charge and bitcell array """ - # FIXME: Update for multiport - for port in self.read_ports: - for col in range(self.num_cols): - precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).uc() - precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.bl_names[port]+"_{}".format(col)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.br_names[port]+"_{}".format(col)).bc() + inst2 = self.precharge_array_inst[port] + inst1 = self.bitcell_array_inst + inst1_bl_name = self.bl_names[port]+"_{}" + inst1_br_name = self.br_names[port]+"_{}" + self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + - 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_precharge_array(self): + def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ # Only do this if we have a column mux! if self.col_addr_size==0: return - # FIXME: Update for multiport - for port in self.all_ports: - bottom_inst = self.col_mux_array_inst[port] - top_inst = self.precharge_array_inst[port] - self.connect_bitlines(top_inst, bottom_inst, self.num_cols) + bottom_inst = self.column_mux_array_inst[port] + top_inst = self.precharge_array_inst[port] + self.connect_bitlines(top_inst, bottom_inst, self.num_cols) + def route_column_mux_to_bitcell_array(self, port): + """ Routing of BL and BR between col mux bitcell array """ + + # Only do this if we have a column mux! + if self.col_addr_size==0: + return + + bottom_inst = self.column_mux_array_inst[port] + top_inst = self.bitcell_array_inst + self.connect_bitlines(top_inst, bottom_inst, self.num_cols) + - def route_sense_amp_to_col_mux_or_precharge_array(self): + def route_sense_amp_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ - - for port in self.read_ports: - bottom_inst = self.sense_amp_array_inst[port] + bottom_inst = self.sense_amp_array_inst[port] + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + top_inst = self.column_mux_array_inst[port] + top_bl = "bl_out_{}" + top_br = "br_out_{}" + else: + # Sense amp is directly connected to the precharge array + top_inst = self.precharge_array_inst[port] + top_bl = "bl_{}" + top_br = "br_{}" - if self.col_addr_size>0: - # Sense amp is connected to the col mux - top_inst = self.col_mux_array_inst[port] - top_bl = "bl_out_{}" - top_br = "br_out_{}" - else: - # Sense amp is directly connected to the precharge array - top_inst = self.precharge_array_inst[port] - top_bl = "bl_{}" - top_br = "br_{}" - - self.connect_bitlines(top_inst, bottom_inst, self.word_size, - top_bl_name=top_bl, top_br_name=top_br) + self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size, + inst1_bl_name=top_bl, inst1_br_name=top_br) - def route_write_driver_to_sense_amp(self): + def route_write_driver_to_column_mux_or_precharge_array(self, port): + """ Routing of BL and BR between sense_amp and column mux or precharge array """ + bottom_inst = self.write_driver_array_inst[port] + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + top_inst = self.column_mux_array_inst[port] + top_bl = "bl_out_{}" + top_br = "br_out_{}" + else: + # Sense amp is directly connected to the precharge array + top_inst = self.precharge_array_inst[port] + top_bl = "bl_{}" + top_br = "br_{}" + + self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size, + inst1_bl_name=top_bl, inst1_br_name=top_br) + + def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ - - for port in self.write_ports: - bottom_inst = self.write_driver_array_inst[port] - top_inst = self.sense_amp_array_inst[port] - self.connect_bitlines(top_inst, bottom_inst, self.word_size) - + + inst1 = self.write_driver_array_inst[port] + inst2 = self.sense_amp_array_inst[port] + self.connect_bitlines(inst1, inst2, self.word_size) - def route_sense_amp_out(self): + def route_sense_amp_out(self, port): """ Add pins for the sense amp output """ - - # FIXME: Update for multiport - for port in self.read_ports: - for bit in range(self.word_size): - data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) - self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit), - layer=data_pin.layer, - offset=data_pin.center(), - height=data_pin.height(), - width=data_pin.width()) + + for bit in range(self.word_size): + data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) + self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit), + layer=data_pin.layer, + offset=data_pin.center(), + height=data_pin.height(), + width=data_pin.width()) - def route_row_decoder(self): + def route_row_decoder(self, port): """ Routes the row decoder inputs and supplies """ - # FIXME: Update for multiport # Create inputs for the row address lines - for port in self.all_ports: - for row in range(self.row_addr_size): - addr_idx = row + self.col_addr_size - decoder_name = "addr_{}".format(row) - addr_name = "addr{0}_{1}".format(port,addr_idx) - self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name) + for row in range(self.row_addr_size): + addr_idx = row + self.col_addr_size + decoder_name = "addr_{}".format(row) + addr_name = "addr{0}_{1}".format(port,addr_idx) + self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name) - def route_write_driver(self): + def route_write_driver_in(self, port): """ Connecting write driver """ - for port in self.all_ports: - for row in range(self.word_size): - data_name = "data_{}".format(row) - din_name = "din{0}_{1}".format(port,row) - self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) - def connect_bitlines(self, top_inst, bottom_inst, num_items, - top_bl_name="bl_{}", top_br_name="br_{}", bottom_bl_name="bl_{}", bottom_br_name="br_{}"): + for row in range(self.word_size): + data_name = "data_{}".format(row) + din_name = "din{0}_{1}".format(port,row) + self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) + + def connect_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", + inst2_bl_name="bl_{}", inst2_br_name="br_{}"): """ Connect the bl and br of two modules. This assumes that they have sufficient space to create a jog - in the middle between the two modules (if needed) + in the middle between the two modules (if needed). """ - for col in range(num_items): + + # determine top and bottom automatically. + # since they don't overlap, we can just check the bottom y coordinate. + if inst1.by() < inst2.by(): + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + else: + (bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + + for col in range(num_bits): bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc() bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc() top_bl = top_inst.get_pin(top_bl_name.format(col)).bc() @@ -832,61 +996,125 @@ class bank(design.design): self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), vector(top_br.x,yoffset), top_br]) - - def route_wordline_driver(self): + + def route_wordline_driver(self, port): + """ Connect Wordline driver to bitcell array wordline """ + if port%2: + self.route_wordline_driver_right(port) + else: + self.route_wordline_driver_left(port) + + def route_wordline_driver_left(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ - for port in self.all_ports: - for row 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[port].get_pin("decode_{}".format(row)).rc() - driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).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[port].get_pin("wl_{}".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).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 row 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[port].get_pin("decode_{}".format(row)).rc() + driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).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]) - def route_column_address_lines(self): + # The mid guarantees we exit the input cell to the right. + driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).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]) + + + def route_wordline_driver_right(self, port): + """ Connecting Wordline driver output to Bitcell WL connection """ + + for row 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[port].get_pin("decode_{}".format(row)).lc() + driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).rc() + 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[port].get_pin("wl_{}".format(row)).lc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc() + 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]) + + def route_column_address_lines(self, port): + if port%2: + self.route_column_address_lines_right(port) + else: + self.route_column_address_lines_left(port) + + def route_column_address_lines_left(self, port): """ Connecting the select lines of column mux to the address bus """ if not self.col_addr_size>0: return - for port in self.all_ports: - if self.col_addr_size == 1: + if self.col_addr_size == 1: + + # Connect to sel[0] and sel[1] + decode_names = ["Zb", "Z"] + + # The Address LSB + self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - # Connect to sel[0] and sel[1] - decode_names = ["Zb", "Z"] + elif self.col_addr_size > 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out_{}".format(i)) + + for i in range(self.col_addr_size): + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port,i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) + + offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) + + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + + route_map = list(zip(decode_names, sel_names)) + decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names } + column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names } + # Combine the dff and bank pins into a single dictionary of pin name to pin. + all_pins = {**decode_pins, **column_mux_pins} + self.create_vertical_channel_route(route_map, all_pins, offset) + + def route_column_address_lines_right(self, port): + """ Connecting the select lines of column mux to the address bus """ + if not self.col_addr_size>0: + return + + if self.col_addr_size == 1: + + # Connect to sel[0] and sel[1] + decode_names = ["Zb", "Z"] + + # The Address LSB + self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - # The Address LSB - self.copy_layout_pin(self.col_decoder_inst[port], "A", "addr{}_0".format(port)) - - elif self.col_addr_size > 1: - decode_names = [] - for i in range(self.num_col_addr_lines): - decode_names.append("out_{}".format(i)) + elif self.col_addr_size > 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out_{}".format(i)) - for i in range(self.col_addr_size): - decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port,i) - self.copy_layout_pin(self.col_decoder_inst[port], decoder_name, addr_name) + for i in range(self.col_addr_size): + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port,i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) - offset = self.col_decoder_inst[port].lr() + vector(self.m2_pitch, 0) + offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) - sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] - - route_map = list(zip(decode_names, sel_names)) - decode_pins = {key: self.col_decoder_inst[port].get_pin(key) for key in decode_names } - col_mux_pins = {key: self.col_mux_array_inst[port].get_pin(key) for key in sel_names } - # Combine the dff and bank pins into a single dictionary of pin name to pin. - all_pins = {**decode_pins, **col_mux_pins} - self.create_vertical_channel_route(route_map, all_pins, offset) + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + route_map = list(zip(decode_names, sel_names)) + decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names } + column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names } + # Combine the dff and bank pins into a single dictionary of pin name to pin. + all_pins = {**decode_pins, **column_mux_pins} + self.create_vertical_channel_route(route_map, all_pins, offset) + def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. @@ -923,16 +1151,17 @@ class bank(design.design): # offset=data_pin.center()) # Add labels on the decoder - for i in range(self.word_size): - data_name = "dec_out_{}".format(i) - pin_name = "in_{}".format(i) - data_pin = self.wordline_driver_inst[0].get_pin(pin_name) - self.add_label(text=data_name, - layer="metal1", - offset=data_pin.center()) + for port in self.write_ports: + for i in range(self.word_size): + data_name = "dec_out_{}".format(i) + pin_name = "in_{}".format(i) + data_pin = self.wordline_driver_inst[port].get_pin(pin_name) + self.add_label(text=data_name, + layer="metal1", + offset=data_pin.center()) - def route_control_lines(self): + def route_control_lines(self, port): """ Route the control lines of the entire bank """ # Make a list of tuples that we will connect. @@ -942,37 +1171,34 @@ class bank(design.design): write_inst = 0 read_inst = 0 - # Control lines for RW ports - for port in self.all_ports: - connection = [] - if port in self.read_ports: - connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc())) + connection = [] + if port in self.read_ports: + connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc())) - if port in self.write_ports: - connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) + if port in self.write_ports: + connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) - if port in self.read_ports: - connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc())) + if port in self.read_ports: + connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc())) - for (control_signal, pin_pos) in connection: - control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) - self.add_path("metal1", [control_pos, pin_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) - - # clk to wordline_driver - control_signal = self.prefix+"clk_buf{}".format(port) - pin_pos = self.wordline_driver_inst[port].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) - self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) - control_via_pos = vector(control_x_offset, mid_pos.y) + for (control_signal, pin_pos) in connection: + control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) + self.add_path("metal1", [control_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_via_pos, + offset=control_pos, rotate=90) + # clk to wordline_driver + control_signal = self.prefix+"clk_buf{}".format(port) + pin_pos = self.wordline_driver_inst[port].get_pin("en").uc() + mid_pos = pin_pos + vector(0,self.m1_pitch) + control_x_offset = self.bus_xoffset[port][control_signal].x + control_pos = vector(control_x_offset, mid_pos.y) + self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=control_pos, + rotate=90) + def analytical_delay(self, vdd, slew, load): """ return analytical delay of the bank""" diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 2b78f739..7141de2a 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -27,12 +27,12 @@ class bank_select(design.design): def create_netlist(self): self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() def create_layout(self): self.calculate_module_offsets() - self.place_modules() - self.route_modules() + self.place_instances() + self.route_instances() self.DRC_LVS() @@ -99,7 +99,7 @@ class bank_select(design.design): self.height = self.yoffset_maxpoint + 2*self.m1_pitch self.width = self.xoffset_inv + self.inv4x.width - def create_modules(self): + def create_instances(self): self.bank_sel_inv=self.add_inst(name="bank_sel_inv", mod=self.inv_sel) @@ -152,7 +152,7 @@ class bank_select(design.design): "vdd", "gnd"]) - def place_modules(self): + def place_instances(self): # bank select inverter self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) @@ -195,7 +195,7 @@ class bank_select(design.design): mirror=mirror) - def route_modules(self): + def route_instances(self): # bank_sel is vertical wire bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 8328a0cf..1d9b1539 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -34,7 +34,7 @@ class bitcell_array(design.design): """ Create and connect the netlist """ self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): @@ -85,7 +85,7 @@ class bitcell_array(design.design): self.cell = self.mod_bitcell() self.add_mod(self.cell) - def create_modules(self): + def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 4ac32967..31e239a4 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -41,12 +41,12 @@ class control_logic(design.design): self.setup_signal_busses() self.add_pins() self.add_modules() - self.create_modules() + self.create_instances() def create_layout(self): """ Create layout and route between modules """ self.route_rails() - self.place_modules() + self.place_instances() self.route_all() #self.add_lvs_correspondence_points() @@ -155,8 +155,8 @@ class control_logic(design.design): self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height) - def create_modules(self): - """ Create all the modules """ + def create_instances(self): + """ Create all the instances """ self.create_dffs() self.create_clk_row() if (self.port_type == "rw") or (self.port_type == "w"): @@ -167,8 +167,8 @@ class control_logic(design.design): self.create_rbl() - def place_modules(self): - """ Place all the modules """ + def place_instances(self): + """ Place all the instances """ # Keep track of all right-most instances to determine row boundary # and add the vdd/gnd pins self.row_end_inst = [] diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 395216bf..6361b220 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -36,13 +36,13 @@ class dff_buf(design.design): def create_netlist(self): self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): self.width = self.dff.width + self.inv1.width + self.inv2.width self.height = self.dff.height - self.place_modules() + self.place_instances() self.route_wires() self.add_layout_pins() self.DRC_LVS() @@ -70,7 +70,7 @@ class dff_buf(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_modules(self): + def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) @@ -83,7 +83,7 @@ class dff_buf(design.design): mod=self.inv2) self.connect_inst(["Qb", "Q", "vdd", "gnd"]) - def place_modules(self): + def place_instances(self): # Add the DFF self.dff_inst.place(vector(0,0)) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index dc6622ed..72d0d99f 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -31,8 +31,8 @@ class hierarchical_predecode(design.design): self.add_pin("vdd") self.add_pin("gnd") - def create_modules(self): - """ Create the INV and NAND gate """ + def add_modules(self): + """ Add the INV and NAND gate modules """ self.inv = pinv() self.add_mod(self.inv) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 813cbf81..4a7609bd 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -18,7 +18,7 @@ class hierarchical_predecode2x4(hierarchical_predecode): def create_netlist(self): self.add_pins() - self.create_modules() + self.add_modules() self.create_input_inverters() self.create_output_inverters() connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"], diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index c8cac345..f0bb4b39 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -18,7 +18,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): def create_netlist(self): self.add_pins() - self.create_modules() + self.add_modules() self.create_input_inverters() self.create_output_inverters() connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"], diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 5de24948..d402b145 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -54,8 +54,8 @@ class multibank(design.design): self.compute_sizes() self.add_pins() - self.create_modules() self.add_modules() + self.create_instances() self.setup_layout_constraints() # FIXME: Move this to the add modules function @@ -111,7 +111,7 @@ class multibank(design.design): self.route_supplies() - def add_modules(self): + def create_instances(self): """ Add modules. The order should not matter! """ # Above the bitcell array @@ -175,8 +175,8 @@ class multibank(design.design): - def create_modules(self): - """ Create all the modules using the class loader """ + def add_modules(self): + """ Add all the modules using the class loader """ self.tri = self.mod_tri_gate() self.bitcell = self.mod_bitcell() diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 32e87c34..5c14b14d 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -29,11 +29,11 @@ class replica_bitline(design.design): def create_netlist(self): self.add_modules() self.add_pins() - self.create_modules() + self.create_instances() def create_layout(self): self.calculate_module_offsets() - self.place_modules() + self.place_instances() self.route() self.add_layout_pins() @@ -104,7 +104,7 @@ class replica_bitline(design.design): self.access_tx = ptx(tx_type="pmos") self.add_mod(self.access_tx) - def create_modules(self): + def create_instances(self): """ Create all of the module instances in the logical netlist """ # This is the threshold detect inverter on the output of the RBL @@ -152,7 +152,7 @@ class replica_bitline(design.design): self.wl_list = self.rbl.cell.list_all_wl_names() self.bl_list = self.rbl.cell.list_all_bl_names() - def place_modules(self): + def place_instances(self): """ Add all of the module instances in the logical netlist """ # This is the threshold detect inverter on the output of the RBL diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index dd1039b0..ed379bcc 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -129,16 +129,15 @@ class wordline_driver(design.design): nand2_xoffset = inv1_xoffset + self.inv.width inv2_xoffset = nand2_xoffset + self.nand2.width - self.width = inv2_xoffset + self.inv.height - driver_height = self.inv.height + self.width = inv2_xoffset + self.inv.width self.height = self.inv.height * self.rows for row in range(self.rows): if (row % 2): - y_offset = driver_height*(row + 1) + y_offset = self.inv.height*(row + 1) inst_mirror = "MX" else: - y_offset = driver_height*row + y_offset = self.inv.height*row inst_mirror = "R0" inv1_offset = [inv1_xoffset, y_offset] diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 16ff78d5..cfbf1a9e 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -34,14 +34,14 @@ class sram_1bank(sram_base): self.bank_inst=self.create_bank(0) - self.control_logic_inst = self.create_control_logic() + self.control_logic_insts = self.create_control_logic() - self.row_addr_dff_inst = self.create_row_addr_dff() + self.row_addr_dff_insts = self.create_row_addr_dff() if self.col_addr_dff: - self.col_addr_dff_inst = self.create_col_addr_dff() + self.col_addr_dff_insts = self.create_col_addr_dff() - self.data_dff_inst = self.create_data_dff() + self.data_dff_insts = self.create_data_dff() def place_modules(self): """ @@ -58,14 +58,14 @@ class sram_1bank(sram_base): # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # up to the row address DFFs. for port in self.all_ports: - control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, - self.bank.bank_center.y - self.control_logic.control_logic_center.y) - self.control_logic_inst[port].place(control_pos) + control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch, + self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y) + self.control_logic_insts[port].place(control_pos) # The row address bits are placed above the control logic aligned on the right. - row_addr_pos = vector(self.control_logic_inst[0].rx() - self.row_addr_dff.width, - self.control_logic_inst[0].uy()) - self.row_addr_dff_inst[port].place(row_addr_pos) + row_addr_pos = vector(self.control_logic_insts[0].rx() - self.row_addr_dff.width, + self.control_logic_insts[0].uy()) + self.row_addr_dff_insts[port].place(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) @@ -75,7 +75,7 @@ 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.col_addr_dff_inst[port].place(col_addr_pos) + self.col_addr_dff_insts[port].place(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: @@ -84,7 +84,7 @@ class sram_1bank(sram_base): # sense amps. data_pos = vector(self.bank.bank_center.x, data_gap - self.data_dff.height) - self.data_dff_inst[port].place(data_pos) + self.data_dff_insts[port].place(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 @@ -97,7 +97,7 @@ class sram_1bank(sram_base): for port in self.all_ports: # Connect the control pins as inputs for signal in self.control_logic_inputs[port] + ["clk"]: - self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port)) + self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port)) if port in self.read_ports: for bit in range(self.word_size): @@ -105,14 +105,14 @@ class sram_1bank(sram_base): # Lower address bits for bit in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit)) # Upper address bits for bit in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) + self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) if port in self.write_ports: for bit in range(self.word_size): - self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) def route(self): """ Route a single bank SRAM """ @@ -135,7 +135,7 @@ class sram_1bank(sram_base): # This is the actual input to the SRAM for port in self.all_ports: - self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port)) + self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) # Connect all of these clock pins to the clock in the central bus # This is something like a "spine" clock distribution. The two spines @@ -147,23 +147,23 @@ class sram_1bank(sram_base): bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center() if self.col_addr_dff: - dff_clk_pin = self.col_addr_dff_inst[port].get_pin("clk") + dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") dff_clk_pos = dff_clk_pin.center() mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y) self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos]) - data_dff_clk_pin = self.data_dff_inst[port].get_pin("clk") + data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") data_dff_clk_pos = data_dff_clk_pin.center() mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y) self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos]) # This uses a metal2 track to the right of the control/row addr DFF # to route vertically. - control_clk_buf_pin = self.control_logic_inst[port].get_pin("clk_buf") + control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") control_clk_buf_pos = control_clk_buf_pin.rc() - row_addr_clk_pin = self.row_addr_dff_inst[port].get_pin("clk") + row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") row_addr_clk_pos = row_addr_clk_pin.rc() - mid1_pos = vector(self.row_addr_dff_inst[port].rx() + self.m2_pitch, + mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, row_addr_clk_pos.y) mid2_pos = vector(mid1_pos.x, control_clk_buf_pos.y) @@ -176,7 +176,7 @@ class sram_1bank(sram_base): """ Route the outputs from the control logic module """ for port in self.all_ports: for signal in self.control_logic_outputs[port]: - src_pin = self.control_logic_inst[port].get_pin(signal) + src_pin = self.control_logic_insts[port].get_pin(signal) dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) self.connect_rail_from_left_m2m3(src_pin, dest_pin) self.add_via_center(layers=("metal1","via1","metal2"), @@ -190,7 +190,7 @@ class sram_1bank(sram_base): for bit in range(self.row_addr_size): flop_name = "dout_{}".format(bit) bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) - flop_pin = self.row_addr_dff_inst[port].get_pin(flop_name) + flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) bank_pin = self.bank_inst.get_pin(bank_name) flop_pos = flop_pin.center() bank_pos = bank_pin.center() @@ -206,13 +206,13 @@ class sram_1bank(sram_base): bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", pitch=self.m1_pitch, - offset=self.col_addr_dff_inst[port].ul() + vector(0, self.m1_pitch), + offset=self.col_addr_dff_insts[port].ul() + vector(0, self.m1_pitch), names=bus_names, - length=self.col_addr_dff_inst[port].width) + length=self.col_addr_dff_insts[port].width) dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst[port], col_addr_bus_offsets) + self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets) bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)] data_bank_map = zip(bank_names, bus_names) @@ -223,13 +223,13 @@ class sram_1bank(sram_base): """ Connect the output of the data flops to the write driver """ # This is where the channel will start (y-dimension at least) for port in self.write_ports: - offset = self.data_dff_inst[port].ul() + vector(0, 2*self.m1_pitch) + offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) dff_names = ["dout_{}".format(x) for x in range(self.word_size)] bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] route_map = list(zip(bank_names, dff_names)) - dff_pins = {key: self.data_dff_inst[port].get_pin(key) for key in dff_names } + dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names } bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names } # Combine the dff and bank pins into a single dictionary of pin name to pin. all_pins = {**dff_pins, **bank_pins} @@ -245,7 +245,7 @@ class sram_1bank(sram_base): """ for n in self.control_logic_outputs[0]: - pin = self.control_logic_inst[0].get_pin(n) + pin = self.control_logic_insts[0].get_pin(n) self.add_label(text=n, layer=pin.layer, offset=pin.center()) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 2166aaba..74220e63 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -216,20 +216,24 @@ class sram_base(design): self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - #c = reload(__import__(OPTS.control_logic)) - #self.mod_control_logic = getattr(c, OPTS.control_logic) - + c = reload(__import__(OPTS.control_logic)) + self.mod_control_logic = getattr(c, OPTS.control_logic) - from control_logic import control_logic # Create the control logic module for each port type - if OPTS.num_rw_ports>0: - self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw") + if len(self.readwrite_ports)>0: + self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + port_type="rw") self.add_mod(self.control_logic_rw) - if OPTS.num_w_ports>0: - self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w") + if len(self.write_ports)>0: + self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + port_type="w") self.add_mod(self.control_logic_w) - if OPTS.num_r_ports>0: - self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r") + if len(self.read_ports)>0: + self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + port_type="r") self.add_mod(self.control_logic_r) # Create the address and control flops (but not the clk) @@ -382,7 +386,8 @@ class sram_base(design): def create_control_logic(self): - """ Add and place control logic """ + """ Add control logic instances """ + insts = [] for port in self.all_ports: if port in self.readwrite_ports: @@ -392,8 +397,7 @@ class sram_base(design): else: mod = self.control_logic_r - insts.append(self.add_inst(name="control{}".format(port), - mod=mod)) + insts.append(self.add_inst(name="control{}".format(port), mod=mod)) temp = ["csb{}".format(port)] if port in self.readwrite_ports: