Simulate calibre extracted netlists without requiring extra layout ports

This commit is contained in:
ota2 2021-02-27 19:29:18 -05:00
parent 9a2987ad07
commit 9d025604ff
7 changed files with 122 additions and 15 deletions

View File

@ -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.

View File

@ -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]

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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]:

View File

@ -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