2019-04-26 12:21:50 -07:00
# See LICENSE for licensing information.
#
2024-01-03 14:32:44 -08:00
# Copyright (c) 2016-2024 Regents of the University of California and The Board
2019-06-14 08:43:41 -07: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 12:21:50 -07:00
#
2020-10-06 16:27:02 -07:00
from math import log , sqrt , ceil
2022-11-27 13:01:20 -08:00
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
2018-08-31 12:03:28 -07:00
2020-10-06 16:27:02 -07:00
2018-08-31 12:03:28 -07:00
class sram_config :
""" This is a structure that is used to hold the SRAM configuration options. """
2020-11-03 06:29:17 -08:00
2022-02-16 10:54:39 -08: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 12:03:28 -07:00
self . word_size = word_size
self . num_words = num_words
2021-07-09 12:31:35 -07:00
# Don't add a write mask if it is the same size as the data word
2022-08-10 15:36:41 -07:00
self . write_size_init = write_size
2022-07-13 16:20:06 -07:00
if write_size :
2021-07-09 12:31:35 -07:00
self . write_size = write_size
2022-07-13 16:20:06 -07:00
else :
self . write_size = word_size
2018-08-31 12:03:28 -07:00
self . num_banks = num_banks
2019-12-08 13:24:39 +00:00
self . num_spare_rows = num_spare_rows
2020-04-14 03:09:10 +00:00
self . num_spare_cols = num_spare_cols
2018-08-31 12:03:28 -07:00
2021-06-29 11:27:54 -07:00
try :
2022-11-27 13:01:20 -08:00
from openram.tech import array_row_multiple
2021-06-29 11:27:54 -07:00
self . array_row_multiple = array_row_multiple
except ImportError :
self . array_row_multiple = 1
try :
2022-11-27 13:01:20 -08:00
from openram.tech import array_col_multiple
2021-06-29 11:27:54 -07:00
self . array_col_multiple = array_col_multiple
except ImportError :
self . array_col_multiple = 1
2022-02-16 10:54:39 -08:00
if not self . num_spare_cols :
self . num_spare_cols = 0
if not self . num_spare_rows :
self . num_spare_rows = 0
2021-06-29 11:27:54 -07:00
2018-08-31 12:03:28 -07:00
# This will get over-written when we determine the organization
2019-01-30 11:43:47 -08:00
self . words_per_row = words_per_row
2018-08-31 12:03:28 -07:00
2020-10-06 16:27:02 -07:00
self . compute_sizes ()
2018-08-31 12:03:28 -07:00
2021-09-07 16:49:31 -07: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
2018-08-31 12:03:28 -07:00
def set_local_config ( self , module ):
2018-09-04 10:47:24 -07:00
""" Copy all of the member variables to the given module for convenience """
2020-11-03 06:29:17 -08:00
2018-09-04 10:47:24 -07: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-06 16:27:02 -07:00
setattr ( module , member , getattr ( self , member ))
2018-09-04 10:47:24 -07:00
def compute_sizes ( self ):
""" Computes the organization of the memory using bitcell size by trying to make it square."""
2020-11-03 13:18:46 -08:00
bitcell = factory . create ( module_type = OPTS . bitcell )
2020-11-03 06:29:17 -08:00
2022-06-09 21:40:19 -07:00
debug . check ( ceil ( log ( self . num_banks , 2 )) == log ( self . num_banks , 2 ) ,
"Number of banks should be power of 2." )
2018-09-04 10:47:24 -07:00
2020-10-06 16:27:02 -07: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-21 15:16:36 -07:00
2018-09-04 10:47:24 -07: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-06 16:27:02 -07:00
self . bank_area = bitcell . width * bitcell . height * self . num_bits_per_bank
2018-09-04 10:47:24 -07: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-06 16:27:02 -07:00
self . tentative_num_cols = int ( self . bank_side_length / bitcell . width )
2018-09-04 10:47:24 -07: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-06 16:27:02 -07:00
self . tentative_num_rows = self . num_bits_per_bank / ( self . words_per_row * self . word_size )
2018-09-04 10:47:24 -07:00
self . words_per_row = self . amend_words_per_row ( self . tentative_num_rows , self . words_per_row )
2018-10-22 17:02:21 -07:00
2019-07-19 14:58:37 -07:00
self . recompute_sizes ()
2018-12-06 13:11:47 -08:00
2021-06-21 15:16:36 -07:00
# Set word_per_row in OPTS
2021-02-15 15:20:32 -08:00
OPTS . words_per_row = self . words_per_row
debug . info ( 1 , "Set SRAM Words Per Row= {} " . format ( OPTS . words_per_row ))
2018-12-06 13:11:47 -08:00
def recompute_sizes ( self ):
2020-11-03 06:29:17 -08: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 13:11:47 -08:00
SRAM for testing.
"""
2020-10-06 16:27:02 -07:00
debug . info ( 1 , "Recomputing with words per row: {} " . format ( self . words_per_row ))
2020-11-03 06:29:17 -08:00
2018-12-06 13:11:47 -08:00
# If the banks changed
2020-10-06 16:27:02 -07: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 06:29:17 -08:00
2018-09-04 10:47:24 -07:00
# Fix the number of columns and rows
2020-10-06 16:27:02 -07: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 13:24:39 +00:00
self . num_rows = self . num_rows_temp + self . num_spare_rows
2020-10-06 16:27:02 -07:00
debug . info ( 1 , "Rows: {} Cols: {} " . format ( self . num_rows_temp , self . num_cols ))
2018-09-04 10:47:24 -07:00
2022-08-10 15:36:41 -07:00
# Fix the write_size
if self . write_size_init :
self . write_size = self . write_size_init
else :
self . write_size = self . word_size
2018-09-04 10:47:24 -07:00
# Compute the address and bank sizes
2019-12-08 13:24:39 +00:00
self . row_addr_size = ceil ( log ( self . num_rows , 2 ))
2018-09-04 10:47:24 -07: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 ))
2022-08-04 16:36:26 -07:00
#self.addr_size = self.bank_addr_size
2020-10-06 16:27:02 -07:00
debug . info ( 1 , "Row addr size: {} " . format ( self . row_addr_size )
2019-08-05 13:53:14 -07:00
+ " Col addr size: {} " . format ( self . col_addr_size )
+ " Bank addr size: {} " . format ( self . bank_addr_size ))
2018-09-04 10:47:24 -07:00
2021-06-18 18:10:12 -07:00
num_ports = OPTS . num_rw_ports + OPTS . num_r_ports + OPTS . num_w_ports
2026-01-14 12:08:26 -08:00
#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-21 15:16:36 -07:00
2020-10-06 16:27:02 -07:00
def estimate_words_per_row ( self , tentative_num_cols , word_size ):
2018-09-04 10:47:24 -07:00
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
2020-10-06 16:27:02 -07: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 10:47:24 -07:00
2020-10-06 16:27:02 -07: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 10:47:24 -07: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-06 16:27:02 -07: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 06:29:17 -08:00
2018-09-04 10:47:24 -07:00
# Recompute the words per row given a hard min
2020-10-06 16:27:02 -07: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 06:29:17 -08:00
2018-09-04 10:47:24 -07:00
return words_per_row
2022-02-16 10:54:39 -08:00
def setup_multiport_constants ( self ):
"""
These are contants and lists that aid multiport design.
Ports are always in the order RW, W, R.
Port indices start from 0 and increment.
A first RW port will have clk0, csb0, web0, addr0, data0
A first W port (with no RW ports) will be: clk0, csb0, addr0, data0
"""
total_ports = OPTS . num_rw_ports + OPTS . num_w_ports + OPTS . num_r_ports
# These are the read/write port indices.
self . readwrite_ports = []
# These are the read/write and write-only port indices
self . write_ports = []
# These are the write-only port indices.
self . writeonly_ports = []
# These are the read/write and read-only port indices
self . read_ports = []
# These are the read-only port indices.
self . readonly_ports = []
# These are all the ports
self . all_ports = list ( range ( total_ports ))
# The order is always fixed as RW, W, R
port_number = 0
for port in range ( OPTS . num_rw_ports ):
self . readwrite_ports . append ( port_number )
self . write_ports . append ( port_number )
self . read_ports . append ( port_number )
port_number += 1
for port in range ( OPTS . num_w_ports ):
self . write_ports . append ( port_number )
self . writeonly_ports . append ( port_number )
port_number += 1
for port in range ( OPTS . num_r_ports ):
self . read_ports . append ( port_number )
self . readonly_ports . append ( port_number )
port_number += 1