diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py index 1a76e431..1602a92b 100644 --- a/compiler/modules/rom_base_array.py +++ b/compiler/modules/rom_base_array.py @@ -12,25 +12,27 @@ from .bitcell_base_array import bitcell_base_array from base import vector from globals import OPTS from sram_factory import factory +import tech +from tech import drc class rom_base_array(bitcell_base_array): - def __init__(self, rows, cols, strap_spacing, bitmap, name="", column_offset=0): - + def __init__(self, rows, cols, strap_spacing, bitmap, name="", column_offset=0, route_layer="li", output_layer="m2"): super().__init__(name=name, rows=rows, cols=cols, column_offset=column_offset) - - #TODO: data is input in col-major order for ease of parsing, create a function to convert a row-major input to col-major - self.data = bitmap - self.route_layer = 'm1' - self.strap_spacing = strap_spacing - self.data_col_size = self.column_size - self.array_col_size = self.column_size + math.ceil(self.column_size / strap_spacing) + self.data = bitmap + self.route_layer = route_layer + self.output_layer = output_layer + self.strap_spacing = strap_spacing + self.data_col_size = self.column_size + if strap_spacing != 0: + self.array_col_size = self.column_size + math.ceil(self.column_size / strap_spacing) + else: + self.array_col_size = self.column_size self.create_all_bitline_names() self.create_all_wordline_names() self.create_netlist() - self.create_layout() @@ -38,31 +40,44 @@ class rom_base_array(bitcell_base_array): self.add_modules() self.add_pins() - self.create_instances() + self.create_cell_instances() + self.create_precharge_inst() def create_layout(self): self.place_array() + self.place_wordline_contacts() + self.place_bitline_contacts() + self.place_precharge() self.place_rails() - #self.route_bitlines() - #self.route_wordlines() + self.route_precharge() + self.add_boundary() + 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): - self.width = self.dummy.width * self.column_size - self.height = self.dummy.height * self.row_size - super().add_boundary() - - def add_modules(self): - - # dummy cell, # "dummy" cells represent 0 - self.dummy = factory.create(module_type="rom_dummy_cell", route_layer=self.route_layer) + ll = self.find_lowest_coords() + bottom_offset = - self.dummy.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 + def add_modules(self): + + # dummy cell, "dummy" cells represent 0 + self.dummy = factory.create(module_type="rom_dummy_cell", route_layer=self.route_layer) #base cell with no contacts self.cell_nc = factory.create(module_name="base_mod_0_contact", module_type="rom_base_cell") @@ -77,14 +92,24 @@ class rom_base_array(bitcell_base_array): self.zero_tap = factory.create(module_type="rom_poly_tap", strap_length=0) - self.gnd_rail = factory.create(module_type="rom_array_gnd_tap", length=self.row_size) - def create_instances(self): + self.precharge_array = factory.create(module_type="rom_precharge_array", cols=self.column_size, strap_spacing=self.strap_spacing, route_layer=self.route_layer) + + + def add_pins(self): + for bl_name in self.get_bitline_names(): + self.add_pin(bl_name, "INOUT") + for wl_name in self.get_wordline_names(): + self.add_pin(wl_name, "INPUT") + self.add_pin("precharge_gate", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_cell_instances(self): self.tap_inst = {} self.cell_inst = {} self.cell_list = [] self.current_row = 0 - #list of current bitline interconnect nets, starts as the same as the bitline list and is updated when new insts of cells are added int_bl_list = self.bitline_names[0] #When rotated correctly rows are word lines @@ -93,62 +118,61 @@ 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 strap_offset = 0 - #when rotated correctly cols are bit lines - for col in range(self.array_col_size): + # when rotated correctly cols are bit lines + for col in range(self.column_size): - name = "bit_r{0}_c{1}".format(row, col) - - data_col = col - strap_offset if col % self.strap_spacing == 0: name = "tap_r{0}_c{1}".format(row, col) - print("tap instance added at c{0}, r{1}".format(col, row)) - self.cell_inst[row, col]=self.add_inst(name=name, mod=self.poly_tap) + #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([]) strap_offset += 1 - continue + + name = "bit_r{0}_c{1}".format(row, col) - if self.data[row][data_col] == 1: + 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 - #(row == 0 and self.data[row + 1][col] == 0): - if (row < self.row_size - 1 and row > 0 and self.data[row + 1][data_col] == 0 and self.data[row - 1][data_col] == 0) or \ - (row == self.row_size - 1 and self.data[row - 1][data_col] == 0): + 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): + + new_inst = self.add_inst(name=name, mod=self.cell_ac) - self.cell_inst[row, col]=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][data_col] == 0): - self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell_sc) + elif (row > 0 and self.data[row - 1][col] == 0) or \ + (row == 0): - elif (row < self.row_size - 1 and self.data[row + 1][data_col] == 0) or \ + 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): - self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell_dc) + new_inst=self.add_inst(name=name, mod=self.cell_dc) else: - self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell_nc) + 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, data_col) == -1: + if row == self.row_size - 1 or self.get_next_cell_in_bl(row, col) == -1: - bl_l = int_bl_list[data_col] + bl_l = int_bl_list[col] bl_h = "gnd" else: - bl_l = int_bl_list[data_col] - int_bl_list[data_col] = "bl_int_{0}_{1}".format(row, data_col) - bl_h = int_bl_list[data_col] + bl_l = int_bl_list[col] + int_bl_list[col] = "bl_int_{0}_{1}".format(row, col) + bl_h = 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: - - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.dummy) + 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 @@ -156,18 +180,30 @@ class rom_base_array(bitcell_base_array): # - row_list.append(self.cell_inst[row, col]) + row_list.append(new_inst) name = "tap_r{0}_c{1}".format(row, self.array_col_size) #print(*row_list) - self.cell_inst[row, self.array_col_size]=self.add_inst(name=name, mod=self.zero_tap) + self.tap_inst[row, self.column_size]=self.add_inst(name=name, mod=self.zero_tap) self.connect_inst([]) self.cell_list.append(row_list) + def create_precharge_inst(self): + prechrg_pins = [] + + for bl in range(self.column_size): + prechrg_pins.append(self.bitline_names[0][bl]) + + prechrg_pins.append("precharge_gate") + prechrg_pins.append("vdd") + self.precharge_inst = self.add_inst(name="decode_array_precharge", mod=self.precharge_array) + self.connect_inst(prechrg_pins) + + def create_all_bitline_names(self): for col in range(self.column_size): @@ -176,110 +212,138 @@ class rom_base_array(bitcell_base_array): # Make a flat list too self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] - def place_taps(self): - - self.tap_pos = {} - for row in range(self.row_size): - for col in range(0, self.column_size, self.strap_spacing): - - - tap_x = self.dummy.width * col - tap_y = self.dummy.height * row - - self.tap_pos[row, col] = vector(tap_x, tap_y) - self.tap_inst[row, col].place(self.tap_pos[row, col]) - tap_x = self.dummy.width * self.column_size + self.poly_tap.width - tap_y = self.dummy.height * row - - self.tap_pos[row, self.column_size] = vector(tap_x, tap_y) - self.tap_inst[row, self.column_size].place(self.tap_pos[row, self.column_size]) - def place_rails(self): + + width = drc("minwidth_" + self.route_layer) + drc_rule = "{0}_to_{0}".format(self.route_layer) + spacing = drc(drc_rule) - rail_y = self.dummy.height * (self.row_size) + self.mcon_width * 0.5 - start_x = self.cell_inst[self.row_size - 1, 0].rx() - end_x = self.cell_inst[self.row_size - 1, self.array_col_size - 1].cx() + 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) - self.add_layout_pin_rect_ends( name="gnd", + 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"]) + + prechrg_vdd = self.precharge_inst.get_pin("vdd") + + def place_array(self): self.cell_pos = {} - + self.strap_pos = {} # rows are wordlines for row in range(self.row_size): - strap_cols = -1 + # strap_cols = -1 + cell_y = row * (self.dummy.height) - # columns are bit lines - for col in range(self.array_col_size): - + cell_x = 0 + + for col in range(self.column_size): + if col % self.strap_spacing == 0: - strap_cols += 1 - rot = 0 - else: - rot = 90 - - bit_cols = col - strap_cols - - if col == 0: - cell_x = 0 - else: - cell_x = (self.dummy.width * bit_cols) + (self.poly_tap.width * strap_cols) - + 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 + self.cell_pos[row, col] = vector(cell_x, cell_y) - self.cell_inst[row, col].place(self.cell_pos[row, col], rotate=rot) + self.cell_inst[row, col].place(self.cell_pos[row, col]) + cell_x += self.dummy.width + self.add_label("debug", "li", self.cell_pos[row, col]) - strap_cols += 1 - bit_cols = self.array_col_size - strap_cols - cell_x = (self.dummy.width * bit_cols) + (self.poly_tap.width * strap_cols) - self.cell_pos[row, self.array_col_size] = vector(cell_x, cell_y) - self.cell_inst[row, self.array_col_size].place(self.cell_pos[row, self.array_col_size]) - + + 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]) + + + # tap_pin = self.cell_inst[row, self.array_col_size].get_pin("poly_tap").center() + # self.add_layout_pin_rect_center("wl{}".format(row), "m2", tap_pin) + + + 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_inst.place(offset=self.precharge_offset) + + self.copy_layout_pin(self.precharge_inst, "vdd") + + + def place_wordline_contacts(self): + + width = drc["minwidth_{}".format(self.route_layer)] + + height = drc["minwidth_{}".format(self.route_layer)] + + 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) + + corrected_offset = offset - vector(0.5 * width, 0.5 * height) + # self.create_horizontal_pin_bus(self.route_layer, offset=corrected_offset, names=self.wordline_names[0], pitch=self.dummy.height, length=None) + + def place_bitline_contacts(self): + + src_pin = self.cell_nc.source_pos + + for bl in range(self.column_size): - def route_bitlines(self): + # self.copy_layout_pin(self.cell_list[0][bl], "S", self.bitline_names[0][bl]) - #get first nmos in col + src_pin = self.cell_list[0][bl].get_pin("S") + prechg_pin_name = "pre_bl{0}_out".format(bl) + pre_pin = self.precharge_inst.get_pin(prechg_pin_name) - #connect to main bitline wire + + # offset = src_pin_offset + vector(src_pin.x, 0) + - #get next nmos in col + middle_offset = (pre_pin.cy() - src_pin.cy()) * 0.5 - #route source to drain + corrected = vector(src_pin.cx(), src_pin.cy() - middle_offset) + self.add_via_stack_center(corrected, self.route_layer, self.output_layer) + self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.output_layer, corrected ) - #loop + + # self.gnd[0].y() - for col in range(self.column_size): - for row in range(self.row_size ): - - #nmos at this position and another nmos further down - if self.data[row][col] == 1 : - - next_row = self.get_next_cell_in_bl(row, col) - if next_row != -1: - - drain_pin = self.cell_inst[row, col].get_pin("D") - source_pin = self.cell_inst[next_row, col].get_pin("S") - - source_pos = source_pin.bc() - drain_pos = drain_pin.bc() - self.add_path(self.route_layer, [drain_pos, source_pos]) - - + def route_precharge(self): + for bl in range(self.column_size): + bl_pin = self.cell_list[0][bl].get_pin("S") + prechg_pin = "pre_bl{0}_out".format(bl) + pre_out_pin = self.precharge_inst.get_pin(prechg_pin) + bl_start = bl_pin.center() + bl_end = vector(bl_start.x, pre_out_pin.cy()) + self.add_segment_center(self.route_layer, bl_start, bl_end) def get_next_cell_in_bl(self, row_start, col): for row in range(row_start + 1, self.row_size): diff --git a/compiler/modules/rom_base_bank.py b/compiler/modules/rom_base_bank.py new file mode 100644 index 00000000..b9cb090e --- /dev/null +++ b/compiler/modules/rom_base_bank.py @@ -0,0 +1,177 @@ + +import math +from base import vector +from base import design +from globals import OPTS +from sram_factory import factory +import tech +from tech import drc + +class rom_base_bank(design): + + def __init__(self, strap_spacing=0, data_file=None, name="") -> None: + self.rows = 4 + self.cols = 4 + self.num_inputs = 2 + 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" + self.bus_layer = "m1" + self.interconnect_layer = "m2" + + + + super().__init__(name=name) + self.setup_layout_constants() + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.create_instances() + + def create_layout(self): + self.place_instances() + self.create_wl_bus() + self.route_decode_outputs() + self.route_array_inputs() + + self.route_supplies() + self.height = self.array_inst.height + self.width = self.array_inst.width + self.add_boundary() + + + def setup_layout_constants(self): + self.route_layer_width = drc["minwidth_{}".format(self.route_layer)] + self.route_layer_pitch = drc["{0}_to_{0}".format(self.route_layer)] + self.bus_layer_width = drc["minwidth_{}".format(self.bus_layer)] + self.bus_layer_pitch = drc["{0}_to_{0}".format(self.bus_layer)] + + + 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) + self.decode_array = factory.create(module_type="rom_decoder", num_outputs=self.rows, strap_spacing=self.strap_spacing, route_layer=self.route_layer) + + + + def create_instances(self): + array_pins = [] + decode_pins = [] + + for bl in range(self.cols): + name = "bl_{}".format(bl) + array_pins.append(name) + for wl in range(self.rows): + name = "wl_{}".format(wl) + array_pins.append(wl) + + array_pins.append("array_precharge") + array_pins.append("vdd") + array_pins.append("gnd") + + + for addr in range(self.num_inputs): + name = "addr_{}".format(addr) + decode_pins.append(name) + for wl in range(self.rows): + name = "wl_{}".format(wl) + decode_pins.append(name) + + decode_pins.append("decode_precharge") + decode_pins.append("vdd") + decode_pins.append("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.connect_inst(decode_pins) + + + + def place_instances(self): + + array_x = self.decode_inst.width + (self.rows + 2) * ( self.route_layer_width + self.route_layer_pitch ) + array_y = self.array.height + + self.array_offset = vector(array_x ,array_y) + self.decode_offset = vector(0, 0) + + self.array_inst.place(offset=self.array_offset, mirror="MX") + + self.decode_inst.place(offset=self.decode_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)] ) + bus_y = self.array_inst.by() + self.bus_layer_pitch + self.bus_layer_width + self.wl_interconnects = [] + + for wl in range(self.rows): + self.wl_interconnects.append("wl_interconnect_{}".format(wl)) + + self.wl_bus = self.create_vertical_bus(self.bus_layer, vector(bus_x, bus_y), self.wl_interconnects, self.decode_inst.uy() - self.array_inst.by() ) + + def route_decode_outputs(self): + + for wl in range(self.rows): + decode_output = self.decode_array.output_names[wl] + decode_out_pin = self.decode_inst.get_pin(decode_output) + + wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]] + + start = decode_out_pin.center() + end = vector(wl_bus_wire.cx(), start.y) + + self.add_segment_center(self.interconnect_layer, start, end) + self.add_via_stack_center(end, self.route_layer, self.interconnect_layer ) + + + def route_array_inputs(self): + + for wl in range(self.rows): + 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]] + + end = array_wl_pin.center() + start = vector(wl_bus_wire.cx(), end.y) + + self.add_segment_center(self.interconnect_layer, start, end) + self.add_via_stack_center(start, self.route_layer, self.interconnect_layer ) + + + def route_supplies(self): + gnd_start = vector(self.array_inst.get_pins("gnd")[0].cx(),0) + print() + print(self.decode_inst.get_pin("gnd").center()) + decode_gnd = self.decode_inst.get_pin("gnd") + decode_vdd = self.decode_inst.get_pin("vdd") + array_vdd = self.array_inst.get_pin("vdd") + + self.add_segment_center("m1", gnd_start, decode_gnd.center()) + + + + self.add_power_pin("gnd", decode_vdd.center()) + self.add_power_pin("vdd", decode_gnd.center()) + + vdd_start = vector(array_vdd.lx() + 0.5 * self.via1_space, array_vdd.cy()) + end = vector(decode_vdd.lx(), vdd_start.y) + + self.add_segment_center(self.interconnect_layer, vdd_start, end) + self.add_via_stack_center(vdd_start, "m1", self.interconnect_layer) + + vdd_start = vector(decode_vdd.cx(), vdd_start.y) + + self.add_segment_center(self.interconnect_layer, vdd_start, decode_vdd.center()) + + + + + + + + diff --git a/compiler/modules/rom_base_cell.py b/compiler/modules/rom_base_cell.py index 8c0ce4e2..ced516ec 100644 --- a/compiler/modules/rom_base_cell.py +++ b/compiler/modules/rom_base_cell.py @@ -6,10 +6,7 @@ # All rights reserved. # - -import math from .rom_dummy_cell import rom_dummy_cell -from base import design from base import vector from globals import OPTS from sram_factory import factory @@ -26,9 +23,7 @@ class rom_base_cell(rom_dummy_cell): #self.create_layout() - def create_netlist(self): - print("using base cell netlist creation") - + def create_netlist(self): self.add_pins() self.add_nmos() self.create_nmos() @@ -39,9 +34,6 @@ class rom_base_cell(rom_dummy_cell): self.place_nmos() self.add_boundary() - print(self.height) - print(self.width) - def create_nmos(self): self.cell_inst = self.add_inst( name=self.name + "_nmos", @@ -58,19 +50,30 @@ class rom_base_cell(rom_dummy_cell): def place_nmos(self): - - poly_offset = vector(0.5 * self.nmos.active_contact.width + self.nmos.active_contact_to_gate, - self.nmos.poly_height) + # 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) + # 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) # add rect of poly to account for offset from drc spacing - self.add_rect("poly", poly_offset, self.nmos.poly_width, self.poly_extend_active_spacing ) + self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.nmos.poly_width) - self.cell_inst.place(nmos_offset) - self.add_label("CELL ZERO", self.route_layer) - self.add_label("S", self.route_layer, self.cell_inst.get_pin("S").center()) - self.add_label("D", self.route_layer, self.cell_inst.get_pin("D").center()) + self.cell_inst.place(nmos_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() + # if self.add_source_contact != False: + # # drain_x = 0 + # # drain_y = 0.5 * (self.width - self.poly_extend_active_spacing) + + + # print("drained") + # print(drain_pos) + # self.add_layout_pin_rect_center("S", self.route_layer, drain_pos) + # self.add_label("S", self.route_layer, self.cell_inst.get_pin("S").center()) diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py new file mode 100644 index 00000000..024df34d --- /dev/null +++ b/compiler/modules/rom_decoder.py @@ -0,0 +1,249 @@ +# 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 math +from base import design +from sram_factory import factory +from base import vector +from globals import OPTS +import tech +from tech import drc + + +class rom_decoder(design): + def __init__(self, num_outputs, strap_spacing, name="", route_layer="li", 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 + # array gets rotated 90deg so that rows/cols switch + self.strap_spacing=strap_spacing + self.num_outputs = num_outputs + self.num_inputs = math.ceil(math.log(num_outputs, 2)) + self.create_decode_map() + + for i in range(2 * self.num_inputs): print(self.decode_map[i]) + + super().__init__(name) + + b = factory.create(module_type=OPTS.bitcell) + self.cell_height = b.height + self.route_layer = route_layer + self.output_layer = output_layer + self.inv_route_layer = "m1" + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_instances() + + + def create_layout(self): + 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.connect_inputs() + self.route_supplies() + self.add_boundary() + + def create_decode_map(self): + self.decode_map = [] + # create decoding map that will be the bitmap for the rom decoder + # row/col order in the map will be switched in the placed decoder/ + for col in range(self.num_inputs): + + # odd cols are address + # even cols are address bar + col_array = [] + inv_col_array = [] + for row in range(self.num_outputs): + + addr_idx = -col - 1 + + addr = format(row, 'b') + if col >= len(addr) : + bin_digit = 0 + else: + bin_digit = int(addr[addr_idx]) + + col_array.append(bin_digit) + # print("addr {0}, at indx {1}, digit {2}".format(addr, addr_idx, bin_digit)) + + if bin_digit == 0 : inv_col_array.append(1) + else : inv_col_array.append(0) + + + + self.decode_map.append(col_array) + self.decode_map.append(inv_col_array) + self.decode_map.reverse() + + + def add_pins(self): + for i in range(self.num_inputs): + self.add_pin("in_{0}".format(i), "INPUT") + + for j in range(self.num_outputs): + self.add_pin("out_{0}".format(j), "OUTPUT") + self.add_pin("precharge_gate", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + + def add_modules(self): + + self.inv_array = factory.create(module_type="rom_inv_array", cols=self.num_inputs) + + self.array_mod = factory.create(module_type="rom_base_array", \ + module_name="rom_decode_array", \ + cols=self.num_outputs, \ + rows=2 * self.num_inputs, \ + bitmap=self.decode_map, + strap_spacing = self.strap_spacing, + route_layer=self.route_layer, + output_layer=self.output_layer) + + + def create_instances(self): + + self.create_input_inverters() + self.create_array_inst() + + + def create_input_inverters(self): + name = "pre_inv_array" + self.inv_inst = self.add_inst(name=name, mod=self.inv_array) + + inv_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) + + + def create_array_inst(self): + self.array_inst = self.add_inst(name="decode_array_inst", mod=self.array_mod) + + array_pins = [] + + for j in range(self.num_outputs): + name = "out_{0}".format(j) + array_pins.append(name) + + + for i in reversed(range(self.num_inputs)): + array_pins.append("inbar_{0}".format(i)) + array_pins.append("in_{0}".format(i)) + array_pins.append("precharge_gate") + array_pins.append("vdd") + array_pins.append("gnd") + self.connect_inst(array_pins) + + + def place_input_inverters(self): + print(self.array_inst.ll().x) + 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) + self.array_inst.place(offset, rotate=90) + + def create_outputs(self): + + self.output_names = [] + self.outputs = [] + for j in range(self.num_outputs): + name = "out_{0}".format(j) + self.output_names.append(name) + + + 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): + + for i in range(self.num_inputs): + wl = self.num_inputs * 2 - 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_start = inv_in_pin.center() + addr_end = vector(addr_start.x, addr_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) + + def route_supplies(self): + minwidth = drc["minwidth_{}".format(self.inv_route_layer)] + pitch = drc["{0}_to_{0}".format(self.inv_route_layer)] + + + # 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] + + end = vector(array_vdd.cx(), inv_vdd.cy() - 0.5 * minwidth) + self.add_segment_center("m1", array_vdd.center(), end) + end = vector(array_vdd.cx() + 0.5 * minwidth, inv_vdd.cy()) + self.add_segment_center(self.route_layer, inv_vdd.center(), end) + + end = vector(array_vdd.cx(), inv_vdd.cy()) + self.add_via_stack_center(end, self.route_layer, "m1") + self.add_layout_pin_rect_center("vdd", "m1", end) + + # route pin on inv gnd + + inv_gnd = self.inv_inst.get_pins("gnd")[0] + array_gnd = self.array_inst.get_pins("gnd") + + # add x jog + + start = vector(array_gnd[0].cx(), inv_gnd.cy()) + self.add_via_stack_center(start, self.route_layer, "m1") + self.add_layout_pin_rect_center("gnd", "m1", start) + + end = array_gnd[0].center() + self.add_segment_center("m1", start, end) + # add y jog + + + width = minwidth + height = array_gnd[0].uy() - array_gnd[-1].uy() + minwidth + + offset = vector(-0.5 *width ,0.5 * (array_gnd[0].cy() + array_gnd[-1].cy())) + + + + # self.add_rect_center(self.route_layer, offset, width, height) + + + start = end - vector(0, 0.5 * minwidth) + end = vector(start.x, array_gnd[1].uy()) + # self.add_segment_center("m1", start, end) + + + diff --git a/compiler/modules/rom_dummy_cell.py b/compiler/modules/rom_dummy_cell.py index 67384a83..b6f8f27b 100644 --- a/compiler/modules/rom_dummy_cell.py +++ b/compiler/modules/rom_dummy_cell.py @@ -41,9 +41,7 @@ class rom_dummy_cell(design): self.add_boundary() self.add_poly() self.add_metal() - print(self.height) - print(self.width) - self.add_label("0,0", self.route_layer) + #self.add_label("0,0", self.route_layer) @@ -65,8 +63,6 @@ class rom_dummy_cell(design): #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 @@ -77,8 +73,8 @@ class rom_dummy_cell(design): def add_boundary(self): #cell width with offsets applied, height becomes width when the cells are rotated + # self.width = self.nmos.height + self.poly_extend_active_spacing + 2 * self.nmos.poly_extend_active self.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 self.height = self.base_width - min(self.cell_diffusion_offset, 0) - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0) @@ -88,18 +84,22 @@ class rom_dummy_cell(design): def add_poly(self): - poly_x = 0.5 * self.nmos.contact_width + self.contact_to_gate + poly_x = 0.5 * (self.nmos.poly_height + self.poly_extend_active_spacing) + # 0.5 * self.nmos.contact_width + self.contact_to_gate - self.poly = self.add_rect("poly", vector(poly_x, 0), self.poly_width, self.nmos.poly_height + self.poly_extend_active_spacing) - print(self.poly_width, self.height) + self.poly = self.add_rect_center("poly", vector(poly_x, self.base_width * 0.5), 2 * poly_x, self.poly_width) def add_metal(self): - wire_x = min(self.cell_diffusion_offset, 0) + min(self.poly_active_offset, 0) - self.mcon_width * 0.5 - wire_y = 0.5 * (self.width - self.poly_extend_active_spacing) + if self.route_layer == "li": + via = "mcon" + else: + via = "via{}".format(self.route_layer[len(self.route_layer) - 1]) + wire_y = self.height + drc["minwidth_{}".format(via)] * 0.5 + wire_x = 0.5 * (self.width - self.poly_extend_active_spacing) - wire_start = vector( wire_x, wire_y ) - wire_end = vector(self.height + self.mcon_width * 0.5, wire_y) + wire_start = vector( wire_x, 0) + wire_end = vector(wire_x, wire_y) # if self.route_layer == 'm1': @@ -109,11 +109,18 @@ class rom_dummy_cell(design): # self.add_via_center(self.li_stack, [self.width, wire_y]) self.add_path(self.route_layer, [wire_start, wire_end]) + + # drain_x = 0 + # drain_y = 0.5 * (self.width) + source_x = 0.5 * (self.width - self.poly_extend_active_spacing) + source_y = 0 + source_pos = vector(source_x, source_y) + self.add_layout_pin_rect_center("S", self.route_layer, source_pos) + + drain_pos = vector(source_x, self.height) + 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: diff --git a/compiler/modules/rom_inv_array.py b/compiler/modules/rom_inv_array.py new file mode 100644 index 00000000..9e54044f --- /dev/null +++ b/compiler/modules/rom_inv_array.py @@ -0,0 +1,130 @@ +# 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 base import design +from sram_factory import factory +from base import vector +from 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) + # 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: + print("TAP ME DOWN") + 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 765a0e17..57f97ca5 100644 --- a/compiler/modules/rom_poly_tap.py +++ b/compiler/modules/rom_poly_tap.py @@ -2,54 +2,94 @@ from base import design from base import vector +from globals import OPTS from sram_factory import factory class rom_poly_tap(design): - def __init__(self, name, strap_length=0, cell_name=None, prop=None, strap_layer="m2"): + def __init__(self, name, strap_length=0, cell_name=None, prop=None, tx_type="nmos", strap_layer="m1"): super().__init__(name, cell_name, prop) self.strap_layer=strap_layer self.length = strap_length + self.tx_type = tx_type self.create_netlist() self.create_layout() def create_netlist(self): - - #for layout constants self.dummy = factory.create(module_type="rom_dummy_cell") + self.pmos = factory.create(module_type="ptx", tx_type="pmos") def create_layout(self): self.place_via() + # if self.tx_type == "pmos": + self.extend_poly() self.add_boundary() if self.length != 0: - self.place_strap(self.length) + self.place_strap() + def add_boundary(self): self.height = self.dummy.height - self.width = self.poly_contact.width + self.width = self.poly_contact.width + 2 * self.contact_x_offset super().add_boundary() def place_via(self): - - contact_width = self.poly_contact.width - contact_x = - contact_width * 0.5 - contact_y = self.dummy.poly.offset.x + (self.poly_width * 0.5) + contact_width = self.poly_contact.width + # DRC rule here is hard coded since licon.9 isnt included in skywater130 tech file - contact_offset = vector(contact_x, contact_y) + # poly contact spacing to P-diffusion < 0.235um (licon.9 + psdm.5a) + if OPTS.tech_name == "sky130": + self.contact_x_offset = 0.235 - (contact_width - self.pmos.contact_width) * 0.5 - self.poly_extend_active + else: + assert(False) + + if self.tx_type == "nmos": + + contact_y = self.dummy.poly.cy() + # contact_y = self.dummy.poly.offset.x + (self.poly_width * 0.5) + # self.contact_x_offset = 0 + else: + contact_y = self.pmos.poly_positions[0].x - self.pmos.active_offset.x + print(self.tx_type) + print(contact_y) + + # contact_x = - contact_width * 0.5 - self.contact_x_offset + contact_x = contact_width * 0.5 + self.contact_x_offset + self.contact_offset = vector(contact_x, contact_y) + print("polycule") + print(self.contact_offset) self.via = self.add_via_stack_center(from_layer="poly", to_layer=self.strap_layer, - offset=contact_offset) + offset=self.contact_offset) + self.add_layout_pin_rect_center("via", self.strap_layer, self.contact_offset) + # if self.length == 0: + # self.add_layout_pin_rect_center(text="poly_tap", + # layer=self.strap_layer, + # offset=print()contact_offset, + # ) - self.add_label("ZERO", "poly") - def place_strap(self, length): - strap_start = vector(self.via.cx(), self.via.cy()) + def place_strap(self): - strap_end = vector( self.dummy.width * (length - 1) + self.m2_width, self.via.cy()) + strap_start = vector(self.via.lx() , self.via.cy()) + + strap_end = vector( self.dummy.width * (self.length + 1), self.via.cy()) self.strap = self.add_path(self.strap_layer, (strap_start, strap_end)) + def extend_poly(self): + poly_x = self.poly_contact.width + self.contact_x_offset + poly_y = self.contact_offset.y - self.poly_width * 0.5 + extend_offset = vector(poly_x, poly_y) + self.add_rect("poly", extend_offset, self.contact_x_offset, self.poly_width) + + poly_x = 0 + extend_offset = vector(poly_x, poly_y) + + self.add_rect("poly", extend_offset, self.contact_x_offset, self.poly_width) + + diff --git a/compiler/modules/rom_precharge_array.py b/compiler/modules/rom_precharge_array.py new file mode 100644 index 00000000..172f4670 --- /dev/null +++ b/compiler/modules/rom_precharge_array.py @@ -0,0 +1,201 @@ +# 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 math +from base import geometry +from base import design +from sram_factory import factory +from base import vector +from tech import layer, drc + + + +class rom_precharge_array(design): + """ + An array of inverters to create the inverted address lines for the rom decoder + """ + def __init__(self, cols, pmos_size=None, name="", route_layer="li", strap_spacing=None): + self.cols = cols + self.route_layer = route_layer + if name=="": + name = "rom_inv_array_{0}".format(cols) + # if pmos_size == None: + # self.pmos_size = dff.height * 0.5 + # else: + # self.pmos_size = inv_size + if strap_spacing != None: + self.strap_spacing = strap_spacing + else: + self.strap_spacing = 0 + + if "li" in layer: + self.inv_layer = "li" + else: + self.inv_layer = "m1" + + + if strap_spacing != 0: + self.num_straps = math.ceil(self.cols / self.strap_spacing) + self.array_col_size = self.cols + self.num_straps + else: + self.num_straps = 0 + self.array_col_size = self.cols + + 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.pmos.width + self.num_straps * self.poly_tap.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) + ur = self.find_highest_coords() + 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.height = ur.y + + def create_modules(self): + + self.pmos = factory.create(module_type="rom_precharge_cell", module_name="precharge_cell", route_layer=self.route_layer) + + # 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") + + def add_pins(self): + for col in range(self.cols): + self.add_pin("pre_bl{0}_out".format(col), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gate", "INPUT") + + def create_instances(self): + self.array_insts = [] + self.pmos_insts = [] + self.tap_insts = [] + 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([]) + 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) + + + + def place_instances(self): + self.add_label("ZERO", self.route_layer) + + self.array_pos = [] + strap_num = 0 + cell_y = 0 + # columns are bit lines4 + cell_x = 0 + print("starting array place") + + 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 + + self.pmos_insts[col].place(vector(cell_x, cell_y)) + self.add_label("debug", "li", vector(cell_x, cell_y)) + cell_x += self.pmos.width + + + def create_layout_pins(self): + for col in range(self.cols): + source_pin = self.pmos_insts[col].get_pin("D") + bl = "pre_bl{0}_out".format(col) + 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 + contact_x = self.pmos_insts[self.cols - 1].rx() + self.active_space + contact_offset = vector(contact_x, self.pmos.height * 0.5) + + self.nwell_contact = self.add_via_center(layers=layer_stack, + offset=contact_offset, + implant_type="n", + well_type="n", + 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) + + self.vdd = self.add_layout_pin_segment_center("vdd", "m1", start, end) + + for i in range(self.cols): + start = self.pmos_insts[i].get_pin("S").center() + end = vector(start.x, self.vdd.cy()) + + self.add_segment_center(self.route_layer, start, end) + self.add_via_stack_center(end, self.route_layer, "m1") + + + # connect nwell tap to vdd + + start = end + end = vector(self.nwell_contact.cx(), start.y) + self.add_segment_center(self.route_layer, start, end) + + start = end - vector(0, 0.5 * self.mcon_width) + end = self.nwell_contact.center() + self.add_segment_center(self.route_layer, start, end) + diff --git a/compiler/modules/rom_precharge_cell.py b/compiler/modules/rom_precharge_cell.py new file mode 100644 index 00000000..ca890b80 --- /dev/null +++ b/compiler/modules/rom_precharge_cell.py @@ -0,0 +1,107 @@ +# 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 base import design +from base import vector +from globals import OPTS +from sram_factory import factory + +from tech import drc + + +class rom_precharge_cell(design): + + def __init__(self, name="", cell_name=None, 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() + + + def create_netlist(self): + self.add_pins() + self.add_pmos() + self.create_pmos() + + + def create_layout(self): + self.setup_layout_constants() + self.place_pmos() + self.add_boundary() + + + def add_pmos(self): + + self.pmos = factory.create(module_type="ptx", + module_name="pre_pmos_mod", + tx_type="pmos" + ) + + def create_pmos(self): + self.cell_inst = self.add_inst( name="precharge_pmos", + mod=self.pmos, + ) + self.connect_inst(["bitline", "gate", "vdd", "vdd"]) + + + def add_pins(self): + pin_list = ["vdd", "gate", "bitline", "vdd"] + dir_list = ["OUTPUT", "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) + + #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") ) + + #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 + + # 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 - (self.poly_width + 2 * self.active_enclose_contact + self.pmos.contact_width)) - self.poly_to_active + + #so that the poly taps are far enough apart + self.poly_tap_offset = (self.base_width - self.poly_contact.width - self.poly_active_offset) - drc("poly_to_poly") + + + def place_pmos(self): + + poly_offset = vector(self.poly_extend_active_spacing * 0.5 + self.pmos.height + 2 * self.poly_extend_active, 0.5 * self.pmos.width) + + # 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 ) + + 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()) + + 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 + + # 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/tests/05_rom_array_test.py b/compiler/tests/05_rom_array_test.py index fe75d054..1228dd9f 100644 --- a/compiler/tests/05_rom_array_test.py +++ b/compiler/tests/05_rom_array_test.py @@ -29,7 +29,6 @@ class rom_array_test(openram_test): a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4) self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/05_rom_base_bank_test.py b/compiler/tests/05_rom_base_bank_test.py new file mode 100644 index 00000000..d2caacad --- /dev/null +++ b/compiler/tests/05_rom_base_bank_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 globals +from globals import OPTS +from sram_factory import factory +import debug + + +class rom_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Testing 4x4 array for rom cell") + + a = factory.create(module_type="rom_base_bank", strap_spacing = 2) + + self.local_check(a) + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/05_rom_decoder_test.py b/compiler/tests/05_rom_decoder_test.py new file mode 100644 index 00000000..d1a3875a --- /dev/null +++ b/compiler/tests/05_rom_decoder_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 globals +from globals import OPTS +from sram_factory import factory +import debug + + +class rom_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Testing 2x4 decoder for rom cell") + + + a = factory.create(module_type="rom_decoder", num_outputs=8, strap_spacing=2) + self.local_check(a) + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_rom_precharge_array_test.py b/compiler/tests/05_rom_precharge_array_test.py new file mode 100644 index 00000000..847ffaf2 --- /dev/null +++ b/compiler/tests/05_rom_precharge_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 globals +from globals import OPTS +from sram_factory import factory +import debug + + +class rom_precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Testing precharge array for rom cell") + + + a = factory.create(module_type="rom_precharge_array", cols=4, strap_spacing=2) + self.local_check(a) + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner())