From d99dcd33e245af9928df9dc508cfcc02c31be5ce Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 28 Nov 2018 15:30:52 -0800 Subject: [PATCH] Fix SRAM level control routing errors. --- compiler/modules/bank.py | 28 ++++++++++------- compiler/modules/control_logic.py | 10 +++--- compiler/modules/precharge_array.py | 2 +- compiler/sram_1bank.py | 49 ++++++++++++++++------------- compiler/sram_base.py | 5 ++- 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 1af9260f..f2392326 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -227,7 +227,7 @@ class bank(design.design): # 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 + x_offset = self.m2_gap + 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) @@ -284,7 +284,7 @@ class bank(design.design): # 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 + x_offset = self.bitcell_array.width + self.m2_gap + 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) @@ -350,21 +350,23 @@ class bank(design.design): # FIXME: This spacing should be width dependent... self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space - # Number of control lines in the bus - self.num_control_lines = 4 # The order of the control signals on the control bus: self.input_control_signals = [] port_num = 0 for port in range(OPTS.num_rw_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_w_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "wl_en{}".format(port_num), "w_en{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_r_ports): - self.input_control_signals.append(["clk_buf{}".format(port_num), "wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) port_num += 1 + # Number of control lines in the bus + self.num_control_lines = max([len(x) for x in self.input_control_signals]) + + # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] for port in self.all_ports: @@ -372,6 +374,7 @@ class bank(design.design): self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) else: self.control_signals.append(self.input_control_signals[port]) + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: self.num_col_addr_lines = 2**self.col_addr_size @@ -380,7 +383,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 + 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"), @@ -837,7 +840,8 @@ class bank(design.design): # 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 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 + # The control bus is routed up to two pitches below the bitcell array + control_bus_length = -2*self.m1_pitch - self.min_y_offset self.bus_xoffset[0] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -849,6 +853,8 @@ class bank(design.design): # Port 1 if len(self.all_ports)==2: control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset) + # The other control bus is routed up to two pitches above the bitcell array + control_bus_length = self.max_y_offset + self.bitcell_array.height + 2*self.m1_pitch self.bus_xoffset[1] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -1226,9 +1232,9 @@ class bank(design.design): rotate=90) # clk to wordline_driver - control_signal = self.prefix+"p_en_bar{}".format(port) + control_signal = self.prefix+"wl_en{}".format(port) pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc() - mid_pos = pin_pos - vector(0,self.m1_pitch) + mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus 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]) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 94370b6f..5c21ceb1 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -144,12 +144,12 @@ class control_logic(design.design): self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch # Outputs to the bank - if self.port_type == "r": - self.output_list = ["s_en", "p_en_bar"] - elif self.port_type == "w": - self.output_list = ["w_en"] - else: + if self.port_type == "rw": self.output_list = ["s_en", "w_en", "p_en_bar"] + elif self.port_type == "r": + self.output_list = ["s_en", "p_en_bar"] + else: + self.output_list = ["w_en"] self.output_list.append("wl_en") self.output_list.append("clk_buf") diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index abd16fd8..187c5fc5 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -94,7 +94,7 @@ class precharge_array(design.design): mod=self.pc_cell, offset=offset) self.local_insts.append(inst) - self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en", "vdd"]) + self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"]) def place_insts(self): diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 49ad4f9b..df485982 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -81,7 +81,7 @@ class sram_1bank(sram_base): # Add the col address flops below the bank to the left of the lower-left of bank array if self.col_addr_dff: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width, + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, -data_gap - self.col_addr_dff_insts[port].height) self.col_addr_dff_insts[port].place(col_addr_pos[port]) @@ -178,27 +178,11 @@ class sram_1bank(sram_base): # Connect all of these clock pins to the clock in the central bus # This is something like a "spine" clock distribution. The two spines # are clk_buf and clk_buf_bar + control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") + control_clk_buf_pos = control_clk_buf_pin.center() - bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port)) - bank_clk_buf_pos = bank_clk_buf_pin.center() - bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port)) - bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center() - - if self.col_addr_dff: - 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]) - - if port in self.write_ports: - 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 (for port0) of the control/row addr DFF # to route vertically. For port1, it is to the left. - control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") if port%2: control_clk_buf_pos = control_clk_buf_pin.lc() @@ -210,17 +194,38 @@ class sram_1bank(sram_base): row_addr_clk_pos = row_addr_clk_pin.rc() 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) + + # This is the steiner point where the net branches out + clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) + self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_steiner_pos, + rotate=90) + # Note, the via to the control logic is taken care of when we route # the control logic to the bank - self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos]) + self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos, control_clk_buf_pos]) + if self.col_addr_dff: + dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") + dff_clk_pos = dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + + if port in self.write_ports: + data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") + data_dff_clk_pos = data_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + def route_control_logic(self): """ Route the outputs from the control logic module """ for port in self.all_ports: for signal in self.control_logic_outputs[port]: + # The clock gets routed separately and is not a part of the bank + if "clk" in signal: + continue 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) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 26622079..3182c5b3 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -409,11 +409,14 @@ class sram_base(design): mod = self.control_logic_r insts.append(self.add_inst(name="control{}".format(port), mod=mod)) - + + # Inputs temp = ["csb{}".format(port)] if port in self.readwrite_ports: temp.append("web{}".format(port)) temp.append("clk{}".format(port)) + + # Ouputs if port in self.read_ports: temp.append("s_en{}".format(port)) if port in self.write_ports: