2019-04-26 21:21:50 +02:00
|
|
|
# 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-09-04 19:47:24 +02:00
|
|
|
import debug
|
2020-10-07 01:27:02 +02:00
|
|
|
from math import log, sqrt, ceil
|
2018-08-31 21:03:28 +02:00
|
|
|
from globals import OPTS
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2018-08-31 21:03:28 +02:00
|
|
|
|
2020-10-07 01:27:02 +02:00
|
|
|
|
2018-08-31 21:03:28 +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):
|
2018-08-31 21:03:28 +02:00
|
|
|
self.word_size = word_size
|
|
|
|
|
self.num_words = num_words
|
2019-06-29 00:43:09 +02:00
|
|
|
self.write_size = write_size
|
2018-08-31 21:03:28 +02:00
|
|
|
self.num_banks = num_banks
|
2019-12-08 14:24:39 +01:00
|
|
|
self.num_spare_rows = num_spare_rows
|
2020-04-14 05:09:10 +02:00
|
|
|
self.num_spare_cols = num_spare_cols
|
2018-08-31 21:03:28 +02:00
|
|
|
|
|
|
|
|
# This will get over-written when we determine the organization
|
2019-01-30 20:43:47 +01:00
|
|
|
self.words_per_row = words_per_row
|
2018-08-31 21:03:28 +02:00
|
|
|
|
2020-10-07 01:27:02 +02:00
|
|
|
self.compute_sizes()
|
2018-08-31 21:03:28 +02:00
|
|
|
|
|
|
|
|
def set_local_config(self, module):
|
2018-09-04 19:47:24 +02:00
|
|
|
""" Copy all of the member variables to the given module for convenience """
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-09-04 19:47:24 +02: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))
|
2018-09-04 19:47:24 +02:00
|
|
|
|
|
|
|
|
def compute_sizes(self):
|
|
|
|
|
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
|
|
|
|
|
2020-11-03 22:18:46 +01:00
|
|
|
bitcell = factory.create(module_type=OPTS.bitcell)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-07 01:27:02 +02:00
|
|
|
debug.check(self.num_banks in [1, 2, 4],
|
|
|
|
|
"Valid number of banks are 1 , 2 and 4.")
|
2018-09-04 19:47:24 +02:00
|
|
|
|
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
|
|
|
|
2018-09-04 19:47:24 +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
|
2018-09-04 19:47:24 +02:00
|
|
|
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)
|
2018-09-04 19:47:24 +02:00
|
|
|
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)
|
2018-09-04 19:47:24 +02:00
|
|
|
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
|
2018-10-23 02:02:21 +02:00
|
|
|
|
2019-07-19 23:58:37 +02:00
|
|
|
self.recompute_sizes()
|
2018-12-06 22:11:47 +01:00
|
|
|
|
|
|
|
|
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
|
2018-12-06 22:11:47 +01:00
|
|
|
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
|
|
|
|
2018-12-06 22:11:47 +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
|
|
|
|
2018-09-04 19:47:24 +02: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)
|
2019-12-08 14:24:39 +01:00
|
|
|
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))
|
2018-09-04 19:47:24 +02:00
|
|
|
|
|
|
|
|
# Compute the address and bank sizes
|
2019-12-08 14:24:39 +01:00
|
|
|
self.row_addr_size = ceil(log(self.num_rows, 2))
|
2018-09-04 19:47:24 +02:00
|
|
|
self.col_addr_size = int(log(self.words_per_row, 2))
|
|
|
|
|
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
|
|
|
|
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
2020-10-07 01:27:02 +02:00
|
|
|
debug.info(1, "Row addr size: {}".format(self.row_addr_size)
|
2019-08-05 22:53:14 +02:00
|
|
|
+ " Col addr size: {}".format(self.col_addr_size)
|
|
|
|
|
+ " Bank addr size: {}".format(self.bank_addr_size))
|
2018-09-04 19:47:24 +02:00
|
|
|
|
2020-10-07 01:27:02 +02:00
|
|
|
def estimate_words_per_row(self, tentative_num_cols, word_size):
|
2018-09-04 19:47:24 +02:00
|
|
|
"""
|
|
|
|
|
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.")
|
2018-09-04 19:47:24 +02:00
|
|
|
|
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):
|
2018-09-04 19:47:24 +02:00
|
|
|
"""
|
|
|
|
|
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
|
|
|
|
2018-09-04 19:47:24 +02: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
|
|
|
|
2018-09-04 19:47:24 +02:00
|
|
|
return words_per_row
|