mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into s8_single_port
This commit is contained in:
commit
5e2c199d38
|
|
@ -90,7 +90,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
"LVS failed for {0} with {1} errors(s)".format(self.cell_name,
|
||||
self.lvs_errors))
|
||||
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
|
||||
num_errors))
|
||||
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
os.remove(tempgds)
|
||||
|
||||
def LVS(self, final_verification=False):
|
||||
|
|
@ -134,7 +134,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
debug.check(num_errors == 0,
|
||||
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
|
||||
num_errors))
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,9 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import gdsMill
|
||||
import tech
|
||||
import globals
|
||||
import math
|
||||
import debug
|
||||
import datetime
|
||||
from collections import defaultdict
|
||||
import pdb
|
||||
from tech import layer_names
|
||||
|
||||
|
||||
class lef:
|
||||
"""
|
||||
|
|
@ -20,13 +15,13 @@ class lef:
|
|||
and write them to LEF file.
|
||||
This is inherited by the sram_base class.
|
||||
"""
|
||||
def __init__(self,layers):
|
||||
def __init__(self, layers):
|
||||
# LEF db units per micron
|
||||
self.lef_units = 2000
|
||||
# These are the layers of the obstructions
|
||||
self.lef_layers = layers
|
||||
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
|
||||
self.round_grid = 4;
|
||||
self.round_grid = 4
|
||||
|
||||
def lef_write(self, lef_name):
|
||||
"""Write the entire lef of the object to the file."""
|
||||
|
|
@ -34,7 +29,7 @@ class lef:
|
|||
|
||||
self.indent = "" # To maintain the indent level easily
|
||||
|
||||
self.lef = open(lef_name,"w")
|
||||
self.lef = open(lef_name, "w")
|
||||
self.lef_write_header()
|
||||
for pin in self.pins:
|
||||
self.lef_write_pin(pin)
|
||||
|
|
@ -52,30 +47,29 @@ class lef:
|
|||
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
|
||||
self.lef.write("END UNITS\n")
|
||||
|
||||
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
|
||||
self.lef.write("{0}MACRO {1}\n".format(self.indent, self.name))
|
||||
self.indent += " "
|
||||
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
|
||||
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
|
||||
round(self.width,self.round_grid),
|
||||
round(self.height,self.round_grid)))
|
||||
round(self.width, self.round_grid),
|
||||
round(self.height, self.round_grid)))
|
||||
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
|
||||
|
||||
def lef_write_footer(self):
|
||||
self.lef.write("{0}END {1}\n".format(self.indent,self.name))
|
||||
self.lef.write("{0}END {1}\n".format(self.indent, self.name))
|
||||
self.indent = self.indent[:-3]
|
||||
self.lef.write("END LIBRARY\n")
|
||||
|
||||
|
||||
def lef_write_pin(self, name):
|
||||
pin_dir = self.get_pin_dir(name)
|
||||
pin_type = self.get_pin_type(name)
|
||||
self.lef.write("{0}PIN {1}\n".format(self.indent,name))
|
||||
self.lef.write("{0}PIN {1}\n".format(self.indent, name))
|
||||
self.indent += " "
|
||||
|
||||
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
|
||||
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent, pin_dir))
|
||||
|
||||
if pin_type in ["POWER","GROUND"]:
|
||||
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
|
||||
if pin_type in ["POWER", "GROUND"]:
|
||||
self.lef.write("{0}USE {1} ; \n".format(self.indent, pin_type))
|
||||
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
|
||||
|
||||
self.lef.write("{0}PORT\n".format(self.indent))
|
||||
|
|
@ -84,7 +78,7 @@ class lef:
|
|||
# We could sort these together to minimize different layer sections, but meh.
|
||||
pin_list = self.get_pins(name)
|
||||
for pin in pin_list:
|
||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
|
||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[pin.layer]))
|
||||
self.lef_write_shape(pin.rect)
|
||||
|
||||
# End the PORT
|
||||
|
|
@ -93,19 +87,16 @@ class lef:
|
|||
|
||||
# End the PIN
|
||||
self.indent = self.indent[:-3]
|
||||
self.lef.write("{0}END {1}\n".format(self.indent,name))
|
||||
self.lef.write("{0}END {1}\n".format(self.indent, name))
|
||||
|
||||
def lef_write_obstructions(self):
|
||||
""" Write all the obstructions on each layer """
|
||||
self.lef.write("{0}OBS\n".format(self.indent))
|
||||
for layer in self.lef_layers:
|
||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer))
|
||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer]))
|
||||
self.indent += " "
|
||||
# pdb.set_trace()
|
||||
blockages = self.get_blockages(layer,True)
|
||||
blockages = self.get_blockages(layer, True)
|
||||
for b in blockages:
|
||||
# if len(b) > 2:
|
||||
# print(b)
|
||||
self.lef_write_shape(b)
|
||||
self.indent = self.indent[:-3]
|
||||
self.lef.write("{0}END\n".format(self.indent))
|
||||
|
|
@ -116,13 +107,19 @@ class lef:
|
|||
self.lef.write("{0}RECT ".format(self.indent))
|
||||
for item in rect:
|
||||
# print(rect)
|
||||
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
|
||||
self.lef.write(" {0} {1}".format(round(item[0],
|
||||
self.round_grid),
|
||||
round(item[1],
|
||||
self.round_grid)))
|
||||
self.lef.write(" ;\n")
|
||||
else:
|
||||
""" Write a LEF polygon """
|
||||
self.lef.write("{0}POLYGON ".format(self.indent))
|
||||
for item in rect:
|
||||
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
|
||||
self.lef.write(" {0} {1}".format(round(item[0],
|
||||
self.round_grid),
|
||||
round(item[1],
|
||||
self.round_grid)))
|
||||
# for i in range(0,len(rect)):
|
||||
# self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
|
||||
self.lef.write(" ;\n")
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ class stimuli():
|
|||
|
||||
# create plots for all signals
|
||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.debug_level>0:
|
||||
if OPTS.verbose_level>0:
|
||||
if OPTS.spice_name in ["hspice", "xa"]:
|
||||
self.sf.write(".probe V(*)\n")
|
||||
else:
|
||||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import os
|
|||
import inspect
|
||||
import globals
|
||||
import sys
|
||||
import pdb
|
||||
|
||||
# the debug levels:
|
||||
# 0 = minimum output (default)
|
||||
|
|
@ -26,6 +27,9 @@ def check(check, str):
|
|||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||
os.path.basename(filename), line_number, str))
|
||||
|
||||
if globals.OPTS.debug:
|
||||
pdb.set_trace()
|
||||
|
||||
assert 0
|
||||
|
||||
|
||||
|
|
@ -37,6 +41,9 @@ def error(str, return_value=0):
|
|||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||
os.path.basename(filename), line_number, str))
|
||||
|
||||
if globals.OPTS.debug:
|
||||
pdb.set_trace()
|
||||
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
|
|
@ -90,7 +97,7 @@ log.create_file = True
|
|||
|
||||
def info(lev, str):
|
||||
from globals import OPTS
|
||||
if (OPTS.debug_level >= lev):
|
||||
if (OPTS.verbose_level >= lev):
|
||||
frm = inspect.stack()[1]
|
||||
mod = inspect.getmodule(frm[0])
|
||||
# classname = frm.f_globals['__name__']
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ def parse_args():
|
|||
optparse.make_option("-v",
|
||||
"--verbose",
|
||||
action="count",
|
||||
dest="debug_level",
|
||||
dest="verbose_level",
|
||||
help="Increase the verbosity level"),
|
||||
optparse.make_option("-t",
|
||||
"--tech",
|
||||
|
|
@ -83,11 +83,16 @@ def parse_args():
|
|||
action="store_false",
|
||||
dest="analytical_delay",
|
||||
help="Perform characterization to calculate delays (default is analytical models)"),
|
||||
optparse.make_option("-k",
|
||||
"--keeptemp",
|
||||
action="store_true",
|
||||
dest="keep_temp",
|
||||
help="Keep the contents of the temp directory after a successful run"),
|
||||
optparse.make_option("-d",
|
||||
"--dontpurge",
|
||||
action="store_false",
|
||||
dest="purge_temp",
|
||||
help="Don't purge the contents of the temp directory after a successful run")
|
||||
"--debug",
|
||||
action="store_true",
|
||||
dest="debug",
|
||||
help="Run in debug mode to drop to pdb on failure")
|
||||
# -h --help is implicit.
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +148,7 @@ def check_versions():
|
|||
major_required = 3
|
||||
minor_required = 5
|
||||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
||||
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
|
||||
debug.error("Python {0}.{1} or greater is required.".format(major_required, minor_required), -1)
|
||||
|
||||
# FIXME: Check versions of other tools here??
|
||||
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
||||
|
|
@ -152,7 +157,7 @@ def check_versions():
|
|||
try:
|
||||
import coverage
|
||||
OPTS.coverage = 1
|
||||
except:
|
||||
except ModuleNotFoundError:
|
||||
OPTS.coverage = 0
|
||||
|
||||
|
||||
|
|
@ -309,7 +314,7 @@ def read_config(config_file, is_unit_test=True):
|
|||
try:
|
||||
config = importlib.import_module(module_name)
|
||||
except:
|
||||
debug.error("Unable to read configuration file: {0}".format(config_file),2)
|
||||
debug.error("Unable to read configuration file: {0}".format(config_file), 2)
|
||||
|
||||
OPTS.overridden = {}
|
||||
for k, v in config.__dict__.items():
|
||||
|
|
@ -366,7 +371,7 @@ def cleanup_paths():
|
|||
We should clean up the temp directory after execution.
|
||||
"""
|
||||
global OPTS
|
||||
if not OPTS.purge_temp:
|
||||
if OPTS.keep_temp:
|
||||
debug.info(0,
|
||||
"Preserving temp directory: {}".format(OPTS.openram_temp))
|
||||
return
|
||||
|
|
@ -464,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:
|
||||
|
|
@ -534,12 +539,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
|
|||
global OPTS
|
||||
|
||||
# Don't print during testing
|
||||
if not OPTS.is_unit_test or OPTS.debug_level > 0:
|
||||
if not OPTS.is_unit_test or OPTS.verbose_level > 0:
|
||||
if last_time:
|
||||
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
|
||||
time = str(round((now_time - last_time).total_seconds(), 1)) + " seconds"
|
||||
else:
|
||||
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
|
||||
debug.print_raw("{0} {1}: {2}".format("*"*indentation, name, time))
|
||||
debug.print_raw("{0} {1}: {2}".format("*" * indentation, name, time))
|
||||
|
||||
|
||||
def report_status():
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class bitcell_array(bitcell_base_array):
|
|||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def create_instances(self):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import debug
|
|||
import design
|
||||
from tech import cell_properties
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class bitcell_base_array(design.design):
|
||||
|
|
@ -24,7 +25,7 @@ class bitcell_base_array(design.design):
|
|||
self.column_offset = column_offset
|
||||
|
||||
# Bitcell for port names only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
self.wordline_names = [[] for port in self.all_ports]
|
||||
self.all_wordline_names = []
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class col_cap_array(bitcell_base_array):
|
|||
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||
self.add_mod(self.dummy_cell)
|
||||
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ class dummy_array(bitcell_base_array):
|
|||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
|
||||
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.dummy_cell)
|
||||
|
||||
def create_instances(self):
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class hierarchical_decoder(design.design):
|
|||
self.pre3x8_inst = []
|
||||
self.pre4x16_inst = []
|
||||
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
self.cell_height = b.height
|
||||
|
||||
self.num_outputs = num_outputs
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class hierarchical_predecode(design.design):
|
|||
def __init__(self, name, input_number, height=None):
|
||||
self.number_of_inputs = input_number
|
||||
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
if not height:
|
||||
self.cell_height = b.height
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
# This is just used for names
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||
cols=self.cols,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class bitcell_array(bitcell_base_array):
|
|||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def create_instances(self):
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class port_address(design.design):
|
|||
|
||||
# The polarity must be switched if we have a hierarchical wordline
|
||||
# to compensate for the local array inverters
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
if local_array_size > 0:
|
||||
self.rbl_driver = factory.create(module_type="inv_dec",
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class port_data(design.design):
|
|||
self.num_spare_cols = 0
|
||||
|
||||
if not bit_offsets:
|
||||
bitcell = factory.create(module_type="bitcell")
|
||||
bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
self.bit_offsets = []
|
||||
for i in range(self.num_cols + self.num_spare_cols):
|
||||
self.bit_offsets.append(i * bitcell.width)
|
||||
|
|
@ -191,7 +191,7 @@ class port_data(design.design):
|
|||
# and mirroring happens correctly
|
||||
|
||||
# Used for names/dimensions only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
if self.port == 0:
|
||||
# Append an offset on the left
|
||||
|
|
@ -270,7 +270,7 @@ class port_data(design.design):
|
|||
|
||||
# create arrays of bitline and bitline_bar names for read,
|
||||
# write, or all ports
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
self.bl_names = self.bitcell.get_all_bl_names()
|
||||
self.br_names = self.bitcell.get_all_br_names()
|
||||
self.wl_names = self.bitcell.get_all_wl_names()
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ class replica_bitcell_array(bitcell_base_array):
|
|||
self.supplies = ["vdd", "gnd"]
|
||||
|
||||
# Used for names/dimensions only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
# Main array
|
||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class replica_column(bitcell_base_array):
|
|||
self.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell)
|
||||
self.add_mod(self.edge_cell)
|
||||
# Used for pin names only
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
self.cell_inst = {}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class row_cap_array(bitcell_base_array):
|
|||
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
||||
self.add_mod(self.dummy_cell)
|
||||
|
||||
self.cell = factory.create(module_type="bitcell")
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class sense_amp_array(design.design):
|
|||
|
||||
# This is just used for measurements,
|
||||
# so don't add the module
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
self.local_insts = []
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class wordline_buffer_array(design.design):
|
|||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
self.wl_driver = factory.create(module_type="inv_dec",
|
||||
size=self.cols,
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class write_driver_array(design.design):
|
|||
|
||||
# This is just used for measurements,
|
||||
# so don't add the module
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_write_array(self):
|
||||
self.driver_insts = []
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class write_mask_and_array(design.design):
|
|||
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
|
||||
|
||||
# This is just used for measurements, so don't add the module
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
self.driver = factory.create(module_type="write_driver")
|
||||
if self.bitcell.width > self.driver.width:
|
||||
self.driver_spacing = self.bitcell.width
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ class options(optparse.Values):
|
|||
os.getpid())
|
||||
# This is the verbosity level to control debug information. 0 is none, 1
|
||||
# is minimal, etc.
|
||||
debug_level = 0
|
||||
verbose_level = 0
|
||||
# Drop to pdb on failure?
|
||||
debug = False
|
||||
|
||||
|
||||
###################
|
||||
# Run-time vs accuracy options.
|
||||
|
|
@ -141,7 +144,8 @@ class options(optparse.Values):
|
|||
# Route the input/output pins to the perimeter
|
||||
perimeter_pins = False
|
||||
|
||||
purge_temp = True
|
||||
keep_temp = False
|
||||
|
||||
|
||||
# These are the default modules that can be over-riden
|
||||
bitcell_suffix = ""
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from tech import drc, layer
|
|||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties as cell_props
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class column_mux(pgate.pgate):
|
||||
|
|
@ -64,7 +65,7 @@ class column_mux(pgate.pgate):
|
|||
self.add_pn_wells()
|
||||
|
||||
def add_ptx(self):
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
# Adds nmos_lower,nmos_upper to the module
|
||||
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class pinv_dec(pinv.pinv):
|
|||
"creating pinv_dec structure {0} with size of {1}".format(name,
|
||||
size))
|
||||
if not height:
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
self.cell_height = b.height
|
||||
else:
|
||||
self.cell_height = height
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class precharge(design.design):
|
|||
debug.info(2, "creating precharge cell {0}".format(name))
|
||||
super().__init__(name)
|
||||
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
self.beta = parameter["beta"]
|
||||
self.ptx_width = self.beta * parameter["min_tx_size"]
|
||||
self.ptx_mults = 1
|
||||
|
|
|
|||
|
|
@ -157,10 +157,17 @@ class ptx(design.design):
|
|||
# self.tx_width,
|
||||
# drc("minwidth_poly"))
|
||||
# TEMP FIX: Use old device names if using Calibre.
|
||||
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format("nshort" if self.tx_type == "nmos" else "pshort",
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
elif cell_props.ptx.model_is_subckt:
|
||||
# sky130 requires mult parameter too
|
||||
self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
else:
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
|
|
|
|||
|
|
@ -25,15 +25,15 @@ class pwrite_driver(design.design):
|
|||
super().__init__(name)
|
||||
self.size = size
|
||||
self.beta = parameter["beta"]
|
||||
self.pmos_width = self.beta*self.size*parameter["min_tx_size"]
|
||||
self.nmos_width = self.size*parameter["min_tx_size"]
|
||||
self.pmos_width = self.beta * self.size * parameter["min_tx_size"]
|
||||
self.nmos_width = self.size * parameter["min_tx_size"]
|
||||
|
||||
# The tech M2 pitch is based on old via orientations
|
||||
self.m2_pitch = self.m2_space + self.m2_width
|
||||
|
||||
# Width is matched to the bitcell,
|
||||
# Height will be variable
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
self.width = self.bitcell.width
|
||||
|
||||
# Creates the netlist and layout
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class wordline_driver(design.design):
|
|||
super().__init__(name)
|
||||
|
||||
if height is None:
|
||||
b = factory.create(module_type="bitcell")
|
||||
b = factory.create(module_type=OPTS.bitcell)
|
||||
self.height = b.height
|
||||
else:
|
||||
self.height = height
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class openram_test(unittest.TestCase):
|
|||
if result != 0:
|
||||
self.fail("DRC failed: {}".format(w.name))
|
||||
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
self.cleanup()
|
||||
|
||||
def local_check(self, a, final_verification=False):
|
||||
|
|
@ -49,7 +49,7 @@ class openram_test(unittest.TestCase):
|
|||
if result != 0:
|
||||
self.fail("LVS mismatch: {}".format(a.name))
|
||||
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class sram_factory:
|
||||
"""
|
||||
This is a factory pattern to create modules for usage in an SRAM.
|
||||
|
|
@ -39,6 +40,11 @@ class sram_factory:
|
|||
try:
|
||||
from tech import tech_modules
|
||||
real_module_type = tech_modules[module_type]
|
||||
# If we are given a list of modules, it is indexed by number of ports starting from 1
|
||||
if type(real_module_type) is list:
|
||||
# For now we will just index by the number of ports (except can't have 0 ports)
|
||||
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
|
||||
real_module_type = real_module_type[num_ports - 1]
|
||||
overridden = tech_modules.is_overridden(module_type)
|
||||
except ImportError:
|
||||
# If they didn't define these, then don't use the option types.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
#@unittest.skip("SKIPPING 05_array_test")
|
||||
|
||||
class array_test(openram_test):
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class hierarchical_decoder_test(openram_test):
|
|||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_r_ports = 0
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
|
|
@ -58,9 +58,9 @@ class hierarchical_decoder_test(openram_test):
|
|||
self.local_check(a)
|
||||
|
||||
# Checks 3 x 4x16 and 4-input NAND decoder
|
||||
debug.info(1, "Testing 4096 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=4096)
|
||||
self.local_check(a)
|
||||
# debug.info(1, "Testing 4096 row sample for hierarchical_decoder")
|
||||
# a = factory.create(module_type="hierarchical_decoder", num_outputs=4096)
|
||||
# self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class hierarchical_predecode2x4_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class hierarchical_predecode3x8_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,15 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
|
||||
@unittest.skip("SKIPPING 24_lef_sram_test")
|
||||
class lef_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.route_supplies=False
|
||||
OPTS.check_lvsdrc=False
|
||||
from sram import sram
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=2,
|
||||
|
|
@ -41,8 +43,8 @@ class lef_test(openram_test):
|
|||
s.lef_write(lefname)
|
||||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),leffile)
|
||||
self.assertTrue(self.isdiff(lefname,golden))
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)), leffile)
|
||||
self.assertTrue(self.isdiff(lefname, golden))
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
|
||||
class verilog_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
@ -40,8 +41,8 @@ class verilog_test(openram_test):
|
|||
|
||||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),vfile)
|
||||
self.assertTrue(self.isdiff(vname,golden))
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)), vfile)
|
||||
self.assertTrue(self.isdiff(vname, golden))
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class hspice_pex_pinv_test(openram_test):
|
|||
reload(characterizer)
|
||||
|
||||
# generate the pinv
|
||||
prev_purge_value = OPTS.purge_temp
|
||||
prev_keep_value = OPTS.keep_temp
|
||||
# force set purge to false to save the sp file
|
||||
OPTS.purge_temp = False
|
||||
OPTS.keep_temp = True
|
||||
debug.info(2, "Checking 1x size inverter")
|
||||
tx = pinv.pinv(name="pinv", size=1)
|
||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
|
||||
|
|
@ -52,7 +52,7 @@ class hspice_pex_pinv_test(openram_test):
|
|||
|
||||
# now generate its pex file
|
||||
pex_file = self.run_pex(tx)
|
||||
OPTS.purge_temp = prev_purge_value # restore the old purge value
|
||||
OPTS.keep_temp = prev_keep_value # restore the old keep
|
||||
# generate simulation for pex, make sure the simulation is successful
|
||||
pex_delay = self.simulate_delay(test_module=pex_file,
|
||||
top_level_name=tx.name)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ class ngspice_pex_pinv_test(openram_test):
|
|||
reload(characterizer)
|
||||
|
||||
# generate the pinv module
|
||||
prev_purge_value = OPTS.purge_temp
|
||||
OPTS.purge_temp = False # force set purge to false to save the sp file
|
||||
prev_keep_value = OPTS.keep_temp
|
||||
OPTS.keep_temp = True # force set keep to true to save the sp file
|
||||
debug.info(2, "Checking 1x size inverter")
|
||||
tx = pinv.pinv(name="pinv", size=1)
|
||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
|
||||
|
|
@ -50,8 +50,8 @@ class ngspice_pex_pinv_test(openram_test):
|
|||
|
||||
# now generate its pex file
|
||||
pex_file = self.run_pex(tx)
|
||||
# restore the old purge value
|
||||
OPTS.purge_temp = prev_purge_value
|
||||
# restore the old keep value
|
||||
OPTS.keep_temp = prev_keep_value
|
||||
# generate simulation for pex, make sure the simulation is successful
|
||||
pex_delay = self.simulate_delay(test_module=pex_file,
|
||||
top_level_name=tx.name)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class openram_back_end_test(openram_test):
|
|||
|
||||
# specify the same verbosity for the system call
|
||||
options = ""
|
||||
for i in range(OPTS.debug_level):
|
||||
for i in range(OPTS.verbose_level):
|
||||
options += " -v"
|
||||
|
||||
if OPTS.spice_name:
|
||||
|
|
@ -86,7 +86,7 @@ class openram_back_end_test(openram_test):
|
|||
self.assertEqual(len(re.findall('WARNING', output)), 0)
|
||||
|
||||
# now clean up the directory
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
if os.path.exists(out_path):
|
||||
shutil.rmtree(out_path, ignore_errors=True)
|
||||
self.assertEqual(os.path.exists(out_path), False)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class openram_front_end_test(openram_test):
|
|||
|
||||
# specify the same verbosity for the system call
|
||||
options = ""
|
||||
for i in range(OPTS.debug_level):
|
||||
for i in range(OPTS.verbose_level):
|
||||
options += " -v"
|
||||
|
||||
if OPTS.spice_name:
|
||||
|
|
@ -86,7 +86,7 @@ class openram_front_end_test(openram_test):
|
|||
self.assertEqual(len(re.findall('WARNING', output)), 0)
|
||||
|
||||
# now clean up the directory
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
if os.path.exists(out_path):
|
||||
shutil.rmtree(out_path, ignore_errors=True)
|
||||
self.assertEqual(os.path.exists(out_path), False)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -10,6 +10,8 @@ import sys, os, glob
|
|||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
from globals import OPTS
|
||||
import debug
|
||||
import pdb
|
||||
import traceback
|
||||
|
||||
|
||||
class openram_test(unittest.TestCase):
|
||||
|
|
@ -27,7 +29,7 @@ class openram_test(unittest.TestCase):
|
|||
if result != 0:
|
||||
self.fail("DRC failed: {}".format(w.name))
|
||||
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
self.cleanup()
|
||||
|
||||
def local_check(self, a, final_verification=False):
|
||||
|
|
@ -74,7 +76,7 @@ class openram_test(unittest.TestCase):
|
|||
|
||||
# For debug...
|
||||
# import pdb; pdb.set_trace()
|
||||
if OPTS.purge_temp:
|
||||
if not OPTS.keep_temp:
|
||||
self.cleanup()
|
||||
|
||||
def run_pex(self, a, output=None):
|
||||
|
|
@ -321,9 +323,7 @@ def header(filename, technology):
|
|||
|
||||
def debugTestRunner(post_mortem=None):
|
||||
"""unittest runner doing post mortem debugging on failing tests"""
|
||||
import pdb
|
||||
import traceback
|
||||
if post_mortem is None and not OPTS.purge_temp:
|
||||
if post_mortem is None and OPTS.debug:
|
||||
post_mortem = pdb.post_mortem
|
||||
|
||||
class DebugTestResult(unittest.TextTestResult):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -66,19 +66,27 @@ 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]))
|
||||
f.write("gds polygon subcell true\n")
|
||||
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)
|
||||
|
|
@ -130,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."""
|
||||
|
||||
|
|
@ -166,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")
|
||||
|
||||
|
|
@ -213,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
|
||||
|
|
@ -227,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")
|
||||
|
||||
|
|
@ -237,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()
|
||||
|
|
@ -248,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.")
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,8 +103,7 @@ power_grid = m3_stack
|
|||
# GDS Layer Map
|
||||
###################################################
|
||||
|
||||
# create the GDS layer map
|
||||
# FIXME: parse the gds layer map from the cadence map?
|
||||
# Create the GDS layer map using internal names
|
||||
layer = {}
|
||||
layer["active"] = (1, 0)
|
||||
layer["pwell"] = (2, 0)
|
||||
|
|
@ -138,6 +137,40 @@ layer["m10"] = (29, 0)
|
|||
layer["text"] = (239, 0)
|
||||
layer["boundary"]= (239, 0)
|
||||
|
||||
# Layer names for external PDKs
|
||||
layer_names = {}
|
||||
layer_names["active"] = "active"
|
||||
layer_names["pwell"] = "pwell"
|
||||
layer_names["nwell"] = "nwell"
|
||||
layer_names["nimplant"]= "nimplant"
|
||||
layer_names["pimplant"]= "pimplant"
|
||||
layer_names["vtg"] = "vtg"
|
||||
layer_names["vth"] = "vth"
|
||||
layer_names["thkox"] = "thkox"
|
||||
layer_names["poly"] = "poly"
|
||||
layer_names["contact"] = "contact"
|
||||
layer_names["m1"] = "metal1"
|
||||
layer_names["via1"] = "via1"
|
||||
layer_names["m2"] = "metal2"
|
||||
layer_names["via2"] = "via2"
|
||||
layer_names["m3"] = "metal3"
|
||||
layer_names["via3"] = "via3"
|
||||
layer_names["m4"] = "metal4"
|
||||
layer_names["via4"] = "via4"
|
||||
layer_names["m5"] = "metal5"
|
||||
layer_names["via5"] = "via5"
|
||||
layer_names["m6"] = "metal6"
|
||||
layer_names["via6"] = "via6"
|
||||
layer_names["m7"] = "metal7"
|
||||
layer_names["via7"] = "via7"
|
||||
layer_names["m8"] = "metal8"
|
||||
layer_names["via8"] = "via8"
|
||||
layer_names["m9"] = "metal9"
|
||||
layer_names["via9"] = "via9"
|
||||
layer_names["m10"] = "metal10"
|
||||
layer_names["text"] = "text"
|
||||
layer_names["boundary"]= "boundary"
|
||||
|
||||
###################################################
|
||||
# DRC/LVS Rules Setup
|
||||
###################################################
|
||||
|
|
@ -159,10 +192,10 @@ drc = design_rules("freepdk45")
|
|||
drc["grid"] = 0.0025
|
||||
|
||||
#DRC/LVS test set_up
|
||||
drc["drc_rules"]=drclvs_home+"/calibreDRC.rul"
|
||||
drc["lvs_rules"]=drclvs_home+"/calibreLVS.rul"
|
||||
drc["xrc_rules"]=drclvs_home+"/calibrexRC.rul"
|
||||
drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map"
|
||||
drc["drc_rules"]=drclvs_home + "/calibreDRC.rul"
|
||||
drc["lvs_rules"]=drclvs_home + "/calibreLVS.rul"
|
||||
drc["xrc_rules"]=drclvs_home + "/calibrexRC.rul"
|
||||
drc["layer_map"]=os.environ.get("OPENRAM_TECH") + "/freepdk45/layers.map"
|
||||
|
||||
# minwidth_tx with contact (no dog bone transistors)
|
||||
drc["minwidth_tx"] = 0.09
|
||||
|
|
@ -173,189 +206,189 @@ drc["pwell_to_nwell"] = 0.225
|
|||
# WELL.3 Minimum spacing of nwell/pwell at the same potential
|
||||
# WELL.4 Minimum width of nwell/pwell
|
||||
drc.add_layer("nwell",
|
||||
width = 0.2,
|
||||
spacing = 0.135)
|
||||
width=0.2,
|
||||
spacing=0.135)
|
||||
drc.add_layer("pwell",
|
||||
width = 0.2,
|
||||
spacing = 0.135)
|
||||
width=0.2,
|
||||
spacing=0.135)
|
||||
|
||||
# POLY.1 Minimum width of poly
|
||||
# POLY.2 Minimum spacing of poly AND active
|
||||
drc.add_layer("poly",
|
||||
width = 0.05,
|
||||
spacing = 0.14)
|
||||
width=0.05,
|
||||
spacing=0.14)
|
||||
|
||||
# POLY.3 Minimum poly extension beyond active
|
||||
drc["poly_extend_active"] = 0.055
|
||||
drc["poly_extend_active"]=0.055
|
||||
# Not a rule
|
||||
drc["poly_to_contact"] = 0.075
|
||||
drc["poly_to_contact"]=0.075
|
||||
# POLY.4 Minimum enclosure of active around gate
|
||||
drc["active_enclose_gate"] = 0.07
|
||||
drc["active_enclose_gate"]=0.07
|
||||
# POLY.5 Minimum spacing of field poly to active
|
||||
drc["poly_to_active"] = 0.05
|
||||
drc["poly_to_active"]=0.05
|
||||
# POLY.6 Minimum Minimum spacing of field poly
|
||||
drc["poly_to_field_poly"] = 0.075
|
||||
drc["poly_to_field_poly"]=0.075
|
||||
# Not a rule
|
||||
drc["minarea_poly"] = 0.0
|
||||
drc["minarea_poly"]=0.0
|
||||
|
||||
# ACTIVE.1 Minimum width of active
|
||||
# ACTIVE.2 Minimum spacing of active
|
||||
drc.add_layer("active",
|
||||
width = 0.09,
|
||||
spacing = 0.08)
|
||||
width=0.09,
|
||||
spacing=0.08)
|
||||
# ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active
|
||||
drc.add_enclosure("nwell",
|
||||
layer = "active",
|
||||
enclosure = 0.055)
|
||||
layer="active",
|
||||
enclosure=0.055)
|
||||
drc.add_enclosure("pwell",
|
||||
layer = "active",
|
||||
enclosure = 0.055)
|
||||
layer="active",
|
||||
enclosure=0.055)
|
||||
|
||||
# IMPLANT.1 Minimum spacing of nimplant/ pimplant to channel
|
||||
drc["implant_to_channel"] = 0.07
|
||||
drc["implant_to_channel"]=0.07
|
||||
# Not a rule
|
||||
drc.add_enclosure("implant",
|
||||
layer = "active",
|
||||
enclosure = 0)
|
||||
layer="active",
|
||||
enclosure=0)
|
||||
# Not a rule
|
||||
drc.add_enclosure("implant",
|
||||
layer = "contact",
|
||||
enclosure = 0)
|
||||
layer="contact",
|
||||
enclosure=0)
|
||||
# IMPLANT.2 Minimum spacing of nimplant/ pimplant to contact
|
||||
drc["implant_to_contact"] = 0.025
|
||||
drc["implant_to_contact"]=0.025
|
||||
# IMPLANT.3 Minimum width/ spacing of nimplant/ pimplant
|
||||
# IMPLANT.4 Minimum width/ spacing of nimplant/ pimplant
|
||||
drc.add_layer("implant",
|
||||
width = 0.045,
|
||||
spacing = 0.045)
|
||||
width=0.045,
|
||||
spacing=0.045)
|
||||
|
||||
# CONTACT.1 Minimum width of contact
|
||||
# CONTACT.2 Minimum spacing of contact
|
||||
drc.add_layer("contact",
|
||||
width = 0.065,
|
||||
spacing = 0.075)
|
||||
width=0.065,
|
||||
spacing=0.075)
|
||||
# CONTACT.4 Minimum enclosure of active around contact
|
||||
drc.add_enclosure("active",
|
||||
layer = "contact",
|
||||
enclosure = 0.005)
|
||||
layer="contact",
|
||||
enclosure=0.005)
|
||||
|
||||
# CONTACT.6 Minimum spacing of contact and gate
|
||||
drc["active_contact_to_gate"] = 0.0375 #changed from 0.035
|
||||
drc["active_contact_to_gate"]=0.0375
|
||||
# CONTACT.7 Minimum spacing of contact and poly
|
||||
drc["poly_contact_to_gate"] = 0.090
|
||||
drc["poly_contact_to_gate"]=0.090
|
||||
|
||||
# CONTACT.1 Minimum width of contact
|
||||
# CONTACT.2 Minimum spacing of contact
|
||||
drc.add_layer("contact",
|
||||
width = 0.065,
|
||||
spacing = 0.075)
|
||||
width=0.065,
|
||||
spacing=0.075)
|
||||
# CONTACT.5 Minimum enclosure of poly around contact
|
||||
drc.add_enclosure("poly",
|
||||
layer = "contact",
|
||||
enclosure = 0.005)
|
||||
layer="contact",
|
||||
enclosure=0.005)
|
||||
# CONTACT.6 Minimum spacing of contact and gate
|
||||
drc["contact_to_gate"] = 0.0375 #changed from 0.035
|
||||
drc["contact_to_gate"]=0.0375
|
||||
# CONTACT.7 Minimum spacing of contact and poly
|
||||
drc["contact_to_poly"] = 0.090
|
||||
drc["contact_to_poly"]=0.090
|
||||
|
||||
# METAL1.1 Minimum width of metal1
|
||||
# METAL1.2 Minimum spacing of metal1
|
||||
drc.add_layer("m1",
|
||||
width = 0.065,
|
||||
spacing = 0.065)
|
||||
width=0.065,
|
||||
spacing=0.065)
|
||||
|
||||
# METAL1.3 Minimum enclosure around contact on two opposite sides
|
||||
drc.add_enclosure("m1",
|
||||
layer = "contact",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="contact",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
# METAL1.4 inimum enclosure around via1 on two opposite sides
|
||||
drc.add_enclosure("m1",
|
||||
layer = "via1",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="via1",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
|
||||
# VIA1.1 Minimum width of via1
|
||||
# VIA1.2 Minimum spacing of via1
|
||||
drc.add_layer("via1",
|
||||
width = 0.065,
|
||||
spacing = 0.075)
|
||||
width=0.065,
|
||||
spacing=0.075)
|
||||
|
||||
|
||||
# METALINT.1 Minimum width of intermediate metal
|
||||
# METALINT.2 Minimum spacing of intermediate metal
|
||||
drc.add_layer("m2",
|
||||
width = 0.07,
|
||||
spacing = 0.07)
|
||||
width=0.07,
|
||||
spacing=0.07)
|
||||
|
||||
# METALINT.3 Minimum enclosure around via1 on two opposite sides
|
||||
drc.add_enclosure("m2",
|
||||
layer = "via1",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="via1",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
|
||||
# METALINT.4 Minimum enclosure around via[2-3] on two opposite sides
|
||||
drc.add_enclosure("m2",
|
||||
layer = "via2",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="via2",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
|
||||
# VIA2-3.1 Minimum width of Via[2-3]
|
||||
# VIA2-3.2 Minimum spacing of Via[2-3]
|
||||
drc.add_layer("via2",
|
||||
width = 0.065,
|
||||
spacing = 0.075)
|
||||
width=0.065,
|
||||
spacing=0.075)
|
||||
|
||||
# METALINT.1 Minimum width of intermediate metal
|
||||
# METALINT.2 Minimum spacing of intermediate metal
|
||||
# Minimum spacing of m3 wider than 0.09 & longer than 0.3 = 0.09
|
||||
# Minimum spacing of m3 wider than 0.27 & longer than 0.9 = 0.27
|
||||
# Minimum spacing of m3 wider than 0.5 & longer than 1.8 = 0.5
|
||||
# Minimum spacing of m3 wider than 0.9 & longer than 2.7 = 0.9
|
||||
# Minimum spacing of m3 wider than 1.5 & longer than 4.0 = 1.5
|
||||
# Minimum spacing of m3 wider than 0.09 & longer than 0.3=0.09
|
||||
# Minimum spacing of m3 wider than 0.27 & longer than 0.9=0.27
|
||||
# Minimum spacing of m3 wider than 0.5 & longer than 1.8=0.5
|
||||
# Minimum spacing of m3 wider than 0.9 & longer than 2.7=0.9
|
||||
# Minimum spacing of m3 wider than 1.5 & longer than 4.0=1.5
|
||||
drc.add_layer("m3",
|
||||
width = 0.07,
|
||||
spacing = drc_lut({(0.00, 0.0) : 0.07,
|
||||
(0.09, 0.3) : 0.09,
|
||||
(0.27, 0.9) : 0.27,
|
||||
(0.50, 1.8) : 0.5,
|
||||
(0.90, 2.7) : 0.9,
|
||||
(1.50, 4.0) : 1.5}))
|
||||
width=0.07,
|
||||
spacing=drc_lut({(0.00, 0.0): 0.07,
|
||||
(0.09, 0.3): 0.09,
|
||||
(0.27, 0.9): 0.27,
|
||||
(0.50, 1.8): 0.5,
|
||||
(0.90, 2.7): 0.9,
|
||||
(1.50, 4.0): 1.5}))
|
||||
# METALINT.3 Minimum enclosure around via1 on two opposite sides
|
||||
drc.add_enclosure("m3",
|
||||
layer = "via2",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="via2",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
|
||||
# METALINT.4 Minimum enclosure around via[2-3] on two opposite sides
|
||||
drc.add_enclosure("m3",
|
||||
layer = "via3",
|
||||
enclosure = 0,
|
||||
extension = 0.035)
|
||||
layer="via3",
|
||||
enclosure=0,
|
||||
extension=0.035)
|
||||
|
||||
# VIA2-3.1 Minimum width of Via[2-3]
|
||||
# VIA2-3.2 Minimum spacing of Via[2-3]
|
||||
drc.add_layer("via3",
|
||||
width = 0.07,
|
||||
spacing = 0.085)
|
||||
width=0.07,
|
||||
spacing=0.085)
|
||||
|
||||
# METALSMG.1 Minimum width of semi-global metal
|
||||
# METALSMG.2 Minimum spacing of semi-global metal
|
||||
# Minimum spacing of m4 wider than 0.27 & longer than 0.9 = 0.27
|
||||
# Minimum spacing of m4 wider than 0.5 & longer than 1.8 = 0.5
|
||||
# Minimum spacing of m4 wider than 0.9 & longer than 2.7 = 0.9
|
||||
# Minimum spacing of m4 wider than 1.5 & longer than 4.0 = 1.5
|
||||
# Minimum spacing of m4 wider than 0.27 & longer than 0.9=0.27
|
||||
# Minimum spacing of m4 wider than 0.5 & longer than 1.8=0.5
|
||||
# Minimum spacing of m4 wider than 0.9 & longer than 2.7=0.9
|
||||
# Minimum spacing of m4 wider than 1.5 & longer than 4.0=1.5
|
||||
drc.add_layer("m4",
|
||||
width = 0.14,
|
||||
spacing = drc_lut({(0.00, 0.0) : 0.14,
|
||||
(0.27, 0.9) : 0.27,
|
||||
(0.50, 1.8) : 0.5,
|
||||
(0.90, 2.7) : 0.9,
|
||||
(1.50, 4.0) : 1.5}))
|
||||
width=0.14,
|
||||
spacing=drc_lut({(0.00, 0.0): 0.14,
|
||||
(0.27, 0.9): 0.27,
|
||||
(0.50, 1.8): 0.5,
|
||||
(0.90, 2.7): 0.9,
|
||||
(1.50, 4.0): 1.5}))
|
||||
# METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides
|
||||
drc.add_enclosure("m4",
|
||||
layer = "via3",
|
||||
enclosure = 0.0025)
|
||||
layer="via3",
|
||||
enclosure=0.0025)
|
||||
|
||||
# Metal 5-10 are ommitted
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,26 @@ layer["m4"] = (31, 0)
|
|||
layer["text"] = (63, 0)
|
||||
layer["boundary"] = (63, 0)
|
||||
|
||||
# Layer names for external PDKs
|
||||
layer_names = {}
|
||||
layer_names["active"] = "active"
|
||||
layer_names["pwell"] = "pwell"
|
||||
layer_names["nwell"] = "nwell"
|
||||
layer_names["nimplant"]= "nimplant"
|
||||
layer_names["pimplant"]= "pimplant"
|
||||
layer_names["poly"] = "poly"
|
||||
layer_names["poly_contact"] = "poly_contact"
|
||||
layer_names["active_contact"] = "active_contact"
|
||||
layer_names["m1"] = "metal1"
|
||||
layer_names["via1"] = "via1"
|
||||
layer_names["m2"] = "metal2"
|
||||
layer_names["via2"] = "via2"
|
||||
layer_names["m3"] = "metal3"
|
||||
layer_names["via3"] = "via3"
|
||||
layer_names["m4"] = "metal4"
|
||||
layer_names["text"] = "text"
|
||||
layer_names["boundary"]= "boundary"
|
||||
|
||||
###################################################
|
||||
# DRC/LVS Rules Setup
|
||||
###################################################
|
||||
|
|
|
|||
Loading…
Reference in New Issue