OpenRAM/compiler/modules/rom_bank.py

431 lines
18 KiB
Python
Raw Normal View History

2023-02-28 00:56:24 +01:00
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 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.
#
2022-12-09 23:25:11 +01:00
2023-03-08 09:08:43 +01:00
import datetime
from math import ceil, log
from openram.base import vector
from openram.base import design
from openram import OPTS, print_time
from openram.sram_factory import factory
2023-03-02 00:49:43 +01:00
from openram.tech import drc, layer, parameter
2022-12-09 23:25:11 +01:00
class rom_bank(design):
2022-12-09 23:25:11 +01:00
2023-01-17 01:15:03 +01:00
"""
Rom data bank with row and column decoder + control logic
word size is in bytes
"""
2023-03-08 09:08:43 +01:00
def __init__(self, name, rom_config):
2023-02-01 23:49:59 +01:00
super().__init__(name=name)
2023-03-08 09:08:43 +01:00
self.rom_config = rom_config
rom_config.set_local_config(self)
2022-12-09 23:25:11 +01:00
2023-03-08 09:08:43 +01:00
self.word_size = self.word_bits
2022-12-30 09:35:15 +01:00
self.num_outputs = self.rows
self.num_inputs = ceil(log(self.rows, 2))
2023-01-17 01:15:03 +01:00
self.col_bits = ceil(log(self.words_per_row, 2))
self.row_bits = self.num_inputs
2023-03-01 06:10:52 +01:00
2023-03-08 09:08:43 +01:00
self.tap_spacing = self.strap_spacing
try:
from openram.tech import power_grid
self.supply_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
self.supply_stack = self.m3_stack
2022-12-30 09:35:15 +01:00
self.interconnect_layer = "m1"
2023-01-23 03:34:11 +01:00
self.bitline_layer = "m1"
self.wordline_layer = "m2"
2022-12-30 09:35:15 +01:00
2023-01-23 03:34:11 +01:00
if "li" in layer:
self.route_stack = self.m1_stack
else:
self.route_stack = self.m2_stack
self.route_layer = self.route_stack[0]
2023-02-28 00:56:24 +01:00
2023-03-09 23:12:14 +01:00
if OPTS.is_unit_test:
self.create_netlist()
self.create_layout()
2022-12-09 23:25:11 +01:00
def create_netlist(self):
2023-03-08 09:08:43 +01:00
start_time = datetime.datetime.now()
2022-12-09 23:25:11 +01:00
self.add_modules()
2023-02-01 23:49:59 +01:00
self.add_pins()
2023-03-08 09:08:43 +01:00
self.create_instances()
if not OPTS.is_unit_test:
print_time("Submodules", datetime.datetime.now(), start_time)
2023-02-01 23:49:59 +01:00
2023-03-08 09:08:43 +01:00
def create_layout(self):
2023-01-23 03:34:11 +01:00
2023-03-08 09:08:43 +01:00
start_time = datetime.datetime.now()
2023-03-01 06:10:52 +01:00
2023-03-08 09:08:43 +01:00
self.setup_layout_constants()
2022-12-09 23:25:11 +01:00
self.place_instances()
2023-03-08 09:08:43 +01:00
if not OPTS.is_unit_test:
print_time("Placement", datetime.datetime.now(), start_time)
2023-01-23 03:34:11 +01:00
2022-12-09 23:25:11 +01:00
2023-03-08 09:08:43 +01:00
start_time = datetime.datetime.now()
self.route_layout()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
2023-01-23 03:34:11 +01:00
2023-03-08 09:08:43 +01:00
self.height = self.array_inst.height
self.width = self.array_inst.width
self.add_boundary()
start_time = datetime.datetime.now()
if not OPTS.is_unit_test:
# We only enable final verification if we have routed the design
# Only run this if not a unit test, because unit test will also verify it.
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
print_time("Verification", datetime.datetime.now(), start_time)
def route_layout(self):
self.route_decode_outputs()
self.route_precharge()
2023-01-23 03:34:11 +01:00
self.route_clock()
self.route_array_outputs()
2023-02-01 23:49:59 +01:00
self.place_top_level_pins()
self.route_supplies()
self.route_output_buffers()
2022-12-09 23:25:11 +01:00
def setup_layout_constants(self):
2023-01-23 03:34:11 +01:00
self.route_layer_width = drc["minwidth_{}".format(self.route_stack[0])]
self.route_layer_pitch = drc["{0}_to_{0}".format(self.route_stack[0])]
2022-12-30 09:35:15 +01:00
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):
2023-03-01 06:10:52 +01:00
2023-01-23 03:34:11 +01:00
self.add_pin("clk", "INPUT")
self.add_pin("cs", "INPUT")
2022-12-30 09:35:15 +01:00
2023-02-01 23:49:59 +01:00
for i in range(self.row_bits + self.col_bits):
self.add_pin("addr[{}]".format(i), "INPUT")
2022-12-30 09:35:15 +01:00
out_pins = []
2023-02-01 23:49:59 +01:00
for j in range(self.word_size):
out_pins.append("dout[{}]".format(j))
2022-12-30 09:35:15 +01:00
self.add_pin_list(out_pins, "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
2022-12-09 23:25:11 +01:00
def add_modules(self):
2023-01-23 03:34:11 +01:00
# TODO: provide technology-specific calculation of these parameters
2023-03-01 06:10:52 +01:00
# in sky130 the address control buffer is composed of 2 size 2 NAND gates,
# with a beta of 3, each of these gates has gate capacitance of 2 min sized inverters, therefor a load of 4
2023-03-02 00:49:43 +01:00
2023-03-08 09:08:43 +01:00
2023-03-02 00:49:43 +01:00
addr_control_buffer_effort = parameter['beta'] + 1
# a single min sized nmos makes up 1/4 of the input capacitance of a min sized inverter
bitcell_effort = 0.25
2023-03-01 06:10:52 +01:00
# Takes into account inverter sizing
wordline_effort = bitcell_effort * 0.5
# a single min sized pmos plus a single min sized nmos have approximately half the gate capacitance of a min inverter
# an additional 0.2 accounts for the long wire capacitance and add delay to gaurentee the read timing
precharge_cell_effort = 0.5 + 0.2
2023-03-01 06:10:52 +01:00
self.array = factory.create(module_type="rom_base_array",
cols=self.cols,
rows=self.rows,
strap_spacing=self.strap_spacing,
bitmap=self.data,
2023-01-23 03:34:11 +01:00
bitline_layer=self.bitline_layer,
wordline_layer=self.wordline_layer,
pitch_match=True,
2023-03-01 06:10:52 +01:00
tap_spacing=self.tap_spacing)
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,
fanout=(self.cols)*wordline_effort )
2023-02-01 23:49:59 +01:00
2023-03-01 06:10:52 +01:00
self.column_mux = factory.create(module_type="rom_column_mux_array",
2023-01-23 03:34:11 +01:00
columns=self.cols,
word_size=self.word_size,
2023-02-01 23:49:59 +01:00
tap_spacing=self.strap_spacing,
bitline_layer=self.interconnect_layer,
input_layer=self.bitline_layer)
2023-03-01 06:10:52 +01:00
2023-01-23 03:34:11 +01:00
self.column_decode = factory.create(module_name="rom_column_decode",
2023-03-01 06:10:52 +01:00
module_type="rom_decoder",
num_outputs=self.words_per_row,
strap_spacing=self.strap_spacing,
route_layer=self.route_layer,
fanout=2,
2023-01-23 03:34:11 +01:00
invert_outputs=True )
2023-03-01 06:10:52 +01:00
self.control_logic = factory.create(module_type="rom_control_logic",
num_outputs=(self.cols + self.words_per_row * precharge_cell_effort) \
2023-03-01 06:10:52 +01:00
+ (addr_control_buffer_effort * self.col_bits),
clk_fanout=(self.row_bits * addr_control_buffer_effort) + (precharge_cell_effort * self.rows),
2023-02-01 23:49:59 +01:00
height=self.column_decode.height )
2023-03-01 06:10:52 +01:00
self.bitline_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_bitline_inverter",
rows=self.cols,
fanout=4,
invert_outputs=True,
tap_spacing=0,
flip_io=True)
self.output_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_output_buffer",
2023-02-03 08:39:08 +01:00
rows=self.word_size,
fanout=4,
invert_outputs=True)
2023-01-23 03:34:11 +01:00
2022-12-09 23:25:11 +01:00
def create_instances(self):
2023-01-17 01:15:03 +01:00
gnd = ["gnd"]
vdd = ["vdd"]
prechrg = ["precharge"]
2023-01-23 03:34:11 +01:00
clk = ["clk_int"]
2022-12-09 23:25:11 +01:00
2023-02-01 23:49:59 +01:00
bitlines = ["bl_{}".format(bl) for bl in range(self.cols)]
wordlines = ["wl_{}".format(wl) for wl in range(self.rows)]
2022-12-09 23:25:11 +01:00
addr_msb = ["addr[{}]".format(addr + self.col_bits) for addr in range(self.row_bits)]
addr_lsb = ["addr[{}]".format(addr) for addr in range(self.col_bits)]
2022-12-09 23:25:11 +01:00
2023-02-01 23:49:59 +01:00
select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)]
2023-02-03 08:39:08 +01:00
bitline_bar = ["bl_b_{}".format(bl) for bl in range(self.cols)]
2023-02-03 08:39:08 +01:00
pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)]
outputs = ["dout[{}]".format(bl) for bl in range(self.word_size)]
2023-03-01 06:10:52 +01:00
2022-12-09 23:25:11 +01:00
2023-02-01 23:49:59 +01:00
array_pins = bitlines + wordlines + prechrg + vdd + gnd
2022-12-09 23:25:11 +01:00
row_decode_pins = addr_msb + wordlines + clk + clk + vdd + gnd
col_decode_pins = addr_lsb + select_lines + prechrg + prechrg + vdd + gnd
2022-12-09 23:25:11 +01:00
col_mux_pins = bitline_bar + select_lines + pre_buf_outputs + gnd
2023-02-03 08:39:08 +01:00
bitline_inv_pins = bitlines + bitline_bar + vdd + gnd
output_buf_pins = pre_buf_outputs + outputs + vdd + gnd
2023-01-17 01:15:03 +01:00
2022-12-09 23:25:11 +01:00
self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array)
self.connect_inst(array_pins)
2023-01-17 01:15:03 +01:00
self.decode_inst = self.add_inst(name="rom_row_decoder", mod=self.decode_array)
2023-02-01 23:49:59 +01:00
self.connect_inst(row_decode_pins)
2022-12-09 23:25:11 +01:00
2022-12-30 09:35:15 +01:00
self.control_inst = self.add_inst(name="rom_control", mod=self.control_logic)
self.connect_inst(["clk", "cs", "precharge", "clk_int", "vdd", "gnd"])
2022-12-30 09:35:15 +01:00
2023-01-17 01:15:03 +01:00
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)
self.bitline_inv_inst = self.add_inst(name="rom_bitline_inverter", mod=self.bitline_inv)
self.connect_inst(bitline_inv_pins)
self.output_inv_inst = self.add_inst(name="rom_output_inverter", mod=self.output_inv)
self.connect_inst(output_buf_pins)
2022-12-09 23:25:11 +01:00
def place_instances(self):
2023-01-17 01:15:03 +01:00
self.place_row_decoder()
self.place_data_array()
self.place_bitline_inverter()
2023-01-17 01:15:03 +01:00
self.place_col_mux()
2023-01-23 03:34:11 +01:00
self.place_col_decoder()
self.place_control_logic()
2023-02-03 08:39:08 +01:00
self.place_output_buffer()
2023-01-17 01:15:03 +01:00
def place_row_decoder(self):
self.decode_offset = vector(0, self.control_inst.height )
2023-01-17 01:15:03 +01:00
self.decode_inst.place(offset=self.decode_offset)
2023-03-01 06:10:52 +01:00
2023-01-17 01:15:03 +01:00
def place_data_array(self):
# We approximate the correct position for the array
2023-03-01 06:10:52 +01:00
array_x = self.decode_inst.width + (2) * ( self.route_layer_width + self.route_layer_pitch )
2023-01-17 01:15:03 +01:00
array_y = self.decode_array.buf_inst.height - self.array.precharge_inst.cy() - self.array.zero_cell.height * 0.5
2022-12-09 23:25:11 +01:00
self.array_offset = vector(array_x ,array_y)
2023-01-17 01:15:03 +01:00
self.array_inst.place(offset=self.array_offset)
2022-12-09 23:25:11 +01:00
2023-01-17 01:15:03 +01:00
# 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)))
2023-03-01 06:10:52 +01:00
def place_bitline_inverter(self):
self.bitline_inv_inst.place(offset=[0,0], rotate=90)
inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch
2023-01-23 03:34:11 +01:00
inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx()
self.inv_offset = vector(inv_x_offset, inv_y_offset)
self.bitline_inv_inst.place(offset=self.inv_offset, rotate=90)
2023-03-01 06:10:52 +01:00
def place_control_logic(self):
2023-01-23 03:34:11 +01:00
self.control_offset = vector(self.col_decode_inst.lx() - self.control_inst.width - 3 * self.m1_pitch, self.decode_inst.by() - self.control_logic.height - self.m1_pitch)
self.control_inst.place(offset=self.control_offset)
2022-12-30 09:35:15 +01:00
2023-01-17 01:15:03 +01:00
def place_col_decoder(self):
2023-03-01 06:10:52 +01:00
col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy()
2023-01-23 03:34:11 +01:00
self.col_decode_offset = vector(self.decode_inst.width - self.col_decode_inst.width, col_decode_y)
2023-01-17 01:15:03 +01:00
self.col_decode_inst.place(offset=self.col_decode_offset)
2022-12-09 23:25:11 +01:00
2023-01-17 01:15:03 +01:00
def place_col_mux(self):
mux_y_offset = self.bitline_inv_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch
2022-12-09 23:25:11 +01:00
mux_x_offset = self.bitline_inv_inst.get_pin("out_0").cx() - self.mux_inst.get_pin("bl_0").cx()
2023-01-23 03:34:11 +01:00
self.mux_offset = vector(mux_x_offset, mux_y_offset)
2023-01-17 01:15:03 +01:00
self.mux_inst.place(offset=self.mux_offset)
2023-03-01 06:10:52 +01:00
2023-02-03 08:39:08 +01:00
def place_output_buffer(self):
2023-03-01 06:10:52 +01:00
output_x = self.col_decode_inst.rx() + self.output_inv_inst.height
output_y = self.mux_inst.by() - self.word_size * self.m1_pitch
self.output_inv_offset = vector(output_x, output_y)
self.output_inv_inst.place(offset=self.output_inv_offset, rotate=270)
2023-03-01 06:10:52 +01:00
2022-12-09 23:25:11 +01:00
def route_decode_outputs(self):
2023-01-23 03:34:11 +01:00
# for the row decoder
2023-01-17 01:15:03 +01:00
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)
2022-12-09 23:25:11 +01:00
2023-01-23 03:34:11 +01:00
# then for the column decoder
col_decode_pins = [self.col_decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.words_per_row)]
sel_pins = [self.mux_inst.get_pin("sel_{}".format(wl)) for wl in range(self.words_per_row)]
sel_pins.extend(col_decode_pins)
2023-02-01 23:49:59 +01:00
self.connect_row_pins(self.wordline_layer, sel_pins, round=True)
2022-12-09 23:25:11 +01:00
def route_array_inputs(self):
for wl in range(self.rows):
array_wl = self.array.wordline_names[0][wl]
array_wl_pin = self.array_inst.get_pin(array_wl)
wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]]
end = array_wl_pin.center()
start = vector(wl_bus_wire.cx(), end.y)
self.add_segment_center(self.interconnect_layer, start, end)
2023-03-01 06:10:52 +01:00
2023-01-23 03:34:11 +01:00
def route_precharge(self):
2022-12-30 09:35:15 +01:00
2023-01-23 03:34:11 +01:00
prechrg_control = self.control_inst.get_pin("prechrg")
2023-03-01 06:10:52 +01:00
col_decode_prechrg = self.col_decode_inst.get_pin("precharge_r")
col_decode_clk = self.col_decode_inst.get_pin("clk")
2022-12-30 09:35:15 +01:00
array_prechrg = self.array_inst.get_pin("precharge")
2023-01-23 03:34:11 +01:00
self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=prechrg_control.layer,
offset=prechrg_control.center())
# Route precharge to col decoder
start = prechrg_control.center()
mid1 = vector(self.control_inst.rx(), prechrg_control.cy())
mid2 = vector(self.control_inst.rx(), col_decode_prechrg.cy())
end = col_decode_prechrg.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
2023-01-23 03:34:11 +01:00
self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=col_decode_prechrg.layer,
offset=end)
2023-03-01 06:10:52 +01:00
start = mid1
mid1 = vector(self.control_inst.rx(), start.y)
mid2 = vector(mid1.x, col_decode_clk.cy())
end = col_decode_clk.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
2023-01-23 03:34:11 +01:00
# Route precharge to main array
mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() )
2023-03-01 06:10:52 +01:00
self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()])
2023-01-23 03:34:11 +01:00
def route_clock(self):
clk_out = self.control_inst.get_pin("clk_out")
row_decode_clk = self.decode_inst.get_pin("clk")
2023-01-23 03:34:11 +01:00
self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=clk_out.layer,
offset=clk_out.center())
2023-03-01 06:10:52 +01:00
# Route clock to row decoder
mid = vector(self.control_inst.rx() + self.m1_pitch, clk_out.cy())
addr_control_clk = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0)
row_decode_prechrg = self.decode_inst.get_pin("precharge")
self.add_path(self.route_stack[2], [clk_out.center(), mid, addr_control_clk, row_decode_prechrg.center()])
2022-12-30 09:35:15 +01:00
2023-01-23 03:34:11 +01:00
self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=row_decode_clk.layer,
offset=addr_control_clk)
2022-12-30 09:35:15 +01:00
self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc())
2022-12-30 09:35:15 +01:00
2023-01-23 03:34:11 +01:00
def route_array_outputs(self):
array_out_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.cols)]
inv_in_pins = [self.bitline_inv_inst.get_pin("in_{}".format(bl)) for bl in range(self.cols)]
inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)]
mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)]
self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref")
self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref")
def route_output_buffers(self):
mux = self.mux_inst
buf = self.output_inv_inst
route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)]
channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3)
self.create_horizontal_channel_route(netlist=route_nets, offset=channel_ll, layer_stack=self.m1_stack)
2023-02-01 23:49:59 +01:00
def place_top_level_pins(self):
self.copy_layout_pin(self.control_inst, "CS", "cs")
self.copy_layout_pin(self.control_inst, "clk_in", "clk")
2023-02-01 23:49:59 +01:00
for i in range(self.word_size):
self.copy_layout_pin(self.output_inv_inst, "out_{}".format(i), "dout[{}]".format(i))
2023-02-01 23:49:59 +01:00
for lsb in range(self.col_bits):
name = "addr[{}]".format(lsb)
2023-02-01 23:49:59 +01:00
self.copy_layout_pin(self.col_decode_inst, "A{}".format(lsb), name)
for msb in range(self.col_bits, self.row_bits + self.col_bits):
name = "addr[{}]".format(msb)
2023-03-01 06:10:52 +01:00
pin_num = msb - self.col_bits
2023-02-01 23:49:59 +01:00
self.copy_layout_pin(self.decode_inst, "A{}".format(pin_num), name)
2023-03-01 06:10:52 +01:00
2022-12-09 23:25:11 +01:00
def route_supplies(self):
2023-01-23 03:34:11 +01:00
for inst in self.insts:
2023-02-01 23:49:59 +01:00
if not inst.mod.name.__contains__("contact"):
self.copy_layout_pin(inst, "vdd")
2023-03-08 09:08:43 +01:00
self.copy_layout_pin(inst, "gnd")