From c7d32089f3e7dfd142b1d31267f4e86a23f80859 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 17 Sep 2020 14:45:49 -0700 Subject: [PATCH 1/6] Create RBL wordline buffer with correct polarity. --- compiler/base/hierarchy_layout.py | 10 +++ compiler/modules/bank.py | 49 ++++++------ compiler/modules/global_bitcell_array.py | 6 +- compiler/modules/hierarchical_decoder.py | 4 +- compiler/modules/port_address.py | 98 +++++++++++++++++++---- compiler/modules/wordline_buffer_array.py | 7 +- compiler/modules/wordline_driver_array.py | 6 +- compiler/options.py | 1 + compiler/pgates/pbuf.py | 2 +- compiler/pgates/wordline_driver.py | 29 +++++-- 10 files changed, 156 insertions(+), 56 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index d350c156..0cbe6e8c 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -63,6 +63,16 @@ class layout(): self.translate_all(offset) return offset + def offset_x_coordinates(self): + """ + This function is called after everything is placed to + shift the origin to the furthest left point. + Y offset is unchanged. + """ + offset = self.find_lowest_coords() + self.translate_all(offset.scale(1, 0)) + return offset + def get_gate_offset(self, x_offset, height, inv_num): """ Gets the base offset and y orientation of stacked rows of gates diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 51f6c19b..e4ac72bc 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -223,10 +223,10 @@ class bank(design.design): # UPPER LEFT QUADRANT # To the left of the bitcell array above the predecoders and control logic - x_offset = self.m2_gap + self.port_address.width + x_offset = self.m2_gap + self.port_address[port].width self.port_address_offsets[port] = vector(-x_offset, self.main_bitcell_array_bottom) - self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y + self.predecoder_height = self.port_address[port].predecoder_height + self.port_address_offsets[port].y # LOWER LEFT QUADRANT # Place the col decoder left aligned with wordline driver @@ -234,7 +234,7 @@ class bank(design.design): # control logic to allow control signals to easily pass over in M3 # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # may be routed in M3 or M4 - x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width + x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width y_offset = 1.25 * self.dff.height + self.column_decoder.height @@ -267,7 +267,7 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the right of the bitcell array - x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap + x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap self.port_address_offsets[port] = vector(x_offset, self.main_bitcell_array_bottom) @@ -278,7 +278,7 @@ class bank(design.design): # control logic to allow control signals to easily pass over in M3 # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # may be routed in M3 or M4 - x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width + x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height @@ -366,10 +366,13 @@ class bank(design.design): def add_modules(self): """ Add all the modules using the class loader """ - self.port_address = factory.create(module_type="port_address", - cols=self.num_cols + self.num_spare_cols, - rows=self.num_rows) - self.add_mod(self.port_address) + self.port_address = [] + for port in self.all_ports: + self.port_address.append(factory.create(module_type="port_address", + cols=self.num_cols + self.num_spare_cols, + rows=self.num_rows, + port=port)) + self.add_mod(self.port_address[port]) self.num_rbl = len(self.all_ports) @@ -420,13 +423,10 @@ class bank(design.design): # gnd temp = self.bitcell_array.get_inouts() - wordline_names = self.bitcell_array.get_inputs() - - # Rename the RBL WL to the enable name - for port in self.all_ports: - rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port] - wordline_names = [x.replace(rbl_wl_name, "wl_en{0}".format(port)) for x in wordline_names] - temp.extend(wordline_names) + temp.append("rbl_wl0") + temp.extend(self.bitcell_array.get_wordline_names()) + if len(self.all_ports) > 1: + temp.append("rbl_wl1") temp.append("vdd") temp.append("gnd") @@ -486,13 +486,15 @@ class bank(design.design): self.port_address_inst = [None] * len(self.all_ports) for port in self.all_ports: self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), - mod=self.port_address) + mod=self.port_address[port]) temp = [] for bit in range(self.row_addr_size): temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("wl_en{}".format(port)) - temp.extend(self.bitcell_array.get_wordline_names(port)) + wordline_names = self.bitcell_array.get_wordline_names(port) + temp.extend(wordline_names) + temp.append("rbl_wl{}".format(port)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -852,8 +854,9 @@ class bank(design.design): def route_port_address_left(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ - driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] - for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): + driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"] + rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port] + for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]): # The mid guarantees we exit the input cell to the right. driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pos = driver_wl_pin.rc() @@ -1021,10 +1024,6 @@ class bank(design.design): connection.append((self.prefix + "p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar"))) - rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port) - connection.append((self.prefix + "wl_en{}".format(port), - self.bitcell_array_inst.get_pin(rbl_wl_name[port]))) - if port in self.write_ports: connection.append((self.prefix + "w_en{}".format(port), self.port_data_inst[port].get_pin("w_en"))) @@ -1045,7 +1044,7 @@ class bank(design.design): self.add_via_stack_center(from_layer=pin.layer, to_layer="m2", offset=control_pos) - + # clk to wordline_driver control_signal = self.prefix + "wl_en{}".format(port) if port % 2: diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py index c26e62f1..57f4d716 100644 --- a/compiler/modules/global_bitcell_array.py +++ b/compiler/modules/global_bitcell_array.py @@ -54,6 +54,8 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): """ Add the modules used in this design """ self.local_mods = [] + # Special case of a single local array + # so it should contain the left and possibly right RBL if len(self.column_sizes) == 1: la = factory.create(module_type="local_bitcell_array", rows=self.row_size, @@ -66,19 +68,21 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): return for i, cols in enumerate(self.column_sizes): - # Always add the left RBLs to the first subarray and the right RBLs to the last subarray + # Always add the left RBLs to the first subarray if i == 0: la = factory.create(module_type="local_bitcell_array", rows=self.row_size, cols=cols, rbl=self.rbl, left_rbl=[0]) + # Add the right RBL to the last subarray elif i == len(self.column_sizes) - 1 and len(self.all_ports) > 1: la = factory.create(module_type="local_bitcell_array", rows=self.row_size, cols=cols, rbl=self.rbl, right_rbl=[1]) + # Middle subarrays do not have any RBLs else: la = factory.create(module_type="local_bitcell_array", rows=self.row_size, diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index ca26993f..21aa1c89 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -55,9 +55,9 @@ class hierarchical_decoder(design.design): self.route_decoder_bus() self.route_vdd_gnd() - self.offset_all_coordinates() + self.offset_x_coordinates() - self.width = self.and_inst[0].rx() + self.m1_space + self.width = self.and_inst[0].rx() + 0.5 * self.m1_width self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 532463a7..3f925851 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -17,10 +17,11 @@ class port_address(design.design): Create the address port (row decoder and wordline driver).. """ - def __init__(self, cols, rows, name=""): + def __init__(self, cols, rows, port, name=""): self.num_cols = cols self.num_rows = rows + self.port = port self.addr_size = ceil(log(self.num_rows, 2)) if name == "": @@ -39,6 +40,7 @@ class port_address(design.design): self.add_modules() self.create_row_decoder() self.create_wordline_driver() + self.create_rbl_driver() def create_layout(self): if "li" in layer: @@ -59,6 +61,8 @@ class port_address(design.design): for bit in range(self.num_rows): self.add_pin("wl_{0}".format(bit), "OUTPUT") + + self.add_pin("rbl_wl", "OUTPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -71,10 +75,12 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - for inst in self.insts: + for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]: self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "gnd") + self.copy_power_pins(self.rbl_driver_inst, "vdd") + def route_pins(self): for row in range(self.addr_size): decoder_name = "addr_{}".format(row) @@ -82,16 +88,16 @@ class port_address(design.design): for row in range(self.num_rows): driver_name = "wl_{}".format(row) - self.copy_layout_pin(self.wordline_driver_inst, driver_name) + self.copy_layout_pin(self.wordline_driver_array_inst, driver_name) - self.copy_layout_pin(self.wordline_driver_inst, "en", "wl_en") + self.copy_layout_pin(self.rbl_driver_inst, "Z", "rbl_wl") def route_internal(self): for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) decoder_out_pos = decoder_out_pin.rc() - driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) + driver_in_pin = self.wordline_driver_array_inst.get_pin("in_{}".format(row)) driver_in_pos = driver_in_pin.lc() self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3) @@ -102,6 +108,20 @@ class port_address(design.design): self.add_via_stack_center(from_layer=driver_in_pin.layer, to_layer=self.route_layer, offset=driver_in_pos) + + # Route the RBL from the enable input + en_pin = self.wordline_driver_array_inst.get_pin("en") + rbl_in_pin = self.rbl_driver_inst.get_pin("A") + rbl_in_pos = rbl_in_pin.center() + mid_pos = vector(en_pin.cx(), rbl_in_pin.cy()) + self.add_path(rbl_in_pin.layer, [rbl_in_pos, mid_pos]) + self.add_via_stack_center(from_layer=rbl_in_pin.layer, + to_layer=en_pin.layer, + offset=mid_pos) + self.add_layout_pin_segment_center(text="wl_en", + layer=en_pin.layer, + start=mid_pos, + end=en_pin.center()) def add_modules(self): @@ -109,10 +129,33 @@ class port_address(design.design): num_outputs=self.num_rows) self.add_mod(self.row_decoder) - self.wordline_driver = factory.create(module_type="wordline_driver_array", - rows=self.num_rows, - cols=self.num_cols) - self.add_mod(self.wordline_driver) + self.wordline_driver_array = factory.create(module_type="wordline_driver_array", + rows=self.num_rows, + cols=self.num_cols) + self.add_mod(self.wordline_driver_array) + + try: + local_array_size = OPTS.local_array_size + driver_size = int(self.num_cols / local_array_size) + except AttributeError: + local_array_size = 0 + # Defautl to FO4 + driver_size = int(self.num_cols / 4) + + # The polarity must be switched if we have a hierarchical wordline + # to compensate for the local array inverters + b = factory.create(module_type="bitcell") + + if local_array_size > 0: + self.rbl_driver = factory.create(module_type="inv_dec", + size=driver_size, + height=b.height) + else: + self.rbl_driver = factory.create(module_type="buf_dec", + size=driver_size, + height=b.height) + + self.add_mod(self.rbl_driver) def create_row_decoder(self): """ Create the hierarchical row decoder """ @@ -128,11 +171,24 @@ class port_address(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) + def create_rbl_driver(self): + """ Create the RBL Wordline Driver """ + + self.rbl_driver_inst = self.add_inst(name="rbl_driver", + mod=self.rbl_driver) + + temp = [] + temp.append("wl_en") + temp.append("rbl_wl") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + def create_wordline_driver(self): """ Create the Wordline Driver """ - - self.wordline_driver_inst = self.add_inst(name="wordline_driver", - mod=self.wordline_driver) + + self.wordline_driver_array_inst = self.add_inst(name="wordline_driver", + mod=self.wordline_driver_array) temp = [] for row in range(self.num_rows): @@ -150,11 +206,23 @@ class port_address(design.design): """ row_decoder_offset = vector(0, 0) - wordline_driver_offset = vector(self.row_decoder.width, 0) - self.wordline_driver_inst.place(wordline_driver_offset) self.row_decoder_inst.place(row_decoder_offset) + + wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0) + self.wordline_driver_array_inst.place(wordline_driver_array_offset) + + x_offset = self.wordline_driver_array_inst.rx() - self.rbl_driver.width - self.m1_pitch + if self.port == 0: + rbl_driver_offset = vector(x_offset, + 0) + self.rbl_driver_inst.place(rbl_driver_offset, "MX") + else: + rbl_driver_offset = vector(x_offset, + self.wordline_driver_array.height) + self.rbl_driver_inst.place(rbl_driver_offset) + # Pass this up self.predecoder_height = self.row_decoder.predecoder_height self.height = self.row_decoder.height - self.width = self.wordline_driver_inst.rx() + self.width = self.wordline_driver_array_inst.rx() diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py index 7a1bf8d1..45f3ef33 100644 --- a/compiler/modules/wordline_buffer_array.py +++ b/compiler/modules/wordline_buffer_array.py @@ -109,12 +109,13 @@ class wordline_buffer_array(design.design): def place_drivers(self): for row in range(self.rows): + # These are flipped since we always start with an RBL on the bottom if (row % 2): - y_offset = self.wl_driver.height * (row + 1) - inst_mirror = "MX" - else: y_offset = self.wl_driver.height * row inst_mirror = "R0" + else: + y_offset = self.wl_driver.height * (row + 1) + inst_mirror = "MX" offset = [0, y_offset] diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index ac0a0cb2..c334404e 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -44,7 +44,7 @@ class wordline_driver_array(design.design): self.place_drivers() self.route_layout() self.route_vdd_gnd() - self.offset_all_coordinates() + self.offset_x_coordinates() self.add_boundary() self.DRC_LVS() @@ -60,8 +60,10 @@ class wordline_driver_array(design.design): self.add_pin("gnd", "GROUND") def add_modules(self): + self.wl_driver = factory.create(module_type="wordline_driver", - size=self.cols) + cols=self.cols) + self.add_mod(self.wl_driver) def route_vdd_gnd(self): diff --git a/compiler/options.py b/compiler/options.py index 8d8e3b42..8de3b910 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -139,6 +139,7 @@ class options(optparse.Values): bank_select = "bank_select" bitcell_array = "bitcell_array" bitcell = "bitcell" + buf_dec = "pbuf" column_mux_array = "single_level_column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index e504b89e..c2398d5f 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -96,4 +96,4 @@ class pbuf(pgate.pgate): offset=a_pin.center(), width=a_pin.width(), height=a_pin.height()) - \ No newline at end of file + diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index a8ca76f8..5f110249 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -18,9 +18,9 @@ class wordline_driver(design.design): This is an AND (or NAND) with configurable drive strength to drive the wordlines. It is matched to the bitcell height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, cols=1, height=None): debug.info(1, "Creating wordline_driver {}".format(name)) - self.add_comment("size: {}".format(size)) + self.add_comment("cols: {}".format(cols)) super().__init__(name) if height is None: @@ -28,7 +28,7 @@ class wordline_driver(design.design): self.height = b.height else: self.height = height - self.size = size + self.cols = cols self.create_netlist() if not OPTS.netlist_only: @@ -42,10 +42,25 @@ class wordline_driver(design.design): def create_modules(self): self.nand = factory.create(module_type="nand2_dec", height=self.height) - - self.driver = factory.create(module_type="inv_dec", - size=self.size, - height=self.nand.height) + + try: + local_array_size = OPTS.local_array_size + driver_size = int(self.cols / local_array_size) + except AttributeError: + local_array_size = 0 + # Defautl to FO4 + driver_size = int(self.cols / 4) + + # The polarity must be switched if we have a hierarchical wordline + # to compensate for the local array inverters + if local_array_size > 0: + self.driver = factory.create(module_type="buf_dec", + size=driver_size, + height=self.nand.height) + else: + self.driver = factory.create(module_type="inv_dec", + size=driver_size, + height=self.nand.height) self.add_mod(self.nand) self.add_mod(self.driver) From 6f06bb9dd511fc8b39f072b374bb08a1b8695fa1 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 28 Sep 2020 11:30:21 -0700 Subject: [PATCH 2/6] Create sized RBL WL driver in port_address --- compiler/modules/bank.py | 9 ++++----- compiler/modules/global_bitcell_array.py | 1 + compiler/modules/local_bitcell_array.py | 4 ++-- compiler/modules/port_address.py | 13 ++++++++----- compiler/pgates/wordline_driver.py | 2 +- compiler/tests/04_wordline_driver_test.py | 2 +- compiler/tests/18_port_address_1rw_1r_test.py | 4 ++-- compiler/tests/18_port_address_test.py | 4 ++-- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index e4ac72bc..beb60501 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -372,9 +372,7 @@ class bank(design.design): cols=self.num_cols + self.num_spare_cols, rows=self.num_rows, port=port)) - self.add_mod(self.port_address[port]) - - self.num_rbl = len(self.all_ports) + self.add_mod(self.port_address[port]) try: local_array_size = OPTS.local_array_size @@ -875,8 +873,9 @@ class bank(design.design): def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ - driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] - for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): + driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"] + rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port] + for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]): # The mid guarantees we exit the input cell to the right. driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pos = driver_wl_pin.lc() diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py index 57f4d716..ce35455a 100644 --- a/compiler/modules/global_bitcell_array.py +++ b/compiler/modules/global_bitcell_array.py @@ -267,3 +267,4 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): for inst in self.local_insts: offsets.extend(inst.lx() + x for x in inst.mod.get_column_offsets()) return offsets + diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index d552d77a..af1462c3 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -207,9 +207,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): y_offset = in_pin.cy() if port == 0: - y_offset -= 1.5 * self.m3_pitch + y_offset -= 2 * self.m3_pitch else: - y_offset += 1.5 * self.m3_pitch + y_offset += 2 * self.m3_pitch self.add_layout_pin_segment_center(text=wl_name, layer="m3", diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 3f925851..b30e84cf 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -40,7 +40,7 @@ class port_address(design.design): self.add_modules() self.create_row_decoder() self.create_wordline_driver() - self.create_rbl_driver() + self.create_rbl_driver() def create_layout(self): if "li" in layer: @@ -79,7 +79,8 @@ class port_address(design.design): self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "gnd") - self.copy_power_pins(self.rbl_driver_inst, "vdd") + rbl_vdd_pin = self.rbl_driver_inst.get_pin("vdd") + self.add_power_pin("vdd", rbl_vdd_pin.lc()) def route_pins(self): for row in range(self.addr_size): @@ -111,17 +112,19 @@ class port_address(design.design): # Route the RBL from the enable input en_pin = self.wordline_driver_array_inst.get_pin("en") + en_pos = en_pin.center() rbl_in_pin = self.rbl_driver_inst.get_pin("A") rbl_in_pos = rbl_in_pin.center() + mid_pos = vector(en_pin.cx(), rbl_in_pin.cy()) - self.add_path(rbl_in_pin.layer, [rbl_in_pos, mid_pos]) self.add_via_stack_center(from_layer=rbl_in_pin.layer, to_layer=en_pin.layer, - offset=mid_pos) + offset=rbl_in_pos) + self.add_path(en_pin.layer, [rbl_in_pos, mid_pos, en_pos]) self.add_layout_pin_segment_center(text="wl_en", layer=en_pin.layer, start=mid_pos, - end=en_pin.center()) + end=en_pos) def add_modules(self): diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index 5f110249..e0dbf1b2 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -18,7 +18,7 @@ class wordline_driver(design.design): This is an AND (or NAND) with configurable drive strength to drive the wordlines. It is matched to the bitcell height. """ - def __init__(self, name, cols=1, height=None): + def __init__(self, name, cols, height=None): debug.info(1, "Creating wordline_driver {}".format(name)) self.add_comment("cols: {}".format(cols)) super().__init__(name) diff --git a/compiler/tests/04_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py index ada65db7..74cfb7a7 100755 --- a/compiler/tests/04_wordline_driver_test.py +++ b/compiler/tests/04_wordline_driver_test.py @@ -25,7 +25,7 @@ class wordline_driver_test(openram_test): # check wordline driver for single port debug.info(2, "Checking driver") - tx = factory.create(module_type="wordline_driver") + tx = factory.create(module_type="wordline_driver", cols=8) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index caf2cb96..42a46614 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -27,11 +27,11 @@ class port_address_1rw_1r_test(openram_test): globals.setup_bitcell() debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16) + a = factory.create("port_address", cols=16, rows=16, port=0) self.local_check(a) debug.info(1, "Port address 256 rows") - a = factory.create("port_address", cols=256, rows=256) + a = factory.create("port_address", cols=256, rows=256, port=1) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index 11da333e..94feedb2 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -21,11 +21,11 @@ class port_address_test(openram_test): globals.init_openram(config_file) debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16) + a = factory.create("port_address", cols=16, rows=16, port=0) self.local_check(a) debug.info(1, "Port address 512 rows") - a = factory.create("port_address", cols=256, rows=512) + a = factory.create("port_address", cols=256, rows=512, port=0) self.local_check(a) globals.end_openram() From d65eb16513e5137aa6ad615fc04416a571aaa5bc Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 28 Sep 2020 12:24:55 -0700 Subject: [PATCH 3/6] Zjog the WL enable. Min driver is 1. --- compiler/modules/port_address.py | 14 ++++++-------- compiler/options.py | 3 +++ compiler/pgates/wordline_driver.py | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index b30e84cf..8af6a756 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -116,15 +116,13 @@ class port_address(design.design): rbl_in_pin = self.rbl_driver_inst.get_pin("A") rbl_in_pos = rbl_in_pin.center() - mid_pos = vector(en_pin.cx(), rbl_in_pin.cy()) self.add_via_stack_center(from_layer=rbl_in_pin.layer, to_layer=en_pin.layer, offset=rbl_in_pos) - self.add_path(en_pin.layer, [rbl_in_pos, mid_pos, en_pos]) - self.add_layout_pin_segment_center(text="wl_en", - layer=en_pin.layer, - start=mid_pos, - end=en_pos) + self.add_zjog(en_pin.layer, rbl_in_pos, en_pos) + self.add_layout_pin_rect_center(text="wl_en", + layer=en_pin.layer, + offset=rbl_in_pos) def add_modules(self): @@ -139,11 +137,11 @@ class port_address(design.design): try: local_array_size = OPTS.local_array_size - driver_size = int(self.num_cols / local_array_size) + driver_size = max(int(self.num_cols / local_array_size), 1) except AttributeError: local_array_size = 0 # Defautl to FO4 - driver_size = int(self.num_cols / 4) + driver_size = max(int(self.num_cols / 4), 1) # The polarity must be switched if we have a hierarchical wordline # to compensate for the local array inverters diff --git a/compiler/options.py b/compiler/options.py index 8de3b910..b9be3999 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -30,6 +30,9 @@ class options(optparse.Values): num_r_ports = 0 num_w_ports = 0 + # By default, use local arrays with a max fanout of 16 + #local_array_size = 16 + # Write mask size, default will be overwritten with word_size if not user specified write_size = None diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index e0dbf1b2..6abf7b20 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -45,11 +45,11 @@ class wordline_driver(design.design): try: local_array_size = OPTS.local_array_size - driver_size = int(self.cols / local_array_size) + driver_size = max(int(self.cols / local_array_size), 1) except AttributeError: local_array_size = 0 # Defautl to FO4 - driver_size = int(self.cols / 4) + driver_size = max(int(self.cols / 4), 1) # The polarity must be switched if we have a hierarchical wordline # to compensate for the local array inverters From 5ab0d0177968b49b179b72a30925b71a0da53d45 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 28 Sep 2020 12:48:37 -0700 Subject: [PATCH 4/6] Remove zjog and go with L shape. --- compiler/modules/port_address.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 8af6a756..33cd4474 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -115,11 +115,12 @@ class port_address(design.design): en_pos = en_pin.center() rbl_in_pin = self.rbl_driver_inst.get_pin("A") rbl_in_pos = rbl_in_pin.center() - + + mid_pos = vector(en_pos.x, rbl_in_pos.y) self.add_via_stack_center(from_layer=rbl_in_pin.layer, to_layer=en_pin.layer, offset=rbl_in_pos) - self.add_zjog(en_pin.layer, rbl_in_pos, en_pos) + self.add_path(en_pin.layer, [rbl_in_pos, mid_pos, en_pos]) self.add_layout_pin_rect_center(text="wl_en", layer=en_pin.layer, offset=rbl_in_pos) From 9c6d8d7aed4ef4e7e70261cc2b97037a23035d59 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 28 Sep 2020 13:16:03 -0700 Subject: [PATCH 5/6] Zjob to bottom. --- compiler/modules/port_address.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 33cd4474..f7e85afd 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -112,15 +112,20 @@ class port_address(design.design): # Route the RBL from the enable input en_pin = self.wordline_driver_array_inst.get_pin("en") - en_pos = en_pin.center() + if self.port == 0: + en_pos = en_pin.bc() + else: + en_pos = en_pin.uc() rbl_in_pin = self.rbl_driver_inst.get_pin("A") rbl_in_pos = rbl_in_pin.center() - mid_pos = vector(en_pos.x, rbl_in_pos.y) self.add_via_stack_center(from_layer=rbl_in_pin.layer, to_layer=en_pin.layer, offset=rbl_in_pos) - self.add_path(en_pin.layer, [rbl_in_pos, mid_pos, en_pos]) + self.add_zjog(layer=en_pin.layer, + start=rbl_in_pos, + end=en_pos, + first_direction="V") self.add_layout_pin_rect_center(text="wl_en", layer=en_pin.layer, offset=rbl_in_pos) From 70c90ca7fb686d0eb4dd29d10f055103f8fcef75 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 28 Sep 2020 14:49:33 -0700 Subject: [PATCH 6/6] Replica bitcell array bbox to include unused WL gnd pins. --- compiler/modules/bank.py | 43 +++++++++++++++-------- compiler/modules/replica_bitcell_array.py | 31 ++++++++++------ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index beb60501..183d646a 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -845,11 +845,11 @@ class bank(design.design): self.route_port_address_in(port) if port % 2: - self.route_port_address_right(port) + self.route_port_address_out(port, "right") else: - self.route_port_address_left(port) + self.route_port_address_out(port, "left") - def route_port_address_left(self, port): + def route_port_address_out(self, port, side="left"): """ Connecting Wordline driver output to Bitcell WL connection """ driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"] @@ -857,18 +857,32 @@ class bank(design.design): for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]): # The mid guarantees we exit the input cell to the right. driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) - driver_wl_pos = driver_wl_pin.rc() + if side == "left": + driver_wl_pos = driver_wl_pin.rc() + else: + driver_wl_pos = driver_wl_pin.lc() bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name) - bitcell_wl_pos = bitcell_wl_pin.lc() - mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0) - mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1) - self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2]) - # Via is non-preferred direction because mid1->mid2 is non-preferred direction - self.add_via_stack_center(from_layer=driver_wl_pin.layer, - to_layer=bitcell_wl_pin.layer, - offset=mid2, - directions="nonpref") - self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) + + if side == "left": + bitcell_wl_pos = bitcell_wl_pin.lc() + port_address_pos = self.port_address_inst[port].rx() + bitcell_array_pos = self.bitcell_array_inst.lx() + else: + bitcell_wl_pos = bitcell_wl_pin.rc() + port_address_pos = self.port_address_inst[port].lx() + bitcell_array_pos = self.bitcell_array_inst.rx() + + mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0) + mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) + if driver_wl_pin.layer != bitcell_wl_pin.layer: + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2]) + self.add_via_stack_center(from_layer=driver_wl_pin.layer, + to_layer=bitcell_wl_pin.layer, + offset=mid2) + self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) + else: + self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ @@ -888,6 +902,7 @@ class bank(design.design): to_layer=bitcell_wl_pin.layer, offset=mid2) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) + def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """ diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 6c2d54a7..4e1c4df1 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -305,14 +305,21 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): def create_layout(self): + # We will need unused wordlines grounded, so we need to know their layer + pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) + pin_layer = pin.layer + self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) + self.unused_offset = vector(self.unused_pitch, 0) + + # Add extra width on the left and right for the unused WLs self.height = (self.row_size + self.extra_rows) * self.dummy_row.height - self.width = (self.column_size + self.extra_cols) * self.cell.width + self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) - # Everything is computed with the main array at (0, 0) to start - self.bitcell_array_inst.place(offset=[0, 0]) + # Everything is computed with the main array at (self.unused_pitch, 0) to start + self.bitcell_array_inst.place(offset=self.unused_offset) self.add_replica_columns() @@ -356,7 +363,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset self.replica_col_insts[port].place(offset) # Grow to the right of the bitcell array, array outward for bit, port in enumerate(self.right_rbl): @@ -367,11 +374,13 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): # Add the dummy rows even if we aren't adding the replica column to this bitcell array # These grow up, toward the array for bit in range(self.rbl[0]): - self.dummy_row_replica_insts[bit].place(offset=self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2), + dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset + self.dummy_row_replica_insts[bit].place(offset=dummy_offset, mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") # These grow up, away from the array for bit in range(self.rbl[1]): - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), + dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() + self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, mirror="MX" if bit % 2 else "R0") def add_end_caps(self): @@ -386,12 +395,12 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): # FIXME: These depend on the array size itself # Far bottom dummy row (first row below array IS flipped) flip_dummy = (self.rbl[0] + 1) % 2 - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset self.dummy_row_insts[0].place(offset=dummy_row_offset, mirror="MX" if flip_dummy else "R0") # Far left dummy col # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset self.dummy_col_insts[0].place(offset=dummy_col_offset) # Far right dummy col # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array @@ -495,13 +504,13 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): def ground_pin(self, inst, name): pin = inst.get_pin(name) pin_layer = pin.layer - layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) + left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) # Place the pins a track outside of the array - left_loc = left_pin_loc - vector(layer_pitch, 0) - right_loc = right_pin_loc + vector(layer_pitch, 0) + left_loc = left_pin_loc - vector(self.unused_pitch, 0) + right_loc = right_pin_loc + vector(self.unused_pitch, 0) self.add_power_pin("gnd", left_loc, directions=("H", "H")) self.add_power_pin("gnd", right_loc, directions=("H", "H"))