Move sram and sram_config to openram namespace

This commit is contained in:
Eren Dogan
2022-12-02 15:28:06 -08:00
parent 1aa403d7db
commit 6a4f6cbbed
110 changed files with 124 additions and 122 deletions
-2
View File
@@ -83,6 +83,4 @@ from .write_driver_array import *
from .write_driver import *
from .write_mask_and_array import *
from .sram_1bank import *
from .sram_config import *
from .sram import *
from .internal_base import *
-194
View File
@@ -1,194 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2022 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.
#
import os
import shutil
import datetime
from openram import debug
from openram.characterizer import functional
from openram.modules import sram_config as config
from openram import OPTS, print_time
class sram():
"""
This is not a design module, but contains an SRAM design instance.
It could later try options of number of banks and organization to compare
results.
We can later add visualizer and other high-level functions as needed.
"""
def __init__(self, sram_config=None, name=None):
# Create default configs if custom config isn't provided
if sram_config is None:
sram_config = config(word_size=OPTS.word_size,
num_words=OPTS.num_words,
write_size=OPTS.write_size,
num_banks=OPTS.num_banks,
words_per_row=OPTS.words_per_row,
num_spare_rows=OPTS.num_spare_rows,
num_spare_cols=OPTS.num_spare_cols)
if name is None:
name = OPTS.output_name
sram_config.set_local_config(self)
# reset the static duplicate name checker for unit tests
# in case we create more than one SRAM
from openram.base import design
design.name_map=[]
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
self.num_words,
self.num_banks))
start_time = datetime.datetime.now()
self.name = name
from .sram_1bank import sram_1bank as sram
self.s = sram(name, sram_config)
self.s.create_netlist()
if not OPTS.netlist_only:
self.s.create_layout()
if not OPTS.is_unit_test:
print_time("SRAM creation", datetime.datetime.now(), start_time)
def sp_write(self, name, lvs=False, trim=False):
self.s.sp_write(name, lvs, trim)
def lef_write(self, name):
self.s.lef_write(name)
def gds_write(self, name):
self.s.gds_write(name)
def verilog_write(self, name):
self.s.verilog_write(name)
if self.num_banks != 1:
from .sram_multibank import sram_multibank
mb = sram_multibank(self.s)
mb.verilog_write(name[:-2] + '_top.v')
def extended_config_write(self, name):
"""Dump config file with all options.
Include defaults and anything changed by input config."""
f = open(name, "w")
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
for var_name, var_value in var_dict.items():
if isinstance(var_value, str):
f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n")
else:
f.write(str(var_name) + " = " + str(var_value)+ "\n")
f.close()
def save(self):
""" Save all the output files while reporting time to do it as well. """
# Import this at the last minute so that the proper tech file
# is loaded and the right tools are selected
from openram import verify
# Save the spice file
start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".sp"
debug.print_raw("SP: Writing to {0}".format(spname))
self.sp_write(spname)
functional(self.s,
os.path.basename(spname),
cycles=200,
output_path=OPTS.output_path)
print_time("Spice writing", datetime.datetime.now(), start_time)
if not OPTS.netlist_only:
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.s.name + ".gds"
debug.print_raw("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
if OPTS.check_lvsdrc:
verify.write_drc_script(cell_name=self.s.name,
gds_name=os.path.basename(gdsname),
extract=True,
final_verification=True,
output_path=OPTS.output_path)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef"
debug.print_raw("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Save the LVS file
start_time = datetime.datetime.now()
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
debug.print_raw("LVS: Writing to {0}".format(lvsname))
self.sp_write(lvsname, lvs=True)
if not OPTS.netlist_only and OPTS.check_lvsdrc:
verify.write_lvs_script(cell_name=self.s.name,
gds_name=os.path.basename(gdsname),
sp_name=os.path.basename(lvsname),
final_verification=True,
output_path=OPTS.output_path)
print_time("LVS writing", datetime.datetime.now(), start_time)
# Save the extracted spice file
if OPTS.use_pex:
start_time = datetime.datetime.now()
# Output the extracted design if requested
pexname = OPTS.output_path + self.s.name + ".pex.sp"
spname = OPTS.output_path + self.s.name + ".sp"
verify.run_pex(self.s.name, gdsname, spname, output=pexname)
sp_file = pexname
print_time("Extraction", datetime.datetime.now(), start_time)
else:
# Use generated spice file for characterization
sp_file = spname
# Save a functional simulation file
# Characterize the design
start_time = datetime.datetime.now()
from openram.characterizer import lib
debug.print_raw("LIB: Characterizing... ")
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
print_time("Characterization", datetime.datetime.now(), start_time)
# Write the config file
start_time = datetime.datetime.now()
from shutil import copyfile
copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py')
debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py'))
print_time("Config", datetime.datetime.now(), start_time)
# Write the datasheet
start_time = datetime.datetime.now()
from openram.datasheet import datasheet_gen
dname = OPTS.output_path + self.s.name + ".html"
debug.print_raw("Datasheet: Writing to {0}".format(dname))
datasheet_gen.datasheet_write(dname)
print_time("Datasheet", datetime.datetime.now(), start_time)
# Write a verilog model
start_time = datetime.datetime.now()
vname = OPTS.output_path + self.s.name + '.v'
debug.print_raw("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time)
# Write out options if specified
if OPTS.output_extended_config:
start_time = datetime.datetime.now()
oname = OPTS.output_path + OPTS.output_name + "_extended.py"
debug.print_raw("Extended Config: Writing to {0}".format(oname))
self.extended_config_write(oname)
print_time("Extended Config", datetime.datetime.now(), start_time)
-177
View File
@@ -1,177 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2022 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 math import log, sqrt, ceil
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """
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):
self.word_size = word_size
self.num_words = num_words
# Don't add a write mask if it is the same size as the data word
self.write_size_init = write_size
if write_size:
self.write_size = write_size
else:
self.write_size = word_size
self.num_banks = num_banks
self.num_spare_rows = num_spare_rows
self.num_spare_cols = num_spare_cols
try:
from openram.tech import array_row_multiple
self.array_row_multiple = array_row_multiple
except ImportError:
self.array_row_multiple = 1
try:
from openram.tech import array_col_multiple
self.array_col_multiple = array_col_multiple
except ImportError:
self.array_col_multiple = 1
# 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 = ["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
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 bitcell size by trying to make it square."""
bitcell = factory.create(module_type=OPTS.bitcell)
debug.check(ceil(log(self.num_banks, 2)) == log(self.num_banks, 2) ,
"Number of banks should be power of 2.")
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
# 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)
self.bank_area = bitcell.width * bitcell.height * self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length / bitcell.width)
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
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row * self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
self.recompute_sizes()
# Set word_per_row in OPTS
OPTS.words_per_row = self.words_per_row
debug.info(1, "Set SRAM Words Per Row={}".format(OPTS.words_per_row))
def recompute_sizes(self):
"""
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
SRAM for testing.
"""
debug.info(1, "Recomputing with words per row: {}".format(self.words_per_row))
# If the banks changed
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
# Fix the number of columns and rows
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)
self.num_rows = self.num_rows_temp + self.num_spare_rows
debug.info(1, "Rows: {} Cols: {}".format(self.num_rows_temp, self.num_cols))
# Fix the write_size
if self.write_size_init:
self.write_size = self.write_size_init
else:
self.write_size = self.word_size
# Compute the address and bank sizes
self.row_addr_size = ceil(log(self.num_rows, 2))
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))
#self.addr_size = self.bank_addr_size
debug.info(1, "Row addr size: {}".format(self.row_addr_size)
+ " Col addr size: {}".format(self.col_addr_size)
+ " Bank addr size: {}".format(self.bank_addr_size))
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
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)
def estimate_words_per_row(self, tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
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.")
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):
"""
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):
debug.check(tentative_num_rows * words_per_row <= 4096,
"Number of words exceeds 2048")
return int(words_per_row * tentative_num_rows / 512)
# Recompute the words per row given a hard min
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)
return words_per_row