Merge branch 'drclvs' into dev

This commit is contained in:
mrg 2020-11-09 14:36:36 -08:00
commit 2c203530ad
27 changed files with 259 additions and 224 deletions

View File

@ -306,7 +306,8 @@ class delay(simulation):
self.create_test_cycles()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.delay_stim_sp = "delay_stim.sp"
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.delay_stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
self.load,
@ -350,7 +351,8 @@ class delay(simulation):
self.check_arguments()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.power_stim_sp = "power_stim.sp"
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.power_stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
self.stim = stimuli(self.sf, self.corner)
@ -616,7 +618,7 @@ class delay(simulation):
self.write_delay_stimulus()
self.stim.run_sim()
self.stim.run_sim(self.delay_stim_sp)
return self.check_measurements()
@ -772,7 +774,7 @@ class delay(simulation):
debug.info(1, "Performing leakage power simulations.")
self.write_power_stimulus(trim=False)
self.stim.run_sim()
self.stim.run_sim(self.power_stim_sp)
leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power * 1e3))
@ -780,7 +782,7 @@ class delay(simulation):
# sys.exit(1)
self.write_power_stimulus(trim=True)
self.stim.run_sim()
self.stim.run_sim(self.power_stim_sp)
trim_leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(trim_leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power * 1e3))

View File

@ -21,13 +21,24 @@ class functional(simulation):
for successful SRAM operation.
"""
def __init__(self, sram, spfile, corner, cycles=15):
def __init__(self, sram, spfile, corner=None, cycles=15, period=None, output_path=None):
super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test:
random.seed(12345)
if not corner:
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
if period:
self.period = period
if not output_path:
self.output_path = OPTS.openram_temp
else:
self.output_path = output_path
if self.write_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
@ -58,15 +69,14 @@ class functional(simulation):
self.read_check = []
self.read_results = []
def run(self, feasible_period=None):
if feasible_period: # period defaults to tech.py feasible period otherwise.
self.period = feasible_period
# Generate a random sequence of reads and writes
self.create_random_memory_sequence()
# Run SPICE simulation
# Write SPICE simulation
self.write_functional_stimulus()
self.stim.run_sim()
def run(self):
self.stim.run_sim(self.stim_sp)
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
(success, error) = self.read_stim_results()
@ -330,7 +340,8 @@ class functional(simulation):
def write_functional_stimulus(self):
""" Writes SPICE stimulus. """
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.stim_sp = "functional_stim.sp"
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
self.stim = stimuli(self.sf, self.corner)

View File

@ -27,23 +27,22 @@ class setup_hold():
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
self.period = tech.spice["feasible_period"]
debug.info(2,"Feasible period from technology file: {0} ".format(self.period))
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
self.set_corner(corner)
def set_corner(self,corner):
def set_corner(self, corner):
""" Set the corner values """
self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner
self.gnd_voltage = 0
def write_stimulus(self, mode, target_time, correct_value):
"""Creates a stimulus file for SRAM setup/hold time calculation"""
# creates and opens the stimulus file for writing
temp_stim = OPTS.openram_temp + "stim.sp"
self.stim_sp = "sh_stim.sp"
temp_stim = OPTS.openram_temp + self.stim_sp
self.sf = open(temp_stim, "w")
self.stim = stimuli(self.sf, self.corner)
@ -63,8 +62,7 @@ class setup_hold():
self.write_measures(mode=mode,
correct_value=correct_value)
self.stim.write_control(4*self.period)
self.stim.write_control(4 * self.period)
self.sf.close()
@ -79,7 +77,6 @@ class setup_hold():
self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply()
def write_data(self, mode, target_time, correct_value):
"""Create the data signals for setup/hold analysis. First period is to
initialize it to the opposite polarity. Second period is used for
@ -113,14 +110,12 @@ class setup_hold():
# without using .IC on an internal node.
# Return input to value after one period.
# The second pulse is the characterization one at 2*period
clk_times=[0, 0.1*self.period,self.period,2*self.period],
clk_times=[0, 0.1 * self.period, self.period, 2 * self.period],
data_values=[0, 1, 0, 1],
period=2*self.period,
period=2 * self.period,
slew=self.constrained_input_slew,
setup=0)
def write_measures(self, mode, correct_value):
""" Measure statements for setup/hold with right phases. """
@ -139,7 +134,6 @@ class setup_hold():
else:
din_rise_or_fall = "RISE"
self.sf.write("\n* Measure statements for pass/fail verification\n")
trig_name = "clk"
targ_name = "dout"
@ -152,8 +146,8 @@ class setup_hold():
targ_val=targ_val,
trig_dir="RISE",
targ_dir=dout_rise_or_fall,
trig_td=1.9*self.period,
targ_td=1.9*self.period)
trig_td=1.9 * self.period,
targ_td=1.9 * self.period)
targ_name = "data"
# Start triggers right after initialize value is returned to normal
@ -165,11 +159,8 @@ class setup_hold():
targ_val=targ_val,
trig_dir="RISE",
targ_dir=din_rise_or_fall,
trig_td=1.2*self.period,
targ_td=1.2*self.period)
trig_td=1.2 * self.period,
targ_td=1.2 * self.period)
def bidir_search(self, correct_value, mode):
""" This will perform a bidirectional search for either setup or hold times.
@ -182,23 +173,26 @@ class setup_hold():
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
# first iteration.
if mode == "SETUP":
feasible_bound = 1.25*self.period
infeasible_bound = 2.5*self.period
feasible_bound = 1.25 * self.period
infeasible_bound = 2.5 * self.period
else:
infeasible_bound = 1.5*self.period
feasible_bound = 2.75*self.period
infeasible_bound = 1.5 * self.period
feasible_bound = 2.75 * self.period
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
self.write_stimulus(mode=mode,
target_time=feasible_bound,
correct_value=correct_value)
self.stim.run_sim()
self.stim.run_sim(self.stim_sp)
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2)
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
ideal_clk_to_q,
setuphold_time),
2)
if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9
@ -206,57 +200,53 @@ class setup_hold():
setuphold_time *= 1e9
passing_setuphold_time = setuphold_time
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time,
feasible_bound,
2*self.period))
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time,
feasible_bound,
2 * self.period))
#raw_input("Press Enter to continue...")
while True:
target_time = (feasible_bound + infeasible_bound)/2
target_time = (feasible_bound + infeasible_bound) / 2
self.write_stimulus(mode=mode,
target_time=target_time,
correct_value=correct_value)
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
correct_value,
target_time,
infeasible_bound,
feasible_bound))
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
correct_value,
target_time,
infeasible_bound,
feasible_bound))
self.stim.run_sim()
self.stim.run_sim(self.stim_sp)
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
if type(clk_to_q) == float and (clk_to_q < 1.1 * ideal_clk_to_q) and type(setuphold_time)==float:
if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9
else:
setuphold_time *= 1e9
debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
debug.info(2, "PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
passing_setuphold_time = setuphold_time
feasible_bound = target_time
else:
debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
debug.info(2, "FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
infeasible_bound = target_time
#raw_input("Press Enter to continue...")
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
debug.info(3, "CONVERGE {0} vs {1}".format(feasible_bound, infeasible_bound))
break
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
debug.info(2, "Converged on {0} time {1}.".format(mode, passing_setuphold_time))
return passing_setuphold_time
def setup_LH_time(self):
"""Calculates the setup time for low-to-high transition for a DFF
"""
return self.bidir_search(1, "SETUP")
def setup_HL_time(self):
"""Calculates the setup time for high-to-low transition for a DFF
"""
@ -272,7 +262,6 @@ class setup_hold():
"""
return self.bidir_search(0, "HOLD")
def analyze(self, related_slews, constrained_slews):
"""main function to calculate both setup and hold time for the
DFF and returns a dictionary that contains 4 lists for both
@ -301,10 +290,10 @@ class setup_hold():
# }
# return times
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,
self.constrained_input_slew))
LH_setup_time = self.setup_LH_time()
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
HL_setup_time = self.setup_HL_time()
@ -325,7 +314,7 @@ class setup_hold():
}
return times
def analytical_setuphold(self,related_slews, constrained_slews):
def analytical_setuphold(self, related_slews, constrained_slews):
""" Just return the fixed setup/hold times from the technology.
"""
LH_setup = []
@ -336,10 +325,10 @@ class setup_hold():
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
# convert from ps to ns
LH_setup.append(tech.spice["dff_setup"]/1e3)
HL_setup.append(tech.spice["dff_setup"]/1e3)
LH_hold.append(tech.spice["dff_hold"]/1e3)
HL_hold.append(tech.spice["dff_hold"]/1e3)
LH_setup.append(tech.spice["dff_setup"] / 1e3)
HL_setup.append(tech.spice["dff_setup"] / 1e3)
LH_hold.append(tech.spice["dff_hold"] / 1e3)
HL_hold.append(tech.spice["dff_hold"] / 1e3)
times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup,

View File

@ -285,10 +285,7 @@ class stimuli():
includes = self.device_models + [circuit]
for item in list(includes):
if os.path.isfile(item):
self.sf.write(".include \"{0}\"\n".format(item))
else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
self.sf.write(".include \"{0}\"\n".format(item))
def write_supply(self):
""" Writes supply voltage statements """
@ -299,9 +296,9 @@ class stimuli():
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
def run_sim(self):
def run_sim(self, name):
""" Run hspice in batch mode and output rawfile to parse. """
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
temp_stim = "{0}{1}".format(OPTS.openram_temp, name)
import datetime
start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")

View File

@ -469,7 +469,7 @@ def set_default_corner():
if OPTS.nominal_corner_only:
OPTS.process_corners = ["TT"]
else:
OPTS.process_corners = tech.spice["fet_models"].keys()
OPTS.process_corners = list(tech.spice["fet_models"].keys())
if (OPTS.supply_voltages == ""):
if OPTS.nominal_corner_only:

View File

@ -6,7 +6,10 @@
# All rights reserved.
#
import datetime
import os
import debug
import verify
from characterizer import functional
from globals import OPTS, print_time
@ -84,6 +87,11 @@ class sram():
gdsname = OPTS.output_path + self.s.name + ".gds"
debug.print_raw("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
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
@ -98,6 +106,9 @@ class sram():
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),
output_path=OPTS.output_path)
print_time("Spice writing", datetime.datetime.now(), start_time)
# Save the LVS file
@ -105,11 +116,16 @@ class sram():
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
debug.print_raw("LVS: Writing to {0}".format(lvsname))
self.lvs_write(lvsname)
if not OPTS.netlist_only:
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:
import verify
start_time = datetime.datetime.now()
# Output the extracted design if requested
pexname = OPTS.output_path + self.s.name + ".pex.sp"
@ -121,6 +137,8 @@ class sram():
# Use generated spice file for characterization
sp_file = spname
# Save a functional simulation file
# Characterize the design
start_time = datetime.datetime.now()
from characterizer import lib

View File

@ -56,8 +56,7 @@ class psram_1bank_2mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -57,8 +57,7 @@ class psram_1bank_8mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -56,8 +56,7 @@ class psram_1bank_nomux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -46,8 +46,7 @@ class sram_1bank_2mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -48,8 +48,7 @@ class sram_1bank_2mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -48,8 +48,7 @@ class sram_1bank_2mux_sparecols_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -46,8 +46,7 @@ class sram_1bank_4mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -49,8 +49,7 @@ class sram_1bank_8mux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -49,8 +49,7 @@ class psram_1bank_nomux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -45,8 +45,7 @@ class sram_1bank_nomux_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -46,8 +46,7 @@ class sram_1bank_nomux_sparecols_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -52,8 +52,7 @@ class sram_wmask_1w_1r_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -48,8 +48,7 @@ class sram_wmask_func_test(openram_test):
tempspice = OPTS.openram_temp + "sram.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f = functional(s.s, tempspice)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -77,7 +77,7 @@ class hspice_pex_pinv_test(openram_test):
sim_file = OPTS.openram_temp + "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim()
test_sim.run_sim("stim.sp")
delay = parse_spice_list(log_file_name, "pinv_delay")
return delay

View File

@ -76,7 +76,7 @@ class ngspice_pex_pinv_test(openram_test):
sim_file = OPTS.openram_temp + "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim()
test_sim.run_sim("stim.sp")
delay = parse_spice_list(log_file_name, "pinv_delay")
return delay

View File

@ -40,36 +40,36 @@ else:
OPTS.magic_exe = get_tool("GDS", ["magic"])
if not OPTS.drc_exe:
from .none import run_drc, print_drc_stats
from .none import run_drc, print_drc_stats, write_drc_script
elif "calibre"==OPTS.drc_exe[0]:
from .calibre import run_drc, print_drc_stats
from .calibre import run_drc, print_drc_stats, write_drc_script
elif "assura"==OPTS.drc_exe[0]:
from .assura import run_drc, print_drc_stats
from .assura import run_drc, print_drc_stats, write_drc_script
elif "magic"==OPTS.drc_exe[0]:
from .magic import run_drc, print_drc_stats
from .magic import run_drc, print_drc_stats, write_drc_script
else:
debug.error("Did not find a supported DRC tool."
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.", 2)
if not OPTS.lvs_exe:
from .none import run_lvs, print_lvs_stats
from .none import run_lvs, print_lvs_stats, write_lvs_script
elif "calibre"==OPTS.lvs_exe[0]:
from .calibre import run_lvs, print_lvs_stats
from .calibre import run_lvs, print_lvs_stats, write_lvs_script
elif "assura"==OPTS.lvs_exe[0]:
from .assura import run_lvs, print_lvs_stats
from .assura import run_lvs, print_lvs_stats, write_lvs_script
elif "netgen"==OPTS.lvs_exe[0]:
from .magic import run_lvs, print_lvs_stats
from .magic import run_lvs, print_lvs_stats, write_lvs_script
else:
debug.warning("Did not find a supported LVS tool."
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.", 2)
if not OPTS.pex_exe:
from .none import run_pex,print_pex_stats
from .none import run_pex, print_pex_stats
elif "calibre"==OPTS.pex_exe[0]:
from .calibre import run_pex,print_pex_stats
from .calibre import run_pex, print_pex_stats
elif "magic"==OPTS.pex_exe[0]:
from .magic import run_pex,print_pex_stats
from .magic import run_pex, print_pex_stats
else:
debug.warning("Did not find a supported PEX tool."
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.", 2)

View File

@ -28,7 +28,7 @@ inserted in the runset.
import os
import re
import time
from run_script import *
import debug
from globals import OPTS
@ -37,16 +37,11 @@ num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def run_drc(name, gds_name, final_verification=False):
"""Run DRC check on a given top-level name which is
implemented in gds_name."""
global num_drc_runs
num_drc_runs += 1
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
from tech import drc
drc_rules = drc["drc_rules"]
drc_runset = OPTS.openram_temp + name + ".rsf"
drc_runset = output_path + cell_name + ".rsf"
drc_log_file = "{0}{1}.log".format(OPTS.openram_temp, name)
# write the runset file
@ -64,8 +59,8 @@ def run_drc(name, gds_name, final_verification=False):
f.write("\n")
f.write("avParameters(\n")
f.write(" ?inputLayout ( \"gds2\" \"{}\" )\n".format(gds_name))
f.write(" ?cellName \"{}\"\n".format(name))
f.write(" ?workingDirectory \"{}\"\n".format(OPTS.openram_temp))
f.write(" ?cellName \"{}\"\n".format(cell_name))
f.write(" ?workingDirectory \"{}\"\n".format(output_path))
f.write(" ?rulesFile \"{}\"\n".format(drc_rules))
f.write(" ?set ( \"GridCheck\" )\n")
f.write(" ?avrpt t\n")
@ -73,26 +68,37 @@ def run_drc(name, gds_name, final_verification=False):
f.close()
# run drc
cwd = os.getcwd()
os.chdir(OPTS.openram_temp)
cmd = "assura {0} 2> {1} 1> {2}".format(drc_runset, drc_log_file, drc_log_file)
debug.info(1, cmd)
os.system(cmd)
os.chdir(cwd)
run_file = output_path + "/run_drc.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write("assura {0} 2> {1} 1> {2}\n".format(drc_runset, drc_log_file, drc_log_file))
f.close()
def run_drc(name, gds_name, final_verification=False):
"""Run DRC check on a given top-level name which is
implemented in gds_name."""
global num_drc_runs
num_drc_runs += 1
write_drc_script(name, gds_name, True, final_verification, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(name, "drc")
# count and report errors
errors = 0
try:
f = open(OPTS.openram_temp+name+".err", "r")
f = open(OPTS.openram_temp + name +".err", "r")
except:
debug.error("Unable to retrieve DRC results file.",1)
debug.error("Unable to retrieve DRC results file.", 1)
results = f.readlines()
f.close()
for line in results:
if re.match("Rule No.", line):
if re.search("# INFO:", line) == None:
errors = errors + 1
debug.info(1, line)
errors = errors + 1
debug.info(1, line)
if errors > 0:
debug.error("Errors: {}".format(errors))
@ -100,23 +106,18 @@ def run_drc(name, gds_name, final_verification=False):
return errors
def run_lvs(name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. """
global num_lvs_runs
num_lvs_runs += 1
def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_path):
from tech import drc
lvs_rules = drc["lvs_rules"]
lvs_runset = OPTS.openram_temp + name + ".rsf"
lvs_runset = output_path + name + ".rsf"
# The LVS compare rules must be defined in the tech file for Assura.
lvs_compare = drc["lvs_compare"]
# Define the must-connect names for disconnected LVS nets for Assura
lvs_bindings = drc["lvs_bindings"]
lvs_log_file = "{0}{1}.log".format(OPTS.openram_temp, name)
lvs_log_file = "{0}{1}.log".format(output_path, name)
# Needed when FET models are sub-circuits
if drc.has_key("lvs_subcircuits"):
if "lvs_subcircuits" in drc:
lvs_sub_file = drc["lvs_subcircuits"]
else:
lvs_sub_file = ""
@ -128,7 +129,7 @@ def run_lvs(name, gds_name, sp_name, final_verification=False):
f.write("avParameters(\n")
f.write(" ?inputLayout ( \"gds2\" \"{}\" )\n".format(gds_name))
f.write(" ?cellName \"{}\"\n".format(name))
f.write(" ?workingDirectory \"{}\"\n".format(OPTS.openram_temp))
f.write(" ?workingDirectory \"{}\"\n".format(output_path))
f.write(" ?rulesFile \"{}\"\n".format(lvs_rules))
f.write(" ?autoGrid nil\n")
f.write(" ?avrpt t\n")
@ -160,27 +161,38 @@ def run_lvs(name, gds_name, sp_name, final_verification=False):
f.write("avLVS()\n")
f.close()
# run lvs
cwd = os.getcwd()
os.chdir(OPTS.openram_temp)
cmd = "assura {0} 2> {1} 1> {2}".format(lvs_runset, lvs_log_file, lvs_log_file)
debug.info(1, cmd)
os.system(cmd)
os.chdir(cwd)
# run drc
run_file = output_path + "/run_vls.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write("assura {0} 2> {1} 1> {2}\n".format(lvs_runset, lvs_log_file, lvs_log_file))
f.close()
def run_lvs(name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. """
global num_lvs_runs
num_lvs_runs += 1
write_lvs_script(name, gds_name, sp_name, final_verification, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(name, "drc")
errors = 0
try:
f = open(OPTS.openram_temp+name+".csm", "r")
f = open(OPTS.openram_temp + name + ".csm", "r")
except:
debug.error("Unable to retrieve LVS results file.",1)
debug.error("Unable to retrieve LVS results file.", 1)
results = f.readlines()
f.close()
for line in results:
if re.search("errors", line):
errors = errors + 1
debug.info(1, line)
errors = errors + 1
debug.info(1, line)
elif re.search("Schematic and Layout", line):
debug.info(1, line)
debug.info(1, line)
return errors
@ -188,14 +200,19 @@ def run_lvs(name, gds_name, sp_name, final_verification=False):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
debug.error("PEX extraction not implemented with Assura.",-1)
debug.error("PEX extraction not implemented with Assura.", -1)
global num_pex_runs
num_pex_runs += 1
def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs))
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs))
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs))
debug.info(1, "PEX runs: {0}".format(num_pex_runs))

View File

@ -30,7 +30,7 @@ num_lvs_runs = 0
num_pex_runs = 0
def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
""" Write a Calibre runset file and script to run DRC """
# the runset file contains all the options to run calibre
from tech import drc
@ -38,7 +38,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
drc_runset = {
'drcRulesFile': drc_rules,
'drcRunDir': OPTS.openram_temp,
'drcRunDir': output_path,
'drcLayoutPaths': gds_name,
'drcLayoutPrimary': cell_name,
'drcLayoutSystem': 'GDSII',
@ -50,17 +50,17 @@ def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
}
# write the runset file
f = open(OPTS.openram_temp + "drc_runset", "w")
f = open(output_path + "drc_runset", "w")
for k in sorted(iter(drc_runset.keys())):
f.write("*{0}: {1}\n".format(k, drc_runset[k]))
f.close()
# Create an auxiliary script to run calibre with the runset
run_file = OPTS.openram_temp + "run_drc.sh"
run_file = output_path + "run_drc.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
cmd = "{0} -gui -drc {1}drc_runset -batch".format(OPTS.drc_exe[1],
OPTS.openram_temp)
output_path)
f.write(cmd)
f.write("\n")
f.close()
@ -68,14 +68,14 @@ def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
return drc_runset
def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_path):
""" Write a Calibre runset file and script to run LVS """
from tech import drc
lvs_rules = drc["lvs_rules"]
lvs_runset = {
'lvsRulesFile': lvs_rules,
'lvsRunDir': OPTS.openram_temp,
'lvsRunDir': output_path,
'lvsLayoutPaths': gds_name,
'lvsLayoutPrimary': cell_name,
'lvsSourcePath': sp_name,
@ -111,19 +111,19 @@ def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
# write the runset file
f = open(OPTS.openram_temp + "lvs_runset", "w")
f = open(output_path + "lvs_runset", "w")
for k in sorted(iter(lvs_runset.keys())):
f.write("*{0}: {1}\n".format(k, lvs_runset[k]))
f.close()
# Create an auxiliary script to run calibre with the runset
run_file = OPTS.openram_temp + "run_lvs.sh"
run_file = output_path + "run_lvs.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
PDK_DIR=os.environ.get("PDK_DIR")
f.write("export PDK_DIR={}\n".format(PDK_DIR))
cmd = "{0} -gui -lvs {1}lvs_runset -batch".format(OPTS.lvs_exe[1],
OPTS.openram_temp)
output_path)
f.write(cmd)
f.write("\n")
f.close()
@ -132,16 +132,16 @@ def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
return lvs_runset
def write_calibre_pex_script(cell_name, extract, output, final_verification):
def write_pex_script(cell_name, extract, output, final_verification, output_path):
""" Write a pex script that can either just extract the netlist or the netlist+parasitics """
if output == None:
output = cell_name + ".pex.sp"
# check if lvs report has been done
# if not run drc and lvs
if not os.path.isfile(OPTS.openram_temp + cell_name + ".lvs.report"):
gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds"
sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp"
if not os.path.isfile(output_path + cell_name + ".lvs.report"):
gds_name = output_path +"/"+ cell_name + ".gds"
sp_name = output_path +"/"+ cell_name + ".sp"
run_drc(cell_name, gds_name)
run_lvs(cell_name, gds_name, sp_name)
@ -149,7 +149,7 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification):
pex_rules = drc["xrc_rules"]
pex_runset = {
'pexRulesFile': pex_rules,
'pexRunDir': OPTS.openram_temp,
'pexRunDir': output_path,
'pexLayoutPaths': cell_name + ".gds",
'pexLayoutPrimary': cell_name,
'pexSourcePath': cell_name + ".sp",
@ -162,13 +162,13 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification):
}
# write the runset file
f = open(OPTS.openram_temp + "pex_runset", "w")
f = open(output_path + "pex_runset", "w")
for k in sorted(iter(pex_runset.keys())):
f.write("*{0}: {1}\n".format(k, pex_runset[k]))
f.close()
# Create an auxiliary script to run calibre with the runset
run_file = OPTS.openram_temp + "run_pex.sh"
run_file = output_path + "run_pex.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
cmd = "{0} -gui -pex {1}pex_runset -batch".format(OPTS.pex_exe[1],
@ -198,7 +198,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp)
drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name)
drc_runset = write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name))
@ -212,7 +212,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
try:
f = open(OPTS.openram_temp + drc_runset['drcSummaryFile'], "r")
except:
debug.error("Unable to retrieve DRC results file. Is calibre set up?",1)
debug.error("Unable to retrieve DRC results file. Is calibre set up?", 1)
results = f.readlines()
f.close()
# those lines should be the last 3
@ -241,7 +241,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global num_lvs_runs
num_lvs_runs += 1
lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name)
lvs_runset = write_lvs_script(cell_name, gds_name, sp_name, final_verification, OPTS.openram_temp)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
@ -328,7 +328,7 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
global num_pex_runs
num_pex_runs += 1
write_calibre_pex_script(cell_name, True, output, final_verification)
write_pex_script(cell_name, True, output, final_verification, OPTS.openram_temp)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
@ -390,9 +390,14 @@ def correct_port(name, output_file_name, ref_file_name):
output_file.write(part2)
output_file.close()
def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs))
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs))
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs))
debug.info(1, "PEX runs: {0}".format(num_pex_runs))

View File

@ -66,12 +66,19 @@ def filter_gds(cell_name, input_gds, output_gds):
(outfile, errfile, resultsfile) = run_script(cell_name, "filter")
def write_magic_script(cell_name, extract=False, final_verification=False):
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
""" Write a magic script to perform DRC and optionally extraction. """
global OPTS
run_file = OPTS.openram_temp + "run_drc.sh"
# Copy .magicrc file into the directory
magic_file = OPTS.openram_tech + "tech/.magicrc"
if os.path.exists(magic_file):
shutil.copy(magic_file, output_path)
else:
debug.warning("Could not locate .magicrc file: {}".format(magic_file))
run_file = output_path + "run_drc.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
@ -79,7 +86,7 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
f.write("gds warning default\n")
f.write("gds flatten true\n")
f.write("gds readonly true\n")
f.write("gds read {}.gds\n".format(cell_name))
f.write("gds read {}\n".format(gds_name))
f.write("load {}\n".format(cell_name))
# Flatten the cell to get rid of DRCs spanning multiple layers
# (e.g. with routes)
@ -131,32 +138,6 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
os.system("chmod u+x {}".format(run_file))
def write_netgen_script(cell_name):
""" Write a netgen script to perform LVS. """
global OPTS
setup_file = "setup.tcl"
full_setup_file = OPTS.openram_tech + "tech/" + setup_file
if os.path.exists(full_setup_file):
# Copy setup.tcl file into temp dir
shutil.copy(full_setup_file, OPTS.openram_temp)
else:
setup_file = 'nosetup'
run_file = OPTS.openram_temp + "run_lvs.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write("{} -noconsole << EOF\n".format(OPTS.lvs_exe[1]))
f.write("readnet spice {0}.spice\n".format(cell_name))
f.write("readnet spice {0}.sp\n".format(cell_name))
f.write("lvs {{{0}.spice {0}}} {{{0}.sp {0}}} {1} {0}.lvs.report -json\n".format(cell_name, setup_file))
f.write("quit\n")
f.write("EOF\n")
f.close()
os.system("chmod u+x {}".format(run_file))
def run_drc(cell_name, gds_name, extract=True, final_verification=False):
"""Run DRC check on a cell which is implemented in gds_name."""
@ -167,14 +148,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'):
shutil.copy(gds_name, OPTS.openram_temp)
# Copy .magicrc file into temp dir
magic_file = OPTS.openram_tech + "tech/.magicrc"
if os.path.exists(magic_file):
shutil.copy(magic_file, OPTS.openram_temp)
else:
debug.warning("Could not locate .magicrc file: {}".format(magic_file))
write_magic_script(cell_name, extract, final_verification)
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
@ -214,6 +188,32 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
return errors
def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_path):
""" Write a netgen script to perform LVS. """
global OPTS
setup_file = "setup.tcl"
full_setup_file = OPTS.openram_tech + "tech/" + setup_file
if os.path.exists(full_setup_file):
# Copy setup.tcl file into temp dir
shutil.copy(full_setup_file, output_path)
else:
setup_file = 'nosetup'
run_file = output_path + "/run_lvs.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write("{} -noconsole << EOF\n".format(OPTS.lvs_exe[1]))
# f.write("readnet spice {0}.spice\n".format(cell_name))
# f.write("readnet spice {0}\n".format(sp_name))
f.write("lvs {{{0}.spice {0}}} {{{1} {0}}} {2} {0}.lvs.report -json\n".format(cell_name, sp_name, setup_file))
f.write("quit\n")
f.write("EOF\n")
f.close()
os.system("chmod u+x {}".format(run_file))
def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. Final verification will
@ -228,7 +228,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'):
shutil.copy(sp_name, OPTS.openram_temp)
write_netgen_script(cell_name)
write_lvs_script(cell_name, gds_name, sp_name, final_verification, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
@ -238,7 +238,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
try:
f = open(resultsfile, "r")
except FileNotFoundError:
debug.error("Unable to load LVS results from {}".format(resultsfile),1)
debug.error("Unable to load LVS results from {}".format(resultsfile), 1)
results = f.readlines()
f.close()
@ -249,7 +249,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
if "Subcircuit summary:" in line:
break
else:
final_results.insert(0,line)
final_results.insert(0, line)
# There were property errors in any module.
test = re.compile("Property errors were found.")

View File

@ -17,6 +17,10 @@ lvs_warned = False
pex_warned = False
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
pass
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
global drc_warned
if not drc_warned:
@ -26,6 +30,10 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
return 1
def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_path):
pass
def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global lvs_warned
if not lvs_warned:

View File

@ -13,6 +13,7 @@ import os
import debug
from globals import OPTS
def run_script(cell_name, script="lvs"):
""" Run script and create output files. """
@ -30,5 +31,5 @@ def run_script(cell_name, script="lvs"):
os.system(cmd)
os.chdir(cwd)
return (outfile,errfile,resultsfile)
return (outfile, errfile, resultsfile)