mirror of https://github.com/VLSIDA/OpenRAM.git
147 lines
5.4 KiB
Python
147 lines
5.4 KiB
Python
# See LICENSE for licensing information.
|
|
#
|
|
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
|
# 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. """
|
|
|
|
def __init__(self, word_size, rom_data, words_per_row=None, rom_endian="little", scramble_bits=True, strap_spacing=8, data_type="hex"):
|
|
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
|
|
self.data_type = data_type
|
|
# 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."""
|
|
|
|
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)
|
|
|
|
# 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))
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|