2023-03-08 09:08:43 +01:00
# See LICENSE for licensing information.
#
2024-01-03 23:32:44 +01:00
# Copyright (c) 2016-2024 Regents of the University of California and The Board
2023-03-08 09:08:43 +01:00
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from typing import List
from math import log , sqrt , ceil
from openram import debug
from openram . sram_factory import factory
from openram import OPTS
class rom_config :
""" This is a structure that is used to hold the ROM configuration options. """
2023-04-04 01:04:12 +02:00
def __init__ ( self , word_size , rom_data , words_per_row = None , rom_endian = " little " , scramble_bits = True , strap_spacing = 8 , data_type = " hex " ) :
2023-03-08 09:08:43 +01:00
self . word_size = word_size
self . word_bits = self . word_size * 8
self . rom_data = rom_data
self . strap_spacing = strap_spacing
# TODO: This currently does nothing. It should change the behavior of the chunk funciton.
self . endian = rom_endian
2023-04-04 01:04:12 +02:00
self . data_type = data_type
2023-03-08 09:08:43 +01:00
# This should pretty much always be true. If you want to make silicon art you might set to false
self . scramble_bits = scramble_bits
# This will get over-written when we determine the organization
self . words_per_row = words_per_row
self . compute_sizes ( )
def __str__ ( self ) :
""" override print function output """
config_items = [ " word_size " ,
" num_words " ,
" words_per_row " ,
" endian " ,
" strap_spacing " ,
" rom_data " ]
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 """
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 :
setattr ( module , member , getattr ( self , member ) )
def compute_sizes ( self ) :
""" Computes the organization of the memory using data size by trying to make it a rectangle. """
2023-04-04 01:04:12 +02:00
if self . data_type == " hex " :
raw_data = self . read_data_hex ( )
elif self . data_type == " bin " :
raw_data = self . read_data_bin ( )
else :
debug . error ( f " Invalid input data type: { self . data_type } " , - 1 )
2023-03-08 09:08:43 +01:00
# data size in bytes
data_size = len ( raw_data ) / 8
self . num_words = int ( data_size / self . word_size )
# If this was hard coded, don't dynamically compute it!
if not self . words_per_row :
# Row size if the array was square
bytes_per_row = sqrt ( self . num_words )
# Heuristic to value longer wordlines over long bitlines.
# The extra factor of 2 in the denominator should make the array less square
self . words_per_row = int ( ceil ( bytes_per_row / ( 2 * self . word_size ) ) )
self . cols = self . words_per_row * self . word_size * 8
self . rows = int ( self . num_words / self . words_per_row )
self . chunk_data ( raw_data )
# Set word_per_row in OPTS
OPTS . words_per_row = self . words_per_row
debug . info ( 1 , " Read rom data file: length {0} bytes, {1} words, set number of cols to {2} , rows to {3} , with {4} words per row " . format ( data_size , self . num_words , self . cols , self . rows , self . words_per_row ) )
2023-04-04 01:04:12 +02:00
def read_data_hex ( self ) - > List [ int ] :
# Read data as hexidecimal text file
with open ( self . rom_data , ' r ' ) as hex_file :
hex_data = hex_file . read ( )
# Convert from hex into an int
data_int = int ( hex_data , 16 )
# Then from int into a right aligned, zero padded string
bin_string = bin ( data_int ) [ 2 : ] . zfill ( len ( hex_data ) * 4 )
# Then turn the string into a list of ints
bin_data = list ( bin_string )
raw_data = [ int ( x ) for x in bin_data ]
return raw_data
def read_data_bin ( self ) - > List [ int ] :
# Read data as a binary file
with open ( self . rom_data , ' rb ' ) as bin_file :
bin_data = bin_file . read ( )
# Convert from a list of bytes to a single string of bits
bin_string = " " . join ( f " { n : 08b } " for n in bin_data )
# Then turn the string into a list of ints
bin_data = list ( bin_string )
raw_data = [ int ( x ) for x in bin_data ]
return raw_data
2023-03-08 09:08:43 +01:00
def chunk_data ( self , raw_data : List [ int ] ) :
"""
Chunks a flat list of bits into rows based on the calculated ROM sizes . Handles scrambling of data
"""
bits_per_row = self . cols
chunked_data = [ ]
for i in range ( 0 , len ( raw_data ) , bits_per_row ) :
row_data = raw_data [ i : i + bits_per_row ]
if len ( row_data ) < bits_per_row :
row_data = [ 0 ] * ( bits_per_row - len ( row_data ) ) + row_data
chunked_data . append ( row_data )
self . data = chunked_data
if self . scramble_bits :
scrambled_chunked = [ ]
for row_data in chunked_data :
scambled_data = [ ]
for bit in range ( self . word_bits ) :
for word in range ( self . words_per_row ) :
scambled_data . append ( row_data [ bit + word * self . word_bits ] )
scrambled_chunked . append ( scambled_data )
self . data = scrambled_chunked