mirror of https://github.com/VLSIDA/OpenRAM.git
Simulate calibre extracted netlists without requiring extra layout ports
This commit is contained in:
parent
9a2987ad07
commit
9d025604ff
|
|
@ -220,7 +220,7 @@ class delay(simulation):
|
|||
storage_names = cell_inst.mod.get_storage_net_names()
|
||||
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
|
||||
"supported for characterization. Storage nets={}").format(storage_names))
|
||||
if not OPTS.use_pex:
|
||||
if not OPTS.use_pex or OPTS.calibre_pex:
|
||||
q_name = cell_name + '.' + str(storage_names[0])
|
||||
qbar_name = cell_name + '.' + str(storage_names[1])
|
||||
else:
|
||||
|
|
@ -418,7 +418,9 @@ class delay(simulation):
|
|||
t_rise=self.slew,
|
||||
t_fall=self.slew)
|
||||
|
||||
self.load_all_measure_nets()
|
||||
self.write_delay_measures()
|
||||
self.write_simulation_saves()
|
||||
|
||||
# run until the end of the cycle time
|
||||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||
|
|
@ -596,6 +598,69 @@ class delay(simulation):
|
|||
self.sf.write("* Write ports {}\n".format(write_port))
|
||||
self.write_delay_measures_write_port(write_port)
|
||||
|
||||
def load_pex_net(self, net: str):
|
||||
from subprocess import check_output, CalledProcessError
|
||||
prefix = (self.sram_instance_name + ".").lower()
|
||||
if not net.lower().startswith(prefix) or not OPTS.use_pex or not OPTS.calibre_pex:
|
||||
return net
|
||||
original_net = net
|
||||
net = net[len(prefix):]
|
||||
net = net.replace(".", "_").replace("[", "\[").replace("]", "\]")
|
||||
for pattern in ["\sN_{}_[MXmx]\S+_[gsd]".format(net), net]:
|
||||
try:
|
||||
match = check_output(["grep", "-m1", "-o", "-iE", pattern, self.sp_file])
|
||||
return prefix + match.decode().strip()
|
||||
except CalledProcessError:
|
||||
pass
|
||||
return original_net
|
||||
|
||||
def load_all_measure_nets(self):
|
||||
measurement_nets = set()
|
||||
for port, meas in zip(self.targ_read_ports*len(self.read_meas_lists) +
|
||||
self.targ_write_ports * len(self.write_meas_lists),
|
||||
self.read_meas_lists + self.write_meas_lists):
|
||||
for measurement in meas:
|
||||
visited = getattr(measurement, 'pex_visited', False)
|
||||
for prop in ["trig_name_no_port", "targ_name_no_port"]:
|
||||
if hasattr(measurement, prop):
|
||||
net = getattr(measurement, prop).format(port)
|
||||
if not visited:
|
||||
net = self.load_pex_net(net)
|
||||
setattr(measurement, prop, net)
|
||||
measurement_nets.add(net)
|
||||
measurement.pex_visited = True
|
||||
self.measurement_nets = measurement_nets
|
||||
return measurement_nets
|
||||
|
||||
def write_simulation_saves(self):
|
||||
for net in self.measurement_nets:
|
||||
self.sf.write(".plot V({0}) \n".format(net))
|
||||
probe_nets = set()
|
||||
sram_name = self.sram_instance_name
|
||||
col = self.bitline_column
|
||||
row = self.wordline_row
|
||||
for port in set(self.targ_read_ports + self.targ_write_ports):
|
||||
probe_nets.add("WEB{}".format(port))
|
||||
probe_nets.add("{}.w_en{}".format(self.sram_instance_name, port))
|
||||
probe_nets.add("{0}.Xbank0.Xport_data{1}.Xwrite_driver_array{1}.Xwrite_driver{2}.en_bar".format(
|
||||
self.sram_instance_name, port, self.bitline_column))
|
||||
probe_nets.add("{}.Xbank0.br_{}_{}".format(self.sram_instance_name, port,
|
||||
self.bitline_column))
|
||||
if not OPTS.use_pex:
|
||||
continue
|
||||
probe_nets.add(
|
||||
"{0}.vdd_Xbank0_Xbitcell_array_xbitcell_array_xbit_r{1}_c{2}".format(sram_name, row, col - 1))
|
||||
probe_nets.add(
|
||||
"{0}.p_en_bar{1}_Xbank0_Xport_data{1}_Xprecharge_array{1}_Xpre_column_{2}".format(sram_name, port, col))
|
||||
probe_nets.add(
|
||||
"{0}.vdd_Xbank0_Xport_data{1}_Xprecharge_array{1}_xpre_column_{2}".format(sram_name, port, col))
|
||||
probe_nets.add("{0}.vdd_Xbank0_Xport_data{1}_Xwrite_driver_array{1}_xwrite_driver{2}".format(sram_name,
|
||||
port, col))
|
||||
probe_nets.update(self.measurement_nets)
|
||||
for net in probe_nets:
|
||||
debug.info(2, "Probe: {}".format(net))
|
||||
self.sf.write(".plot V({}) \n".format(self.load_pex_net(net)))
|
||||
|
||||
def write_power_measures(self):
|
||||
"""
|
||||
Write the measure statements to quantify the leakage power only.
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ class simulation():
|
|||
"""
|
||||
|
||||
port = self.read_ports[0]
|
||||
if not OPTS.use_pex:
|
||||
if not OPTS.use_pex or OPTS.calibre_pex: # pex names handled post extraction
|
||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||
|
||||
|
|
@ -523,7 +523,7 @@ class simulation():
|
|||
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
|
||||
enable_name = sa_mods[0].get_enable_name()
|
||||
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
||||
if OPTS.use_pex:
|
||||
if OPTS.use_pex and not OPTS.calibre_pex:
|
||||
sen_name = sen_name.split('.')[-1]
|
||||
return sen_name
|
||||
|
||||
|
|
@ -581,7 +581,7 @@ class simulation():
|
|||
exclude_set = self.get_bl_name_search_exclusions()
|
||||
for int_net in [cell_bl, cell_br]:
|
||||
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
|
||||
if OPTS.use_pex:
|
||||
if OPTS.use_pex and not OPTS.calibre_pex:
|
||||
for i in range(len(bl_names)):
|
||||
bl_names[i] = bl_names[i].split('.')[-1]
|
||||
return bl_names[0], bl_names[1]
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class stimuli():
|
|||
def inst_model(self, pins, model_name):
|
||||
""" Function to instantiate a generic model with a set of pins """
|
||||
|
||||
if OPTS.use_pex:
|
||||
if OPTS.use_pex and not OPTS.calibre_pex:
|
||||
self.inst_pex_model(pins, model_name)
|
||||
else:
|
||||
self.sf.write("X{0} ".format(model_name))
|
||||
|
|
@ -282,15 +282,16 @@ class stimuli():
|
|||
self.sf.write(".OPTIONS HIER_DELIM=1 \n")
|
||||
|
||||
# create plots for all signals
|
||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.verbose_level>0:
|
||||
if OPTS.spice_name in ["hspice", "xa"]:
|
||||
self.sf.write(".probe V(*)\n")
|
||||
if not OPTS.use_pex: # Don't save all for extracted simulations
|
||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.verbose_level>0:
|
||||
if OPTS.spice_name in ["hspice", "xa"]:
|
||||
self.sf.write(".probe V(*)\n")
|
||||
else:
|
||||
self.sf.write(".plot V(*)\n")
|
||||
else:
|
||||
self.sf.write(".plot V(*)\n")
|
||||
else:
|
||||
self.sf.write("*.probe V(*)\n")
|
||||
self.sf.write("*.plot V(*)\n")
|
||||
self.sf.write("*.probe V(*)\n")
|
||||
self.sf.write("*.plot V(*)\n")
|
||||
|
||||
# end the stimulus file
|
||||
self.sf.write(".end\n\n")
|
||||
|
|
@ -349,7 +350,7 @@ class stimuli():
|
|||
valid_retcode = 0
|
||||
elif OPTS.spice_name == "hspice":
|
||||
# TODO: Should make multithreading parameter a configuration option
|
||||
cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
|
||||
cmd = "{0} -d -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
|
||||
OPTS.num_sim_threads,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
word_size = 64
|
||||
num_words = 64
|
||||
|
||||
num_rw_ports = 1
|
||||
num_r_ports = 0
|
||||
num_w_ports = 0
|
||||
num_banks = 1
|
||||
words_per_row = 1
|
||||
spice_name = "hspice"
|
||||
|
||||
|
||||
tech_name = "freepdk45"
|
||||
process_corners = ["TT"]
|
||||
supply_voltages = [1.0]
|
||||
temperatures = [25]
|
||||
|
||||
route_supplies = True
|
||||
perimeter_pins = False
|
||||
check_lvsdrc = True
|
||||
nominal_corner_only = True
|
||||
load_scales = [0.5]
|
||||
slew_scales = [0.5]
|
||||
use_pex = False
|
||||
analytical_delay = False
|
||||
|
||||
output_name = "sram_w_{0}_{1}_{2}".format(word_size, num_words, tech_name)
|
||||
output_path = "macro/{}".format(output_name)
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ class sram_base(design, verilog, lef):
|
|||
highest_coord = self.find_highest_coords()
|
||||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
if OPTS.use_pex:
|
||||
if OPTS.use_pex and not OPTS.calibre_pex:
|
||||
self.add_global_pex_labels()
|
||||
self.add_boundary(ll=vector(0, 0),
|
||||
ur=vector(self.width, self.height))
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ elif "magic"==OPTS.pex_exe[0]:
|
|||
else:
|
||||
debug.warning("Did not find a supported PEX tool."
|
||||
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.", 2)
|
||||
OPTS.calibre_pex = len(OPTS.pex_exe) > 0 and OPTS.pex_exe[0] == "calibre"
|
||||
|
||||
# if OPTS.tech_name == "sky130":
|
||||
# if OPTS.magic_exe and "magic"==OPTS.magic_exe[0]:
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import os
|
|||
import shutil
|
||||
import re
|
||||
import debug
|
||||
import utils
|
||||
from globals import OPTS
|
||||
from run_script import run_script
|
||||
|
||||
|
|
@ -166,6 +167,17 @@ def write_pex_script(cell_name, extract, output, final_verification=False, outpu
|
|||
'pexPexReportFile': cell_name + ".pex.report",
|
||||
'pexMaskDBFile': cell_name + ".maskdb",
|
||||
'cmnFDIDEFLayoutPath': cell_name + ".def",
|
||||
'cmnRunMT': "1",
|
||||
'cmnNumTurbo': "16",
|
||||
'pexPowerNames': "vdd",
|
||||
'pexGroundNames': "gnd",
|
||||
'pexPexGroundName': "1",
|
||||
'pexPexGroundNameValue': "gnd",
|
||||
'pexPexSeparator': "1",
|
||||
'pexPexSeparatorValue': "_",
|
||||
'pexPexNetlistNameSource': 'SOURCENAMES',
|
||||
'pexSVRFCmds': '{SOURCE CASE YES} {LAYOUT CASE YES}',
|
||||
'pexIncludeCmdsType': 'SVRF',
|
||||
}
|
||||
|
||||
# write the runset file
|
||||
|
|
|
|||
Loading…
Reference in New Issue