column control and address precharge

This commit is contained in:
Jacob Walker 2023-01-16 16:15:03 -08:00
parent ce8197d206
commit f7aed247fd
22 changed files with 1479 additions and 520 deletions

View File

@ -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:

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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())

View File

@ -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)

View File

@ -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])

View File

@ -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()])

View File

@ -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

View File

@ -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
)

View File

@ -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]

View File

@ -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])

View File

@ -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)

View File

@ -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()

View File

@ -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))

View File

@ -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)

View File

@ -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())

View File

@ -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())

View File

@ -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()

View File

@ -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())