mirror of https://github.com/VLSIDA/OpenRAM.git
Add spice pruning for speed-up. Fix spice search bugs. Add time in stages to openram output.
This commit is contained in:
parent
e818cdc992
commit
29c5ab48f0
|
|
@ -17,7 +17,7 @@ class bitcell(design.design):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
design.design.__init__(self, "cell_6t")
|
design.design.__init__(self, "cell_6t")
|
||||||
debug.info(2, "Create bitcell object")
|
debug.info(2, "Create bitcell")
|
||||||
|
|
||||||
self.width = bitcell.width
|
self.width = bitcell.width
|
||||||
self.height = bitcell.height
|
self.height = bitcell.height
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class delay():
|
||||||
self.word_size = sram.word_size
|
self.word_size = sram.word_size
|
||||||
self.addr_size = sram.addr_size
|
self.addr_size = sram.addr_size
|
||||||
self.sram_sp_file = spfile
|
self.sram_sp_file = spfile
|
||||||
|
|
||||||
self.vdd = tech.spice["supply_voltage"]
|
self.vdd = tech.spice["supply_voltage"]
|
||||||
self.gnd = tech.spice["gnd_voltage"]
|
self.gnd = tech.spice["gnd_voltage"]
|
||||||
|
|
||||||
|
|
@ -326,6 +326,7 @@ class delay():
|
||||||
self.probe_address = probe_address
|
self.probe_address = probe_address
|
||||||
self.probe_data = probe_data
|
self.probe_data = probe_data
|
||||||
|
|
||||||
|
|
||||||
def analyze(self,probe_address, probe_data, slews, loads):
|
def analyze(self,probe_address, probe_data, slews, loads):
|
||||||
"""main function to calculate the min period for a low_to_high
|
"""main function to calculate the min period for a low_to_high
|
||||||
transistion and a high_to_low transistion returns a dictionary
|
transistion and a high_to_low transistion returns a dictionary
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import delay
|
||||||
import charutils as ch
|
import charutils as ch
|
||||||
import tech
|
import tech
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from trim_spice import trim_spice
|
||||||
|
|
||||||
OPTS = globals.get_opts()
|
OPTS = globals.get_opts()
|
||||||
|
|
||||||
|
|
@ -18,13 +19,25 @@ class lib:
|
||||||
|
|
||||||
def __init__(self, libname, sram, spfile, use_model=OPTS.analytical_delay):
|
def __init__(self, libname, sram, spfile, use_model=OPTS.analytical_delay):
|
||||||
self.sram = sram
|
self.sram = sram
|
||||||
self.spfile = spfile
|
self.sp_file = spfile
|
||||||
self.use_model = use_model
|
self.use_model = use_model
|
||||||
self.name = sram.name
|
self.name = sram.name
|
||||||
self.num_words = sram.num_words
|
self.num_words = sram.num_words
|
||||||
self.word_size = sram.word_size
|
self.word_size = sram.word_size
|
||||||
self.addr_size = sram.addr_size
|
self.addr_size = sram.addr_size
|
||||||
|
|
||||||
|
# Set up to trim the netlist here if that is enabled
|
||||||
|
if OPTS.trim_netlist:
|
||||||
|
self.sim_sp_file = "{}reduced.sp".format(OPTS.openram_temp)
|
||||||
|
self.trimsp=trim_spice(self.sp_file, self.sim_sp_file)
|
||||||
|
self.trimsp.set_configuration(self.sram.num_banks,
|
||||||
|
self.sram.num_rows,
|
||||||
|
self.sram.num_cols,
|
||||||
|
self.sram.word_size)
|
||||||
|
else:
|
||||||
|
# Else, use the non-reduced netlist file for simulation
|
||||||
|
self.sim_sp_file = self.sp_file
|
||||||
|
|
||||||
# These are the parameters to determine the table sizes
|
# These are the parameters to determine the table sizes
|
||||||
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8])
|
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8])
|
||||||
self.load_scales = np.array([0.25, 1, 8])
|
self.load_scales = np.array([0.25, 1, 8])
|
||||||
|
|
@ -378,12 +391,15 @@ class lib:
|
||||||
try:
|
try:
|
||||||
self.d
|
self.d
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.d = delay.delay(self.sram, self.spfile)
|
self.d = delay.delay(self.sram, self.sim_sp_file)
|
||||||
if self.use_model:
|
if self.use_model:
|
||||||
self.delay = self.d.analytical_model(self.sram,self.slews,self.loads)
|
self.delay = self.d.analytical_model(self.sram,self.slews,self.loads)
|
||||||
else:
|
else:
|
||||||
probe_address = "1" * self.addr_size
|
probe_address = "1" * self.addr_size
|
||||||
probe_data = self.word_size - 1
|
probe_data = self.word_size - 1
|
||||||
|
# We must trim based on a specific address and data bit
|
||||||
|
if OPTS.trim_netlist:
|
||||||
|
self.trimsp.trim(probe_address,probe_data)
|
||||||
self.delay = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
|
self.delay = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
|
||||||
|
|
||||||
def compute_setup_hold(self):
|
def compute_setup_hold(self):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
import debug
|
||||||
|
from math import log
|
||||||
|
|
||||||
|
class trim_spice():
|
||||||
|
"""
|
||||||
|
A utility to trim redundant parts of an SRAM spice netlist.
|
||||||
|
Input is an SRAM spice file. Output is an equivalent netlist
|
||||||
|
that works for a single address and range of data bits.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, spfile, reduced_spfile):
|
||||||
|
self.sp_file = spfile
|
||||||
|
self.reduced_spfile = reduced_spfile
|
||||||
|
|
||||||
|
debug.info(1,"Trimming non-critical cells to speed-up characterization: {}.".format(reduced_spfile))
|
||||||
|
|
||||||
|
# Load the file into a buffer for performance
|
||||||
|
sp = open(self.sp_file, "r")
|
||||||
|
self.spice = sp.readlines()
|
||||||
|
for i in range(len(self.spice)):
|
||||||
|
self.spice[i] = self.spice[i].rstrip(" \n")
|
||||||
|
|
||||||
|
|
||||||
|
self.sp_buffer = self.spice
|
||||||
|
|
||||||
|
def set_configuration(self, banks, rows, columns, word_size):
|
||||||
|
""" Set the configuration of SRAM sizes that we are simulating.
|
||||||
|
Need the: number of banks, number of rows in each bank, number of
|
||||||
|
columns in each bank, and data word size."""
|
||||||
|
self.num_banks = banks
|
||||||
|
self.num_rows = rows
|
||||||
|
self.num_columns = columns
|
||||||
|
self.word_size = word_size
|
||||||
|
|
||||||
|
self.words_per_row = self.num_columns / self.word_size
|
||||||
|
self.row_addr_size = int(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))
|
||||||
|
|
||||||
|
|
||||||
|
def trim(self, address, data_bit):
|
||||||
|
""" Reduce the spice netlist but KEEP the given bits at the
|
||||||
|
address (and things that will add capacitive load!)"""
|
||||||
|
|
||||||
|
# Always start fresh if we do multiple reductions
|
||||||
|
self.sp_buffer = self.spice
|
||||||
|
|
||||||
|
# Find the row and column indices for the removals
|
||||||
|
# Convert address froms tring to int
|
||||||
|
address = int(address,2)
|
||||||
|
array_row = address >> self.col_addr_size
|
||||||
|
# Which word in the array (0 if only one word)
|
||||||
|
if self.col_addr_size>0:
|
||||||
|
lower_mask = int(self.col_addr_size-1)
|
||||||
|
lower_address = address & lower_mask
|
||||||
|
else:
|
||||||
|
lower_address=0
|
||||||
|
# Which bit in the array
|
||||||
|
array_bit = lower_address*self.word_size + data_bit
|
||||||
|
|
||||||
|
# 1. Keep cells in the bitcell array based on WL and BL
|
||||||
|
wl_name = "wl[{}]".format(array_row)
|
||||||
|
bl_name = "bl[{}]".format(array_bit)
|
||||||
|
self.remove_insts("bitcell_array",[wl_name,bl_name])
|
||||||
|
|
||||||
|
# 2. Keep sense amps basd on BL
|
||||||
|
self.remove_insts("sense_amp_array",[bl_name])
|
||||||
|
|
||||||
|
# 3. Keep column muxes basd on BL
|
||||||
|
self.remove_insts("column_mux_array",[bl_name])
|
||||||
|
|
||||||
|
# 4. Keep write driver based on DATA
|
||||||
|
data_name = "data[{}]".format(data_bit)
|
||||||
|
self.remove_insts("write_driver_array",[data_name])
|
||||||
|
|
||||||
|
# 5. Keep wordline driver based on WL
|
||||||
|
# Need to keep the gater too
|
||||||
|
#self.remove_insts("wordline_driver",wl_name)
|
||||||
|
|
||||||
|
# 6. Keep precharges based on BL
|
||||||
|
self.remove_insts("precharge_array",[bl_name])
|
||||||
|
|
||||||
|
# Everything else isn't worth removing. :)
|
||||||
|
|
||||||
|
# Finally, write out the buffer as the new reduced file
|
||||||
|
sp = open(self.reduced_spfile, "w")
|
||||||
|
sp.write("\n".join(self.sp_buffer))
|
||||||
|
|
||||||
|
|
||||||
|
def remove_insts(self, subckt_name, keep_inst_list):
|
||||||
|
"""This will remove all of the instances in the list from the named
|
||||||
|
subckt that DO NOT contain a term in the list. It just does a
|
||||||
|
match of the line with a term so you can search for a single
|
||||||
|
net connection, the instance name, anything..
|
||||||
|
"""
|
||||||
|
start_name = ".SUBCKT {}".format(subckt_name)
|
||||||
|
end_name = ".ENDS {}".format(subckt_name)
|
||||||
|
|
||||||
|
in_subckt=False
|
||||||
|
new_buffer=[]
|
||||||
|
for line in self.sp_buffer:
|
||||||
|
if start_name in line:
|
||||||
|
new_buffer.append(line)
|
||||||
|
in_subckt=True
|
||||||
|
elif end_name in line:
|
||||||
|
new_buffer.append(line)
|
||||||
|
in_subckt=False
|
||||||
|
elif in_subckt:
|
||||||
|
for k in keep_inst_list:
|
||||||
|
if k in line:
|
||||||
|
new_buffer.append(line)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
new_buffer.append(line)
|
||||||
|
|
||||||
|
self.sp_buffer = new_buffer
|
||||||
|
|
@ -34,6 +34,9 @@ def info(lev, str):
|
||||||
if (OPTS.debug_level >= lev):
|
if (OPTS.debug_level >= lev):
|
||||||
frm = inspect.stack()[1]
|
frm = inspect.stack()[1]
|
||||||
mod = inspect.getmodule(frm[0])
|
mod = inspect.getmodule(frm[0])
|
||||||
print("[{0}]: {1}".format(frm[0].f_code.co_name,str))
|
#classname = frm.f_globals['__name__']
|
||||||
# This sometimes gets a NoneType mod...
|
if mod.__name__ == None:
|
||||||
# print "[" , mod.__name__ , "]: ", str
|
class_name=""
|
||||||
|
else:
|
||||||
|
class_name=mod.__name__
|
||||||
|
print("[{0}/{1}]: {2}".format(class_name,frm[0].f_code.co_name,str))
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ def parse_args():
|
||||||
help="Technology name"),
|
help="Technology name"),
|
||||||
optparse.make_option("-s", "--spiceversion", dest="spice_version",
|
optparse.make_option("-s", "--spiceversion", dest="spice_version",
|
||||||
help="Spice simulator name"),
|
help="Spice simulator name"),
|
||||||
optparse.make_option("-r", "--reduce_netlist", action="store_true", dest="remove_noncritical",
|
optparse.make_option("-r", "--remove_netlist_trimming", action="store_false", dest="trim_netlist",
|
||||||
help="Remove noncritical memory cells during characterization"),
|
help="Disable removal of noncritical memory cells during characterization"),
|
||||||
optparse.make_option("-a", "--analytical", action="store_true", dest="analytical_delay",
|
optparse.make_option("-a", "--analytical", action="store_true", dest="analytical_delay",
|
||||||
help="Use analytical models to calculate delays (default)"),
|
help="Use analytical models to calculate delays (default)"),
|
||||||
optparse.make_option("-c", "--characterize", action="store_false", dest="analytical_delay",
|
optparse.make_option("-c", "--characterize", action="store_false", dest="analytical_delay",
|
||||||
|
|
@ -163,7 +163,7 @@ def set_calibre():
|
||||||
# everything.
|
# everything.
|
||||||
if not OPTS.check_lvsdrc:
|
if not OPTS.check_lvsdrc:
|
||||||
# over-ride the check LVS/DRC option
|
# over-ride the check LVS/DRC option
|
||||||
debug.info(0,"Over-riding LVS/DRC. Not performing inline LVS/DRC.")
|
debug.info(0,"Over-riding LVS/DRC. Not performing LVS/DRC.")
|
||||||
else:
|
else:
|
||||||
# see if calibre is in the path (extend to other tools later)
|
# see if calibre is in the path (extend to other tools later)
|
||||||
for path in os.environ["PATH"].split(os.pathsep):
|
for path in os.environ["PATH"].split(os.pathsep):
|
||||||
|
|
@ -175,7 +175,7 @@ def set_calibre():
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# otherwise, give warning and procede
|
# otherwise, give warning and procede
|
||||||
debug.warning("Calibre not found. Not performing inline LVS/DRC.")
|
debug.warning("Calibre not found. Not performing LVS/DRC.")
|
||||||
OPTS.check_lvsdrc = False
|
OPTS.check_lvsdrc = False
|
||||||
|
|
||||||
def end_openram():
|
def end_openram():
|
||||||
|
|
@ -234,13 +234,12 @@ def setup_paths():
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_spice(spice_exe):
|
def find_spice(check_exe):
|
||||||
# Check if the preferred spice option exists in the path
|
# Check if the preferred spice option exists in the path
|
||||||
for path in os.environ["PATH"].split(os.pathsep):
|
for path in os.environ["PATH"].split(os.pathsep):
|
||||||
spice_exe = os.path.join(path, OPTS.spice_version)
|
spice_exe = os.path.join(path, check_exe)
|
||||||
# if it is found, then break and use first version
|
# if it is found, then break and use first version
|
||||||
if is_exe(spice_exe):
|
if is_exe(spice_exe):
|
||||||
debug.info(1, "Using spice: " + spice_exe)
|
|
||||||
OPTS.spice_exe = spice_exe
|
OPTS.spice_exe = spice_exe
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
@ -253,21 +252,18 @@ def set_spice():
|
||||||
debug.info(1,"Using analytical delay models (no characterization)")
|
debug.info(1,"Using analytical delay models (no characterization)")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
debug.info(1,"Performing simulation-based characterization (may be slow!)")
|
spice_preferences = ["xa", "hspice", "ngspice", "ngspice.exe"]
|
||||||
|
if OPTS.spice_version != "":
|
||||||
OPTS.spice_exe = ""
|
if not find_spice(OPTS.spice_version):
|
||||||
|
debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_version),1)
|
||||||
spice_preferences = ["xa", "hspice", "ngspice", "ngspice.exe"]
|
else:
|
||||||
|
for spice_name in spice_preferences:
|
||||||
if OPTS.spice_version != "":
|
if find_spice(spice_name):
|
||||||
if not find_spice(OPTS.spice_version):
|
OPTS.spice_version=spice_name
|
||||||
debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_version),1)
|
debug.info(1, "Using spice: " + OPTS.spice_exe)
|
||||||
else:
|
break
|
||||||
for spice_exe in spice_preferences:
|
else:
|
||||||
if find_spice(spice_exe):
|
debug.info(1, "Could not find {}, trying next spice simulator. ".format(spice_name))
|
||||||
break
|
|
||||||
else:
|
|
||||||
debug.warning("Unable to find spice {0}, trying another.".format(spice_exe))
|
|
||||||
|
|
||||||
# set the input dir for spice files if using ngspice
|
# set the input dir for spice files if using ngspice
|
||||||
if OPTS.spice_version == "ngspice":
|
if OPTS.spice_version == "ngspice":
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class nand_2(design.design):
|
||||||
|
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.DRC_LVS()
|
#self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add pins """
|
""" Add pins """
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class nor_2(design.design):
|
||||||
|
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.DRC_LVS()
|
#self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,14 @@ global OPTS
|
||||||
|
|
||||||
(OPTS, args) = globals.parse_args()
|
(OPTS, args) = globals.parse_args()
|
||||||
|
|
||||||
|
def print_time(name, now_time, last_time=None):
|
||||||
|
if last_time:
|
||||||
|
time = round((now_time-last_time).total_seconds(),1)
|
||||||
|
else:
|
||||||
|
time = now_time
|
||||||
|
print("** {0}: {1} seconds".format(name,time))
|
||||||
|
return now_time
|
||||||
|
|
||||||
# These depend on arguments, so don't load them until now.
|
# These depend on arguments, so don't load them until now.
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
@ -59,7 +67,7 @@ if (OPTS.output_name == ""):
|
||||||
num_banks,
|
num_banks,
|
||||||
OPTS.tech_name)
|
OPTS.tech_name)
|
||||||
|
|
||||||
debug.info(1, "Output files are " + OPTS.output_name + ".(sp|gds|v|lib|lef)")
|
print("Output files are " + OPTS.output_name + ".(sp|gds|v|lib|lef)")
|
||||||
|
|
||||||
print("Technology: {0}".format(OPTS.tech_name))
|
print("Technology: {0}".format(OPTS.tech_name))
|
||||||
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(word_size,num_words,num_banks))
|
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(word_size,num_words,num_banks))
|
||||||
|
|
@ -68,20 +76,25 @@ print("Word size: {0}\nWords: {1}\nBanks: {2}".format(word_size,num_words,num_ba
|
||||||
if OPTS.analytical_delay:
|
if OPTS.analytical_delay:
|
||||||
print("Using analytical delay models (no characterization)")
|
print("Using analytical delay models (no characterization)")
|
||||||
else:
|
else:
|
||||||
print("Performing simulation-based characterization (may be slow!)")
|
print("Performing simulation-based characterization with {}".format(OPTS.spice_version))
|
||||||
|
|
||||||
|
if OPTS.trim_netlist:
|
||||||
|
print("Trimming netlist to speed up characterization (sacrificing some accuracy).")
|
||||||
|
|
||||||
# only start importing modules after we have the config file
|
# only start importing modules after we have the config file
|
||||||
import calibre
|
import calibre
|
||||||
import sram
|
import sram
|
||||||
|
|
||||||
print("Start: {0}".format(datetime.datetime.now()))
|
start_time = datetime.datetime.now()
|
||||||
|
last_time = start_time
|
||||||
|
print_time("Start",datetime.datetime.now())
|
||||||
|
|
||||||
# import SRAM test generation
|
# import SRAM test generation
|
||||||
s = sram.sram(word_size=word_size,
|
s = sram.sram(word_size=word_size,
|
||||||
num_words=num_words,
|
num_words=num_words,
|
||||||
num_banks=num_banks,
|
num_banks=num_banks,
|
||||||
name=OPTS.output_name)
|
name=OPTS.output_name)
|
||||||
|
last_time=print_time("SRAM creation", datetime.datetime.now(), last_time)
|
||||||
# Measure design area
|
# Measure design area
|
||||||
# Not working?
|
# Not working?
|
||||||
#cell_size = s.gds.measureSize(s.name)
|
#cell_size = s.gds.measureSize(s.name)
|
||||||
|
|
@ -92,36 +105,40 @@ s = sram.sram(word_size=word_size,
|
||||||
spname = OPTS.output_path + s.name + ".sp"
|
spname = OPTS.output_path + s.name + ".sp"
|
||||||
print("SP: Writing to {0}".format(spname))
|
print("SP: Writing to {0}".format(spname))
|
||||||
s.sp_write(spname)
|
s.sp_write(spname)
|
||||||
|
last_time=print_time("Spice writing", datetime.datetime.now(), last_time)
|
||||||
|
|
||||||
gdsname = OPTS.output_path + s.name + ".gds"
|
# Output the extracted design
|
||||||
print("GDS: Writing to {0}".format(gdsname))
|
|
||||||
s.gds_write(gdsname)
|
|
||||||
|
|
||||||
# Run Characterizer on the design
|
|
||||||
sram_file = spname
|
sram_file = spname
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
sram_file = OPTS.output_path + "temp_pex.sp"
|
sram_file = OPTS.output_path + "temp_pex.sp"
|
||||||
calibre.run_pex(s.name, gdsname, spname, output=sram_file)
|
calibre.run_pex(s.name, gdsname, spname, output=sram_file)
|
||||||
|
|
||||||
|
# Characterize the design
|
||||||
# geenrate verilog
|
|
||||||
import verilog
|
|
||||||
vname = OPTS.output_path + s.name + ".v"
|
|
||||||
print("Verilog: Writing to {0}".format(vname))
|
|
||||||
verilog.verilog(vname,s)
|
|
||||||
|
|
||||||
# generate LEF
|
|
||||||
import lef
|
|
||||||
lefname = OPTS.output_path + s.name + ".lef"
|
|
||||||
print("LEF: Writing to {0}".format(lefname))
|
|
||||||
lef.lef(gdsname,lefname,s)
|
|
||||||
|
|
||||||
# generate lib
|
|
||||||
import lib
|
import lib
|
||||||
libname = OPTS.output_path + s.name + ".lib"
|
libname = OPTS.output_path + s.name + ".lib"
|
||||||
print("LIB: Writing to {0}".format(libname))
|
print("LIB: Writing to {0}".format(libname))
|
||||||
lib.lib(libname,s,sram_file)
|
lib.lib(libname,s,sram_file)
|
||||||
|
last_time=print_time("Characterization", datetime.datetime.now(), last_time)
|
||||||
|
|
||||||
|
# Write the layout
|
||||||
|
gdsname = OPTS.output_path + s.name + ".gds"
|
||||||
|
print("GDS: Writing to {0}".format(gdsname))
|
||||||
|
s.gds_write(gdsname)
|
||||||
|
last_time=print_time("GDS", datetime.datetime.now(), last_time)
|
||||||
|
|
||||||
|
# Create a LEF physical model
|
||||||
|
import lef
|
||||||
|
lefname = OPTS.output_path + s.name + ".lef"
|
||||||
|
print("LEF: Writing to {0}".format(lefname))
|
||||||
|
lef.lef(gdsname,lefname,s)
|
||||||
|
last_time=print_time("LEF writing", datetime.datetime.now(), last_time)
|
||||||
|
|
||||||
|
# Write a verilog model
|
||||||
|
import verilog
|
||||||
|
vname = OPTS.output_path + s.name + ".v"
|
||||||
|
print("Verilog: Writing to {0}".format(vname))
|
||||||
|
verilog.verilog(vname,s)
|
||||||
|
last_time=print_time("Verilog writing", datetime.datetime.now(), last_time)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
print_time("End",datetime.datetime.now(), start_time)
|
||||||
print("End: {0}".format(datetime.datetime.now()))
|
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ class options(optparse.Values):
|
||||||
debug_level = 0
|
debug_level = 0
|
||||||
# This determines whether LVS and DRC is checked for each submodule.
|
# This determines whether LVS and DRC is checked for each submodule.
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
# Variable to select the variant of spice (hspice or ngspice right now)
|
# Variable to select the variant of spice
|
||||||
spice_version = "hspice"
|
spice_version = ""
|
||||||
# Should we print out the banner at startup
|
# Should we print out the banner at startup
|
||||||
print_banner = True
|
print_banner = True
|
||||||
# The Calibre executable being used which is derived from the user PATH.
|
# The Calibre executable being used which is derived from the user PATH.
|
||||||
|
|
@ -29,7 +29,7 @@ class options(optparse.Values):
|
||||||
# Run with extracted parasitics
|
# Run with extracted parasitics
|
||||||
use_pex = False
|
use_pex = False
|
||||||
# Remove noncritical memory cells for characterization speed-up
|
# Remove noncritical memory cells for characterization speed-up
|
||||||
remove_noncritical = False
|
trim_netlist = True
|
||||||
# Define the output file paths
|
# Define the output file paths
|
||||||
output_path = ""
|
output_path = ""
|
||||||
# Define the output file base name
|
# Define the output file base name
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class pinv(design.design):
|
||||||
|
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.DRC_LVS()
|
#self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
"""Adds pins for spice netlist processing"""
|
"""Adds pins for spice netlist processing"""
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class sense_amp(design.design):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(2, "Create Sense Amp object")
|
debug.info(2, "Create sense_amp")
|
||||||
|
|
||||||
self.width = sense_amp.width
|
self.width = sense_amp.width
|
||||||
self.height = sense_amp.height
|
self.height = sense_amp.height
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import math
|
|
||||||
import sys
|
import sys
|
||||||
from tech import drc, spice
|
from tech import drc, spice
|
||||||
import debug
|
import debug
|
||||||
|
|
@ -11,6 +10,7 @@ import getpass
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class sram(design.design):
|
class sram(design.design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated SRAM by connecting banks to control logic. The
|
Dynamically generated SRAM by connecting banks to control logic. The
|
||||||
|
|
@ -88,7 +88,7 @@ class sram(design.design):
|
||||||
|
|
||||||
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
|
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
|
||||||
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
|
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
|
||||||
self.bank_side_length = math.sqrt(self.bank_area)
|
self.bank_side_length = sqrt(self.bank_area)
|
||||||
|
|
||||||
# Estimate the words per row given the height of the bitcell and the square side length
|
# 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/self.bitcell.width)
|
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
|
||||||
|
|
@ -106,7 +106,7 @@ class sram(design.design):
|
||||||
self.row_addr_size = int(log(self.num_rows, 2))
|
self.row_addr_size = int(log(self.num_rows, 2))
|
||||||
self.col_addr_size = int(log(self.words_per_row, 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.bank_addr_size = self.col_addr_size + self.row_addr_size
|
||||||
self.addr_size = self.bank_addr_size + int(math.log(self.num_banks, 2))
|
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
||||||
|
|
||||||
|
|
||||||
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
"""
|
||||||
|
Check the .lib file for an SRAM with pruning
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,isapproxdiff
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
import debug
|
||||||
|
import calibre
|
||||||
|
|
||||||
|
OPTS = globals.get_opts()
|
||||||
|
|
||||||
|
|
||||||
|
class lib_test(unittest.TestCase):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
OPTS.analytical_delay = False
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
# we will manually run lvs/drc
|
||||||
|
OPTS.check_lvsdrc = False
|
||||||
|
|
||||||
|
import sram
|
||||||
|
import lib
|
||||||
|
|
||||||
|
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
|
s = sram.sram(word_size=2,
|
||||||
|
num_words=OPTS.config.num_words,
|
||||||
|
num_banks=OPTS.config.num_banks,
|
||||||
|
name="sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
|
OPTS.check_lvsdrc = True
|
||||||
|
|
||||||
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
|
s.sp_write(tempspice)
|
||||||
|
|
||||||
|
filename = s.name + ".lib"
|
||||||
|
libname = OPTS.openram_temp + filename
|
||||||
|
lib.lib(libname=libname,sram=s,spfile=tempspice,use_model=False)
|
||||||
|
|
||||||
|
# let's diff the result with a golden model
|
||||||
|
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||||
|
# From an experiment, a 15% difference between between pruned and not was found.
|
||||||
|
self.assertEqual(isapproxdiff(libname,golden,0.15),True)
|
||||||
|
|
||||||
|
os.system("rm {0}".format(libname))
|
||||||
|
OPTS.analytical_delay = True
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ class lib_test(unittest.TestCase):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
|
OPTS.trim_netlist = False
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
# we will manually run lvs/drc
|
# we will manually run lvs/drc
|
||||||
OPTS.check_lvsdrc = False
|
OPTS.check_lvsdrc = False
|
||||||
|
|
@ -46,6 +47,7 @@ class lib_test(unittest.TestCase):
|
||||||
|
|
||||||
os.system("rm {0}".format(libname))
|
os.system("rm {0}".format(libname))
|
||||||
OPTS.analytical_delay = True
|
OPTS.analytical_delay = True
|
||||||
|
OPTS.trim_netlist = True
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# instantiate a copdsay of the class to actually run the test
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class tri_gate(design.design):
|
||||||
name = "tri{0}".format(tri_gate.unique_id)
|
name = "tri{0}".format(tri_gate.unique_id)
|
||||||
tri_gate.unique_id += 1
|
tri_gate.unique_id += 1
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(2, "Create tri_gate object")
|
debug.info(2, "Create tri_gate")
|
||||||
|
|
||||||
self.width = tri_gate.width
|
self.width = tri_gate.width
|
||||||
self.height = tri_gate.height
|
self.height = tri_gate.height
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class write_driver(design.design):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(2, "Create write_driver object")
|
debug.info(2, "Create write_driver")
|
||||||
|
|
||||||
self.width = write_driver.width
|
self.width = write_driver.width
|
||||||
self.height = write_driver.height
|
self.height = write_driver.height
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue