mirror of https://github.com/VLSIDA/OpenRAM.git
column control and address precharge
This commit is contained in:
parent
ce8197d206
commit
f7aed247fd
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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])
|
||||
|
|
@ -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()])
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
@ -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])
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
Loading…
Reference in New Issue