From f7aed247fd89f4e2208c8ea45b3acb6d4d75f6cb Mon Sep 17 00:00:00 2001 From: Jacob Walker Date: Mon, 16 Jan 2023 16:15:03 -0800 Subject: [PATCH] column control and address precharge --- compiler/base/hierarchy_layout.py | 4 +- compiler/modules/pbuf_dec.py | 10 +- compiler/modules/rom_address_control_array.py | 120 +++++++++ compiler/modules/rom_address_control_buf.py | 214 ++++++++++++++++ compiler/modules/rom_base_array.py | 201 ++++++--------- compiler/modules/rom_base_bank.py | 113 +++++++-- compiler/modules/rom_base_cell.py | 131 ++++++++-- compiler/modules/rom_column_mux.py | 239 ++++++++++++++++++ compiler/modules/rom_column_mux_array.py | 215 ++++++++++++++++ compiler/modules/rom_control_logic.py | 17 +- compiler/modules/rom_decoder.py | 129 ++++++---- compiler/modules/rom_dummy_cell.py | 63 ----- compiler/modules/rom_inv_array.py | 129 ---------- compiler/modules/rom_poly_tap.py | 27 +- compiler/modules/rom_precharge_array.py | 61 ++--- compiler/modules/rom_precharge_cell.py | 92 +++---- compiler/modules/rom_wordline_driver_array.py | 119 +++++++++ compiler/tests/05_rom_array_test.py | 2 +- .../tests/05_rom_column_mux_array_test.py | 36 +++ .../tests/05_rom_decoder_buffer_array_test.py | 37 +++ compiler/tests/05_rom_decoder_test.py | 2 +- .../05_rom_wordline_driver_array_test.py | 38 +++ 22 files changed, 1479 insertions(+), 520 deletions(-) create mode 100644 compiler/modules/rom_address_control_array.py create mode 100644 compiler/modules/rom_address_control_buf.py create mode 100644 compiler/modules/rom_column_mux.py create mode 100644 compiler/modules/rom_column_mux_array.py delete mode 100644 compiler/modules/rom_inv_array.py create mode 100644 compiler/modules/rom_wordline_driver_array.py create mode 100644 compiler/tests/05_rom_column_mux_array_test.py create mode 100644 compiler/tests/05_rom_decoder_buffer_array_test.py create mode 100644 compiler/tests/05_rom_wordline_driver_array_test.py diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 9a11b68a..eb586a18 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -696,13 +696,15 @@ class layout(): start=left_pos, end=right_pos) - def connect_row_pins(self, layer, pins, name=None, full=False): + def connect_row_pins(self, layer, pins, name=None, full=False, round=False): """ Connects left/right rows that are aligned. """ bins = {} for pin in pins: y = pin.cy() + if round: + y = round_to_grid(y) try: bins[y].append(pin) except KeyError: diff --git a/compiler/modules/pbuf_dec.py b/compiler/modules/pbuf_dec.py index e42c159e..5e2877f3 100644 --- a/compiler/modules/pbuf_dec.py +++ b/compiler/modules/pbuf_dec.py @@ -15,7 +15,7 @@ class pbuf_dec(pgate): """ This is a simple buffer used for driving wordlines. """ - def __init__(self, name, size=4, height=None): + def __init__(self, name, size=4, height=None, add_wells=True): debug.info(1, "creating {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) @@ -25,7 +25,7 @@ class pbuf_dec(pgate): self.height = height # Creates the netlist and layout - pgate.__init__(self, name, height) + pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -51,11 +51,13 @@ class pbuf_dec(pgate): input_size = max(1, int(self.size / self.stage_effort)) self.inv1 = factory.create(module_type="pinv_dec", size=input_size, - height=self.height) + height=self.height, + add_wells=self.add_wells) self.inv2 = factory.create(module_type="pinv_dec", size=self.size, - height=self.height) + height=self.height, + add_wells=self.add_wells) def create_insts(self): self.inv1_inst = self.add_inst(name="buf_inv1", diff --git a/compiler/modules/rom_address_control_array.py b/compiler/modules/rom_address_control_array.py new file mode 100644 index 00000000..2b195bd8 --- /dev/null +++ b/compiler/modules/rom_address_control_array.py @@ -0,0 +1,120 @@ +# 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. +# + +from openram.base import design +from openram.sram_factory import factory +from openram.base import vector +from openram.tech import layer, drc + + + +class rom_address_control_array(design): + """ + Takes the input address lines and creates the address and address bar lines for the decoder. + Adds control logic for the precharge cycle so that all address lines are high before the read cycle + """ + def __init__(self, cols, inv_height=None, inv_size=1, name="", route_layer="m1"): + self.size=inv_size + self.cols = cols + self.route_layer = route_layer + dff = factory.create(module_type="dff") + if name=="": + name = "rom_inv_array_{0}".format(cols) + if inv_height == None: + self.inv_height = dff.height * 0.5 + else: + self.inv_height = inv_height + + + if "li" in layer: + self.inv_layer = "li" + else: + self.inv_layer = "m1" + super().__init__(name) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.create_modules() + self.add_pins() + self.create_instances() + + + def create_layout(self): + self.width = self.cols * self.addr_control.width + self.height = self.addr_control.height + self.setup_layout_constants() + self.place_instances() + self.route_clk() + self.route_sources() + self.copy_pins() + self.add_boundary() + + + def create_modules(self): + + self.addr_control = factory.create(module_type="rom_address_control_buf", size=self.inv_height) + # For layout constants + # self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=0) + + def add_pins(self): + for col in range(self.cols): + self.add_pin("A{0}_in".format(col), "INPUT") + self.add_pin("A{0}_out".format(col), "OUTPUT") + self.add_pin("Abar{0}_out".format(col), "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + + self.buf_insts = [] + + for col in range(self.cols): + + name = "Xaddr_buf_{0}".format(col) + + addr_buf = self.add_inst(name=name, mod=self.addr_control) + + A_in = "A{0}_in".format(col) + Aout = "A{0}_out".format(col) + Abar_out = "Abar{0}_out".format(col) + self.connect_inst([A_in, Aout, Abar_out, "clk", "vdd", "gnd"]) + + self.buf_insts.append(addr_buf) + + def setup_layout_constants(self): + self.route_width = drc["minwidth_{}".format(self.route_layer)] + + def place_instances(self): + for col in range(self.cols): + base = vector((col+1)*(self.addr_control.width), 0) + + self.buf_insts[col].place(offset=base, mirror="MY") + + + def copy_pins(self): + for i in range(self.cols): + self.copy_layout_pin(self.buf_insts[i], "A_out", "A{0}_out".format(i)) + self.copy_layout_pin(self.buf_insts[i], "Abar_out", "Abar{0}_out".format(i)) + self.copy_layout_pin(self.buf_insts[i], "A_in", "A{0}_in".format(i)) + + + + def route_clk(self): + self.route_horizontal_pins("clk", insts=self.buf_insts, layer=self.route_layer) + + + + def route_sources(self): + + self.route_horizontal_pins("vdd", insts=self.buf_insts, layer=self.route_layer) + self.route_horizontal_pins("gnd", insts=self.buf_insts, layer=self.route_layer) + + + diff --git a/compiler/modules/rom_address_control_buf.py b/compiler/modules/rom_address_control_buf.py new file mode 100644 index 00000000..f486e855 --- /dev/null +++ b/compiler/modules/rom_address_control_buf.py @@ -0,0 +1,214 @@ +# 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. +# + +from openram.base import design +from openram.sram_factory import factory +from openram.base import vector +from openram.tech import layer, drc + + + +class rom_address_control_buf(design): + """ + Takes the input address lines and creates the address and address bar lines for the decoder. + Adds control logic for the precharge cycle so that all address lines are high before the read cycle + """ + def __init__(self, size, name="", route_layer="m1", add_wells=False): + + self.route_layer = route_layer + self.add_wells = add_wells + + self.size = size + + + if "li" in layer: + self.inv_layer = "li" + else: + self.inv_layer = "m1" + super().__init__(name) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.create_modules() + self.add_pins() + self.create_instances() + + + def create_layout(self): + self.width = self.cell.height * 2 + self.height = self.inv.width + 2 * self.nand.width + self.setup_layout_constants() + self.place_instances() + # self.place_vias() + self.route_gates() + self.route_sources() + self.add_boundary() + + + def create_modules(self): + + + # self.inv1_mod = factory.create(module_type="pinv", module_name="inv_array_end_mod", height=self.inv_size, add_wells=False) + self.inv = factory.create(module_type="pinv_dec", module_name="inv_array_mod", add_wells=False, size=self.size) + # self.end_inv = factory.create(module_type="pinv", module_name="inv_array_end_mod", size=self.size, add_wells=True) + self.nand = factory.create(module_type="nand2_dec", height=self.inv.height) + # For layout constants + self.cell = factory.create(module_type="rom_base_cell") + + def add_pins(self): + + self.add_pin("A_in", "INPUT") + self.add_pin("A_out", "INOUT") + self.add_pin("Abar_out", "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + + name = "XinvAbar" + + self.inv_inst = self.add_inst(name=name, mod=self.inv) + inst_A = "A_in" + inst_Z = "Abar_internal" + self.connect_inst([inst_A, inst_Z, "vdd", "gnd"]) + + name = "Xnand_addr" + + self.addr_nand = self.add_inst(name=name, mod=self.nand) + inst_A = "clk" + inst_B = "Abar_internal" + inst_Z = "A_out" + self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"]) + + name = "Xnand_addr_bar" + + self.addr_bar_nand = self.add_inst(name=name, mod=self.nand) + inst_A = "clk" + inst_B = "A_out" + inst_Z = "Abar_out" + self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"]) + + + + + def setup_layout_constants(self): + self.route_width = drc["minwidth_{}".format(self.route_layer)] + self.interconnect_width = drc["minwidth_{}".format(self.inv_layer)] + + def place_instances(self): + + + self.inv_inst.place(offset=vector(self.inv_inst.height,0), rotate=90) + self.addr_nand.place(offset=vector(self.addr_nand.height , self.inv_inst.width + self.route_width ), rotate=90) + + self.addr_bar_nand.place(offset=vector( self.addr_bar_nand.height, self.addr_nand.width + self.inv_inst.width + self.route_width), rotate=90) + + + + + def route_gates(self): + clk1_pin = self.addr_nand.get_pin("A") + clk2_pin = self.addr_bar_nand.get_pin("A") + # self.add_label("HERE I AM", "poly", clk_pins.cl()) + + Abar_out = self.addr_bar_nand.get_pin("Z") + A_out = self.addr_nand.get_pin("Z") + + Abar_in = self.addr_nand.get_pin("B") + Abar_int_out = self.inv_inst.get_pin("Z") + + Aint_in = self.addr_bar_nand.get_pin("B") + A_in = self.inv_inst.get_pin("A") + + + # Find the center of the pmos poly/gate + poly_right = clk1_pin.cx() + self.poly_enclose_contact + 0.5 * self.contact_width + + ppoly_center = poly_right - 0.7 * self.poly_width + + contact_offset = vector(ppoly_center, clk2_pin.cy()) + + # Route the two shared clk inputs together by connecting poly + self.add_segment_center("poly", contact_offset, vector(ppoly_center, A_out.cy())) + + + clk_offset = vector(clk2_pin.cx(), self.addr_nand.uy()) + self.add_layout_pin_rect_center("clk", offset=clk_offset, layer=self.route_layer) + + self.add_via_stack_center(from_layer=self.inv_layer, to_layer=self.route_layer, offset=self.addr_bar_nand.get_pin("A").center()) + self.add_segment_center(self.route_layer, clk_offset, vector(clk_offset.x, clk2_pin.cy())) + + + + # Route first NAND output to second NAND input + start = A_out.center() + end = Aint_in.center() + self.add_path("m2", [start, end]) + self.add_via_stack_center(Aint_in.center(), self.inv_layer, "m2") + self.add_via_stack_center(A_out.center(), self.inv_layer, "m2") + + + # Route first NAND to output pin + self.add_segment_center("m2", end, vector(end.x, self.addr_bar_nand.uy())) + self.add_layout_pin_rect_center("A_out", offset=vector(end.x, self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2") + + + # Route second NAND to output pin + self.add_via_stack_center(Abar_out.center(), self.inv_layer, "m2") + self.add_segment_center("m2", Abar_out.center(), vector(Abar_out.cx(), self.addr_bar_nand.uy())) + self.add_layout_pin_rect_center("Abar_out", offset=vector(Abar_out.cx(), self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2") + + + # Route inverter output to NAND + end = vector(Abar_int_out.cx(), Abar_in.cy() + 0.5 * self.interconnect_width) + self.add_segment_center(self.inv_layer, Abar_int_out.center(), end) + self.copy_layout_pin(self.inv_inst, "A", "A_in") + + + + + def route_sources(self): + + self.copy_layout_pin(self.addr_nand, "vdd") + self.copy_layout_pin(self.addr_bar_nand, "vdd") + self.copy_layout_pin(self.inv_inst, "vdd") + + self.copy_layout_pin(self.addr_bar_nand, "gnd") + self.copy_layout_pin(self.addr_nand, "gnd") + self.copy_layout_pin(self.inv_inst, "gnd") + + + """ Add n/p well taps to the layout and connect to supplies """ + + source_pin = self.inv_inst.get_pin("vdd") + gnd_pin = self.inv_inst.get_pin("gnd") + + left_edge = self.inv_inst.get_pin("Z").cx() - 2 * self.contact_width - 2 * self.active_contact_to_gate - 4 * self.active_enclose_contact - self.poly_width - self.active_space + + contact_pos = vector(left_edge, source_pin.cy()) + + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.route_layer) + + contact_pos = vector(left_edge, gnd_pin.cy()) + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.route_layer) + + diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py index a46cce33..030cedd7 100644 --- a/compiler/modules/rom_base_array.py +++ b/compiler/modules/rom_base_array.py @@ -54,25 +54,24 @@ class rom_base_array(bitcell_base_array): self.place_bitline_contacts() - self.place_rails() + self.route_precharge() self.add_boundary() + self.place_rails() self.add_label("ARRAY ZERO", self.route_layer) self.add_label("array height", self.route_layer, [0, self.height]) - #def add_pins(self): def add_boundary(self): ll = self.find_lowest_coords() - bottom_offset = - self.dummy.nmos.end_to_contact + self.precharge_inst.offset.y + 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() ur = vector(ur.x, ur.y - self.m1_width) - #super().add_boundary(ll=vector(lowerx, lowery), ur=vector(upperx, uppery)) super().add_boundary(vector(0, 0), ur) self.width = ur.x self.height = ur.y @@ -80,17 +79,9 @@ class rom_base_array(bitcell_base_array): def add_modules(self): - # dummy cell, "dummy" cells represent 0 - self.dummy = factory.create(module_type="rom_dummy_cell", route_layer=self.route_layer) + self.zero_cell = factory.create(module_name="rom_base_zero_cell", module_type="rom_base_cell", bitline_layer=self.route_layer, bit_value=0) + self.one_cell = factory.create(module_name="rom_base_one_cell", module_type="rom_base_cell", bitline_layer=self.route_layer, bit_value=1) - #base cell with no contacts - self.cell_nc = factory.create(module_name="base_mod_0_contact", module_type="rom_base_cell") - #base cell with drain contact - self.cell_dc = factory.create(module_name="base_mod_d_contact", module_type="rom_base_cell", add_drain_contact=self.route_layer) - #base cell with source contact - self.cell_sc = factory.create(module_name="base_mod_s_contact", module_type="rom_base_cell", add_source_contact=self.route_layer) - #base cell with all contacts - self.cell_ac = factory.create(module_name="base_mod_sd_contact", module_type="rom_base_cell", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=self.strap_spacing) @@ -128,98 +119,74 @@ class rom_base_array(bitcell_base_array): # for each new strap placed, offset the column index refrenced to get correct bit in the data array # cols are bit lines - strap_row = False - pre_strap_row = False + if row % self.strap_spacing == 0 and self.pitch_match: strap_row = True if (row + 1) % self.strap_spacing == 0 and self.pitch_match: pre_strap_row = True + for col in range(self.column_size): if col % self.strap_spacing == 0: + self.create_tap(row, col) - name = "tap_r{0}_c{1}".format(row, col) - #print("tap instance added at c{0}, r{1}".format(col, row)) - self.tap_inst[row, col]=self.add_inst(name=name, mod=self.poly_tap) - self.connect_inst([]) - - name = "bit_r{0}_c{1}".format(row, col) - - if self.data[row][col] == 1: - # if dummy/0 cell above and below a 1, add a tx with contacts on both drain and source - # if the first row and a 0 above, add both contacts - # if the last row and 0 below add both contacts - if (row < self.row_size - 1 and row > 0 and self.data[row + 1][col] == 0 and self.data[row - 1][col] == 0) or \ - (row == self.row_size - 1 and self.data[row - 1][col] == 0) or \ - (row == 0 and self.data[row + 1][col] == 0) or \ - (row < self.row_size - 1 and self.data[row + 1][col] == 0 and strap_row) or \ - (row > 0 and self.data[row - 1][col] == 0 and pre_strap_row): - - new_inst = self.add_inst(name=name, mod=self.cell_ac) - - # if dummy/0 is below and not above, add a source contact - # if in the first row, add a source contact - elif (row > 0 and self.data[row - 1][col] == 0) or \ - (row == 0) or \ - (strap_row): - - new_inst=self.add_inst(name=name, mod=self.cell_sc) - - elif (row < self.row_size - 1 and self.data[row + 1][col] == 0) or \ - (row == self.row_size - 1) or \ - (pre_strap_row): - new_inst=self.add_inst(name=name, mod=self.cell_dc) - - else: - new_inst=self.add_inst(name=name, mod=self.cell_nc) - - if row == self.row_size - 1 or self.get_next_cell_in_bl(row, col) == -1: - - bl_l = self.int_bl_list[col] - bl_h = "gnd" - # elif first_in_col: - # bl_l = self.bitline_names[0][col] - # int_bl_list[col] = "bl_int_{0}_{1}".format(row, col) - # bl_h = int_bl_list[col] - # first_in_col = False - else: - bl_l = self.int_bl_list[col] - self.int_bl_list[col] = "bl_int_{0}_{1}".format(row, col) - bl_h = self.int_bl_list[col] - - - self.cell_inst[row, col] = new_inst - self.connect_inst([bl_h, bl_l, self.wordline_names[0][row], "gnd"]) - - - else: - new_inst = self.add_inst(name=name, mod=self.dummy) - self.cell_inst[row, col] = new_inst - self.connect_inst([]) - - # when col = 0 bl_h is connected to vdd, otherwise connect to previous bl connection - # when col = col_size - 1 connected column_sizeto gnd otherwise create new bl connection - # + new_inst = self.create_cell(row, col) + + self.cell_inst[row, col] = new_inst row_list.append(new_inst) - + name = "tap_r{0}_c{1}".format(row, self.array_col_size) - #print(*row_list) - self.tap_inst[row, self.column_size]=self.add_inst(name=name, mod=self.zero_tap) + self.tap_inst[row, 0]=self.add_inst(name=name, mod=self.zero_tap) self.connect_inst([]) self.cell_list.append(row_list) + def create_poly_tap(self, row, col): + name = "tap_r{0}_c{1}".format(row, col) + self.tap_inst[row, col]=self.add_inst(name=name, mod=self.poly_tap) + self.connect_inst([]) + + + def create_cell(self, row, col): + name = "bit_r{0}_c{1}".format(row, col) + + + # when col = 0, bl_h is connected to vdd, otherwise connect to previous bl connection + # when col = col_size - 1 connected column_sizeto gnd otherwise create new bl connection + if row == self.row_size - 1 or self.get_next_cell_in_bl(row, col) == -1: + + bl_l = self.int_bl_list[col] + bl_h = "gnd" + else: + bl_l = self.int_bl_list[col] + + if self.data[row][col] == 1: + self.int_bl_list[col] = "bl_int_{0}_{1}".format(row, col) + + bl_h = self.int_bl_list[col] + + if self.data[row][col] == 1: + new_inst = self.add_inst(name=name, mod=self.one_cell) + self.connect_inst([bl_h, bl_l, self.wordline_names[0][row], "gnd"]) + else: + new_inst = self.add_inst(name=name, mod=self.zero_cell) + self.connect_inst([bl_h, self.wordline_names[0][row], "gnd"]) + + return new_inst + + + def create_precharge_inst(self): prechrg_pins = self.bitline_names[0].copy() - for bl in range(self.column_size): - # if the internal bl was never updated there are no active cells in the bitline, so it should route straight to ground" - if self.int_bl_list[bl] == prechrg_pins[bl]: - prechrg_pins[bl] = "gnd" + # for bl in range(self.column_size): + # # if the internal bl was never updated there are no active cells in the bitline, so it should route straight to ground" + # if self.int_bl_list[bl] == prechrg_pins[bl]: + # prechrg_pins[bl] = "gnd" prechrg_pins.append("precharge_gate") prechrg_pins.append("vdd") @@ -239,32 +206,21 @@ class rom_base_array(bitcell_base_array): def place_rails(self): - spacing = self.route_pitch + self.route_horizontal_pins("D", insts=self.cell_list[self.row_size - 1]) + self.copy_layout_pin(self, "D", "gnd") + - rail_y = self.cell_list[self.row_size - 1][0].offset.y + self.dummy.base_width + spacing - # self.dummy.height * (self.row_size) - start_x = self.get_pin(self.bitline_names[0][0]).cx() - # self.cell_list[self.row_size - 1][0].offset.x - end_x = self.get_pin(self.bitline_names[0][self.column_size - 1]).cx() - # self.cell_list[self.row_size - 1][self.column_size - 1].offset.x - #self.dummy.height * self.row_size - #self.cell_inst[self.row_size - 1,0].uy() - rail_start = vector(start_x , rail_y) - rail_end = vector(end_x, rail_y) + def place_well_tap(self): + tap_y = self.via.uy() + drc["{0}_to_{0}".format(self.strap_layer)] * 2 - self.gnd = self.add_layout_pin_rect_ends(name="gnd", - layer="m1", - start=rail_start, - end=rail_end) - - for bl in range(self.column_size): - drain_pin = self.cell_list[self.row_size - 1][bl].get_pin("D") - via_pos = vector(drain_pin.cx(), rail_y) - self.add_segment_center(self.route_layer, drain_pin.center(), via_pos) - - - self.add_via_stack_center(via_pos, self.route_layer, "m1", ["H", "V"]) - + contact_pos = vector(self.via.cx(), tap_y) + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_power_pin(name="gnd", + loc=contact_pos, + start_layer=self.active_stack[2]) def place_array(self): self.cell_pos = {} @@ -274,10 +230,10 @@ class rom_base_array(bitcell_base_array): pitch_offset = 0 for row in range(self.row_size): - if row % self.strap_spacing == 0 and row != 0 and self.pitch_match: - pitch_offset += self.poly_tap.width + # if row % self.strap_spacing == 0 and row != 0 and self.pitch_match: + # pitch_offset += self.poly_tap.width - cell_y = row * (self.dummy.height) + pitch_offset + cell_y = row * (self.zero_cell.height) + pitch_offset cell_x = 0 for col in range(self.column_size): @@ -285,19 +241,19 @@ class rom_base_array(bitcell_base_array): if col % self.strap_spacing == 0: self.strap_pos[row, col] = vector(cell_x, cell_y) self.tap_inst[row, col].place(self.strap_pos[row, col]) - cell_x += self.poly_tap.width + # cell_x += self.poly_tap.width self.cell_pos[row, col] = vector(cell_x, cell_y) self.cell_inst[row, col].place(self.cell_pos[row, col]) - cell_x += self.dummy.width + cell_x += self.zero_cell.width self.add_label("debug", "li", self.cell_pos[row, col]) - self.strap_pos[row, self.column_size] = vector(cell_x, cell_y) - self.tap_inst[row, self.column_size].place(self.strap_pos[row, self.column_size]) + self.strap_pos[row, 0] = vector(0, cell_y) + self.tap_inst[row, 0].place(self.strap_pos[row, 0]) - # tap_pin = self.cell_inst[row, self.array_col_size].get_pin("poly_tap").center() + # tap_pin = self.tap_inst[row, 0].get_pin("poly_tap").center() # self.add_layout_pin_rect_center("wl{}".format(row), "m2", tap_pin) @@ -316,7 +272,7 @@ class rom_base_array(bitcell_base_array): def place_precharge(self): - self.precharge_offset = vector(0, - self.precharge_inst.height - self.dummy.nmos.end_to_contact - 2 * drc["nwell_enclose_active"]) + self.precharge_offset = vector(0, - self.precharge_inst.height - self.zero_cell.nmos.end_to_contact - 2 * drc["nwell_enclose_active"]) self.precharge_inst.place(offset=self.precharge_offset) @@ -327,19 +283,12 @@ class rom_base_array(bitcell_base_array): def place_wordline_contacts(self): - width = self.route_width - - height = self.route_width - - offset = vector(self.poly_contact.width * 0.5, self.dummy.poly.offset.y) - for wl in range(self.row_size): - poly_via = self.tap_inst[wl, 0].get_pin("via") self.copy_layout_pin(self.tap_inst[wl, 0], "via", self.wordline_names[0][wl]) # self.add_via_stack_center(poly_via.center(), "m1", self.output_layer) - # self.create_horizontal_pin_bus(self.route_layer, offset=corrected_offset, names=self.wordline_names[0], pitch=self.dummy.height, length=None) + # self.create_horizontal_pin_bus(self.route_layer, offset=corrected_offset, names=self.wordline_names[0], pitch=self.zero_cell.height, length=None) def place_bitline_contacts(self): diff --git a/compiler/modules/rom_base_bank.py b/compiler/modules/rom_base_bank.py index d288fdc2..67bf2796 100644 --- a/compiler/modules/rom_base_bank.py +++ b/compiler/modules/rom_base_bank.py @@ -4,21 +4,32 @@ from openram.base import vector from openram.base import design from openram import OPTS from openram.sram_factory import factory -from openram.tech import drc +from openram.tech import drc, layer class rom_base_bank(design): + """ + Rom data bank with row and column decoder + control logic + + word size is in bytes + """ + def __init__(self, strap_spacing=0, data_file=None, name="", word_size=2) -> None: - # self.cols = word_size * 8 + self.word_size = word_size * 8 self.read_binary(word_size=word_size, data_file=data_file) self.num_outputs = self.rows self.num_inputs = ceil(log(self.rows, 2)) + self.col_bits = ceil(log(self.words_per_row, 2)) + self.row_bits = self.num_inputs # self.data = [[0, 1, 0, 1], [1, 1, 1, 1], [1, 1, 0, 0], [0, 0, 1, 0]] self.strap_spacing = strap_spacing - self.route_layer = "li" + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.bus_layer = "m2" self.interconnect_layer = "m1" @@ -78,7 +89,7 @@ class rom_base_bank(design): self.route_decode_outputs() self.route_control() - self.route_supplies() + # self.route_supplies() self.height = self.array_inst.height self.width = self.array_inst.width self.add_boundary() @@ -102,7 +113,7 @@ class rom_base_bank(design): out_pins = [] - for j in range(self.num_outputs): + for j in range(self.rows): out_pins.append("rom_out_{}".format(j)) self.add_pin_list(out_pins, "OUTPUT") @@ -112,11 +123,16 @@ class rom_base_bank(design): def add_modules(self): self.array = factory.create(module_type="rom_base_array", cols=self.cols, rows=self.rows, strap_spacing=self.strap_spacing, bitmap=self.data, route_layer=self.route_layer, pitch_match=True) - self.decode_array = factory.create(module_type="rom_decoder", num_outputs=self.rows, strap_spacing=self.strap_spacing, route_layer=self.route_layer) - self.control_logic = factory.create(module_type="rom_control_logic", num_outputs=(self.rows + self.cols) / 2, height=self.decode_array.inv_inst.height) + self.decode_array = factory.create(module_name="rom_row_decode", module_type="rom_decoder", num_outputs=self.rows, strap_spacing=self.strap_spacing, route_layer=self.route_layer, cols=self.cols) + self.control_logic = factory.create(module_type="rom_control_logic", num_outputs=(self.rows + self.cols) / 2, height=self.decode_array.buf_inst.height) + self.column_mux = factory.create(module_type="rom_column_mux_array", columns=self.cols, word_size=self.word_size, bitline_layer=self.route_layer) + self.column_decode = factory.create(module_name="rom_column_decode", module_type="rom_decoder", num_outputs=self.words_per_row, strap_spacing=self.strap_spacing, route_layer=self.route_layer, cols=1) def create_instances(self): + gnd = ["gnd"] + vdd = ["vdd"] + prechrg = ["precharge"] array_pins = [] decode_pins = [] @@ -132,8 +148,8 @@ class rom_base_bank(design): array_pins.append("gnd") - for addr in range(self.num_inputs): - name = "addr_{}".format(addr) + for addr in range(self.row_bits): + name = "row_addr_{}".format(addr) decode_pins.append(name) for wl in range(self.rows): name = "wl_{}".format(wl) @@ -144,31 +160,67 @@ class rom_base_bank(design): decode_pins.append("gnd") + bitlines = ["bl_{}".format(bl) for bl in range(self.cols)] + select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)] + bitline_out = ["rom_out_{}".format(bl) for bl in range(self.word_size)] + addr_lsb = ["col_addr_{}".format(addr) for addr in range(self.col_bits)] + col_mux_pins = bitlines + select_lines + bitline_out + gnd + + col_decode_pins = addr_lsb + select_lines + prechrg + vdd + gnd self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array) self.connect_inst(array_pins) - self.decode_inst = self.add_inst(name="rom_decoder", mod=self.decode_array) + self.decode_inst = self.add_inst(name="rom_row_decoder", mod=self.decode_array) self.connect_inst(decode_pins) self.control_inst = self.add_inst(name="rom_control", mod=self.control_logic) self.connect_inst(["READ", "CS", "precharge", "vdd", "gnd"]) + self.mux_inst = self.add_inst(name="rom_column_mux", mod=self.column_mux) + self.connect_inst(col_mux_pins) + + self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode) + self.connect_inst(col_decode_pins) + def place_instances(self): - + self.place_row_decoder() + self.place_data_array() + self.place_control_logic() + self.place_col_decoder() + self.place_col_mux() + + + def place_row_decoder(self): + self.decode_offset = vector(0, self.control_inst.height) + self.decode_inst.place(offset=self.decode_offset) + + def place_data_array(self): + # We approximate the correct position for the array array_x = self.decode_inst.width + (2) * ( self.route_layer_width + self.route_layer_pitch ) - array_y = self.decode_array.inv_inst.height - self.array.precharge_inst.cy() - self.array.dummy.height * 0.5 + array_y = self.decode_array.buf_inst.height - self.array.precharge_inst.cy() - self.array.zero_cell.height * 0.5 self.array_offset = vector(array_x ,array_y) - self.decode_offset = vector(0, 0) - - self.control_offset = vector(0,0) - self.array_inst.place(offset=self.array_offset) - self.decode_inst.place(offset=self.decode_offset) + # now move array to correct alignment with decoder + 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_control_logic(self): + self.control_offset = vector(0,0) + self.control_inst.place(offset=self.control_offset) - self.control_inst.place(offset=self.control_offset, mirror="MX") + def place_col_decoder(self): + self.col_decode_offset = vector(self.control_logic.width * 1.3, 0) + self.col_decode_inst.place(offset=self.col_decode_offset) + + def place_col_mux(self): + + self.mux_offset = vector(self.decode_inst.width, 0) + self.mux_inst.place(offset=self.mux_offset) + + 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)] ) @@ -182,21 +234,26 @@ class rom_base_bank(design): def route_decode_outputs(self): - for wl in range(self.rows): - decode_output = self.decode_array.output_names[wl] - decode_out_pin = self.decode_inst.get_pin(decode_output) + route_pins = [self.array_inst.get_pin("wl_0_{}".format(wl)) for wl in range(self.rows)] + decode_pins = [self.decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.rows)] + route_pins.extend(decode_pins) + self.connect_row_pins(self.interconnect_layer, route_pins, round=True) - array_wl = self.array.wordline_names[0][wl] - array_wl_pin = self.array_inst.get_pin(array_wl) + # for wl in range(self.rows): + # decode_output = self.decode_array.output_names[wl] + # decode_out_pin = self.decode_inst.get_pin(decode_output) + + # array_wl = self.array.wordline_names[0][wl] + # array_wl_pin = self.array_inst.get_pin(array_wl) - # wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]] + # # wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]] - start = decode_out_pin.center() - end = vector(array_wl_pin.cx(), start.y) + # start = decode_out_pin.center() + # end = vector(array_wl_pin.cx(), start.y) - self.add_segment_center(self.bus_layer, start, end) - self.add_via_stack_center(array_wl_pin.center(), self.bus_layer, self.interconnect_layer ) + # self.add_segment_center(self.bus_layer, start, end) + # self.add_via_stack_center(array_wl_pin.center(), self.bus_layer, self.interconnect_layer ) def route_array_inputs(self): diff --git a/compiler/modules/rom_base_cell.py b/compiler/modules/rom_base_cell.py index 668bc82d..83370d4d 100644 --- a/compiler/modules/rom_base_cell.py +++ b/compiler/modules/rom_base_cell.py @@ -6,62 +6,149 @@ # All rights reserved. # -from .rom_dummy_cell import rom_dummy_cell +from openram.base import design from openram.base import vector from openram import OPTS from openram.sram_factory import factory from openram.tech import drc -class rom_base_cell(rom_dummy_cell): - - def __init__(self, name="", cell_name=None, add_source_contact=False, add_drain_contact=False, route_layer="m1"): - super().__init__(name, cell_name, add_source_contact, add_drain_contact, route_layer) +class rom_base_cell(design): + def __init__(self, name="", bitline_layer="li", bit_value=1): + super().__init__(name) + self.bit_value = bit_value + self.bitline_layer = bitline_layer + self.create_netlist() + self.create_layout() + def create_netlist(self): self.add_pins() - self.add_nmos() - self.create_nmos() + self.add_modules() + def create_layout(self): + + self.create_tx() self.setup_drc_offsets() - self.place_nmos() self.add_boundary() + self.place_tx() + self.place_bitline() + self.place_poly() + if self.bit_value == 0: + self.short_gate() + - def create_nmos(self): + # Calculates offsets of cell width and height so that tiling of cells does not violate any drc rules + def setup_drc_offsets(self): + + + + self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active) + #nmos contact to gate distance + self.contact_to_gate = 0.5 * (self.nmos.width - 2 * self.nmos.contact_width - self.nmos.poly_width - 2 * self.active_enclose_contact) + + #height offset to account for active-to-active spacing between adjacent bitlines + self.poly_extend_active_spacing = abs( 2 * self.nmos.poly_extend_active - drc("active_to_active") ) + + #contact to contact distance, minimum cell width before drc offsets + self.base_width = self.nmos.width - 2 * self.active_enclose_contact - self.nmos.contact_width + + #width offset to account for active-to-active spacing between cells on the same bitline + #this is calculated as a negative value + self.cell_diffusion_offset = ((self.base_width - 2 * self.active_enclose_contact - self.nmos.contact_width) - drc("active_to_active")) * 0.5 + + # width offset to account for poly-active spacing between base and dummy cells on the same bitline + self.poly_active_offset = 0.5 * (self.base_width - 2 * self.cell_diffusion_offset - (self.poly_width + 2 * self.active_enclose_contact + self.nmos.contact_width)) - self.poly_to_active + + #so that the poly taps are far enough apart + self.poly_tap_offset = (self.base_width - self.cell_diffusion_offset - self.poly_contact.width - self.poly_active_offset) - drc("poly_to_poly") + + + def add_boundary(self): + + height = self.cell_inst.width + self.active_space + + #cell width with offsets applied, height becomes width when the cells are rotated + width = self.cell_inst.height + 2 * self.poly_extend_active + # cell height with offsets applied, width becomes height when the cells are rotated, if the offsets are positive (greater than 0) they are not applied + # height = self.base_width - min(self.cell_diffusion_offset, 0) - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0) + + # make the cells square so the pitch of wordlines will match bitlines + print("height: {0} width: {1}".format(height, width)) + if width > height: + self.width = width + self.height = width + else: + self.width = height + self.height = height + + super().add_boundary() + + + def add_modules(self): + + self.nmos = factory.create(module_type="ptx", + module_name="nmos_rom_mod", + tx_type="nmos", + add_source_contact=self.bitline_layer, + add_drain_contact=self.bitline_layer + ) + + + def create_tx(self): self.cell_inst = self.add_inst( name=self.name + "_nmos", mod=self.nmos, ) - self.connect_inst(["bl_h", "wl", "bl_l", "gnd"]) + if self.bit_value == 0: + self.connect_inst(["bl", "wl", "bl", "gnd"]) + else: + self.connect_inst(["bl_h", "wl", "bl_l", "gnd"]) - def add_pins(self): - pin_list = ["bl_h", "bl_l", "wl", "gnd"] - dir_list = ["INOUT", "INOUT", "INPUT", "GROUND"] + def add_pins(self): + if self.bit_value == 0 : + pin_list = ["bl", "wl", "gnd"] + dir_list = ["INOUT", "INPUT", "GROUND"] + else: + pin_list = ["bl_h", "bl_l", "wl", "gnd"] + dir_list = ["INOUT", "INOUT", "INPUT", "GROUND"] self.add_pin_list(pin_list, dir_list) - def place_nmos(self): + def place_tx(self): - # 0.5 * self.nmos.active_contact.width + self.nmos.active_contact_to_gate - poly_offset = vector(self.poly_extend_active_spacing * 0.5 + self.nmos.height + 2 * self.poly_extend_active, self.nmos.width * 0.5 - 0.5 * self.nmos.contact_width - self.active_enclose_contact) - # nmos_offset = vector(- 0.5 * self.nmos.contact_width - self.active_enclose_contact, self.nmos.poly_extend_active) - print("{} poly spacing".format(self.poly_extend_active_spacing)) - - nmos_offset = vector(self.nmos.poly_extend_active + self.nmos.height ,- 0.5 * self.nmos.contact_width - self.active_enclose_contact) + tx_offset = vector(self.poly_extend_active + self.cell_inst.height + (self.poly_size) ,- 0.5 * self.contact_width - self.active_enclose_contact) # add rect of poly to account for offset from drc spacing - self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.nmos.poly_width) + # self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.poly_width) - self.cell_inst.place(nmos_offset, rotate=90) + self.cell_inst.place(tx_offset, rotate=90) # self.add_label("CELL ZERO", self.route_layer) self.copy_layout_pin(self.cell_inst, "S", "S") self.copy_layout_pin(self.cell_inst, "D", "D") self.source_pos = self.cell_inst.get_pin("S").center() + def place_poly(self): + poly_offset = vector(0, self.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact) + + start = poly_offset + end = poly_offset + vector(self.poly_size, 0) + self.add_segment_center("poly", start, end) + + + def place_bitline(self): + + start = self.get_pin("D").center() + end = start + vector(0, 2 * self.active_enclose_contact + 0.5 * self.contact_width + self.active_space) + self.add_segment_center(self.bitline_layer, start, end) + + def short_gate(self): + + self.add_segment_center(self.bitline_layer, self.get_pin("D").center(), self.get_pin("S").center()) diff --git a/compiler/modules/rom_column_mux.py b/compiler/modules/rom_column_mux.py new file mode 100644 index 00000000..ae7ece30 --- /dev/null +++ b/compiler/modules/rom_column_mux.py @@ -0,0 +1,239 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 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. +# +from openram import debug +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import drc, layer +from openram.tech import cell_properties as cell_props +from openram import OPTS +from .pgate import * + + +class rom_column_mux(pgate): + """ + This module implements the columnmux bitline cell used in the design. + Creates a single column mux cell with the given integer size relative + to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: + Column-mux transistors driven by the decoder must be sized + for optimal speed + """ + def __init__(self, name, tx_size=8, bitline_layer="li"): + + debug.info(2, "creating single ROM column mux cell: {0}".format(name)) + self.tx_size = int(tx_size) + self.bitline_layer = bitline_layer + + super().__init__(name) + + + + def get_bl_names(self): + return "bl" + + + def create_netlist(self): + self.add_pins() + self.add_ptx() + + def create_layout(self): + + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if self.bitline_layer == "li" : + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + self.pin_layer = self.bitcell.bitline_layer + self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer)) + self.pin_width = getattr(self, "{}_width".format(self.pin_layer)) + self.pin_height = 2 * self.pin_width + + self.place_ptx() + + # cell = factory.create(module_type=OPTS.bitcell) + # if(cell_props.use_strap == True and OPTS.num_ports == 1): + # strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version) + # precharge_width = cell.width + strap.width + # else: + # precharge_width = cell.width + self.width = self.bitcell.width + self.height = self.nmos_lower.uy() + self.pin_height + + self.connect_poly() + self.add_bitline_pins() + self.connect_bitlines() + # self.add_pn_wells() + + def add_ptx(self): + self.bitcell = factory.create(module_type="rom_base_cell", bitline_layer=self.bitline_layer) + + # Adds nmos_lower,nmos_upper to the module + self.ptx_width = self.tx_size * drc("minwidth_tx") + self.nmos = factory.create(module_type="ptx", + width=self.ptx_width) + + # Space it in the center + self.nmos_lower = self.add_inst(name="mux_tx1", + mod=self.nmos) + self.connect_inst(["bl", "sel", "bl_out", "gnd"]) + + + def add_pins(self): + self.add_pin_list(["bl", "bl_out", "sel", "gnd"]) + + def add_bitline_pins(self): + """ Add the top and bottom pins to this cell """ + + bl_pos = vector(self.pin_pitch, 0) + + # bl and br + self.add_layout_pin(text="bl", + layer=self.pin_layer, + offset=bl_pos + vector(0, self.height - self.pin_height), + height=self.pin_height) + + # bl_out and br_out + self.add_layout_pin(text="bl_out", + layer=self.col_mux_stack[2], + offset=bl_pos, + height=self.pin_height) + + + def place_ptx(self): + """ Create the two pass gate NMOS transistors to switch the bitlines""" + + # Space it in the center + nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ + + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) + self.nmos_lower.place(nmos_lower_position) + + # # This aligns it directly above the other tx with gates abutting + # nmos_upper_position = nmos_lower_position \ + # + vector(0, self.nmos.active_height + max(self.active_space, self.poly_space)) + # self.nmos_upper.place(nmos_upper_position) + + # if cell_props.pgate.add_implants: + # self.extend_implants() + + def connect_poly(self): + """ Connect the poly gate of the two pass transistors """ + + # offset is the top of the lower nmos' diffusion + # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) + offset = self.nmos_lower.get_pin("G").ul() - vector(0, self.poly_extend_active) + height = self.poly_extend_active - offset.y + self.add_rect(layer="poly", + offset=offset, + height=height) + + # Add the sel pin to the bottom of the mux + self.add_layout_pin(text="sel", + layer="poly", + offset=self.nmos_lower.get_pin("G").ll(), + height=self.poly_extend_active) + + def connect_bitlines(self): + """ Connect the bitlines to the mux transistors """ + + bl_pin = self.get_pin("bl") + # br_pin = self.get_pin("br") + bl_out_pin = self.get_pin("bl_out") + + nmos_lower_s_pin = self.nmos_lower.get_pin("S") + nmos_lower_d_pin = self.nmos_lower.get_pin("D") + + + # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D + # self.add_via_stack_center(from_layer=bl_pin.layer, + # to_layer=self.col_mux_stack[0], + # offset=bl_pin.bc()) + # self.add_via_stack_center(from_layer=br_out_pin.layer, + # to_layer=self.col_mux_stack[0], + # offset=br_out_pin.uc()) + # self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer, + # to_layer=self.col_mux_stack[2], + # offset=nmos_upper_s_pin.center()) + self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer, + to_layer=self.col_mux_stack[2], + offset=nmos_lower_d_pin.center()) + + # bl -> nmos_upper/D on metal1 + # bl_out -> nmos_upper/S on metal2 + mid1 = bl_pin.bc().scale(1, 0.4) \ + + nmos_lower_s_pin.uc().scale(0, 0.5) + mid2 = bl_pin.bc().scale(0, 0.4) \ + + nmos_lower_s_pin.uc().scale(1, 0.5) + self.add_path(self.col_mux_stack[0], + [bl_pin.bc(), mid1, mid2, nmos_lower_s_pin.center()]) + # halfway up, move over + mid1 = bl_out_pin.uc().scale(1, 0.4) \ + + nmos_lower_d_pin.bc().scale(0, 0.4) + mid2 = bl_out_pin.uc().scale(0, 0.4) \ + + nmos_lower_d_pin.bc().scale(1, 0.4) + self.add_path(self.col_mux_stack[2], + [bl_out_pin.uc(), mid1, mid2, nmos_lower_d_pin.center()]) + + # # br -> nmos_lower/D on metal2 + # # br_out -> nmos_lower/S on metal1 + # self.add_path(self.col_mux_stack[0], + # [br_out_pin.uc(), + # vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), + # nmos_lower_s_pin.center()]) + # # halfway up, move over + # mid1 = br_pin.bc().scale(1, 0.5) \ + # + nmos_lower_d_pin.uc().scale(0, 0.5) + # mid2 = br_pin.bc().scale(0, 0.5) \ + # + nmos_lower_d_pin.uc().scale(1, 0.5) + # self.add_path(self.col_mux_stack[2], + # [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) + + def extend_implants(self): + """ + Add top-to-bottom implants for adjacency issues in s8. + """ + # Route to the bottom + ll = (self.nmos_lower.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0) + # Don't route to the top + ur = self.nmos_upper.ur() + vector(self.implant_enclose_active, 0) + self.add_rect("nimplant", + ll, + ur.x - ll.x, + ur.y - ll.y) + + def add_pn_wells(self): + """ + Add a well and implant over the whole cell. Also, add the + pwell contact (if it exists) + """ + # if(cell_props.use_strap == True and OPTS.num_ports == 1): + # strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version) + # rbc_width = self.bitcell.width + strap.width + # else: + # rbc_width = self.bitcell.width + # Add it to the right, aligned in between the two tx + active_pos = vector(self.bitcell.width, + self.nmos_upper.by() - 0.5 * self.poly_space) + + self.add_via_center(layers=self.active_stack, + offset=active_pos, + implant_type="p", + well_type="p") + + # If there is a li layer, include it in the power stack + self.add_via_center(layers=self.col_mux_stack, + offset=active_pos) + + self.add_layout_pin_rect_center(text="gnd", + layer="m1", + offset=active_pos) + + # Add well enclosure over all the tx and contact + if "pwell" in layer: + self.add_rect(layer="pwell", + offset=vector(0, 0), + width=rbc_width, + height=self.height) diff --git a/compiler/modules/rom_column_mux_array.py b/compiler/modules/rom_column_mux_array.py new file mode 100644 index 00000000..a1a1fc29 --- /dev/null +++ b/compiler/modules/rom_column_mux_array.py @@ -0,0 +1,215 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 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. +# +from openram import debug +from openram.base import design +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import layer, preferred_directions +from openram.tech import layer_properties as layer_props +from openram import OPTS + + +class rom_column_mux_array(design): + """ + Dynamically generated column mux array. + Array of column mux to read the bitlines from ROM, based on the RAM column mux + """ + + def __init__(self, name, columns, word_size, offsets=None, column_offset=0, bitline_layer="m1"): + super().__init__(name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("cols: {0} word_size: {1} ".format(columns, word_size)) + + self.columns = columns + self.word_size = word_size + self.offsets = offsets + self.words_per_row = int(self.columns / self.word_size) + self.column_offset = column_offset + + self.sel_layer = layer_props.column_mux_array.select_layer + self.sel_pitch = getattr(self, self.sel_layer + "_pitch") + self.bitline_layer = bitline_layer + + if preferred_directions[self.sel_layer] == "V": + self.via_directions = ("H", "H") + else: + self.via_directions = "pref" + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def get_bl_name(self): + bl_name = self.mux.get_bl_names() + return bl_name + + def get_br_name(self, port=0): + br_name = self.mux.get_br_names() + return br_name + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_array() + + def create_layout(self): + self.setup_layout_constants() + self.place_array() + self.add_routing() + # Find the highest shapes to determine height before adding well + highest = self.find_highest_coords() + self.height = highest.y + self.add_layout_pins() + if "pwell" in layer: + self.add_enclosure(self.mux_inst, "pwell") + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + for i in range(self.columns): + self.add_pin("bl_{}".format(i)) + for i in range(self.words_per_row): + self.add_pin("sel_{}".format(i)) + for i in range(self.word_size): + self.add_pin("bl_out_{}".format(i)) + self.add_pin("gnd") + + def add_modules(self): + self.mux = factory.create(module_type="rom_column_mux") + + self.cell = factory.create(module_type="rom_base_cell") + + def setup_layout_constants(self): + self.column_addr_size = int(self.words_per_row / 2) + self.width = self.columns * self.mux.width + # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br + # one extra route pitch is to space from the sense amp + self.route_height = (self.words_per_row + 3) * self.sel_pitch + + def create_array(self): + self.mux_inst = [] + # For every column, add a pass gate + for col_num in range(self.columns): + name = "XMUX{0}".format(col_num) + self.mux_inst.append(self.add_inst(name=name, + mod=self.mux)) + + self.connect_inst(["bl_{}".format(col_num), + "bl_out_{}".format(int(col_num / self.words_per_row)), + "sel_{}".format(col_num % self.words_per_row), + "gnd"]) + + def place_array(self): + # Default to single spaced columns + if not self.offsets: + self.offsets = [n * self.mux.width for n in range(self.columns)] + + # For every column, add a pass gate + for col_num, xoffset in enumerate(self.offsets[0:self.columns]): + # if self.cell.mirror.y and (col_num + self.column_offset) % 2: + # mirror = "MY" + # xoffset = xoffset + self.mux.width + # else: + # mirror = "" + + offset = vector(xoffset, self.route_height) + self.mux_inst[col_num].place(offset=offset) + + def add_layout_pins(self): + """ Add the pins after we determine the height. """ + # For every column, add a pass gate + for col_num in range(self.columns): + mux_inst = self.mux_inst[col_num] + bl_pin = mux_inst.get_pin("bl") + offset = bl_pin.ll() + self.add_layout_pin(text="bl_{}".format(col_num), + layer=bl_pin.layer, + offset=offset, + height=self.height - offset.y) + + + def route_supplies(self): + self.route_horizontal_pins("gnd", self.insts) + + def add_routing(self): + self.add_horizontal_input_rail() + self.add_vertical_poly_rail() + self.route_bitlines() + self.route_supplies() + + def add_horizontal_input_rail(self): + """ Create address input rails below the mux transistors """ + for j in range(self.words_per_row): + offset = vector(0, self.route_height + (j - self.words_per_row) * self.sel_pitch) + self.add_layout_pin(text="sel_{}".format(j), + layer=self.sel_layer, + offset=offset, + width=self.mux_inst[-1].rx()) + + def add_vertical_poly_rail(self): + """ Connect the poly to the address rails """ + + # Offset to the first transistor gate in the pass gate + for col in range(self.columns): + # which select bit should this column connect to depends on the position in the word + sel_index = col % self.words_per_row + # Add the column x offset to find the right select bit + gate_offset = self.mux_inst[col].get_pin("sel").bc() + # use the y offset from the sel pin and the x offset from the gate + + offset = vector(gate_offset.x, + self.get_pin("sel_{}".format(sel_index)).cy()) + + bl_offset = offset.scale(0, 1) + vector((self.mux_inst[col].get_pin("bl_out").cx()), 0) + self.add_via_stack_center(from_layer="poly", + to_layer=self.sel_layer, + offset=bl_offset, + directions=self.via_directions) + self.add_path("poly", [offset, gate_offset, bl_offset]) + + def route_bitlines(self): + """ Connect the output bit-lines to form the appropriate width mux """ + for j in range(self.columns): + + bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc() + + bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.sel_pitch) + + # Add the horizontal wires for the first bit + if j % self.words_per_row == 0: + bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc() + bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch) + + self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end]) + + # Extend the bitline output rails and gnd downward on the first bit of each n-way mux + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), + layer=self.bitline_layer, + start=bl_offset_begin, + end=bl_out_offset_begin) + + + else: + self.add_path(self.bitline_layer, [bl_out_offset_begin, bl_offset_begin]) + + # This via is on the right of the wire + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.sel_layer, + offset=bl_out_offset_begin, + directions=self.via_directions) + + + + def graph_exclude_columns(self, column_include_num): + """ + Excludes all columns muxes unrelated to the target bit being simulated. + Each mux in mux_inst corresponds to respective column in bitcell array. + """ + for i in range(len(self.mux_inst)): + if i != column_include_num: + self.graph_inst_exclude.add(self.mux_inst[i]) diff --git a/compiler/modules/rom_control_logic.py b/compiler/modules/rom_control_logic.py index 4d3da899..7801fad5 100644 --- a/compiler/modules/rom_control_logic.py +++ b/compiler/modules/rom_control_logic.py @@ -15,6 +15,11 @@ class rom_control_logic(design): self.output_size = num_outputs self.mod_height = height + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + # dff = factory.create(module_type="dff") # if height == None: @@ -43,11 +48,11 @@ class rom_control_logic(design): self.inv_mod = factory.create(module_type="pinv", module_name="rom_control_logic_pinv", height=self.mod_height) self.nand_mod = factory.create(module_type="pnand2", module_name="rom_control_nand", height=self.mod_height) - self.driver_mod = factory.create(module_type="pdriver", inverting=True, fanout=self.output_size, height=self.mod_height, add_wells=False) + self.driver_mod = factory.create(module_type="pdriver", inverting=True, fanout=self.output_size, height=self.mod_height, add_wells=True) def add_pins(self): - self.add_pin("READ", "INPUT") + self.add_pin("clk", "INPUT") self.add_pin("CS", "INPUT") self.add_pin("prechrg", "OUTPUT") self.add_pin("vdd", "POWER") @@ -56,10 +61,10 @@ class rom_control_logic(design): def create_instances(self): self.inv_inst = self.add_inst(name="read_signal_inv", mod=self.inv_mod) - self.connect_inst(["READ", "READ_BAR", "vdd", "gnd"]) + self.connect_inst(["clk", "clk_bar", "vdd", "gnd"]) self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod) - self.connect_inst(["CS", "READ_BAR", "pre_drive", "vdd", "gnd"]) + self.connect_inst(["CS", "clk_bar", "pre_drive", "vdd", "gnd"]) self.driver_inst = self.add_inst(name="driver_inst", mod=self.driver_mod) self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"]) @@ -75,7 +80,7 @@ class rom_control_logic(design): self.copy_layout_pin(self.driver_inst, "Z", "prechrg") self.copy_layout_pin(self.nand_inst, "B", "CS") - self.add_path("li", [self.inv_inst.get_pin("Z").center(), self.nand_inst.get_pin("A").center()]) + self.add_path(self.route_layer, [self.inv_inst.get_pin("Z").center(), self.nand_inst.get_pin("A").center()]) - self.add_path("li", [self.nand_inst.get_pin("Z").center(), self.driver_inst.get_pin("A").center()]) + self.add_path(self.route_layer, [self.nand_inst.get_pin("Z").center(), self.driver_inst.get_pin("A").center()]) \ No newline at end of file diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py index 34fb5453..46687611 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, strap_spacing, name="", route_layer="li", output_layer="m2"): + def __init__(self, num_outputs, cols, strap_spacing, name="", route_layer="m1", output_layer="m2"): # 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 @@ -32,7 +32,8 @@ class rom_decoder(design): self.cell_height = b.height self.route_layer = route_layer self.output_layer = output_layer - self.inv_route_layer = "m1" + self.inv_route_layer = "m2" + self.cols=cols self.create_netlist() self.create_layout() @@ -43,15 +44,20 @@ class rom_decoder(design): def create_layout(self): + self.setup_layout_constants() self.place_array() - self.place_input_inverters() - self.create_outputs() - self.width = self.array_inst.height - self.height = self.array_inst.width + self.inv_inst.height + self.place_input_buffer() + self.place_driver() + self.route_outputs() + self.width = self.array_inst.height + self.wordline_buf_inst.width + self.height = self.array_inst.width + self.buf_inst.height self.connect_inputs() - self.route_supplies() + # self.route_supplies() self.add_boundary() + def setup_layout_constants(self): + self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)] + def create_decode_map(self): self.decode_map = [] # create decoding map that will be the bitmap for the rom decoder @@ -64,7 +70,7 @@ class rom_decoder(design): inv_col_array = [] for row in range(self.num_outputs): - addr_idx = -col - 1 + addr_idx = -col - 1 addr = format(row, 'b') if col >= len(addr) : @@ -87,10 +93,10 @@ class rom_decoder(design): def add_pins(self): for i in range(self.num_inputs): - self.add_pin("in_{0}".format(i), "INPUT") + self.add_pin("A{0}".format(i), "INPUT") for j in range(self.num_outputs): - self.add_pin("out_{0}".format(j), "OUTPUT") + self.add_pin("wl_{0}".format(j), "OUTPUT") self.add_pin("precharge_gate", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -98,10 +104,14 @@ class rom_decoder(design): def add_modules(self): - self.inv_array = factory.create(module_type="rom_inv_array", cols=self.num_inputs) + self.control_array = factory.create(module_type="rom_address_control_array", cols=self.num_inputs) + + self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name), \ + rows=self.num_outputs, \ + cols=self.cols) self.array_mod = factory.create(module_type="rom_base_array", \ - module_name="rom_decode_array", \ + module_name="{}_array".format(self.name), \ cols=self.num_outputs, \ rows=2 * self.num_inputs, \ bitmap=self.decode_map, @@ -112,23 +122,25 @@ class rom_decoder(design): def create_instances(self): - self.create_input_inverters() self.create_array_inst() + self.create_input_buffer() + self.create_wordline_buffer() - def create_input_inverters(self): - name = "pre_inv_array" - self.inv_inst = self.add_inst(name=name, mod=self.inv_array) + def create_input_buffer(self): + name = "pre_control_array" + self.buf_inst = self.add_inst(name=name, mod=self.control_array) - inv_pins = [] + control_pins = [] for i in range(self.num_inputs): - inv_pins.append("in_{0}".format(i)) - inv_pins.append("inbar_{0}".format(i)) - - inv_pins.append("vdd") - inv_pins.append("gnd") - self.connect_inst(inv_pins) + control_pins.append("A{0}".format(i)) + control_pins.append("A{0}_int".format(i)) + control_pins.append("Abar{0}_int".format(i)) + control_pins.append("clk") + control_pins.append("vdd") + control_pins.append("gnd") + self.connect_inst(control_pins) def create_array_inst(self): @@ -137,7 +149,7 @@ class rom_decoder(design): array_pins = [] for j in range(self.num_outputs): - name = "out_{0}".format(j) + name = "wl_int{0}".format(j) array_pins.append(name) @@ -149,51 +161,70 @@ class rom_decoder(design): array_pins.append("gnd") self.connect_inst(array_pins) + def create_wordline_buffer(self): + self.wordline_buf_inst = self.add_inst("rom_wordline_driver", mod=self.wordline_buf) + in_pins = ["wl_int{}".format(wl) for wl in range(self.num_outputs)] + out_pins = ["wl_{}".format(wl) for wl in range(self.num_outputs)] + pwr_pins = ["vdd", "gnd"] + self.connect_inst(in_pins + out_pins + pwr_pins) + + + + def place_input_buffer(self): + wl = self.array_mod.row_size - 1 + align = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl]).cx() - self.buf_inst.get_pin("A0_out").cx() + print("align: {}".format(align)) + + self.buf_inst.place(vector(align, 0)) - def place_input_inverters(self): - self.inv_inst.place(vector(self.array_inst.ll().x, 0)) def place_array(self): - offset = vector(self.array_mod.height, self.inv_array.height + self.m1_width + self.poly_contact.width) + offset = vector(self.array_mod.height, self.control_array.height + self.m1_width + self.poly_contact.width) self.array_inst.place(offset, rotate=90) + + def place_driver(self): + + offset = vector(self.array_inst.height + self.m1_width, self.array_inst.by()) + self.wordline_buf_inst.place(offset) - def create_outputs(self): + # calculate the offset between the decode array and the buffer inputs now that their zeros are aligned + pin_offset = self.array_inst.get_pin("bl_0_0").cy() - self.wordline_buf_inst.get_pin("in_0").cy() + self.wordline_buf_inst.place(offset + vector(0, pin_offset)) - self.output_names = [] + def route_outputs(self): for j in range(self.num_outputs): - name = "out_{0}".format(j) - self.output_names.append(name) + self.copy_layout_pin(self.wordline_buf_inst, "out_{}".format(j), "wl_{}".format(j)) + + array_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.num_outputs)] + driver_pins = [self.wordline_buf_inst.get_pin("in_{}".format(bl)) for bl in range(self.num_outputs)] + + route_pins = array_pins + driver_pins + self.connect_row_pins(self.output_layer, route_pins, round=True) - for bl in range(self.num_outputs): - self.copy_layout_pin(self.array_inst, self.array_mod.bitline_names[0][bl], self.output_names[bl]) - # prechg_pin = self.array_mod.bitline_names[0][bl] - # src_pin = self.array_inst.get_pin(prechg_pin) - # offset = src_pin.center() - # self.add_via_stack_center(offset, self.route_layer, self.output_layer) - # self.outputs.append(self.add_layout_pin_rect_center(self.output_names[bl], self.output_layer, offset )) def connect_inputs(self): self.copy_layout_pin(self.array_inst, "precharge") for i in range(self.num_inputs): - wl = self.num_inputs * 2 - i * 2 - 1 + wl = (self.num_inputs - i) * 2 - 1 wl_bar = wl - 1 addr_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl]) addr_bar_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl_bar]) - inv_in_pin = self.inv_inst.get_pin("inv{}_in".format(i)) - inv_out_pin = self.inv_inst.get_pin("inv{}_out".format(i)) + addr_out_pin = self.buf_inst.get_pin("A{}_out".format(i)) + addr_bar_out_pin = self.buf_inst.get_pin("Abar{}_out".format(i)) - addr_start = inv_in_pin.center() - addr_end = vector(addr_start.x, addr_pin.cy()) + addr_middle = vector(addr_pin.cx(), addr_out_pin.cy()) + + addr_bar_middle = vector(addr_bar_pin.cx(), addr_bar_out_pin.cy()) - addr_bar_start = inv_out_pin.center() - addr_bar_end = vector(addr_bar_start.x, addr_bar_pin.cy()) - self.add_segment_center(self.inv_route_layer, addr_start, addr_end) - self.add_segment_center(self.inv_route_layer, addr_bar_start, addr_bar_end) + self.add_path(self.inv_route_layer, [addr_out_pin.center(), addr_middle, addr_pin.center()]) + self.add_path(self.inv_route_layer, [addr_bar_out_pin.center(), addr_bar_middle, addr_bar_pin.center()]) + + # self.add_segment_center(self.inv_route_layer, addr_bar_middle + vector(0, self.inv_route_width * 0.5), addr_bar_out_pin.center() + vector(0, self.inv_route_width * 0.5), self.inv_route_width) def route_supplies(self): minwidth = drc["minwidth_{}".format(self.inv_route_layer)] @@ -202,7 +233,7 @@ class rom_decoder(design): # route decode array vdd and inv array vdd together array_vdd = self.array_inst.get_pin("vdd") - inv_vdd = self.inv_inst.get_pins("vdd")[-1] + inv_vdd = self.buf_inst.get_pins("vdd")[-1] end = vector(array_vdd.cx(), inv_vdd.cy() - 0.5 * minwidth) self.add_segment_center("m1", array_vdd.center(), end) @@ -215,7 +246,7 @@ class rom_decoder(design): # route pin on inv gnd - inv_gnd = self.inv_inst.get_pins("gnd")[0] + inv_gnd = self.buf_inst.get_pins("gnd")[0] array_gnd = self.array_inst.get_pins("gnd") # add x jog diff --git a/compiler/modules/rom_dummy_cell.py b/compiler/modules/rom_dummy_cell.py index bf5932b6..8ebd138f 100644 --- a/compiler/modules/rom_dummy_cell.py +++ b/compiler/modules/rom_dummy_cell.py @@ -41,55 +41,8 @@ class rom_dummy_cell(design): self.add_metal() #self.add_label("0,0", self.route_layer) - - - # Calculates offsets of cell width and height so that tiling of cells does not violate any drc rules - def setup_drc_offsets(self): - - #nmos contact to gate distance - self.contact_to_gate = 0.5 * (self.nmos.width - 2 * self.nmos.contact_width - self.nmos.poly_width - 2 * self.active_enclose_contact) - - #height offset to account for active-to-active spacing between adjacent bitlines - self.poly_extend_active_spacing = abs( 2 * self.nmos.poly_extend_active - drc("active_to_active") ) - - #contact to contact distance, minimum cell width before drc offsets - self.base_width = self.nmos.width - 2 * self.active_enclose_contact - self.nmos.contact_width - - #width offset to account for active-to-active spacing between cells on the same bitline - #this is calculated as a negative value - self.cell_diffusion_offset = ((self.base_width - 2 * self.active_enclose_contact - self.nmos.contact_width) - drc("active_to_active")) * 0.5 - - # width offset to account for poly-active spacing between base and dummy cells on the same bitline - self.poly_active_offset = 0.5 * (self.base_width - 2 * self.cell_diffusion_offset - (self.poly_width + 2 * self.active_enclose_contact + self.nmos.contact_width)) - self.poly_to_active - - #so that the poly taps are far enough apart - self.poly_tap_offset = (self.base_width - self.cell_diffusion_offset - self.poly_contact.width - self.poly_active_offset) - drc("poly_to_poly") - - - def add_boundary(self): - - width = self.nmos.width + self.active_space - - #cell width with offsets applied, height becomes width when the cells are rotated - # width = self.nmos.height + self.poly_extend_active_spacing + 2 * self.nmos.poly_extend_active - # cell height with offsets applied, width becomes height when the cells are rotated, if the offsets are positive (greater than 0) they are not applied - height = self.base_width - min(self.cell_diffusion_offset, 0) - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0) - - # make the cells square so the pitch of wordlines will match bitlines - print("height: {0} width: {1}".format(height, width)) - if width > height: - self.width = width - self.height = width - else: - self.width = height - self.height = height - - super().add_boundary() - - - def add_poly(self): poly_x = 0.5 * (self.nmos.poly_height + self.poly_extend_active_spacing) @@ -129,22 +82,6 @@ class rom_dummy_cell(design): self.add_layout_pin_rect_center("D", self.route_layer, drain_pos) - def add_nmos(self): - #used only for layout constants - # if not self.source_contact: - # add_source = False - # else: - # add_source = self.route_layer - # if not self.drain_contact: - # add_drain = False - # else: - # add_drain = self.route_layer - self.nmos = factory.create(module_type="ptx", - module_name="nmos_rom_mod", - tx_type="nmos", - add_source_contact=self.add_source_contact, - add_drain_contact=self.add_drain_contact - ) diff --git a/compiler/modules/rom_inv_array.py b/compiler/modules/rom_inv_array.py deleted file mode 100644 index 5774e2fc..00000000 --- a/compiler/modules/rom_inv_array.py +++ /dev/null @@ -1,129 +0,0 @@ -# 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. -# - -from openram.base import design -from openram.sram_factory import factory -from openram.base import vector -from openram.tech import layer, drc - - - -class rom_inv_array(design): - """ - An array of inverters to create the inverted address lines for the rom decoder - """ - def __init__(self, cols, inv_size=None, name="", route_layer="m1"): - self.cols = cols - self.route_layer = route_layer - dff = factory.create(module_type="dff") - if name=="": - name = "rom_inv_array_{0}".format(cols) - if inv_size == None: - self.inv_size = dff.height * 0.5 - else: - self.inv_size = inv_size - - - if "li" in layer: - self.inv_layer = "li" - else: - self.inv_layer = "m1" - super().__init__(name) - self.create_netlist() - self.create_layout() - - def create_netlist(self): - self.create_modules() - self.add_pins() - self.create_instances() - - - def create_layout(self): - self.width = self.cols * self.poly_tap.height * 2 - self.height = self.inv_mod.height - self.setup_layout_constants() - self.place_instances() - self.place_vias() - self.route_sources() - self.add_boundary() - - - def create_modules(self): - - self.inv_mod = factory.create(module_type="pinv", module_name="inv_array_mod", height=self.inv_size, add_wells=False) - self.end_inv = factory.create(module_type="pinv", module_name="inv_array_end_mod", height=self.inv_size, add_wells=True) - # For layout constants - self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=0) - - def add_pins(self): - for col in range(self.cols): - self.add_pin("inv{0}_in".format(col), "INPUT") - self.add_pin("inv{0}_out".format(col), "OUTPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def create_instances(self): - self.inv_insts = [] - - for col in range(self.cols): - name = "Xinv_c{0}".format(col) - if col == self.cols - 1: - self.inv_insts.append(self.add_inst(name=name, mod=self.end_inv)) - else: - self.inv_insts.append(self.add_inst(name=name, mod=self.inv_mod)) - inst_A = "inv{0}_in".format(col) - inst_Z = "inv{0}_out".format(col) - self.connect_inst([inst_A, inst_Z, "vdd", "gnd"]) - - def setup_layout_constants(self): - input_pin = self.inv_insts[0].get_pin("A") - output_pin = self.inv_insts[0].get_pin("Z") - - # NEED TO OFFSET OUTPUT VIA IN ORDER TO ALIGN WITH PITCH OF ADDRESS INPUTS TO ARRAY - - # print(self.poly_tap.get_pin("poly_tap").center()) - - # distance between input and output pins of inverter - in_out_distance = output_pin.cx() - input_pin.cx() - # distance from left edge of inverter to input plus right edge to output - edge_to_pins_distance = input_pin.cx() - self.inv_insts[0].lx() + self.inv_insts[0].rx() - output_pin.cx() - - self.alignment_offset = edge_to_pins_distance - in_out_distance - - def place_instances(self): - self.add_label("ZERO", self.route_layer) - for col in range(self.cols): - # base = vector(col*(self.inv_mod.width - self.alignment_offset), 0) - base = vector(col*(self.poly_tap.height * 2), 0) - self.inv_insts[col].place(offset=base) - #vdd_pin = self.inv_insts[0].get_pin("vdd").center() - #self.add_layout_pin_rect_center("vdd_align", self.inv_layer, vdd_pin, 0, 0) - - def place_vias(self): - for i in range(self.cols): - input_pin = self.inv_insts[i].get_pin("A") - output_pin = self.inv_insts[i].get_pin("Z") - - self.add_via_stack_center(input_pin.center(), self.inv_mod.route_layer, self.route_layer) - self.add_via_stack_center(output_pin.center(), self.inv_mod.route_layer, self.route_layer) - self.add_layout_pin_rect_center("inv{}_in".format(i), offset=input_pin.center(), layer=self.route_layer) - self.add_layout_pin_rect_center("inv{}_out".format(i), offset=output_pin.center(), layer=self.route_layer) - - def route_sources(self): - - vdd_start = self.inv_insts[0].get_pin("vdd") - vdd_end = self.inv_insts[-1].get_pin("vdd") - - gnd_start = self.inv_insts[0].get_pin("gnd") - gnd_end = self.inv_insts[-1].get_pin("gnd") - - self.copy_layout_pin(self.inv_insts[0], "vdd") - self.copy_layout_pin(self.inv_insts[0], "gnd") - # self.vdd = self.add_layout_pin_rect_ends("vdd", self.inv_layer, vdd_start.center(), vdd_end.center())[-1] - # self.gnd = self.add_layout_pin_rect_ends("gnd", self.inv_layer, gnd_start.center(), gnd_end.center())[-1] - diff --git a/compiler/modules/rom_poly_tap.py b/compiler/modules/rom_poly_tap.py index ec6a1a4e..c09ef984 100644 --- a/compiler/modules/rom_poly_tap.py +++ b/compiler/modules/rom_poly_tap.py @@ -10,10 +10,11 @@ from openram.base import design from openram.base import vector from openram import OPTS from openram.sram_factory import factory +from openram.tech import drc class rom_poly_tap(design): - def __init__(self, name="", strap_length=0, cell_name=None, prop=None, tx_type="nmos", strap_layer="m1"): + def __init__(self, name="", strap_length=0, cell_name=None, prop=None, tx_type="nmos", strap_layer="m2"): super().__init__(name, cell_name, prop) self.strap_layer=strap_layer self.length = strap_length @@ -23,7 +24,7 @@ class rom_poly_tap(design): def create_netlist(self): #for layout constants - self.dummy = factory.create(module_type="rom_dummy_cell") + self.dummy = factory.create(module_type="rom_base_cell") self.pmos = factory.create(module_type="ptx", tx_type="pmos") def create_layout(self): @@ -31,20 +32,19 @@ class rom_poly_tap(design): self.place_via() # if self.tx_type == "pmos": self.extend_poly() + self.place_ptap() self.add_boundary() - if self.length != 0: - self.place_strap() + # if self.length != 0: + # self.place_strap() def add_boundary(self): contact_width = self.poly_contact.width + 2 * self.contact_x_offset offset = self.active_space - (contact_width - self.active_enclose_contact - self.active_extend_contact) - print("THINGY {}".format(offset)) self.height = self.dummy.height self.width = contact_width + self.pitch_offset - print("poly height: {0}, width: {1}".format(self.height, self.width)) super().add_boundary() def place_via(self): @@ -61,7 +61,7 @@ class rom_poly_tap(design): if self.tx_type == "nmos": - contact_y = self.dummy.poly.cy() + contact_y = self.dummy.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact # contact_y = self.dummy.poly.offset.x + (self.poly_width * 0.5) # self.contact_x_offset = 0 else: @@ -77,8 +77,6 @@ class rom_poly_tap(design): self.add_layout_pin_rect_center("via", self.strap_layer, self.contact_offset) - - def place_strap(self): strap_start = vector(self.via.lx() , self.via.cy()) @@ -105,4 +103,15 @@ class rom_poly_tap(design): self.add_rect("poly", extend_offset, self.contact_x_offset , self.poly_width) + def place_ptap(self): + tap_y = self.via.uy() + drc["{0}_to_{0}".format(self.strap_layer)] * 2 + + contact_pos = vector(self.via.cx(), tap_y) + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_power_pin(name="gnd", + loc=contact_pos, + start_layer=self.active_stack[2]) diff --git a/compiler/modules/rom_precharge_array.py b/compiler/modules/rom_precharge_array.py index 8acfb8c8..247d2d4e 100644 --- a/compiler/modules/rom_precharge_array.py +++ b/compiler/modules/rom_precharge_array.py @@ -58,24 +58,23 @@ class rom_precharge_array(design): def create_layout(self): - self.width = self.cols * self.pmos.width + self.num_straps * self.poly_tap.width + self.width = self.cols * self.pmos.width self.height = self.pmos.width self.place_instances() self.create_layout_pins() self.add_well_tap() self.route_supply() - self.extend_implant() self.add_boundary() def add_boundary(self): - self.translate_all(self.well_ll) + # self.translate_all(self.well_ll) ur = self.find_highest_coords() - ur = vector(ur.x, ur.y - self.well_ll.y) + # ur = vector(ur.x, ur.y - self.well_ll.y) super().add_boundary(vector(0, 0), ur) - self.width = self.cols * self.pmos.width + self.num_straps * self.poly_tap.width + self.width = self.cols * self.pmos.width self.height = ur.y def create_modules(self): @@ -84,7 +83,7 @@ class rom_precharge_array(design): # For layout constants self.dummy = factory.create(module_type="rom_base_cell") - self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=self.strap_spacing, tx_type="pmos") + self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=self.strap_spacing) def add_pins(self): for col in range(self.cols): @@ -96,21 +95,25 @@ class rom_precharge_array(design): self.array_insts = [] self.pmos_insts = [] self.tap_insts = [] + + self.tap_insts.append(self.add_inst(name="tap_0", mod=self.poly_tap)) + self.connect_inst([]) for col in range(self.cols): - if col % self.strap_spacing == 0: - name = "tap_c{}".format(col) - tap = self.add_inst(name=name, mod=self.poly_tap) - self.array_insts.append(tap) - self.tap_insts.append(tap) - self.connect_inst([]) + # if col % self.strap_spacing == 0: + # name = "tap_c{}".format(col) + # tap = self.add_inst(name=name, mod=self.poly_tap) + # self.array_insts.append(tap) + # self.tap_insts.append(tap) + # self.connect_inst([]) name = "Xpmos_c{0}".format(col) pmos = self.add_inst(name=name, mod=self.pmos) self.array_insts.append(pmos) self.pmos_insts.append(pmos) bl = "pre_bl{0}_out".format(col) self.connect_inst(["vdd", "gate", bl, "vdd"]) + print(self.array_insts) @@ -123,15 +126,16 @@ class rom_precharge_array(design): cell_y = 0 # columns are bit lines4 cell_x = 0 - print("starting array place") + + self.tap_insts[0].place(vector(cell_x, cell_y)) for col in range(self.cols): - if col % self.strap_spacing == 0 : - self.tap_insts[strap_num].place(vector(cell_x, cell_y)) - self.add_label("debug", "li", vector(cell_x, cell_y)) - cell_x += self.poly_tap.width - strap_num += 1 + # if col % self.strap_spacing == 0 : + # self.tap_insts[strap_num].place(vector(cell_x, cell_y)) + # self.add_label("debug", "li", vector(cell_x, cell_y)) + # cell_x += self.poly_tap.width + # strap_num += 1 self.pmos_insts[col].place(vector(cell_x, cell_y)) self.add_label("debug", "li", vector(cell_x, cell_y)) @@ -146,21 +150,6 @@ class rom_precharge_array(design): self.add_layout_pin_rect_center(bl, self.route_layer, source_pin.center()) - def extend_implant(self): - layer = "nwell" - # center of source contact minus radius of the well generated by the contact gives the lowermost well position - contact_well_by = self.pmos.pmos.source_contacts[0].cx() - self.pmos.pmos.source_contacts[0].mod.well_width * 0.5 - - ptx_well_by = 0.5 * self.pmos.pmos.active_width - 0.5 * self.pmos.pmos.well_width - - well_extend = ptx_well_by - contact_well_by - - - self.well_ll = vector(0, min(contact_well_by, ptx_well_by)) - - height = self.pmos.pmos.active_width + 2 * self.well_enclose_active - self.add_rect(layer, self.well_ll, self.width + well_extend, height + 2 * well_extend) - def add_well_tap(self): layer_stack = self.active_stack @@ -174,11 +163,13 @@ class rom_precharge_array(design): directions=("V", "V")) def route_supply(self): + + start_pin = self.pmos_insts[0].get_pin("S").lx() end_pin = self.pmos_insts[-1].get_pin("S").rx() spacing = drc["{0}_to_{0}".format(self.route_layer)] - start = vector(start_pin, -1.5*spacing) - end = vector(end_pin, -1.5*spacing) + start = vector(start_pin, -2*spacing) + end = vector(end_pin, -2*spacing) self.vdd = self.add_layout_pin_segment_center("vdd", "m1", start, end) diff --git a/compiler/modules/rom_precharge_cell.py b/compiler/modules/rom_precharge_cell.py index d41f07c2..57477626 100644 --- a/compiler/modules/rom_precharge_cell.py +++ b/compiler/modules/rom_precharge_cell.py @@ -6,47 +6,41 @@ # All rights reserved. # -from openram.base import design +from .rom_base_cell import rom_base_cell from openram.base import vector from openram import OPTS from openram.sram_factory import factory from openram.tech import drc -class rom_precharge_cell(design): +class rom_precharge_cell(rom_base_cell): - def __init__(self, name="", cell_name=None, route_layer="m1"): + def __init__(self, name="", route_layer="m1"): - super().__init__(name, cell_name) - self.route_layer = route_layer - self.create_netlist() - self.create_layout() - - #self.route_layer= route_layer - #self.create_netlist() - #self.create_layout() + super().__init__(name=name, bitline_layer=route_layer) - def create_netlist(self): - self.add_pins() - self.add_pmos() - self.create_pmos() + + # def create_netlist(self): + # self.add_pins() + # self.add_modules() + # self.create_tx() def create_layout(self): - self.setup_layout_constants() - self.place_pmos() - self.add_boundary() + super().create_layout() + self.extend_well() - def add_pmos(self): + + def add_modules(self): self.pmos = factory.create(module_type="ptx", module_name="pre_pmos_mod", tx_type="pmos" ) - def create_pmos(self): + def create_tx(self): self.cell_inst = self.add_inst( name="precharge_pmos", mod=self.pmos, ) @@ -55,17 +49,15 @@ class rom_precharge_cell(design): def add_pins(self): pin_list = ["vdd", "gate", "bitline", "body"] - dir_list = ["POWER", "INPUT", "OUTPUT", "INPUT"] + dir_list = ["POWER", "INPUT", "OUTPUT", "POWER"] self.add_pin_list(pin_list, dir_list) - def setup_layout_constants(self): - - #pmos contact to gate distance - self.contact_to_gate = 0.5 * (self.pmos.width - 2 * self.pmos.contact_width - self.pmos.poly_width - 2 * self.active_enclose_contact) + def setup_drc_offsets(self): - #height offset to account for active-to-active spacing between adjacent bitlines - self.poly_extend_active_spacing = abs( 2 * self.pmos.poly_extend_active - drc("active_to_active") ) + self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active) + + #contact to contact distance, minimum cell width before drc offsets self.base_width = self.pmos.width - 2 * self.active_enclose_contact - self.pmos.contact_width @@ -77,30 +69,38 @@ class rom_precharge_cell(design): self.poly_tap_offset = (self.base_width - self.poly_contact.width - self.poly_active_offset) - drc("poly_to_poly") - def place_pmos(self): + def extend_well(self): + self.pmos - poly_offset = vector(self.poly_extend_active_spacing * 0.5 + self.pmos.height + 2 * self.poly_extend_active, 0.5 * self.pmos.width) + well_y = - (0.5 * self.nwell_width) + well_ll = vector(0, well_y) + # height = self.active_width + 2 * self.well_enclose_active + height = self.height + 0.5 * self.nwell_width + self.add_rect("nwell", well_ll, self.width , height) + # def place_tx(self): - # pmos_offset = vector(self.pmos.poly_extend_active, - 0.5 * self.pmos.contact_width - self.active_enclose_contact) - - # pmos_offset = vector(-self.pmos.poly_extend_active - self.poly_extend_active_spacing, 0) - pmos_offset = vector(self.pmos.poly_extend_active + self.pmos.height, 0) - # add rect of poly to account for offset from drc spacing - self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.pmos.poly_width ) + # pmos_offset = vector(self.pmos.poly_extend_active + self.pmos.height, 0) - self.cell_inst.place(pmos_offset, rotate=90) - # self.add_label("CELL ZERO", self.route_layer) - self.add_label("inst_zero", self.route_layer) - self.add_layout_pin_rect_center("S", self.route_layer, self.cell_inst.get_pin("S").center()) - self.add_layout_pin_rect_center("D", self.route_layer, self.cell_inst.get_pin("D").center()) + # self.cell_inst.place(pmos_offset, rotate=90) + # self.add_label("inst_zero", self.bitline_layer) + # self.add_layout_pin_rect_center("S", self.bitline_layer, self.cell_inst.get_pin("S").center()) + # self.add_layout_pin_rect_center("D", self.bitline_layer, self.cell_inst.get_pin("D").center()) - def add_boundary(self): - #cell width with offsets applied, height becomes width when the cells are rotated - self.width = self.pmos.height + self.poly_extend_active_spacing + 2 * self.pmos.poly_extend_active + # def place_poly(self): + # poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.rx() + self.poly_extend_active) + # poly_offset = vector(self.cell_inst.rx() + self.poly_extend_active, self.cell_inst.width * 0.5 ) - # cell height with offsets applied, width becomes height when the cells are rotated, if the offsets are positive (greater than 0) they are not applied - self.height = self.base_width - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0) + # start = poly_offset + # end = poly_offset + vector(poly_size, 0) + # self.add_segment_center("poly", start, end) + # def add_boundary(self): - super().add_boundary() + # #cell width with offsets applied, height becomes width when the cells are rotated + # self.width = self.pmos.height + self.poly_extend_active_spacing + 2 * self.pmos.poly_extend_active + + # # cell height with offsets applied, width becomes height when the cells are rotated, if the offsets are positive (greater than 0) they are not applied + # # self.height = self.base_width - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0) + + # super().add_boundary() diff --git a/compiler/modules/rom_wordline_driver_array.py b/compiler/modules/rom_wordline_driver_array.py new file mode 100644 index 00000000..9a19919c --- /dev/null +++ b/compiler/modules/rom_wordline_driver_array.py @@ -0,0 +1,119 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 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. +# +from openram import debug +from openram.base import design, drc +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import layer +from openram.tech import layer_properties as layer_props +from openram import OPTS + + +class rom_wordline_driver_array(design): + """ + Creates a Wordline Buffer/Inverter array + """ + + def __init__(self, name, rows, cols): + design.__init__(self, name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.rows = rows + self.cols = cols + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_drivers() + + def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.place_drivers() + self.route_layout() + self.route_supplies() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + # inputs to wordline_driver. + for i in range(self.rows): + self.add_pin("in_{0}".format(i), "INPUT") + # Outputs from wordline_driver. + for i in range(self.rows): + self.add_pin("out_{0}".format(i), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + b = factory.create(module_type="rom_base_cell") + + self.wl_driver = factory.create(module_type="pbuf_dec", + size=self.cols, + height=b.height, + add_wells=False) + + def route_supplies(self): + """ + Add a pin for each row of vdd/gnd which + are must-connects next level up. + """ + if layer_props.wordline_driver.vertical_supply: + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) + else: + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) + + def create_drivers(self): + self.wld_inst = [] + for row in range(self.rows): + self.wld_inst.append(self.add_inst(name="wld{0}".format(row), + mod=self.wl_driver)) + self.connect_inst(["in_{0}".format(row), + "out_{0}".format(row), + "vdd", "gnd"]) + + def place_drivers(self): + + for row in range(self.rows): + # These are flipped since we always start with an RBL on the bottom + y_offset = self.wl_driver.height * row + + offset = [0, y_offset] + + self.wld_inst[row].place(offset=offset) + + self.width = self.wl_driver.width + self.height = self.wl_driver.height * self.rows + + def route_layout(self): + """ Route all of the signals """ + route_width = drc["minwidth_{}".format(self.route_layer)] + for row in range(self.rows): + inst = self.wld_inst[row] + + self.copy_layout_pin(inst, "A", "in_{0}".format(row)) + + # output each WL on the right + wl_offset = inst.get_pin("Z").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) + + 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_array_test.py b/compiler/tests/05_rom_array_test.py index 790d8839..a7d70abe 100644 --- a/compiler/tests/05_rom_array_test.py +++ b/compiler/tests/05_rom_array_test.py @@ -25,7 +25,7 @@ class rom_array_test(openram_test): debug.info(2, "Testing 4x4 array for rom cell") - data = [[1, 0, 0, 0, 0, 1, 0, 0, 1], [0, 1, 1, 1, 0, 1, 0, 0, 1], [1, 0, 1, 1, 0, 1, 0, 0, 1], [1, 1, 0, 1, 1, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 1, 1, 1, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 0, 0, 1], [1, 1, 0, 0, 1, 1, 0, 0, 1]] + data = [[1, 0, 0, 1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0]] a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4, pitch_match=True) self.local_check(a) diff --git a/compiler/tests/05_rom_column_mux_array_test.py b/compiler/tests/05_rom_column_mux_array_test.py new file mode 100644 index 00000000..17915852 --- /dev/null +++ b/compiler/tests/05_rom_column_mux_array_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 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 sys, os +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class rom_column_mux_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 sample for 2-way rom column_mux_array") + a = factory.create(module_type="rom_column_mux_array", columns=16, word_size=8) + 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()) diff --git a/compiler/tests/05_rom_decoder_buffer_array_test.py b/compiler/tests/05_rom_decoder_buffer_array_test.py new file mode 100644 index 00000000..915dfe34 --- /dev/null +++ b/compiler/tests/05_rom_decoder_buffer_array_test.py @@ -0,0 +1,37 @@ +#!/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_decoder_buffer_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(2, "Testing 4 col decoder buffer for rom decoder") + + + a = factory.create(module_type="rom_address_control_array", cols=4) + 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()) diff --git a/compiler/tests/05_rom_decoder_test.py b/compiler/tests/05_rom_decoder_test.py index 9146ca42..48f977c8 100644 --- a/compiler/tests/05_rom_decoder_test.py +++ b/compiler/tests/05_rom_decoder_test.py @@ -25,7 +25,7 @@ class rom_decoder_test(openram_test): debug.info(2, "Testing 2x4 decoder for rom cell") - a = factory.create(module_type="rom_decoder", num_outputs=8, strap_spacing=2) + a = factory.create(module_type="rom_decoder", num_outputs=20, strap_spacing=2, cols=16) self.local_check(a) openram.end_openram() diff --git a/compiler/tests/05_rom_wordline_driver_array_test.py b/compiler/tests/05_rom_wordline_driver_array_test.py new file mode 100644 index 00000000..36144661 --- /dev/null +++ b/compiler/tests/05_rom_wordline_driver_array_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 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 sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class wordline_driver_array_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) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="rom_wordline_driver_array", rows=8, cols=32) + self.local_check(tx) + + 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())