From bbf2cd2913cecc9bc12d0c8c7cb5b92606050da7 Mon Sep 17 00:00:00 2001 From: Jacob Walker Date: Mon, 27 Feb 2023 14:48:10 -0800 Subject: [PATCH] Changes for test generation and simulation --- compiler/base/hierarchy_layout.py | 7 +- compiler/modules/pinv_dec.py | 18 +- compiler/modules/rom_array_gnd_tap.py | 39 --- compiler/modules/rom_base_array.py | 10 +- compiler/modules/rom_base_bank.py | 248 +++++++++++------- compiler/modules/rom_control_logic.py | 40 +-- compiler/modules/rom_decoder.py | 19 +- compiler/modules/rom_wordline_driver_array.py | 38 ++- compiler/tests/05_rom_base_bank_1kB_test.py | 5 +- compiler/tests/05_rom_base_bank_2kB_test.py | 39 +++ compiler/tests/05_rom_base_bank_4kB_test.py | 39 +++ compiler/tests/05_rom_base_bank_8kB_test.py | 39 +++ 12 files changed, 363 insertions(+), 178 deletions(-) delete mode 100644 compiler/modules/rom_array_gnd_tap.py create mode 100644 compiler/tests/05_rom_base_bank_2kB_test.py create mode 100644 compiler/tests/05_rom_base_bank_4kB_test.py create mode 100644 compiler/tests/05_rom_base_bank_8kB_test.py diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index eb586a18..c1227cc2 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -787,13 +787,15 @@ class layout(): end=bot_pos) - def connect_col_pins(self, layer, pins, name=None, full=False): + def connect_col_pins(self, layer, pins, name=None, full=False, round=False, directions="pref"): """ Connects top/bot columns that are aligned. """ bins = {} for pin in pins: x = pin.cx() + if round: + x = round_to_grid(x) try: bins[x].append(pin) except KeyError: @@ -819,7 +821,8 @@ class layout(): self.add_via_stack_center(from_layer=pin.layer, to_layer=layer, offset=pin.center(), - min_area=True) + min_area=True, + directions=directions) if name: self.add_layout_pin_segment_center(text=name, diff --git a/compiler/modules/pinv_dec.py b/compiler/modules/pinv_dec.py index eda634f5..1ee75266 100644 --- a/compiler/modules/pinv_dec.py +++ b/compiler/modules/pinv_dec.py @@ -20,7 +20,7 @@ class pinv_dec(pinv): Other stuff is the same (netlist, sizes, etc.) """ - def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True, flip_io=False): debug.info(2, "creating pinv_dec structure {0} with size of {1}".format(name, @@ -37,7 +37,7 @@ class pinv_dec(pinv): self.supply_layer = "m1" else: self.supply_layer = "m2" - + self.flip_io=flip_io super().__init__(name, size, beta, self.cell_height, add_wells) def determine_tx_mults(self): @@ -76,9 +76,14 @@ class pinv_dec(pinv): pmos_gate_pos = pmos_gate_pin.lc() self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) + # Center is completely symmetric. contact_width = self.poly_contact.width - contact_offset = nmos_gate_pin.lc() \ + if self.flip_io: + contact_offset = pmos_gate_pin.rc() \ + + vector(self.poly_extend_active + 0.6 * contact_width, 0) + else: + contact_offset = nmos_gate_pin.lc() \ - vector(self.poly_extend_active + 0.5 * contact_width, 0) via = self.add_via_stack_center(from_layer="poly", to_layer=self.route_layer, @@ -107,8 +112,11 @@ class pinv_dec(pinv): height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active) if "nwell" in layer: - ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active]) + poly_offset = 0 + if self.flip_io: + poly_offset = 1.2 * self.poly_contact.width + ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - vector(0, poly_offset) + ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active + poly_offset]) self.add_rect(layer="nwell", offset=ll, width=ur.x - ll.x, diff --git a/compiler/modules/rom_array_gnd_tap.py b/compiler/modules/rom_array_gnd_tap.py deleted file mode 100644 index bbd7a06d..00000000 --- a/compiler/modules/rom_array_gnd_tap.py +++ /dev/null @@ -1,39 +0,0 @@ - - -from openram.base import design -from openram.base import vector -from openram.sram_factory import factory - -class rom_array_gnd_tap(design): - - def __init__(self, name, length, cell_name=None, prop=None): - super().__init__(name, cell_name, prop) - self.length = length - self.create_layout() - - def create_layout(self): - - self.add_cell() - self.add_boundary() - self.place_gnd_rail() - - - def add_boundary(self): - self.height = self.dummy.height - self.width = self.dummy.width - super().add_boundary() - - def add_cell(self): - self.dummy = factory.create(module_type="rom_dummy_cell") - - def place_gnd_rail(self): - rail_start = vector(-self.dummy.width / 2 ,0) - rail_end = vector(self.dummy.width * self.length, 0) - - self.add_layout_pin_rect_ends( name="gnd", - layer="m1", - start=rail_start, - end=rail_end) - - - diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py index 4e228660..c123af06 100644 --- a/compiler/modules/rom_base_array.py +++ b/compiler/modules/rom_base_array.py @@ -58,16 +58,17 @@ class rom_base_array(bitcell_base_array): self.place_precharge() self.place_wordline_contacts() self.place_bitline_contacts() - self.add_boundary() self.route_precharge() + self.add_boundary() + self.place_rails() self.connect_taps() + def add_boundary(self): ll = self.find_lowest_coords() - bottom_offset = - self.zero_cell.nmos.end_to_contact + self.precharge_inst.offset.y m1_offset = self.m1_width self.translate_all(vector(0, ll.y + 0.5 * m1_offset)) ur = self.find_highest_coords() @@ -209,7 +210,7 @@ class rom_base_array(bitcell_base_array): prechrg_pins.append("precharge") prechrg_pins.append("vdd") - self.precharge_inst = self.add_inst(name="decode_array_precharge", mod=self.precharge_array) + self.precharge_inst = self.add_inst(name="bitcell_array_precharge", mod=self.precharge_array) self.connect_inst(prechrg_pins) @@ -390,6 +391,9 @@ class rom_base_array(bitcell_base_array): self.add_path(layer="m1", coordinates=[start, mid1, mid2, end]) + self.add_layout_pin_rect_center(text="precharge_r", layer="m1", offset=mid1) + + def connect_taps(self): array_pins = [self.tap_list[i].get_pin("poly_tap") for i in range(len(self.tap_list))] diff --git a/compiler/modules/rom_base_bank.py b/compiler/modules/rom_base_bank.py index 27fa40f4..fedb27f0 100644 --- a/compiler/modules/rom_base_bank.py +++ b/compiler/modules/rom_base_bank.py @@ -17,7 +17,7 @@ class rom_base_bank(design): def __init__(self, strap_spacing=0, data_file=None, name="", word_size=2): super().__init__(name=name) self.word_size = word_size * 8 - self.read_binary(word_size=word_size, data_file=data_file) + self.read_binary(word_size=word_size, data_file=data_file, scramble_bits=True, endian="little") self.num_outputs = self.rows self.num_inputs = ceil(log(self.rows, 2)) @@ -48,7 +48,7 @@ class rom_base_bank(design): sets the row and column size based on the size of binary input, tries to keep array as square as possible, """ - def read_binary(self, data_file, word_size=2, endian="big"): + def read_binary(self, data_file, word_size=2, endian="big", scramble_bits=False): # Read data as hexidecimal text file hex_file = open(data_file, 'r') hex_data = hex_file.read() @@ -72,25 +72,56 @@ class rom_base_bank(design): bits_per_row = self.words_per_row * word_size * 8 + self.cols = bits_per_row + self.rows = int(num_words / (self.words_per_row)) chunked_data = [] for i in range(0, len(bin_data), bits_per_row): - word = bin_data[i:i + bits_per_row] - if len(word) < bits_per_row: - word = [0] * (bits_per_row - len(word)) + word - chunked_data.append(word) - - if endian == "big": - chunked_data.reverse() - + row_data = bin_data[i:i + bits_per_row] + if len(row_data) < bits_per_row: + row_data = [0] * (bits_per_row - len(row_data)) + row_data + chunked_data.append(row_data) + + + # if endian == "big": + + self.data = chunked_data - self.cols = bits_per_row - self.rows = int(num_words / (self.words_per_row)) - debug.info(1, "Read rom binary: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, num_words, self.cols, self.rows, self.words_per_row)) + if scramble_bits: + scrambled_chunked = [] + for row_data in chunked_data: + scambled_data = [] + for bit in range(self.word_size): + for word in range(self.words_per_row): + scambled_data.append(row_data[bit + word * self.word_size]) + scrambled_chunked.append(scambled_data) + self.data = scrambled_chunked + + + + # self.data.reverse() + + debug.info(1, "Read rom binary: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, num_words, self.cols, self.rows, self.words_per_row)) + # self.print_data(chunked_data) + # print("Scrambled") + # self.print_data(scrambled_chunked) + + # self.print_word(self.data, 0, 0) + # self.print_word(self.data, 0, 1) + # self.print_word(self.data, 0, 2) + # self.print_word(self.data, 0, 3) # print("hex: {0}, binary: {1}, chunked: {2}".format(hex_data, bin_data, chunked_data)) + def print_data(self, data_array): + for row in range(len(data_array)): + print(data_array[row]) + def print_word(self, data_array, bl, word): + for bit in range(self.word_size): + print(data_array[bl][word + self.words_per_row * bit], end =" ") + print("") + def create_netlist(self): self.add_modules() self.add_pins() @@ -147,6 +178,20 @@ class rom_base_bank(design): def add_modules(self): print("Creating bank modules") + # TODO: provide technology-specific calculation of these parameters + # in sky130 the address control buffer is composed of 2 size 2 NAND gates, + # with a beta of 3, each of these gates has gate capacitance of 2 min sized inverters, therefor a load of 4 + addr_control_buffer_effort = 4 + # a single min sized nmos makes up 1/4 of the input capacitance of a min sized inverter + bitcell_effort = 0.25 + + # Takes into account inverter sizing + wordline_effort = bitcell_effort * 0.5 + + # a single min sized pmos plus a single min sized nmos have approximately half the gate capacitance of a min inverter + # an additional 0.2 accounts for the long wire capacitance and add delay to gaurentee the read timing + precharge_cell_effort = 0.5 + 0.2 + self.array = factory.create(module_type="rom_base_array", cols=self.cols, rows=self.rows, @@ -163,7 +208,7 @@ class rom_base_bank(design): num_outputs=self.rows, strap_spacing=self.strap_spacing, route_layer=self.route_layer, - cols=self.cols) + fanout=(self.cols)*wordline_effort ) self.column_mux = factory.create(module_type="rom_column_mux_array", @@ -178,18 +223,27 @@ class rom_base_bank(design): num_outputs=self.words_per_row, strap_spacing=self.strap_spacing, route_layer=self.route_layer, - cols=1, + fanout=2, invert_outputs=True ) self.control_logic = factory.create(module_type="rom_control_logic", - num_outputs=(self.rows + self.cols + self.words_per_row) * 0.5, - clk_fanout=(self.col_bits + self.row_bits) * 2, + num_outputs=(self.cols + self.words_per_row * precharge_cell_effort) \ + + (addr_control_buffer_effort * self.col_bits), + clk_fanout=(self.row_bits * addr_control_buffer_effort) + (precharge_cell_effort * self.rows), height=self.column_decode.height ) - self.output_buffer = factory.create(module_type="rom_wordline_driver_array", + self.bitline_inv = factory.create(module_type="rom_wordline_driver_array", + module_name="rom_bitline_inverter", + rows=self.cols, + fanout=4, + invert_outputs=True, + tap_spacing=0, + flip_io=True) + self.output_inv = factory.create(module_type="rom_wordline_driver_array", module_name="rom_output_buffer", rows=self.word_size, - cols=4) + fanout=4, + invert_outputs=True) def create_instances(self): @@ -206,18 +260,21 @@ class rom_base_bank(design): select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)] + bitline_bar = ["bl_b_{}".format(bl) for bl in range(self.cols)] pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)] outputs = ["rom_out_{}".format(bl) for bl in range(self.word_size)] array_pins = bitlines + wordlines + prechrg + vdd + gnd - row_decode_pins = addr_msb + wordlines + prechrg + clk + vdd + gnd - col_decode_pins = addr_lsb + select_lines + prechrg + clk + vdd + gnd + row_decode_pins = addr_msb + wordlines + clk + clk + vdd + gnd + col_decode_pins = addr_lsb + select_lines + prechrg + prechrg + vdd + gnd - col_mux_pins = bitlines + select_lines + pre_buf_outputs + gnd + col_mux_pins = bitline_bar + select_lines + pre_buf_outputs + gnd - output_buffer_pins = pre_buf_outputs + outputs + vdd + gnd + bitline_inv_pins = bitlines + bitline_bar + vdd + gnd + + output_buf_pins = pre_buf_outputs + outputs + vdd + gnd self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array) self.connect_inst(array_pins) @@ -234,14 +291,19 @@ class rom_base_bank(design): self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode) self.connect_inst(col_decode_pins) - self.output_buf_inst = self.add_inst(name="rom_output_buffer", mod=self.output_buffer) - self.connect_inst(output_buffer_pins) + self.bitline_inv_inst = self.add_inst(name="rom_bitline_inverter", mod=self.bitline_inv) + self.connect_inst(bitline_inv_pins) + + self.output_inv_inst = self.add_inst(name="rom_output_inverter", mod=self.output_inv) + self.connect_inst(output_buf_pins) + def place_instances(self): self.place_row_decoder() self.place_data_array() + self.place_bitline_inverter() self.place_col_mux() self.place_col_decoder() self.place_control_logic() @@ -249,7 +311,7 @@ class rom_base_bank(design): def place_row_decoder(self): - self.decode_offset = vector(0, self.control_inst.height - self.decode_array.control_array.height) + self.decode_offset = vector(0, self.control_inst.height ) self.decode_inst.place(offset=self.decode_offset) def place_data_array(self): @@ -263,11 +325,18 @@ class rom_base_bank(design): array_align = self.decode_inst.get_pin("wl_0").cy() - self.array_inst.get_pin("wl_0_0").cy() self.array_inst.place(offset=(self.array_offset + vector(0, array_align))) + def place_bitline_inverter(self): + self.bitline_inv_inst.place(offset=[0,0], rotate=90) + inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch + + inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx() + self.inv_offset = vector(inv_x_offset, inv_y_offset) + self.bitline_inv_inst.place(offset=self.inv_offset, rotate=90) + def place_control_logic(self): - - self.control_offset = vector(self.control_inst.width + self.decode_array.control_array.width + 2 * (self.route_layer_pitch + self.route_layer_width), self.col_decode_inst.by() + self.control_logic.height) - self.control_inst.place(offset=self.control_offset, mirror="XY") + self.control_offset = vector(self.col_decode_inst.lx() - self.control_inst.width - 3 * self.m1_pitch, self.decode_inst.by() - self.control_logic.height - self.m1_pitch) + self.control_inst.place(offset=self.control_offset) def place_col_decoder(self): col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy() @@ -275,28 +344,18 @@ class rom_base_bank(design): self.col_decode_inst.place(offset=self.col_decode_offset) def place_col_mux(self): - mux_y_offset = self.array_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch + mux_y_offset = self.bitline_inv_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch - mux_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.mux_inst.get_pin("bl_0").cx() + mux_x_offset = self.bitline_inv_inst.get_pin("out_0").cx() - self.mux_inst.get_pin("bl_0").cx() self.mux_offset = vector(mux_x_offset, mux_y_offset) self.mux_inst.place(offset=self.mux_offset) def place_output_buffer(self): - output_x = self.col_decode_inst.rx() + self.output_buf_inst.height - output_y = self.col_decode_inst.by() + self.output_buf_inst.width - self.output_buf_offset = vector(output_x, output_y) - self.output_buf_inst.place(offset=self.output_buf_offset, rotate=270) + output_x = self.col_decode_inst.rx() + self.output_inv_inst.height + output_y = self.mux_inst.by() - self.word_size * self.m1_pitch + self.output_inv_offset = vector(output_x, output_y) + self.output_inv_inst.place(offset=self.output_inv_offset, rotate=270) - # def create_wl_bus(self): - # bus_x = self.decode_inst.width + ( drc["minwidth_{}".format(self.bus_layer)] + 1.5 * drc["{0}_to_{0}".format(self.bus_layer)] ) - # bus_y = self.array_inst.by() + self.bus_layer_pitch + self.bus_layer_width - # self.wl_interconnects = [] - - # for wl in range(self.rows): - # self.wl_interconnects.append("wl_interconnect_{}".format(wl)) - - # self.wl_bus = self.create_vertical_bus(self.bus_layer, vector(bus_x, bus_y), self.wl_interconnects, self.decode_inst.uy() - self.array_inst.by() ) - def route_decode_outputs(self): # for the row decoder route_pins = [self.array_inst.get_pin("wl_0_{}".format(wl)) for wl in range(self.rows)] @@ -325,88 +384,105 @@ class rom_base_bank(design): start = vector(wl_bus_wire.cx(), end.y) self.add_segment_center(self.interconnect_layer, start, end) - self.add_via_stack_center(start, self.route_layer, self.interconnect_layer ) def route_precharge(self): prechrg_control = self.control_inst.get_pin("prechrg") - row_decode_prechrg = self.decode_inst.get_pin("precharge") - col_decode_prechrg = self.col_decode_inst.get_pin("precharge") + + col_decode_prechrg = self.col_decode_inst.get_pin("precharge_r") + col_decode_clk = self.col_decode_inst.get_pin("clk") array_prechrg = self.array_inst.get_pin("precharge") # Route precharge signal to the row decoder - end = vector(row_decode_prechrg.cx() - 0.5 * self.interconnect_layer_width, prechrg_control.cy()) + # end = vector(row_decode_prechrg.cx() - 0.5 * self.interconnect_layer_width, prechrg_control.cy()) - self.add_segment_center(self.interconnect_layer, prechrg_control.center(), end) + # self.add_segment_center(self.interconnect_layer, prechrg_control.center(), end) - start = end + vector(0.5 * self.interconnect_layer_width, 0) - self.add_segment_center(self.interconnect_layer, start, row_decode_prechrg.center()) + # start = end + vector(0.5 * self.interconnect_layer_width, 0) + # self.add_segment_center(self.interconnect_layer, start, row_decode_prechrg.center()) self.add_via_stack_center(from_layer=self.route_stack[0], to_layer=prechrg_control.layer, offset=prechrg_control.center()) # Route precharge to col decoder - start = row_decode_prechrg.center() - vector(0, self.route_layer_pitch + 2 * self.route_layer_width) - mid = vector(col_decode_prechrg.cx(), start.y) - end = vector(col_decode_prechrg.cx(), 0.5 * (self.col_decode_inst.uy() + mid.y) ) - self.add_path(self.route_stack[0], [start, mid, end]) + start = prechrg_control.center() + mid1 = vector(self.control_inst.rx(), prechrg_control.cy()) + mid2 = vector(self.control_inst.rx(), col_decode_prechrg.cy()) + end = col_decode_prechrg.center() + self.add_path(self.route_stack[0], [start, mid1, mid2, end]) self.add_via_stack_center(from_layer=self.route_stack[0], to_layer=col_decode_prechrg.layer, offset=end) - self.add_segment_center(col_decode_prechrg.layer, end, col_decode_prechrg.center()) + + start = mid1 + mid1 = vector(self.control_inst.rx(), start.y) + mid2 = vector(mid1.x, col_decode_clk.cy()) + end = col_decode_clk.center() + self.add_path(self.route_stack[0], [start, mid1, mid2, end]) + + # self.add_segment_center(col_decode_prechrg.layer, end, col_decode_prechrg.center()) # Route precharge to main array - end = vector(col_decode_prechrg.cx(), array_prechrg.cy()) - self.add_segment_center(self.route_stack[0], array_prechrg.center(), end) + # end = vector(col_decode_prechrg.cx(), array_prechrg.cy()) + mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() ) + self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()]) def route_clock(self): clk_out = self.control_inst.get_pin("clk_out") row_decode_clk = self.decode_inst.get_pin("clk") - col_decode_clk = self.col_decode_inst.get_pin("clk") + self.add_via_stack_center(from_layer=self.route_stack[2], to_layer=clk_out.layer, offset=clk_out.center()) # Route clock to row decoder - end = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) - self.add_path(self.route_stack[2], [clk_out.center(), end]) + mid = vector(self.control_inst.rx() + self.m1_pitch, clk_out.cy()) + + addr_control_clk = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) + row_decode_prechrg = self.decode_inst.get_pin("precharge") + + self.add_path(self.route_stack[2], [clk_out.center(), mid, addr_control_clk, row_decode_prechrg.center()]) self.add_via_stack_center(from_layer=self.route_stack[2], to_layer=row_decode_clk.layer, - offset=end) + offset=addr_control_clk) - self.add_segment_center(row_decode_clk.layer, end, row_decode_clk.rc()) + self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc()) # Route clock to column decoder - end = col_decode_clk.lc() - vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) - self.add_path(self.route_stack[2], [clk_out.center(), end]) + # end = col_decode_clk.lc() - vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) + # self.add_path(self.route_stack[2], [clk_out.center(), end]) - self.add_via_stack_center(from_layer=self.route_stack[2], - to_layer=row_decode_clk.layer, - offset=end) + # self.add_via_stack_center(from_layer=self.route_stack[2], + # to_layer=row_decode_clk.layer, + # offset=end) - self.add_segment_center(col_decode_clk.layer, end, col_decode_clk.lc()) + # self.add_segment_center(col_decode_clk.layer, end, col_decode_clk.lc()) def route_array_outputs(self): - for i in range(self.cols): - bl_out = self.array_inst.get_pin("bl_0_{}".format(i)).center() + array_out_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.cols)] + inv_in_pins = [self.bitline_inv_inst.get_pin("in_{}".format(bl)) for bl in range(self.cols)] + inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)] + mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)] + + self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref") + self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref") + - bl_mux = self.mux_inst.get_pin("bl_{}".format(i)).center() - self.add_path(self.array.bitline_layer, [bl_out, bl_mux]) def route_output_buffers(self): mux = self.mux_inst - buf = self.output_buf_inst + buf = self.output_inv_inst route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)] channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3) @@ -429,7 +505,7 @@ class rom_base_bank(design): self.copy_layout_pin(self.control_inst, "clk_in", "clk") for i in range(self.word_size): - self.copy_layout_pin(self.output_buf_inst, "out_{}".format(i), "rom_out_{}".format(i)) + self.copy_layout_pin(self.output_inv_inst, "out_{}".format(i), "rom_out_{}".format(i)) for lsb in range(self.col_bits): name = "addr_{}".format(lsb) self.copy_layout_pin(self.col_decode_inst, "A{}".format(lsb), name) @@ -448,27 +524,7 @@ class rom_base_bank(design): if not inst.mod.name.__contains__("contact"): self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "gnd") - # gnd_start = vector(self.array_inst.get_pins("gnd")[0].cx(),0) - # decode_gnd = self.decode_inst.get_pin("gnd") - # decode_vdd = self.decode_inst.get_pin("vdd") - # array_vdd = self.array_inst.get_pin("vdd") - - # # self.add_segment_center("m1", gnd_start, decode_gnd.center()) - - - # self.add_power_pin("gnd", decode_vdd.center()) - # self.add_power_pin("vdd", decode_gnd.center()) - - # vdd_start = vector(array_vdd.lx() + 0.5 * self.via1_space, array_vdd.cy()) - # end = vector(decode_vdd.lx(), vdd_start.y) - - # self.add_segment_center(self.interconnect_layer, vdd_start, end) - # self.add_via_stack_center(vdd_start, "m1", self.interconnect_layer) - - # vdd_start = vector(decode_vdd.cx(), vdd_start.y) - - # self.add_segment_center(self.interconnect_layer, vdd_start, decode_vdd.center()) diff --git a/compiler/modules/rom_control_logic.py b/compiler/modules/rom_control_logic.py index 0668bd19..b1157fc7 100644 --- a/compiler/modules/rom_control_logic.py +++ b/compiler/modules/rom_control_logic.py @@ -20,7 +20,7 @@ class rom_control_logic(design): if self.height is not None: self.driver_height = 0.5 * self.height - self.gate_height = 0.25 * self.height + self.gate_height = 0.5 * self.height else: self.gate_height = 20 * self.m1_pitch self.driver_height = self.gate_height @@ -51,16 +51,18 @@ class rom_control_logic(design): self.route_insts() def add_modules(self): - - self.buf_mod = factory.create(module_type="pinvbuf", - module_name="rom_control_logic_pinv", + self.buf_mod = factory.create(module_type="pdriver", + module_name="rom_clock_driver", height=self.gate_height, - route_in_cell=True ) + fanout=self.clk_fanout + 2, + add_wells=True, + ) self.nand_mod = factory.create(module_type="pnand2", module_name="rom_control_nand", height=self.gate_height, add_wells=False) self.driver_mod = factory.create(module_type="pdriver", + module_name="rom_precharge_driver", inverting=True, fanout=self.output_size, height=self.driver_height, @@ -77,34 +79,36 @@ class rom_control_logic(design): def create_instances(self): - self.buf_inst = self.add_inst(name="clk_invbuf", mod=self.buf_mod) - self.connect_inst(["clk_in", "clk_bar", "clk_out", "vdd", "gnd"]) + self.buf_inst = self.add_inst(name="clk_driver", mod=self.buf_mod) + self.connect_inst(["clk_in", "clk_out", "vdd", "gnd"]) self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod) self.connect_inst(["CS", "clk_out", "pre_drive", "vdd", "gnd"]) - self.driver_inst = self.add_inst(name="driver_inst", mod=self.driver_mod) + self.driver_inst = self.add_inst(name="precharge_driver", mod=self.driver_mod) self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"]) def place_instances(self): - buf_correction = drc["minwidth_{}".format(self.route_stack[0])] * 0.5 # nand_y = self.buf_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy() - self.nand_inst.place(offset=[0, self.nand_inst.height + self.buf_mod.inv2.height + buf_correction], mirror="MX") - self.driver_inst.place(offset=[0, self.buf_inst.height + buf_correction]) + self.nand_inst.place(offset=[self.buf_inst.width, 0]) + self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height], mirror="MX") + + offset = self.driver_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy() + print("offset: {}".format(offset)) + self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height - offset], mirror="MX") + def route_insts(self): route_width = drc["minwidth_{}".format(self.route_stack[2])] self.copy_layout_pin(self.buf_inst, "A", "clk_in") - self.copy_layout_pin(self.buf_inst, "Zb", "clkb_out") self.copy_layout_pin(self.buf_inst, "Z", "clk_out") self.copy_layout_pin(self.driver_inst, "Z", "prechrg") self.copy_layout_pin(self.nand_inst, "A", "CS") self.copy_layout_pin(self.buf_inst, "gnd") self.copy_layout_pin(self.driver_inst, "vdd") self.copy_layout_pin(self.buf_inst, "vdd") - # self.copy_layout_pin(self.buf_inst, "vdd") clk = self.buf_inst.get_pin("Z") @@ -114,7 +118,7 @@ class rom_control_logic(design): # Connect buffered clock bar to nand input mid = vector(clk.lx() - route_width - 2 * self.m1_space) - self.add_path(self.route_stack[2], [clk.center(), mid, nand_B.center()]) + self.add_path(self.route_stack[2], [clk.center(), nand_B.center()]) self.add_via_stack_center(from_layer=clk.layer, to_layer=self.route_stack[2], @@ -126,15 +130,17 @@ class rom_control_logic(design): # Connect nand output to precharge driver nand_Z = self.nand_inst.get_pin("Z") - nand_output = vector(nand_Z.cx(), nand_B.cy() + 3 * route_width) driver_A = self.driver_inst.get_pin("A") - self.add_path(self.route_stack[2], [nand_output, driver_A.center()]) + + mid = vector(driver_A.cx(), driver_A.cy() - 4 * route_width) + + self.add_path(self.route_stack[2], [nand_Z.center(), mid, driver_A.center()]) self.add_via_stack_center(from_layer=nand_Z.layer, to_layer=self.route_stack[2], - offset=nand_output) + offset=nand_Z.center()) self.add_via_stack_center(from_layer=driver_A.layer, to_layer=self.route_stack[2], diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py index 10d55f85..bb8f0839 100644 --- a/compiler/modules/rom_decoder.py +++ b/compiler/modules/rom_decoder.py @@ -14,7 +14,7 @@ from openram.tech import drc class rom_decoder(design): - def __init__(self, num_outputs, cols, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False): + def __init__(self, num_outputs, fanout, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False): # word lines/ rows / inputs in the base array become the address lines / cols / inputs in the decoder # bit lines / cols / outputs in the base array become the word lines / rows / outputs in the decoder @@ -33,7 +33,7 @@ class rom_decoder(design): self.route_layer = route_layer self.output_layer = output_layer self.inv_route_layer = "m2" - self.cols=cols + self.fanout=fanout self.invert_outputs=invert_outputs self.create_netlist() @@ -57,6 +57,17 @@ class rom_decoder(design): self.connect_inputs() self.route_supplies() self.add_boundary() + + def add_boundary(self): + ll = self.find_lowest_coords() + m1_offset = self.m1_width + self.translate_all(vector(0, ll.y)) + ur = self.find_highest_coords() + + ur = vector(ur.x, ur.y) + super().add_boundary(ll, ur) + self.width = ur.x + self.height = ur.y def setup_layout_constants(self): self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)] @@ -114,7 +125,7 @@ class rom_decoder(design): self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name), rows=self.num_outputs, - cols=ceil(self.cols * 0.5), + fanout=ceil(self.fanout), invert_outputs=self.invert_outputs, tap_spacing=self.strap_spacing) @@ -224,7 +235,7 @@ class rom_decoder(design): def connect_inputs(self): self.copy_layout_pin(self.array_inst, "precharge") - + self.copy_layout_pin(self.array_inst, "precharge_r") for i in range(self.num_inputs): wl = (self.num_inputs - i) * 2 - 1 wl_bar = wl - 1 diff --git a/compiler/modules/rom_wordline_driver_array.py b/compiler/modules/rom_wordline_driver_array.py index 4d3fde06..ae2ea848 100644 --- a/compiler/modules/rom_wordline_driver_array.py +++ b/compiler/modules/rom_wordline_driver_array.py @@ -19,16 +19,16 @@ class rom_wordline_driver_array(design): Creates a Wordline Buffer/Inverter array """ - def __init__(self, name, rows, cols, invert_outputs=False, tap_spacing=4): + def __init__(self, name, rows, fanout, invert_outputs=False, tap_spacing=4, flip_io=False): design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + self.add_comment("rows: {0} Buffer size of: {1}".format(rows, fanout)) self.rows = rows - self.cols = cols + self.fanout = fanout self.invert_outputs=invert_outputs self.tap_spacing = tap_spacing - + self.flip_io = flip_io if OPTS.tech_name == "sky130": self.supply_layer = "m1" else: @@ -51,7 +51,8 @@ class rom_wordline_driver_array(design): self.place_drivers() self.route_layout() self.route_supplies() - self.place_taps() + if self.tap_spacing != 0: + self.place_taps() self.add_boundary() def add_pins(self): @@ -70,13 +71,14 @@ class rom_wordline_driver_array(design): if self.invert_outputs: self.wl_driver = factory.create(module_type="pinv_dec", - size=self.cols, + size=self.fanout, height=b.height, - add_wells=False) + add_wells=False, + flip_io=self.flip_io) else: self.wl_driver = factory.create(module_type="pbuf_dec", - size=self.cols, + size=self.fanout, height=b.height, add_wells=False) @@ -108,7 +110,7 @@ class rom_wordline_driver_array(design): def place_drivers(self): y_offset = 0 for row in range(self.rows): - if row % self.tap_spacing == 0: + if self.tap_spacing != 0 and row % self.tap_spacing == 0: y_offset += self.tap.pitch_offset offset = [0, y_offset] @@ -123,20 +125,34 @@ class rom_wordline_driver_array(design): """ Route all of the signals """ route_width = drc["minwidth_{}".format(self.route_layer)] for row in range(self.rows): - inst = self.wld_inst[row] + if self.flip_io: + row_num = self.rows - row - 1 + else: + row_num = row + inst = self.wld_inst[row_num] self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "gnd") self.copy_layout_pin(inst, "A", "in_{0}".format(row)) + out_pin = inst.get_pin("Z") # output each WL on the right - wl_offset = inst.get_pin("Z").rc() - vector( 0.5 * route_width, 0) + if self.flip_io: + wl_offset = out_pin.lc() - vector(1.6 * route_width, 0) + + else: + wl_offset = out_pin.rc() - vector( 0.5 * route_width, 0) + end = vector(wl_offset.x, \ self.get_pin("in_{}".format(row)).cy() + 0.5 * route_width) self.add_segment_center(layer=self.route_layer, start=wl_offset, end=end) + if self.flip_io: + self.add_segment_center(layer=self.route_layer, + start=out_pin.lc(), + end=vector(wl_offset.x - 0.5 * route_width, out_pin.cy())) self.add_layout_pin_rect_center(text="out_{}".format(row), layer=self.route_layer, offset=end - vector(0, 0.5 * route_width)) diff --git a/compiler/tests/05_rom_base_bank_1kB_test.py b/compiler/tests/05_rom_base_bank_1kB_test.py index 8f742f94..39a9e41f 100644 --- a/compiler/tests/05_rom_base_bank_1kB_test.py +++ b/compiler/tests/05_rom_base_bank_1kB_test.py @@ -24,8 +24,11 @@ class rom_bank_test(openram_test): debug.info(1, "Testing 1kB rom cell") a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_1kB", word_size=1) - + print('wriitng file') + a.sp_write(OPTS.openram_temp + 'simulation_file.sp') self.local_check(a) + + openram.end_openram() # run the test from the command line diff --git a/compiler/tests/05_rom_base_bank_2kB_test.py b/compiler/tests/05_rom_base_bank_2kB_test.py new file mode 100644 index 00000000..d1f59fe7 --- /dev/null +++ b/compiler/tests/05_rom_base_bank_2kB_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + debug.info(1, "Testing 2kB rom cell") + + a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_2kB", word_size=1) + print('wriitng file') + a.sp_write(OPTS.openram_temp + 'simulation_file.sp') + self.local_check(a) + + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/05_rom_base_bank_4kB_test.py b/compiler/tests/05_rom_base_bank_4kB_test.py new file mode 100644 index 00000000..eb63776b --- /dev/null +++ b/compiler/tests/05_rom_base_bank_4kB_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + debug.info(1, "Testing 4kB rom cell") + + a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_4kB", word_size=2) + print('wriitng file') + a.sp_write(OPTS.openram_temp + 'simulation_file.sp') + self.local_check(a) + + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/05_rom_base_bank_8kB_test.py b/compiler/tests/05_rom_base_bank_8kB_test.py new file mode 100644 index 00000000..591d5055 --- /dev/null +++ b/compiler/tests/05_rom_base_bank_8kB_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + debug.info(1, "Testing 8kB rom cell") + + a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_8kB", word_size=2) + print('wriitng file') + a.sp_write(OPTS.openram_temp + 'simulation_file_8kB.sp') + self.local_check(a) + + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file