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
2022-02-16 19:54:39 +01: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
2021-07-09 21:31:35 +02:00
# 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 :
2021-07-09 21:31:35 +02:00
self . write_size = write_size
2022-07-14 01:20:06 +02:00
else :
self . write_size = word_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
2021-06-29 20:27:54 +02:00
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
2022-02-16 19:54:39 +01: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 20:27:54 +02:00
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
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
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
2022-06-10 06:40:19 +02: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 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
2021-06-22 00:16:36 +02: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
2021-06-22 00:16:36 +02:00
# Set word_per_row in OPTS
2021-02-16 00:20:32 +01: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 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
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
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 ) )
2022-08-05 01:36:26 +02:00
#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 )
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
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 :
2021-06-29 20:27:54 +02:00
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 )
2021-06-18 23:21:02 +02:00
2021-06-29 20:27:54 +02:00
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 ) :
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
2022-02-16 19:54:39 +01: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