mirror of https://github.com/VLSIDA/OpenRAM.git
pitch match decoder and array
This commit is contained in:
parent
e697efa5f6
commit
ce8197d206
|
|
@ -16,11 +16,12 @@ from openram.tech import drc
|
|||
|
||||
class rom_base_array(bitcell_base_array):
|
||||
|
||||
def __init__(self, rows, cols, strap_spacing, bitmap, name="", column_offset=0, route_layer="li", output_layer="m2"):
|
||||
def __init__(self, rows, cols, strap_spacing, bitmap, name="", column_offset=0, route_layer="li", output_layer="m2", pitch_match=False):
|
||||
|
||||
super().__init__(name=name, rows=rows, cols=cols, column_offset=column_offset)
|
||||
|
||||
self.data = bitmap
|
||||
self.pitch_match = pitch_match
|
||||
self.route_layer = route_layer
|
||||
self.output_layer = output_layer
|
||||
self.strap_spacing = strap_spacing
|
||||
|
|
@ -44,14 +45,18 @@ class rom_base_array(bitcell_base_array):
|
|||
|
||||
|
||||
def create_layout(self):
|
||||
self.create_layout_constants()
|
||||
self.place_array()
|
||||
if self.pitch_match:
|
||||
self.route_pitch_offsets()
|
||||
self.place_precharge()
|
||||
self.place_wordline_contacts()
|
||||
self.place_bitline_contacts()
|
||||
self.place_precharge()
|
||||
|
||||
|
||||
self.place_rails()
|
||||
self.route_precharge()
|
||||
|
||||
|
||||
self.add_boundary()
|
||||
self.add_label("ARRAY ZERO", self.route_layer)
|
||||
self.add_label("array height", self.route_layer, [0, self.height])
|
||||
|
|
@ -95,6 +100,12 @@ class rom_base_array(bitcell_base_array):
|
|||
self.precharge_array = factory.create(module_type="rom_precharge_array", cols=self.column_size, strap_spacing=self.strap_spacing, route_layer=self.route_layer)
|
||||
|
||||
|
||||
def create_layout_constants(self):
|
||||
self.route_width = drc("minwidth_" + self.route_layer)
|
||||
|
||||
self.route_pitch = drc("{0}_to_{0}".format(self.route_layer))
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
for bl_name in self.get_bitline_names():
|
||||
self.add_pin(bl_name, "INOUT")
|
||||
|
|
@ -116,9 +127,13 @@ class rom_base_array(bitcell_base_array):
|
|||
row_list = []
|
||||
|
||||
# for each new strap placed, offset the column index refrenced to get correct bit in the data array
|
||||
strap_offset = 0
|
||||
first_in_col = True
|
||||
# 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:
|
||||
|
|
@ -127,7 +142,6 @@ class rom_base_array(bitcell_base_array):
|
|||
#print("tap instance added at c{0}, r{1}".format(col, row))
|
||||
self.tap_inst[row, col]=self.add_inst(name=name, mod=self.poly_tap)
|
||||
self.connect_inst([])
|
||||
strap_offset += 1
|
||||
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
|
||||
|
|
@ -137,19 +151,23 @@ class rom_base_array(bitcell_base_array):
|
|||
# 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):
|
||||
(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):
|
||||
(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):
|
||||
(row == self.row_size - 1) or \
|
||||
(pre_strap_row):
|
||||
new_inst=self.add_inst(name=name, mod=self.cell_dc)
|
||||
|
||||
else:
|
||||
|
|
@ -187,7 +205,6 @@ class rom_base_array(bitcell_base_array):
|
|||
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)
|
||||
|
|
@ -222,9 +239,7 @@ class rom_base_array(bitcell_base_array):
|
|||
|
||||
def place_rails(self):
|
||||
|
||||
width = drc("minwidth_" + self.route_layer)
|
||||
drc_rule = "{0}_to_{0}".format(self.route_layer)
|
||||
spacing = drc(drc_rule)
|
||||
spacing = self.route_pitch
|
||||
|
||||
rail_y = self.cell_list[self.row_size - 1][0].offset.y + self.dummy.base_width + spacing
|
||||
# self.dummy.height * (self.row_size)
|
||||
|
|
@ -237,7 +252,7 @@ class rom_base_array(bitcell_base_array):
|
|||
rail_start = vector(start_x , rail_y)
|
||||
rail_end = vector(end_x, rail_y)
|
||||
|
||||
self.gnd = self.add_layout_pin_rect_ends( name="gnd",
|
||||
self.gnd = self.add_layout_pin_rect_ends(name="gnd",
|
||||
layer="m1",
|
||||
start=rail_start,
|
||||
end=rail_end)
|
||||
|
|
@ -249,22 +264,22 @@ class rom_base_array(bitcell_base_array):
|
|||
|
||||
|
||||
self.add_via_stack_center(via_pos, self.route_layer, "m1", ["H", "V"])
|
||||
|
||||
prechrg_vdd = self.precharge_inst.get_pin("vdd")
|
||||
|
||||
|
||||
def place_array(self):
|
||||
self.cell_pos = {}
|
||||
self.strap_pos = {}
|
||||
# rows are wordlines
|
||||
|
||||
pitch_offset = 0
|
||||
for row in range(self.row_size):
|
||||
|
||||
# strap_cols = -1
|
||||
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)
|
||||
cell_y = row * (self.dummy.height) + pitch_offset
|
||||
|
||||
cell_x = 0
|
||||
|
||||
for col in range(self.column_size):
|
||||
|
||||
if col % self.strap_spacing == 0:
|
||||
|
|
@ -286,6 +301,19 @@ class rom_base_array(bitcell_base_array):
|
|||
# self.add_layout_pin_rect_center("wl{}".format(row), "m2", tap_pin)
|
||||
|
||||
|
||||
def route_pitch_offsets(self):
|
||||
|
||||
for row in range(0 , self.row_size, self.strap_spacing):
|
||||
if row != 0:
|
||||
for col in range(self.column_size):
|
||||
source = self.cell_inst[row, col].get_pin("S")
|
||||
drain = self.cell_inst[row - 1, col].get_pin("D")
|
||||
|
||||
start = vector(drain.cx(), source.cy())
|
||||
end = drain.center()
|
||||
self.add_segment_center(self.route_layer, start, end)
|
||||
|
||||
|
||||
def place_precharge(self):
|
||||
|
||||
self.precharge_offset = vector(0, - self.precharge_inst.height - self.dummy.nmos.end_to_contact - 2 * drc["nwell_enclose_active"])
|
||||
|
|
@ -293,14 +321,15 @@ class rom_base_array(bitcell_base_array):
|
|||
self.precharge_inst.place(offset=self.precharge_offset)
|
||||
|
||||
self.copy_layout_pin(self.precharge_inst, "vdd")
|
||||
self.copy_layout_pin(self.precharge_inst, "gate", "precharge")
|
||||
|
||||
|
||||
|
||||
def place_wordline_contacts(self):
|
||||
|
||||
width = drc["minwidth_{}".format(self.route_layer)]
|
||||
width = self.route_width
|
||||
|
||||
height = drc["minwidth_{}".format(self.route_layer)]
|
||||
height = self.route_width
|
||||
|
||||
offset = vector(self.poly_contact.width * 0.5, self.dummy.poly.offset.y)
|
||||
|
||||
|
|
@ -308,36 +337,31 @@ class rom_base_array(bitcell_base_array):
|
|||
|
||||
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.add_via_stack_center(poly_via.center(), "m1", self.output_layer)
|
||||
|
||||
corrected_offset = offset - vector(0.5 * width, 0.5 * height)
|
||||
# self.create_horizontal_pin_bus(self.route_layer, offset=corrected_offset, names=self.wordline_names[0], pitch=self.dummy.height, length=None)
|
||||
|
||||
def place_bitline_contacts(self):
|
||||
|
||||
src_pin = self.cell_nc.source_pos
|
||||
rail_y = self.precharge_inst.get_pin("vdd").cy()
|
||||
|
||||
for bl in range(self.column_size):
|
||||
|
||||
|
||||
# self.copy_layout_pin(self.cell_list[0][bl], "S", self.bitline_names[0][bl])
|
||||
|
||||
src_pin = self.cell_list[0][bl].get_pin("S")
|
||||
prechg_pin_name = "pre_bl{0}_out".format(bl)
|
||||
pre_pin = self.precharge_inst.get_pin(prechg_pin_name)
|
||||
|
||||
|
||||
# offset = src_pin_offset + vector(src_pin.x, 0)
|
||||
|
||||
|
||||
middle_offset = (pre_pin.cy() - src_pin.cy()) * 0.5
|
||||
middle_offset = (src_pin.cy() - pre_pin.cy() ) * 0.5
|
||||
|
||||
corrected = vector(src_pin.cx(), src_pin.cy() - middle_offset)
|
||||
self.add_via_stack_center(corrected, self.route_layer, self.output_layer)
|
||||
self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.output_layer, corrected )
|
||||
|
||||
output_pos = vector(corrected.x, rail_y)
|
||||
|
||||
self.add_segment_center(self.output_layer, corrected, output_pos)
|
||||
|
||||
# self.gnd[0].y()
|
||||
self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.output_layer, output_pos )
|
||||
|
||||
|
||||
|
||||
def route_precharge(self):
|
||||
|
|
|
|||
|
|
@ -1,40 +1,82 @@
|
|||
|
||||
import math
|
||||
from math import ceil, log, sqrt
|
||||
from openram.base import vector
|
||||
from openram.base import design
|
||||
from openram import OPTS
|
||||
from openram.sram_factory import factory
|
||||
import tech
|
||||
from openram.tech import drc
|
||||
|
||||
class rom_base_bank(design):
|
||||
|
||||
def __init__(self, strap_spacing=0, data_file=None, name="") -> None:
|
||||
self.rows = 4
|
||||
self.cols = 4
|
||||
self.num_inputs = 2
|
||||
self.data = [[0, 1, 0, 1], [1, 1, 1, 1], [1, 1, 0, 0], [0, 0, 1, 0]]
|
||||
def __init__(self, strap_spacing=0, data_file=None, name="", word_size=2) -> None:
|
||||
|
||||
# self.cols = 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.data = [[0, 1, 0, 1], [1, 1, 1, 1], [1, 1, 0, 0], [0, 0, 1, 0]]
|
||||
self.strap_spacing = strap_spacing
|
||||
self.route_layer = "li"
|
||||
self.bus_layer = "m1"
|
||||
self.interconnect_layer = "m2"
|
||||
self.bus_layer = "m2"
|
||||
self.interconnect_layer = "m1"
|
||||
|
||||
|
||||
|
||||
super().__init__(name=name)
|
||||
self.setup_layout_constants()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
"""
|
||||
Reads a hexadecimal file from a given directory to be used as the data written to the ROM
|
||||
endian is either "big" or "little"
|
||||
word_size is the number of bytes per word
|
||||
sets the row and column size based on the size of binary input, tries to keep array as square as possible,
|
||||
"""
|
||||
|
||||
def read_binary(self, data_file, word_size=2, endian="big"):
|
||||
|
||||
hex_file = open(data_file, 'r')
|
||||
hex_data = hex_file.read()
|
||||
bin_data = list("{0:08b}".format(int(hex_data, 16)))
|
||||
bin_data = [int(x) for x in bin_data]
|
||||
|
||||
# data size in bytes
|
||||
data_size = len(bin_data) / 8
|
||||
num_words = int(data_size / word_size)
|
||||
|
||||
bytes_per_col = sqrt(num_words)
|
||||
|
||||
self.words_per_row = int(ceil(bytes_per_col /(2*word_size)))
|
||||
|
||||
bits_per_row = self.words_per_row * word_size * 8
|
||||
|
||||
chunked_data = []
|
||||
|
||||
for i in range(0, len(bin_data), bits_per_row):
|
||||
word = bin_data[i:i + bits_per_row]
|
||||
if len(word) < bits_per_row:
|
||||
word = [0] * (bits_per_row - len(word)) + word
|
||||
chunked_data.append(word)
|
||||
|
||||
if endian == "big":
|
||||
chunked_data.reverse()
|
||||
|
||||
self.data = chunked_data
|
||||
self.cols = bits_per_row
|
||||
self.rows = int(num_words / (self.words_per_row))
|
||||
# print("hex: {0}, binary: {1}, chunked: {2}".format(hex_data, bin_data, chunked_data))
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
# self.add_pins()
|
||||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_instances()
|
||||
self.create_wl_bus()
|
||||
# self.channel_route()
|
||||
self.route_decode_outputs()
|
||||
self.route_array_inputs()
|
||||
self.route_control()
|
||||
|
||||
self.route_supplies()
|
||||
self.height = self.array_inst.height
|
||||
|
|
@ -47,12 +89,31 @@ class rom_base_bank(design):
|
|||
self.route_layer_pitch = drc["{0}_to_{0}".format(self.route_layer)]
|
||||
self.bus_layer_width = drc["minwidth_{}".format(self.bus_layer)]
|
||||
self.bus_layer_pitch = drc["{0}_to_{0}".format(self.bus_layer)]
|
||||
self.interconnect_layer_width = drc["minwidth_{}".format(self.interconnect_layer)]
|
||||
self.interconnect_layer_pitch = drc["{0}_to_{0}".format(self.interconnect_layer)]
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
self.add_pin("READ", "INPUT")
|
||||
self.add_pin("CS", "INPUT")
|
||||
|
||||
for i in range(self.num_inputs):
|
||||
self.add_pin("addr_{}".format(i), "INPUT")
|
||||
|
||||
|
||||
out_pins = []
|
||||
for j in range(self.num_outputs):
|
||||
out_pins.append("rom_out_{}".format(j))
|
||||
self.add_pin_list(out_pins, "OUTPUT")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.array = factory.create(module_type="rom_base_array", cols=self.cols, rows=self.rows, strap_spacing=self.strap_spacing, bitmap=self.data, route_layer=self.route_layer)
|
||||
self.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)
|
||||
|
||||
|
||||
def create_instances(self):
|
||||
|
|
@ -66,7 +127,7 @@ class rom_base_bank(design):
|
|||
name = "wl_{}".format(wl)
|
||||
array_pins.append(wl)
|
||||
|
||||
array_pins.append("array_precharge")
|
||||
array_pins.append("precharge")
|
||||
array_pins.append("vdd")
|
||||
array_pins.append("gnd")
|
||||
|
||||
|
|
@ -78,7 +139,7 @@ class rom_base_bank(design):
|
|||
name = "wl_{}".format(wl)
|
||||
decode_pins.append(name)
|
||||
|
||||
decode_pins.append("decode_precharge")
|
||||
decode_pins.append("precharge")
|
||||
decode_pins.append("vdd")
|
||||
decode_pins.append("gnd")
|
||||
|
||||
|
|
@ -89,20 +150,26 @@ class rom_base_bank(design):
|
|||
self.decode_inst = self.add_inst(name="rom_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"])
|
||||
|
||||
|
||||
|
||||
def place_instances(self):
|
||||
|
||||
array_x = self.decode_inst.width + (self.rows + 2) * ( self.route_layer_width + self.route_layer_pitch )
|
||||
array_y = self.array.height
|
||||
|
||||
|
||||
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
|
||||
self.array_offset = vector(array_x ,array_y)
|
||||
self.decode_offset = vector(0, 0)
|
||||
|
||||
self.array_inst.place(offset=self.array_offset, mirror="MX")
|
||||
self.control_offset = vector(0,0)
|
||||
|
||||
self.array_inst.place(offset=self.array_offset)
|
||||
|
||||
self.decode_inst.place(offset=self.decode_offset)
|
||||
|
||||
self.control_inst.place(offset=self.control_offset, mirror="MX")
|
||||
|
||||
def create_wl_bus(self):
|
||||
bus_x = self.decode_inst.width + ( drc["minwidth_{}".format(self.bus_layer)] + 1.5 * drc["{0}_to_{0}".format(self.bus_layer)] )
|
||||
bus_y = self.array_inst.by() + self.bus_layer_pitch + self.bus_layer_width
|
||||
|
|
@ -119,13 +186,17 @@ class rom_base_bank(design):
|
|||
decode_output = self.decode_array.output_names[wl]
|
||||
decode_out_pin = self.decode_inst.get_pin(decode_output)
|
||||
|
||||
wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]]
|
||||
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]]
|
||||
|
||||
start = decode_out_pin.center()
|
||||
end = vector(wl_bus_wire.cx(), start.y)
|
||||
end = vector(array_wl_pin.cx(), start.y)
|
||||
|
||||
self.add_segment_center(self.interconnect_layer, start, end)
|
||||
self.add_via_stack_center(end, self.route_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):
|
||||
|
|
@ -142,18 +213,52 @@ class rom_base_bank(design):
|
|||
self.add_segment_center(self.interconnect_layer, start, end)
|
||||
self.add_via_stack_center(start, self.route_layer, self.interconnect_layer )
|
||||
|
||||
def channel_route(self):
|
||||
route_nets = []
|
||||
for wl in range(self.rows):
|
||||
array_wl = self.array.wordline_names[0][wl]
|
||||
|
||||
array_wl_pin = self.array_inst.get_pin(array_wl)
|
||||
|
||||
decode_output = self.decode_array.output_names[wl]
|
||||
decode_out_pin = self.decode_inst.get_pin(decode_output)
|
||||
|
||||
route_nets.append([array_wl_pin, decode_out_pin])
|
||||
|
||||
array_prechrg = self.array_inst.get_pin("precharge")
|
||||
decode_prechrg = self.decode_inst.get_pin("precharge")
|
||||
route_nets.append([array_prechrg, decode_prechrg])
|
||||
|
||||
channel_start = vector(decode_out_pin.cx(), self.decode_array.array_inst.by())
|
||||
|
||||
channel = self.create_vertical_channel_route(netlist=route_nets, offset=channel_start, layer_stack=self.m1_stack, directions="nonpref")
|
||||
|
||||
|
||||
def route_control(self):
|
||||
|
||||
prechrg_control = self.control_inst.get_pin("prechrg")
|
||||
decode_prechrg = self.decode_inst.get_pin("precharge")
|
||||
array_prechrg = self.array_inst.get_pin("precharge")
|
||||
|
||||
end = vector(decode_prechrg.cx() - 0.5 * self.interconnect_layer_width, prechrg_control.cy())
|
||||
|
||||
self.add_segment_center(self.interconnect_layer, prechrg_control.center(), end)
|
||||
|
||||
start = end + vector(0.5 * self.interconnect_layer_width, 0)
|
||||
self.add_segment_center(self.interconnect_layer, start, decode_prechrg.center())
|
||||
|
||||
|
||||
|
||||
|
||||
def route_supplies(self):
|
||||
gnd_start = vector(self.array_inst.get_pins("gnd")[0].cx(),0)
|
||||
print()
|
||||
print(self.decode_inst.get_pin("gnd").center())
|
||||
|
||||
decode_gnd = self.decode_inst.get_pin("gnd")
|
||||
decode_vdd = self.decode_inst.get_pin("vdd")
|
||||
array_vdd = self.array_inst.get_pin("vdd")
|
||||
|
||||
self.add_segment_center("m1", gnd_start, decode_gnd.center())
|
||||
# self.add_segment_center("m1", gnd_start, decode_gnd.center())
|
||||
|
||||
|
||||
|
||||
self.add_power_pin("gnd", decode_vdd.center())
|
||||
self.add_power_pin("vdd", decode_gnd.center())
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ 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)
|
||||
#self.route_layer= route_layer
|
||||
#self.create_netlist()
|
||||
#self.create_layout()
|
||||
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -27,7 +25,6 @@ class rom_base_cell(rom_dummy_cell):
|
|||
self.add_nmos()
|
||||
self.create_nmos()
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_drc_offsets()
|
||||
self.place_nmos()
|
||||
|
|
@ -64,15 +61,7 @@ class rom_base_cell(rom_dummy_cell):
|
|||
self.copy_layout_pin(self.cell_inst, "S", "S")
|
||||
self.copy_layout_pin(self.cell_inst, "D", "D")
|
||||
self.source_pos = self.cell_inst.get_pin("S").center()
|
||||
# if self.add_source_contact != False:
|
||||
# # drain_x = 0
|
||||
# # drain_y = 0.5 * (self.width - self.poly_extend_active_spacing)
|
||||
|
||||
|
||||
# print("drained")
|
||||
# print(drain_pos)
|
||||
# self.add_layout_pin_rect_center("S", self.route_layer, drain_pos)
|
||||
# self.add_label("S", self.route_layer, self.cell_inst.get_pin("S").center())
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
# 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.sram_factory import factory
|
||||
from openram.base import vector, design
|
||||
|
||||
|
||||
class rom_control_logic(design):
|
||||
def __init__(self, num_outputs, name="", height=None):
|
||||
self.output_size = num_outputs
|
||||
self.mod_height = height
|
||||
|
||||
# dff = factory.create(module_type="dff")
|
||||
|
||||
# if height == None:
|
||||
# self.mod_height = dff.height * 0.5
|
||||
# else:
|
||||
# self.mod_height = height
|
||||
|
||||
super().__init__(name)
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
self.create_instances()
|
||||
self.height=self.nand_inst.height
|
||||
self.width=self.nand_inst.width + self.inv_inst.width + self.driver_inst.width
|
||||
self.place_instances()
|
||||
self.route_insts()
|
||||
|
||||
def add_modules(self):
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("READ", "INPUT")
|
||||
self.add_pin("CS", "INPUT")
|
||||
self.add_pin("prechrg", "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
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.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod)
|
||||
self.connect_inst(["CS", "READ_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"])
|
||||
|
||||
|
||||
def place_instances(self):
|
||||
self.nand_inst.place(offset=[self.inv_inst.width, 0])
|
||||
self.driver_inst.place(offset=[self.nand_inst.width + self.inv_inst.width, 0])
|
||||
|
||||
def route_insts(self):
|
||||
|
||||
self.copy_layout_pin(self.inv_inst, "A", "READ")
|
||||
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("li", [self.nand_inst.get_pin("Z").center(), self.driver_inst.get_pin("A").center()])
|
||||
|
||||
|
|
@ -5,15 +5,14 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
from math import ceil, log
|
||||
from openram.base import design
|
||||
from openram.sram_factory import factory
|
||||
from openram.base import vector
|
||||
from openram.base import vector, design
|
||||
from openram import OPTS
|
||||
from openram.tech import drc
|
||||
|
||||
|
||||
|
||||
class rom_decoder(design):
|
||||
def __init__(self, num_outputs, strap_spacing, name="", route_layer="li", output_layer="m2"):
|
||||
|
||||
|
|
@ -25,7 +24,7 @@ class rom_decoder(design):
|
|||
self.num_inputs = ceil(log(num_outputs, 2))
|
||||
self.create_decode_map()
|
||||
|
||||
for i in range(2 * self.num_inputs): print(self.decode_map[i])
|
||||
# for i in range(2 * self.num_inputs): print(self.decode_map[i])
|
||||
|
||||
super().__init__(name)
|
||||
|
||||
|
|
@ -152,7 +151,6 @@ class rom_decoder(design):
|
|||
|
||||
|
||||
def place_input_inverters(self):
|
||||
print(self.array_inst.ll().x)
|
||||
self.inv_inst.place(vector(self.array_inst.ll().x, 0))
|
||||
|
||||
|
||||
|
|
@ -163,7 +161,6 @@ class rom_decoder(design):
|
|||
def create_outputs(self):
|
||||
|
||||
self.output_names = []
|
||||
self.outputs = []
|
||||
for j in range(self.num_outputs):
|
||||
name = "out_{0}".format(j)
|
||||
self.output_names.append(name)
|
||||
|
|
@ -179,6 +176,8 @@ class rom_decoder(design):
|
|||
|
||||
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_bar = wl - 1
|
||||
|
|
@ -235,14 +234,8 @@ class rom_decoder(design):
|
|||
|
||||
offset = vector(-0.5 *width ,0.5 * (array_gnd[0].cy() + array_gnd[-1].cy()))
|
||||
|
||||
|
||||
|
||||
# self.add_rect_center(self.route_layer, offset, width, height)
|
||||
|
||||
|
||||
start = end - vector(0, 0.5 * minwidth)
|
||||
end = vector(start.x, array_gnd[1].uy())
|
||||
# self.add_segment_center("m1", start, end)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ class rom_dummy_cell(design):
|
|||
def __init__(self, name="", cell_name=None, add_source_contact=False, add_drain_contact=False, route_layer="m1"):
|
||||
super().__init__(name, cell_name)
|
||||
self.route_layer = route_layer
|
||||
self.add_source_contact=add_source_contact
|
||||
self.add_drain_contact=add_drain_contact
|
||||
self.add_source_contact="li"
|
||||
self.add_drain_contact="li"
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
|
|
@ -70,12 +70,22 @@ class rom_dummy_cell(design):
|
|||
|
||||
def add_boundary(self):
|
||||
|
||||
#cell width with offsets applied, height becomes width when the cells are rotated
|
||||
# self.width = self.nmos.height + self.poly_extend_active_spacing + 2 * self.nmos.poly_extend_active
|
||||
self.width = self.nmos.height + self.poly_extend_active_spacing + 2 * self.nmos.poly_extend_active
|
||||
# cell height with offsets applied, width becomes height when the cells are rotated, if the offsets are positive (greater than 0) they are not applied
|
||||
self.height = self.base_width - min(self.cell_diffusion_offset, 0) - min(self.poly_active_offset, 0) - min(self.poly_tap_offset, 0)
|
||||
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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class rom_inv_array(design):
|
|||
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)
|
||||
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)
|
||||
|
||||
|
|
@ -73,7 +73,6 @@ class rom_inv_array(design):
|
|||
for col in range(self.cols):
|
||||
name = "Xinv_c{0}".format(col)
|
||||
if col == self.cols - 1:
|
||||
print("TAP ME DOWN")
|
||||
self.inv_insts.append(self.add_inst(name=name, mod=self.end_inv))
|
||||
else:
|
||||
self.inv_insts.append(self.add_inst(name=name, mod=self.inv_mod))
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from openram.sram_factory import factory
|
|||
|
||||
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="m1"):
|
||||
super().__init__(name, cell_name, prop)
|
||||
self.strap_layer=strap_layer
|
||||
self.length = strap_length
|
||||
|
|
@ -37,13 +37,20 @@ class rom_poly_tap(design):
|
|||
|
||||
|
||||
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 = self.poly_contact.width + 2 * self.contact_x_offset
|
||||
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):
|
||||
|
||||
contact_width = self.poly_contact.width
|
||||
|
||||
# DRC rule here is hard coded since licon.9 isnt included in skywater130 tech file
|
||||
|
||||
# poly contact spacing to P-diffusion < 0.235um (licon.9 + psdm.5a)
|
||||
|
|
@ -59,23 +66,16 @@ class rom_poly_tap(design):
|
|||
# self.contact_x_offset = 0
|
||||
else:
|
||||
contact_y = self.pmos.poly_positions[0].x - self.pmos.active_offset.x
|
||||
print(self.tx_type)
|
||||
print(contact_y)
|
||||
|
||||
# contact_x = - contact_width * 0.5 - self.contact_x_offset
|
||||
contact_x = contact_width * 0.5 + self.contact_x_offset
|
||||
self.contact_offset = vector(contact_x, contact_y)
|
||||
print("polycule")
|
||||
print(self.contact_offset)
|
||||
|
||||
self.via = self.add_via_stack_center(from_layer="poly",
|
||||
to_layer=self.strap_layer,
|
||||
offset=self.contact_offset)
|
||||
self.add_layout_pin_rect_center("via", self.strap_layer, self.contact_offset)
|
||||
# if self.length == 0:
|
||||
# self.add_layout_pin_rect_center(text="poly_tap",
|
||||
# layer=self.strap_layer,
|
||||
# offset=print()contact_offset,
|
||||
# )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -88,14 +88,21 @@ class rom_poly_tap(design):
|
|||
self.strap = self.add_path(self.strap_layer, (strap_start, strap_end))
|
||||
|
||||
def extend_poly(self):
|
||||
|
||||
base_contact_width = self.poly_contact.width + 2 * self.contact_x_offset
|
||||
|
||||
self.pitch_offset = (base_contact_width - self.active_enclose_contact - self.active_extend_contact) - self.active_space
|
||||
|
||||
|
||||
poly_x = self.poly_contact.width + self.contact_x_offset
|
||||
poly_y = self.contact_offset.y - self.poly_width * 0.5
|
||||
extend_offset = vector(poly_x, poly_y)
|
||||
self.add_rect("poly", extend_offset, self.contact_x_offset, self.poly_width)
|
||||
|
||||
self.add_rect("poly", extend_offset, self.contact_x_offset + self.pitch_offset, self.poly_width)
|
||||
|
||||
poly_x = 0
|
||||
extend_offset = vector(poly_x, poly_y)
|
||||
|
||||
self.add_rect("poly", extend_offset, self.contact_x_offset, self.poly_width)
|
||||
self.add_rect("poly", extend_offset, self.contact_x_offset , self.poly_width)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ class rom_precharge_array(design):
|
|||
|
||||
|
||||
def create_layout_pins(self):
|
||||
self.copy_layout_pin(self.tap_insts[0], "via", "gate")
|
||||
for col in range(self.cols):
|
||||
source_pin = self.pmos_insts[col].get_pin("D")
|
||||
bl = "pre_bl{0}_out".format(col)
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@ class rom_precharge_cell(design):
|
|||
self.cell_inst = self.add_inst( name="precharge_pmos",
|
||||
mod=self.pmos,
|
||||
)
|
||||
self.connect_inst(["bitline", "gate", "vdd", "vdd"])
|
||||
self.connect_inst(["bitline", "gate", "vdd", "body"])
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
pin_list = ["vdd", "gate", "bitline", "vdd"]
|
||||
dir_list = ["OUTPUT", "INPUT", "OUTPUT", "POWER"]
|
||||
pin_list = ["vdd", "gate", "bitline", "body"]
|
||||
dir_list = ["POWER", "INPUT", "OUTPUT", "INPUT"]
|
||||
|
||||
self.add_pin_list(pin_list, dir_list)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class rom_array_test(openram_test):
|
|||
|
||||
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]]
|
||||
|
||||
a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4)
|
||||
a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4, pitch_match=True)
|
||||
self.local_check(a)
|
||||
openram.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class rom_bank_test(openram_test):
|
|||
|
||||
debug.info(2, "Testing 4x4 array for rom cell")
|
||||
|
||||
a = factory.create(module_type="rom_base_bank", strap_spacing = 2)
|
||||
a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data", word_size=1)
|
||||
|
||||
self.local_check(a)
|
||||
openram.end_openram()
|
||||
|
|
|
|||
|
|
@ -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_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 control logic for rom cell")
|
||||
|
||||
|
||||
a = factory.create(module_type="rom_control_logic", num_outputs=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())
|
||||
Loading…
Reference in New Issue