OpenRAM/compiler/modules/sram_config.py

178 lines
7.6 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2021-01-22 20:23:28 +01:00
# Copyright (c) 2016-2021 Regents of the University of California and The Board
2019-06-14 17:43:41 +02:00
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
2020-10-07 01:27:02 +02:00
from math import log, sqrt, ceil
from globals import OPTS
2019-01-17 01:15:38 +01:00
from sram_factory import factory
2020-10-07 01:27:02 +02:00
class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """
2020-11-03 15:29:17 +01:00
2020-10-07 01:27:02 +02:00
def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0):
self.word_size = word_size
self.num_words = num_words
# Don't add a write mask if it is the same size as the data word
2022-08-11 00:36:41 +02:00
self.write_size_init = write_size
2022-07-14 01:20:06 +02:00
if write_size:
self.write_size = write_size
2022-07-14 01:20:06 +02:00
else:
self.write_size = word_size
self.num_banks = num_banks
self.num_spare_rows = num_spare_rows
2020-04-14 05:09:10 +02:00
self.num_spare_cols = num_spare_cols
try:
from tech import array_row_multiple
self.array_row_multiple = array_row_multiple
except ImportError:
self.array_row_multiple = 1
try:
from tech import array_col_multiple
self.array_col_multiple = array_col_multiple
except ImportError:
self.array_col_multiple = 1
# This will get over-written when we determine the organization
self.words_per_row = words_per_row
2020-10-07 01:27:02 +02:00
self.compute_sizes()
2021-09-08 01:49:31 +02:00
def __str__(self):
""" override print function output """
config_items = ["num_banks",
"word_size",
"num_words",
"words_per_row",
"write_size",
"num_spare_rows",
"num_spare_cols"]
str = ""
for item in config_items:
val = getattr(self, item)
str += "{} : {}\n".format(item, val)
return str
def set_local_config(self, module):
""" Copy all of the member variables to the given module for convenience """
2020-11-03 15:29:17 +01:00
members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")]
# Copy all the variables to the local module
for member in members:
2020-10-07 01:27:02 +02:00
setattr(module, member, getattr(self, member))
def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square."""
bitcell = factory.create(module_type=OPTS.bitcell)
2020-11-03 15:29:17 +01:00
debug.check(ceil(log(self.num_banks, 2)) == log(self.num_banks, 2) ,
"Number of banks should be power of 2.")
2020-10-07 01:27:02 +02:00
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
2021-06-22 00:16:36 +02:00
# If this was hard coded, don't dynamically compute it!
if not self.words_per_row:
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
2020-10-07 01:27:02 +02:00
self.bank_area = bitcell.width * bitcell.height * self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
2020-10-07 01:27:02 +02:00
self.tentative_num_cols = int(self.bank_side_length / bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
2020-10-07 01:27:02 +02:00
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row * self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
self.recompute_sizes()
2021-06-22 00:16:36 +02:00
# Set word_per_row in OPTS
OPTS.words_per_row = self.words_per_row
debug.info(1, "Set SRAM Words Per Row={}".format(OPTS.words_per_row))
def recompute_sizes(self):
2020-11-03 15:29:17 +01:00
"""
Calculate the auxiliary values assuming fixed number of words per row.
This can be called multiple times from the unit test when we reconfigure an
SRAM for testing.
"""
2020-10-07 01:27:02 +02:00
debug.info(1, "Recomputing with words per row: {}".format(self.words_per_row))
2020-11-03 15:29:17 +01:00
# If the banks changed
2020-10-07 01:27:02 +02:00
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
2020-11-03 15:29:17 +01:00
# Fix the number of columns and rows
2020-10-07 01:27:02 +02:00
self.num_cols = int(self.words_per_row * self.word_size)
self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows
2020-10-07 01:27:02 +02:00
debug.info(1, "Rows: {} Cols: {}".format(self.num_rows_temp, self.num_cols))
2022-08-11 00:36:41 +02:00
# Fix the write_size
if self.write_size_init:
self.write_size = self.write_size_init
else:
self.write_size = self.word_size
# Compute the address and bank sizes
self.row_addr_size = ceil(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
2022-08-05 01:36:26 +02:00
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
#self.addr_size = self.bank_addr_size
2020-10-07 01:27:02 +02:00
debug.info(1, "Row addr size: {}".format(self.row_addr_size)
+ " Col addr size: {}".format(self.col_addr_size)
+ " Bank addr size: {}".format(self.bank_addr_size))
2021-06-19 03:10:12 +02:00
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
if num_ports == 1:
if ((self.num_cols + num_ports + self.num_spare_cols) % self.array_col_multiple != 0):
debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, self.array_col_multiple), -1)
if ((self.num_rows + num_ports) % self.array_row_multiple != 0):
debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, self.array_row_multiple), -1)
2021-06-22 00:16:36 +02:00
2020-10-07 01:27:02 +02:00
def estimate_words_per_row(self, tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
2020-10-07 01:27:02 +02:00
tentative_column_ways = tentative_num_cols / word_size
column_mux_sizes = [1, 2, 4, 8, 16]
# If we are double, we may want a larger column mux
if tentative_column_ways > 2 * column_mux_sizes[-1]:
debug.warning("Extremely large number of columns for 16-way maximum column mux.")
2020-10-07 01:27:02 +02:00
closest_way = min(column_mux_sizes, key=lambda x: abs(x - tentative_column_ways))
return closest_way
def amend_words_per_row(self, tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(not OPTS.is_unit_test and tentative_num_rows > 512):
2020-10-07 01:27:02 +02:00
debug.check(tentative_num_rows * words_per_row <= 4096,
"Number of words exceeds 2048")
return int(words_per_row * tentative_num_rows / 512)
2020-11-03 15:29:17 +01:00
# Recompute the words per row given a hard min
2020-10-07 01:27:02 +02:00
if (not OPTS.is_unit_test and tentative_num_rows < 16):
debug.check(tentative_num_rows * words_per_row >= 16,
"Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row * tentative_num_rows / 16)
2020-11-03 15:29:17 +01:00
return words_per_row