Use readspice to define ports from sp netlist in Magic extract.

This commit is contained in:
mrg 2020-11-10 17:06:24 -08:00
parent 31ae56ff39
commit 03dad01e4c
5 changed files with 93 additions and 69 deletions

View File

@ -73,11 +73,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
elif (OPTS.inline_lvsdrc or force_check or final_verification):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.gds_write(tempgds)
# Final verification option does not allow nets to be connected by label.
self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
self.drc_errors = verify.run_drc(self.cell_name, tempgds, tempspice, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
# force_check is used to determine decoder height and other things, so we shouldn't fail
@ -105,9 +105,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
num_errors = verify.run_drc(self.cell_name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
num_errors))
@ -126,9 +128,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.cell_name)
self.lvs_write(tempspice)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.name)
self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0,

View File

@ -48,7 +48,7 @@ class openram_test(unittest.TestCase):
# Run both DRC and LVS even if DRC might fail
# Magic can still extract despite DRC failing, so it might be ok in some techs
# if we ignore things like minimum metal area of pins
drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification)
drc_result=verify.run_drc(a.name, tempgds, tempspice, extract=True, final_verification=final_verification)
# We can still run LVS even if DRC fails in Magic OR Calibre
lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification)

View File

@ -30,9 +30,13 @@ num_lvs_runs = 0
num_pex_runs = 0
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None):
""" Write a Calibre runset file and script to run DRC """
# the runset file contains all the options to run calibre
if not output_path:
output_path = OPTS.openram_temp
from tech import drc
drc_rules = drc["drc_rules"]
@ -68,9 +72,12 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
return drc_runset
def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_path):
def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
""" Write a Calibre runset file and script to run LVS """
if not output_path:
output_path = OPTS.openram_temp
from tech import drc
lvs_rules = drc["lvs_rules"]
lvs_runset = {
@ -132,8 +139,12 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_pa
return lvs_runset
def write_pex_script(cell_name, extract, output, final_verification, output_path):
def write_pex_script(cell_name, extract, output, final_verification=False, output_path=None):
""" Write a pex script that can either just extract the netlist or the netlist+parasitics """
if not output_path:
output_path = OPTS.openram_temp
if output == None:
output = cell_name + ".pex.sp"
@ -142,7 +153,7 @@ def write_pex_script(cell_name, extract, output, final_verification, output_path
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_drc(cell_name, gds_name, sp_name)
run_lvs(cell_name, gds_name, sp_name)
from tech import drc
@ -181,7 +192,7 @@ def write_pex_script(cell_name, extract, output, final_verification, output_path
return pex_runset
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False):
"""Run DRC check on a given top-level name which is
implemented in gds_name."""

View File

@ -66,7 +66,7 @@ num_pex_runs = 0
# (outfile, errfile, resultsfile) = run_script(cell_name, "filter")
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None):
""" Write a magic script to perform DRC and optionally extraction. """
global OPTS
@ -102,35 +102,38 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("drc catchup\n")
f.write("drc count total\n")
f.write("drc count\n")
f.write("port makeall\n")
if not sp_name:
f.write("port makeall\n")
else:
f.write("readspice {}\n".format(sp_name))
if not extract:
pre = "#"
else:
pre = ""
if final_verification and OPTS.route_supplies:
f.write(pre + "extract unique all\n".format(cell_name))
f.write(pre + "extract unique all\n")
# Hack to work around unit scales in SkyWater
if OPTS.tech_name=="sky130":
f.write(pre + "extract style ngspice(si)\n")
f.write(pre + "extract\n".format(cell_name))
f.write(pre + "extract\n")
# f.write(pre + "ext2spice hierarchy on\n")
# f.write(pre + "ext2spice scale off\n")
# lvs exists in 8.2.79, but be backword compatible for now
#f.write(pre+"ext2spice lvs\n")
f.write(pre+"ext2spice hierarchy on\n")
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice cthresh infinite\n")
f.write(pre+"ext2spice rthresh infinite\n")
f.write(pre+"ext2spice renumber off\n")
f.write(pre+"ext2spice scale off\n")
f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top on\n")
f.write(pre+"ext2spice global off\n")
# f.write(pre + "ext2spice lvs\n")
f.write(pre + "ext2spice hierarchy on\n")
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice cthresh infinite\n")
f.write(pre + "ext2spice rthresh infinite\n")
f.write(pre + "ext2spice renumber off\n")
f.write(pre + "ext2spice scale off\n")
f.write(pre + "ext2spice blackbox on\n")
f.write(pre + "ext2spice subcircuit top on\n")
f.write(pre + "ext2spice global off\n")
# Can choose hspice, ngspice, or spice3,
# but they all seem compatible enough.
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("EOF\n")
@ -138,7 +141,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
os.system("chmod u+x {}".format(run_file))
def run_drc(cell_name, gds_name, extract=True, final_verification=False):
def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=False):
"""Run DRC check on a cell which is implemented in gds_name."""
global num_drc_runs
@ -148,7 +151,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)
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp)
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp, sp_name=sp_name)
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
@ -161,7 +164,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
try:
f = open(outfile, "r")
except FileNotFoundError:
debug.error("Unable to load DRC results file from {}. Is magic set up?".format(outfile),1)
debug.error("Unable to load DRC results file from {}. Is magic set up?".format(outfile), 1)
results = f.readlines()
f.close()
@ -172,7 +175,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
errors = int(re.split(": ", line)[1])
break
else:
debug.error("Unable to find the total error line in Magic output.",1)
debug.error("Unable to find the total error line in Magic output.", 1)
# always display this summary
@ -180,7 +183,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False):
if errors > 0:
for line in results:
if "error tiles" in line:
debug.info(1,line.rstrip("\n"))
debug.info(1, line.rstrip("\n"))
debug.warning(result_str)
else:
debug.info(1, result_str)
@ -188,11 +191,14 @@ 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):
def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
""" Write a netgen script to perform LVS. """
global OPTS
if not output_path:
output_path = OPTS.openram_temp
setup_file = "setup.tcl"
full_setup_file = OPTS.openram_tech + "tech/" + setup_file
if os.path.exists(full_setup_file):
@ -214,7 +220,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_pa
os.system("chmod u+x {}".format(run_file))
def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
"""Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. Final verification will
ensure that there are no remaining virtual conections. """
@ -222,13 +228,16 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global num_lvs_runs
num_lvs_runs += 1
if not output_path:
output_path = OPTS.openram_temp
# Copy file to local dir if it isn't already
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'):
shutil.copy(gds_name, OPTS.openram_temp)
if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'):
shutil.copy(sp_name, OPTS.openram_temp)
if os.path.dirname(gds_name) != output_path.rstrip('/'):
shutil.copy(gds_name, output_path)
if os.path.dirname(sp_name) != output_path.rstrip('/'):
shutil.copy(sp_name, output_path)
write_lvs_script(cell_name, gds_name, sp_name, final_verification, OPTS.openram_temp)
write_lvs_script(cell_name, gds_name, sp_name, final_verification)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
@ -289,13 +298,17 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return total_errors
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False, output_path=None):
"""Run pex on a given top-level name which is
implemented in gds_name and sp_name. """
global num_pex_runs
num_pex_runs += 1
os.chdir(OPTS.openram_temp)
os.chdir(output_path)
if not output_path:
output_path = OPTS.openram_temp
if output == None:
output = name + ".pex.netlist"
@ -311,8 +324,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
# the dev old code using batch mode does not run and is split into functions
pex_runset = write_script_pex_rule(gds_name, name, output)
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name)
errfile = "{0}{1}.pex.err".format(output_path, name)
outfile = "{0}{1}.pex.out".format(output_path, name)
script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
errfile,
@ -387,7 +400,7 @@ def write_batch_pex_rule(gds_name, name, sp_name, output):
return file
def write_script_pex_rule(gds_name, cell_name, output):
def write_script_pex_rule(gds_name, cell_name, sp_name, output):
global OPTS
run_file = OPTS.openram_temp + "run_pex.sh"
f = open(run_file, "w")
@ -399,26 +412,24 @@ def write_script_pex_rule(gds_name, cell_name, output):
f.write("load {}\n".format(cell_name))
f.write("select top cell\n")
f.write("expand\n")
f.write("port makeall\n")
extract = True
if not extract:
pre = "#"
if not sp_name:
f.write("port makeall\n")
else:
pre = ""
f.write(pre + "extract\n")
f.write(pre + "ext2sim labels on\n")
f.write(pre + "ext2sim\n")
f.write(pre + "extresist simplify off\n")
f.write(pre + "extresist all\n")
f.write(pre + "ext2spice hierarchy off\n")
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice renumber off\n")
f.write(pre + "ext2spice scale off\n")
f.write(pre + "ext2spice blackbox on\n")
f.write(pre + "ext2spice subcircuit top on\n")
f.write(pre + "ext2spice global off\n")
f.write(pre + "ext2spice extresist on\n")
f.write(pre + "ext2spice {}\n".format(cell_name))
f.write("readspice {}\n".format(sp_name))
f.write("extract\n")
f.write("ext2sim labels on\n")
f.write("ext2sim\n")
f.write("extresist simplify off\n")
f.write("extresist all\n")
f.write("ext2spice hierarchy off\n")
f.write("ext2spice format ngspice\n")
f.write("ext2spice renumber off\n")
f.write("ext2spice scale off\n")
f.write("ext2spice blackbox on\n")
f.write("ext2spice subcircuit top on\n")
f.write("ext2spice global off\n")
f.write("ext2spice extresist on\n")
f.write("ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("eof\n")
f.write("mv {0}.spice {1}\n".format(cell_name, output))

View File

@ -17,11 +17,11 @@ lvs_warned = False
pex_warned = False
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path):
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None):
pass
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):
global drc_warned
if not drc_warned:
debug.error("DRC unable to run.", -1)
@ -30,11 +30,11 @@ 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):
def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
pass
def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
global lvs_warned
if not lvs_warned:
debug.error("LVS unable to run.", -1)
@ -43,7 +43,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
return 1
def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False, output_path=None):
global pex_warned
if not pex_warned:
debug.error("PEX unable to run.", -1)