Merge branch 'wlbuffer' into dev

This commit is contained in:
mrg 2020-10-05 15:33:54 -07:00
commit da83824a70
18 changed files with 422 additions and 650 deletions

View File

@ -211,6 +211,12 @@ class layout():
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
""" Adds an instance of a mod to this module """
# Contacts are not really instances, so skip them
if "contact" not in mod.name:
# Check that the instance name is unique
for inst in self.insts:
debug.check(name != inst.name, "Duplicate named instance in {0}: {1}".format(self.name, name))
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons
@ -1029,7 +1035,7 @@ class layout():
"""
import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
self.add_inst("vc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
@ -1038,7 +1044,7 @@ class layout():
"""
import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
def add_boundary(self, ll=vector(0, 0), ur=None):

View File

@ -56,8 +56,14 @@ class delay(simulation):
""" Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power",
"disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_power"]
self.power_meas_names = ["read0_power",
"read1_power",
"write0_power",
"write1_power",
"disabled_read0_power",
"disabled_read1_power",
"disabled_write0_power",
"disabled_write1_power"]
# self.voltage_when_names = ["volt_bl", "volt_br"]
# self.bitline_delay_names = ["delay_bl", "delay_br"]
@ -133,18 +139,18 @@ class delay(simulation):
"""
self.bitline_volt_meas = []
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
self.bl_name))
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
self.bl_name))
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO",
self.br_name))
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO",
self.br_name))
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE",
self.bl_name))
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE",
self.bl_name))
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE",
self.br_name))
self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE",
self.br_name))
self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE
return self.bitline_volt_meas
@ -174,16 +180,16 @@ class delay(simulation):
self.dout_volt_meas = []
for meas in self.delay_meas:
# Output voltage measures
self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name),
meas.targ_name_no_port))
self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name),
meas.targ_name_no_port))
self.dout_volt_meas[-1].meta_str = meas.meta_str
if not OPTS.use_pex:
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9)
else:
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9)
self.sen_meas.meta_str = sram_op.READ_ZERO
self.sen_meas.meta_str = sram_op.READ_ZERO
self.sen_meas.meta_add_delay = True
return self.dout_volt_meas + [self.sen_meas]
@ -191,26 +197,26 @@ class delay(simulation):
def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """
self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
for polarity, meas in single_bit_meas.items():
meas.meta_str = cycle
self.read_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list]
return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list]
def create_write_bit_measures(self):
""" Adds bit measurements for write0 and write1 cycles """
self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
for polarity, meas in single_bit_meas.items():
meas.meta_str = cycle
self.write_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
@ -279,12 +285,8 @@ class delay(simulation):
# instantiate the sram
self.sf.write("\n* Instantiation of the SRAM\n")
if not OPTS.use_pex:
self.stim.inst_model(pins=self.pins,
model_name=self.sram.name)
else:
self.stim.inst_sram_pex(pins=self.pins,
model_name=self.sram.name)
self.stim.inst_model(pins=self.pins,
model_name=self.sram.name)
self.sf.write("\n* SRAM output loads\n")
for port in self.read_ports:
@ -320,7 +322,6 @@ class delay(simulation):
self.gen_data()
self.gen_addr()
# generate control signals
self.sf.write("\n* Generation of control signals\n")
self.gen_control()
@ -382,7 +383,7 @@ class delay(simulation):
self.sf.write("\n* Generation of global clock signal\n")
for port in self.all_ports:
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
self.write_power_measures()
@ -433,7 +434,7 @@ class delay(simulation):
# These measurements have there time further delayed to the neg. edge of the clock.
if delay_obj.meta_add_delay:
meas_cycle_delay += self.period/2
meas_cycle_delay += self.period / 2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port)
@ -442,7 +443,7 @@ class delay(simulation):
# Return value is intended to match the power measure format: t_initial, t_final, port
t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]]
t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1]
t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str] + 1]
return (t_initial, t_final, port)
@ -455,7 +456,7 @@ class delay(simulation):
# Measurement occurs slightly into the next period so we know that the value
# "stuck" after the end of the period -> current period start + 1.25*period
at_time = meas_cycle+1.25*self.period
at_time = meas_cycle + 1.25 * self.period
return (at_time, port)
@ -465,7 +466,7 @@ class delay(simulation):
"""
# Only checking 0 value reads for now.
t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]]
t_trig = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]]
return (t_trig, self.vdd_voltage, port)
@ -480,7 +481,6 @@ class delay(simulation):
measure_variant_inp_tuple = self.get_measure_variants(port, measure, "read")
measure.write_measure(self.stim, measure_variant_inp_tuple)
def write_delay_measures_write_port(self, port):
"""
Write the measure statements to quantify the power results for a write port.
@ -513,7 +513,6 @@ class delay(simulation):
self.sf.write("* Write ports {}\n".format(write_port))
self.write_delay_measures_write_port(write_port)
def write_power_measures(self):
"""
Write the measure statements to quantify the leakage power only.
@ -523,7 +522,7 @@ class delay(simulation):
# add measure statements for power
t_initial = self.period
t_final = 2*self.period
t_final = 2 * self.period
self.stim.gen_meas_power(meas_name="leakage_power",
t_initial=t_initial,
t_final=t_final)
@ -543,7 +542,7 @@ class delay(simulation):
while True:
time_out -= 1
if (time_out <= 0):
debug.error("Timed out, could not find a feasible period.",2)
debug.error("Timed out, could not find a feasible period.", 2)
# Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]]
@ -589,7 +588,6 @@ class delay(simulation):
feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0])
previous_period = self.period
# Loops through all the ports checks if the feasible period works. Everything restarts it if does not.
# Write ports do not produce delays which is why they are not included here.
i = 1
@ -614,7 +612,7 @@ class delay(simulation):
include leakage of all cells.
"""
debug.check(self.period > 0, "Target simulation period non-positive")
debug.check(self.period > 0, "Target simulation period non-positive")
self.write_delay_stimulus()
@ -630,30 +628,29 @@ class delay(simulation):
for port in self.targ_write_ports:
if not self.check_bit_measures(self.write_bit_meas, port):
return(False,{})
return(False, {})
debug.info(2, "Checking write values for port {}".format(port))
debug.info(2, "Checking write values for port {}".format(port))
write_port_dict = {}
for measure in self.write_lib_meas:
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not check_dict_values_is_float(write_port_dict):
debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1)
debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict), 1)
result[port].update(write_port_dict)
for port in self.targ_read_ports:
# First, check that the memory has the right values at the right times
if not self.check_bit_measures(self.read_bit_meas, port):
return(False,{})
return(False, {})
debug.info(2, "Checking read delay values for port {}".format(port))
# Check sen timing, then bitlines, then general measurements.
if not self.check_sen_measure(port):
return (False,{})
return (False, {})
if not self.check_read_debug_measures(port):
return (False,{})
return (False, {})
# Check timing for read ports. Power is only checked if it was read correctly
read_port_dict = {}
@ -661,26 +658,25 @@ class delay(simulation):
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not self.check_valid_delays(read_port_dict):
return (False,{})
return (False, {})
if not check_dict_values_is_float(read_port_dict):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1)
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
result[port].update(read_port_dict)
return (True,result)
return (True, result)
def check_sen_measure(self, port):
"""Checks that the sen occurred within a half-period"""
sen_val = self.sen_meas.retrieve_measure(port=port)
debug.info(2,"s_en delay={}ns".format(sen_val))
debug.info(2, "s_en delay={}ns".format(sen_val))
if self.sen_meas.meta_add_delay:
max_delay = self.period/2
max_delay = self.period / 2
else:
max_delay = self.period
return not (type(sen_val) != float or sen_val > max_delay)
def check_read_debug_measures(self, port):
"""Debug measures that indicate special conditions."""
@ -694,23 +690,23 @@ class delay(simulation):
val = meas.retrieve_measure(port=port)
if self.bl_name == meas.targ_name_no_port:
bl_vals[meas.meta_str] = val
elif self.br_name == meas.targ_name_no_port:
elif self.br_name == meas.targ_name_no_port:
br_vals[meas.meta_str] = val
debug.info(2,"{}={}".format(meas.name,val))
debug.info(2, "{}={}".format(meas.name, val))
dout_success = True
bl_success = False
for meas in self.dout_volt_meas:
val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val))
debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name,val))
debug.info(2, "{}={}".format(meas.name, val))
debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name, val))
if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage*0.1:
if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage * 0.1:
dout_success = False
debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val))
bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE])
elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage*0.9:
elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage * 0.9:
dout_success = False
debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val))
bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE])
@ -718,10 +714,9 @@ class delay(simulation):
# If the bitlines have a correct value while the output does not then that is a
# sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
if not dout_success and bl_success:
debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.",1)
debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.", 1)
return dout_success
def check_bit_measures(self, bit_measures, port):
"""
@ -732,29 +727,29 @@ class delay(simulation):
for polarity, meas_list in bit_measures.items():
for meas in meas_list:
val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val))
debug.info(2, "{}={}".format(meas.name, val))
if type(val) != float:
continue
meas_cycle = meas.meta_str
# Loose error conditions. Assume it's not metastable but account for noise during reads.
if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING):
success = val < self.vdd_voltage/2
success = val < self.vdd_voltage / 2
elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING):
success = val > self.vdd_voltage/2
success = val > self.vdd_voltage / 2
elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING):
success = val > self.vdd_voltage/2
success = val > self.vdd_voltage / 2
elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\
(meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING):
success = val < self.vdd_voltage/2
success = val < self.vdd_voltage / 2
if not success:
debug.info(1,("Wrong value detected on probe bit during read/write cycle. "
"Check writes and control logic for bugs.\n measure={}, op={}, "
"bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name,val))
debug.info(1, ("Wrong value detected on probe bit during read/write cycle. "
"Check writes and control logic for bugs.\n measure={}, op={}, "
"bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name, val))
return success
return success
def check_bitline_meas(self, v_discharged_bl, v_charged_bl):
"""
@ -764,11 +759,11 @@ class delay(simulation):
# The inputs looks at discharge/charged bitline rather than left or right (bl/br)
# Performs two checks, discharging bitline is at least 10% away from vdd and there is a
# 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error.
min_dicharge = v_discharged_bl < self.vdd_voltage*0.9
min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage*0.1
min_dicharge = v_discharged_bl < self.vdd_voltage * 0.9
min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage * 0.1
debug.info(1,"min_dicharge={}, min_diff={}".format(min_dicharge,min_diff))
return (min_dicharge and min_diff)
debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff))
return (min_dicharge and min_diff)
def run_power_simulation(self):
"""
@ -779,20 +774,20 @@ class delay(simulation):
self.write_power_stimulus(trim=False)
self.stim.run_sim()
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))
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))
# debug
# sys.exit(1)
self.write_power_stimulus(trim=True)
self.stim.run_sim()
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))
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))
# For debug, you sometimes want to inspect each simulation.
# key=raw_input("press return to continue")
return (leakage_power*1e3, trim_leakage_power*1e3)
return (leakage_power * 1e3, trim_leakage_power * 1e3)
def check_valid_delays(self, result_dict):
""" Check if the measurements are defined and if they are valid. """
@ -802,30 +797,31 @@ class delay(simulation):
delay_lh = result_dict["delay_lh"]
slew_hl = result_dict["slew_hl"]
slew_lh = result_dict["slew_lh"]
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period, self.load, self.slew)
# if it failed or the read was longer than a period
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh)
debug.info(2, "Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return False
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
half_period = self.period/2 # high-to-low delays start at neg. clk edge, so they need to be less than half_period
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh)
# high-to-low delays start at neg. clk edge, so they need to be less than half_period
half_period = self.period / 2
if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \
or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0:
debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
debug.info(2, "UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return False
else:
debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
debug.info(2, "Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return True
@ -844,12 +840,12 @@ class delay(simulation):
target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period)
# The min period of one port becomes the new lower bound. Reset the upper_bound.
lb_period = target_period
ub_period = feasible_period
ub_period = feasible_period
# Clear the target ports before leaving
self.targ_read_ports = []
self.targ_write_ports = []
return target_period
return target_period
def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period):
"""
@ -870,7 +866,7 @@ class delay(simulation):
while True:
time_out -= 1
if (time_out <= 0):
debug.error("Timed out, could not converge on minimum period.",2)
debug.error("Timed out, could not converge on minimum period.", 2)
self.period = target_period
debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period,
@ -1077,7 +1073,6 @@ class delay(simulation):
data_ones = "1" * self.word_size
data_zeros = "0" * self.word_size
wmask_ones = "1" * self.num_wmasks
wmask_zeroes = "0" * self.num_wmasks
if self.t_current == 0:
self.add_noop_all_ports("Idle cycle (no positive clock edge)")
@ -1132,7 +1127,6 @@ class delay(simulation):
self.add_noop_clock_one_port(read_port)
self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1
# This also ensures we will have a L->H transition on the next read
self.add_read("R data 0 address {} to clear dout caps".format(inverse_address),
inverse_address,
@ -1173,8 +1167,10 @@ class delay(simulation):
# Get any available read/write port in case only a single write or read ports is being characterized.
cur_read_port = self.get_available_port(get_read_port=True)
cur_write_port = self.get_available_port(get_read_port=False)
debug.check(cur_read_port != None, "Characterizer requires at least 1 read port")
debug.check(cur_write_port != None, "Characterizer requires at least 1 write port")
debug.check(cur_read_port != None,
"Characterizer requires at least 1 read port")
debug.check(cur_write_port != None,
"Characterizer requires at least 1 write port")
# Create test cycles for specified target ports.
write_pos = 0

View File

@ -229,17 +229,25 @@ class functional(simulation):
sp_read_value = ""
for bit in range(self.word_size + self.num_spare_cols):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check))
if value > self.v_high:
sp_read_value = "1" + sp_read_value
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
eo_period,
self.v_low,
self.v_high)
try:
value = float(value)
if value > self.v_high:
sp_read_value = "1" + sp_read_value
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
eo_period,
self.v_low,
self.v_high)
except ValueError:
error ="FAILED: {0}_{1} value {2} at time {3}n is not a float.".format(dout_port,
bit,
value,
eo_period)
return (0, error)
self.read_results.append([sp_read_value, dout_port, eo_period, check])
@ -315,7 +323,7 @@ class functional(simulation):
else:
expected_value = self.word_size + self.num_spare_cols
for i in range(expected_value - len(new_value)):
new_value = "0" + new_value
new_value = "0" + new_value
# print("Binary Conversion: {} to {}".format(value, new_value))
return new_value
@ -348,8 +356,8 @@ class functional(simulation):
# Write important signals to stim file
self.sf.write("\n\n* Important signals for debug\n")
self.sf.write("* bl: {}\n".format(self.bl_name))
self.sf.write("* br: {}\n".format(self.br_name))
self.sf.write("* bl: {}\n".format(self.bl_name.format(port)))
self.sf.write("* br: {}\n".format(self.br_name.format(port)))
self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name))

View File

@ -51,23 +51,21 @@ class simulation():
self.load = tech.spice["dff_in_cap"] * 4
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
self.gnd_voltage = 0
def create_signal_names(self):
self.addr_name = "a"
self.din_name = "din"
self.dout_name = "dout"
self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name),
port_info=(len(self.all_ports),self.write_ports,self.read_ports),
self.pins = self.gen_pin_names(port_signal_names=(self.addr_name, self.din_name, self.dout_name),
port_info=(len(self.all_ports), self.write_ports, self.read_ports),
abits=self.addr_size,
dbits=self.word_size + self.num_spare_cols)
debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins))
#This is TODO once multiport control has been finalized.
#self.control_name = "CSB"
self.pins))
def set_stimulus_variables(self):
# Clock signals
@ -96,7 +94,7 @@ class simulation():
def add_control_one_port(self, port, op):
"""Appends control signals for operation to a given port"""
#Determine values to write to port
# Determine values to write to port
web_val = 1
csb_val = 1
if op == "read":
@ -406,7 +404,9 @@ class simulation():
return pin_names
def add_graph_exclusions(self):
"""Exclude portions of SRAM from timing graph which are not relevant"""
"""
Exclude portions of SRAM from timing graph which are not relevant
"""
# other initializations can only be done during analysis when a bit has been selected
# for testing.
@ -414,10 +414,12 @@ class simulation():
self.sram.graph_exclude_addr_dff()
self.sram.graph_exclude_data_dff()
self.sram.graph_exclude_ctrl_dffs()
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
def set_internal_spice_names(self):
"""Sets important names for characterization such as Sense amp enable and internal bit nets."""
"""
Sets important names for characterization such as Sense amp enable and internal bit nets.
"""
port = self.read_ports[0]
if not OPTS.use_pex:
@ -458,11 +460,10 @@ class simulation():
self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(2, "s_en name = {}".format(self.sen_name))
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
def get_sen_name(self, paths, assumed_port=None):
"""
@ -481,7 +482,9 @@ class simulation():
return sen_name
def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output."""
"""
Creates timing graph to generate the timing paths for the SRAM output.
"""
self.sram.clear_exclude_bits() # Removes previous bit exclusions
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
@ -492,7 +495,9 @@ class simulation():
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
def get_bl_name_search_exclusions(self):
"""Gets the mods as a set which should be excluded while searching for name."""
"""
Gets the mods as a set which should be excluded while searching for name.
"""
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward
@ -519,7 +524,9 @@ class simulation():
return path_net_name
def get_bl_name(self, paths, port):
"""Gets the signal name associated with the bitlines in the bank."""
"""
Gets the signal name associated with the bitlines in the bank.
"""
cell_mod = factory.create(module_type=OPTS.bitcell)
cell_bl = cell_mod.get_bl_name(port)

View File

@ -15,7 +15,6 @@ import tech
import debug
import subprocess
import os
import sys
import numpy as np
from globals import OPTS
@ -40,32 +39,26 @@ class stimuli():
debug.info(2, "Not using spice library")
self.device_models = tech.spice["fet_models"][self.process]
self.sram_name = "Xsram"
def inst_sram(self, pins, inst_name):
""" Function to instatiate an SRAM subckt. """
self.sf.write("{} ".format(self.sram_name))
for pin in self.sram_pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(inst_name))
def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name))
if OPTS.use_pex:
self.inst_pex_model(pins, model_name)
else:
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name))
def inst_sram_pex(self, pins, model_name):
def inst_pex_model(self, pins, model_name):
self.sf.write("X{0} ".format(model_name))
for pin in pins:
self.sf.write("{0} ".format(pin))
for bank in range(OPTS.num_banks):
row = int(OPTS.num_words / OPTS.words_per_row) - 1
col = int(OPTS.word_size * OPTS.words_per_row) - 1
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col))
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col))
col = int(OPTS.word_size * OPTS.words_per_row) - 1
self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank, row, col))
self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank, row, col))
# can't add all bitcells to top level due to ngspice max port count of 1005
# for row in range(int(OPTS.num_words / OPTS.words_per_row)):
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
@ -76,7 +69,6 @@ class stimuli():
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
self.sf.write("bl{0}_{1} ".format(port, col))
self.sf.write("br{0}_{1} ".format(port, col))
self.sf.write("s_en{0} ".format(bank))
self.sf.write("{0}\n".format(model_name))
@ -94,14 +86,13 @@ class stimuli():
self.tx_length))
self.sf.write(".ENDS test_inv\n")
def create_buffer(self, buffer_name, size=[1,3], beta=2.5):
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
"""
Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple.
"""
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
self.gnd_name,
buffer_name))
self.sf.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
@ -122,8 +113,6 @@ class stimuli():
self.tx_length))
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
"""
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
@ -140,7 +129,6 @@ class stimuli():
0.5*period-0.5*t_rise-0.5*t_fall,
period))
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
"""
Generate a PWL stimulus given a signal name and data values at each period.
@ -149,18 +137,22 @@ class stimuli():
to the initial value.
"""
# the initial value is not a clock time
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match. {0} clock values, {1} data values for {2}".format(len(clk_times), len(data_values), sig_name))
str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}"
debug.check(len(clk_times)==len(data_values),
str.format(len(clk_times),
len(data_values),
sig_name))
# shift signal times earlier for setup time
times = np.array(clk_times) - setup*period
times = np.array(clk_times) - setup * period
values = np.array(data_values) * self.voltage
half_slew = 0.5 * slew
self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values))))
self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
values[i-1],
times[i]+half_slew,
for i in range(1, len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i] - half_slew,
values[i - 1],
times[i] + half_slew,
values[i]))
self.sf.write(")\n")
@ -169,9 +161,9 @@ class stimuli():
self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
def get_inverse_voltage(self, value):
if value > 0.5*self.voltage:
if value > 0.5 * self.voltage:
return 0
elif value <= 0.5*self.voltage:
elif value <= 0.5 * self.voltage:
return self.voltage
else:
debug.error("Invalid value to get an inverse of: {0}".format(value))
@ -184,7 +176,6 @@ class stimuli():
else:
debug.error("Invalid value to get an inverse of: {0}".format(value))
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
""" Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
@ -230,7 +221,7 @@ class stimuli():
def gen_meas_value(self, meas_name, dout, t_intital, t_final):
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
self.sf.write(measure_string)
def write_control(self, end_time, runlvl=4):
""" Write the control cards to run and end the simulation """
@ -243,10 +234,10 @@ class stimuli():
reltol = 0.005 # 0.5%
else:
reltol = 0.001 # 0.1%
timestep = 10 #ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
# UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep,end_time))
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
self.sf.write(".TEMP {}\n".format(self.temperature))
if OPTS.spice_name == "ngspice":
# ngspice sometimes has convergence problems if not using gear method
@ -260,7 +251,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.spice_name in ["hspice","xa"]:
if OPTS.spice_name in ["hspice", "xa"]:
self.sf.write(".probe V(*)\n")
else:
self.sf.write(".plot V(*)\n")
@ -271,7 +262,6 @@ class stimuli():
# end the stimulus file
self.sf.write(".end\n\n")
def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files"""
@ -291,13 +281,12 @@ class stimuli():
else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(self):
""" Writes supply voltage statements """
gnd_node_name = "0"
self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage))
#Adding a commented out supply for simulators where gnd and 0 are not global grounds.
# Adding a commented out supply for simulators where gnd and 0 are not global grounds.
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))
@ -306,7 +295,7 @@ class stimuli():
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
import datetime
start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe!="","No spice simulator has been found.")
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
if OPTS.spice_name == "xa":
# Output the xa configurations here. FIXME: Move this to write it once.
@ -314,27 +303,32 @@ class stimuli():
xa_cfg.write("set_sim_level -level 7\n")
xa_cfg.write("set_powernet_level 7 -node vdd\n")
xa_cfg.close()
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp,
OPTS.num_threads)
valid_retcode=0
elif OPTS.spice_name == "hspice":
# TODO: Should make multithreading parameter a configuration option
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)
cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
OPTS.num_threads,
temp_stim,
OPTS.openram_temp)
valid_retcode=0
else:
# ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit
# Measurements can't be made with a raw file set in ngspice
# -r {2}timing.raw
ng_cfg = open("{}.spiceinit".format(OPTS.openram_temp), "w")
ng_cfg.write("set num_threads={}\n".format(OPTS.num_threads))
ng_cfg.close()
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings
valid_retcode=1
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
@ -348,7 +342,7 @@ class stimuli():
debug.error("Spice simulation error: " + cmd, -1)
else:
end_time = datetime.datetime.now()
delta_time = round((end_time-start_time).total_seconds(),1)
debug.info(2,"*** Spice: {} seconds".format(delta_time))
delta_time = round((end_time - start_time).total_seconds(), 1)
debug.info(2, "*** Spice: {} seconds".format(delta_time))

View File

@ -124,7 +124,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.wl_insts = []
self.driver_wordline_outputs = []
for port in self.all_ports:
self.wl_insts.append(self.add_inst(name="wl_driver",
self.wl_insts.append(self.add_inst(name="wl_driver{}".format(port),
mod=self.wl_array))
temp = []
temp += [self.get_rbl_wordline_names(port)[port]]

View File

@ -119,6 +119,9 @@ class options(optparse.Values):
# For sky130, we need magic for filtering.
magic_exe = None
# Number of threads to use
num_threads = 2
# Should we print out the banner at startup
print_banner = True

View File

@ -67,7 +67,7 @@ class sram():
"""Dump config file with all options.
Include defaults and anything changed by input config."""
f = open(name, "w")
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
for var_name, var_value in var_dict.items():
if isinstance(var_value, str):
f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n")
@ -156,4 +156,4 @@ class sram():
oname = OPTS.output_path + OPTS.output_name + "_extended.py"
debug.print_raw("Extended Config: Writing to {0}".format(oname))
self.extended_config_write(oname)
print_time("Extended Config", datetime.datetime.now(), start_time)
print_time("Extended Config", datetime.datetime.now(), start_time)

View File

@ -414,7 +414,7 @@ class sram_1bank(sram_base):
layer_stack=self.m1_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
self.col_addr_bus_size[port] = cr.height
@ -470,7 +470,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
@ -482,7 +482,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap

View File

@ -15,9 +15,6 @@ from design import design
from verilog import verilog
from lef import lef
from sram_factory import factory
from tech import drc
import numpy as np
import logical_effort
class sram_base(design, verilog, lef):
@ -43,9 +40,6 @@ class sram_base(design, verilog, lef):
if not self.num_spare_cols:
self.num_spare_cols = 0
# For logical effort delay calculations.
self.all_mods_except_control_done = False
def add_pins(self):
""" Add pins for entire SRAM. """
@ -87,18 +81,20 @@ class sram_base(design, verilog, lef):
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_global_pex_labels(self):
"""
Add pex labels at the sram level for spice analysis
"""
# add pex labels for bitcells
for bank_num in range(len(self.bank_insts)):
bank = self.bank_insts[bank_num]
pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name)
pex_data = bank.reverse_transformation_bitcell(self.bitcell.name)
bank_offset = pex_data[0] # offset bank relative to sram
Q_offset = pex_data[1] # offset of storage relative to bank
@ -112,46 +108,61 @@ class sram_base(design, verilog, lef):
br = []
storage_layer_name = "m1"
bitline_layer_name = "m2"
bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)):
Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]]
Q = [bank_offset[cell][0] + Q_offset[cell][0],
bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0],
bank_offset[cell][1] + Q_bar_offset[cell][1]]
OPTS.words_per_row = self.words_per_row
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar)
row = int(cell % (OPTS.num_words / self.words_per_row))
col = int(cell / (OPTS.num_words))
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q_bar)
for cell in range(len(bl_offsets)):
col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)):
col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)):
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0])
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]),
bitline_layer_name, bl[i][0])
for i in range(len(br)):
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0])
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]),
bitline_layer_name, br[i][0])
# add pex labels for control logic
for i in range (len(self.control_logic_insts)):
for i in range(len(self.control_logic_insts)):
instance = self.control_logic_insts[i]
control_logic_offset = instance.offset
for output in instance.mod.output_list:
pin = instance.mod.get_pin(output)
pin.transform([0,0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset)
pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0],
control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i),
storage_layer_name,
offset)
def create_netlist(self):
""" Netlist creation """
@ -370,10 +381,6 @@ class sram_base(design, verilog, lef):
self.bank_count = 0
# The control logic can resize itself based on the other modules.
# Requires all other modules added before control logic.
self.all_mods_except_control_done = True
c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic)
@ -619,6 +626,7 @@ class sram_base(design, verilog, lef):
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],

View File

@ -9,8 +9,8 @@ Run regression tests/pex test on an extracted pinv to ensure pex functionality
with HSPICE.
"""
import unittest
from testutils import header,openram_test
import sys,os
from testutils import header, openram_test
import sys, os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
@ -35,43 +35,43 @@ class hspice_pex_pinv_test(openram_test):
# generate the pinv
prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file
# force set purge to false to save the sp file
OPTS.purge_temp = False
debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp)
# make sure that the library simulation is successful\
sp_delay = self.simulate_delay(test_module = tempsp,
top_level_name = tx.name)
if sp_delay is "Failed":
# make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name=tx.name)
if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected')
# now generate its pex file
pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value
OPTS.purge_temp = prev_purge_value # restore the old purge 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)
pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name=tx.name)
# make sure the extracted spice simulated
if pex_delay is "Failed":
if pex_delay == "Failed":
self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided
# comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay))
# debug.info(2,"pex_delay: {0}".format(pex_delay))
# debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay)
# assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
# .format(pex_delay,sp_delay)
globals.end_openram()
def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
@ -87,43 +87,43 @@ class hspice_pex_pinv_test(openram_test):
from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner)
simulation = stimuli(sim_file, corner)
# library files
simulation.write_include(cir_file)
# supply voltages
simulation.gen_constant(sig_name ="vdd",
v_val = tech.spice["nom_supply_voltage"])
simulation.gen_constant(sig_name = "gnd",
v_val = "0v")
simulation.gen_constant(sig_name="vdd",
v_val=tech.spice["nom_supply_voltage"])
simulation.gen_constant(sig_name="gnd",
v_val="0v")
run_time = tech.spice["feasible_period"] * 4
# input voltage
clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input",
clk_times = [clk_period,clk_period],
data_values = [1,0],
period = clk_period,
slew = 0.001*tech.spice["feasible_period"],
setup = 0)
simulation.gen_pwl(sig_name="input",
clk_times=[clk_period, clk_period],
data_values=[1, 0],
period=clk_period,
slew=0.001 * tech.spice["feasible_period"],
setup=0)
# instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"],
model_name = top_module_name)
simulation.inst_model(pins=["input", "output", "vdd", "gnd"],
model_name=top_module_name)
# delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay",
trig_name = "input",
targ_name = "output",
trig_dir_str = "FALL",
targ_dir_str = "RISE",
has_port = False)
delay_measure = measurements.delay_measure(measure_name="pinv_delay",
trig_name="input",
targ_name="output",
trig_dir_str="FALL",
targ_dir_str="RISE",
has_port=False)
trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"]
rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time)
simulation.write_control(end_time=run_time)
sim_file.close()
return simulation

View File

@ -34,43 +34,43 @@ class ngspice_pex_pinv_test(openram_test):
# generate the pinv module
prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file
OPTS.purge_temp = False # force set purge to false 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)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp)
# make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module = tempsp,
top_level_name = tx.name)
if sp_delay is "Failed":
sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name=tx.name)
if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected')
# now generate its pex file
pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value
# restore the old purge value
OPTS.purge_temp = prev_purge_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)
pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name=tx.name)
# make sure the extracted spice simulated
if pex_delay is "Failed":
if pex_delay == "Failed":
self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided
# comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay))
# debug.info(2,"pex_delay: {0}".format(pex_delay))
# debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay)
# assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
# .format(pex_delay,sp_delay)
globals.end_openram()
def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
@ -86,47 +86,46 @@ class ngspice_pex_pinv_test(openram_test):
from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner)
simulation = stimuli(sim_file, corner)
# library files
simulation.write_include(cir_file)
# supply voltages
simulation.gen_constant(sig_name ="vdd",
v_val = tech.spice["nom_supply_voltage"])
simulation.gen_constant(sig_name="vdd",
v_val=tech.spice["nom_supply_voltage"])
# The scn4m_subm and ngspice combination will have a gnd source error:
# "Fatal error: instance vgnd is a shorted VSRC"
# However, remove gnd power for all techa pass for this test
# simulation.gen_constant(sig_name = "gnd",
# v_val = "0v")
run_time = tech.spice["feasible_period"] * 4
# input voltage
clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input",
clk_times = [clk_period,clk_period],
data_values = [1,0],
period = clk_period,
slew = 0.001*tech.spice["feasible_period"],
setup = 0)
simulation.gen_pwl(sig_name="input",
clk_times=[clk_period, clk_period],
data_values=[1, 0],
period=clk_period,
slew=0.001 * tech.spice["feasible_period"],
setup=0)
# instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"],
model_name = top_module_name)
simulation.inst_model(pins=["input", "output", "vdd", "gnd"],
model_name=top_module_name)
# delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay",
trig_name = "input",
targ_name = "output",
trig_dir_str = "FALL",
targ_dir_str = "RISE",
has_port = False)
delay_measure = measurements.delay_measure(measure_name="pinv_delay",
trig_name="input",
targ_name="output",
trig_dir_str="FALL",
targ_dir_str="RISE",
has_port=False)
trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"]
rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time)
simulation.write_control(end_time=run_time)
sim_file.close()
return simulation

View File

@ -1,319 +0,0 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
@unittest.skip("SKIPPING 26_pex_test")
class sram_func_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.use_pex = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import setup_hold
if not OPTS.spice_exe:
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
self.func_test(bank_num=1)
self.func_test(bank_num=2)
self.func_test(bank_num=4)
globals.end_openram()
def func_test(self, bank_num):
import sram
import tech
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram.sram(word_size=OPTS.word_size,
num_words=OPTS.num_words,
num_banks=OPTS.num_banks,
name="test_sram1")
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
s.sp_write(tempspice)
s.gds_write(tempgds)
self.assertFalse(verify.run_drc(s.name, tempgds))
self.assertFalse(verify.run_lvs(s.name, tempgds, tempspice))
self.assertFalse(verify.run_pex(s.name, tempgds,
tempspice, output=OPTS.openram_temp + "temp_pex.sp"))
import sp_file
stimulus_file = OPTS.openram_temp + "stimulus.sp"
a_stimulus = sp_file.sp_file(stimulus_file)
self.write_stimulus(a_stimulus)
simulator_file = OPTS.openram_temp + "simulator.sp"
a_simulator = sp_file.sp_file(simulator_file)
self.write_simulator(a_simulator)
result_file = OPTS.openram_temp + "result"
import os
if OPTS.spice_name == "hspice":
cmd = "hspice -mt 2 -i {0} > {1} ".format(
simulator_file, result_file)
else:
cmd = "ngspice -b -i {0} > {1} ".format(
simulator_file, result_file)
os.system(cmd)
import re
sp_result = open(result_file, "r")
contents = sp_result.read()
key = "vr1"
val = re.search(
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
val = val.group(3)
value1 = float(self.convert_voltage_unit(val))
key = "vr2"
val = re.search(
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
val = val.group(3)
value2 = float(self.convert_voltage_unit(val))
self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"])
self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"])
def convert_voltage_unit(self, string):
newstring = ""
for letter in string:
if letter == "m":
letter = "10e-3"
elif letter == "u":
letter = "10e-6"
else:
letter = letter
newstring = str(newstring) + str(letter)
return newstring
def convert_time_unit(self, string):
newstring = ""
for letter in string:
if letter == "f":
letter = "10e-15"
elif letter == "p":
letter = "10e-12"
elif letter == "n":
letter = "10e-9"
elif letter == "u":
letter = "10e-6"
elif letter == "m":
letter = "10e-3"
else:
letter = letter
newstring = str(newstring) + str(letter)
return newstring
def write_simulator(self, sim_file):
sim_file.write("\n")
import tech
time_step = tech.spice["clock_period"]
for model in tech.spice["fet_models"]:
sim_file.write(".inc " + str(model) + "\n")
sim_file.write(".inc stimulus.sp\n")
sim_file.write(".inc temp_pex.sp\n")
sim_file.write(".options post runlvl=6\n")
sim_file.write("\n")
sim_file.write(
"Xsource DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss source\n")
sim_file.write(
"Xsram DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd vss test_sram1\n")
sim_file.write("\n")
sim_file.write(".MEASURE TRAN vr1 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
4.5 * tech.spice["clock_period"], 5 * tech.spice["clock_period"]))
sim_file.write(".MEASURE TRAN vr2 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
9.5 * tech.spice["clock_period"], 10 * tech.spice["clock_period"]))
sim_file.write("\n")
if OPTS.spice_name in ["hspice","xa"]:
sim_file.write(".probe v(x*.*)\n")
sim_file.write(".tran 0.1ns {0}ns\n".format(
10 * tech.spice["clock_period"]))
sim_file.write(".end\n")
else:
sim_file.write(
".meas tran DELAY1.0 TRIG v(clk) VAL=0.5 RISE=6 TARG v(DATA[0]) VAL=0.5 TD=0.5n RISE=1\n")
sim_file.write(".tran 0.1ns {0}ns\n".format(
10 * tech.spice["clock_period"]))
sim_file.write(".control\n")
sim_file.write("run\n")
#sim_file.write("plot CSb WEb OEb \n")
#sim_file.write("plot clk DATA0 \n")
sim_file.write("quit\n")
sim_file.write(".endc\n")
sim_file.write(".end\n")
sim_file.file.close()
def write_stimulus(self, sti_file):
import tech
import sp_file
sti_file.write(
".subckt source DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss\n")
time_step = tech.spice["clock_period"]
clk = sp_file.PWL(name="clk", port=["clk", "0"])
for i in range(0, 11):
clk.write_pulse(i * time_step, time_step, "UP")
clk.write_to_sp(sti_file)
WEB_inv = sp_file.PWL(name="WEb_inv", port=["WEb_inv", "0"])
WEB = sp_file.PWL(name="WEB", port=["WEb", "0"])
OEb = sp_file.PWL(name="OEb", port=["OEb", "0"])
CSb = sp_file.PWL(name="CSb", port=["CSb", "0"])
# write
CSb.write_pulse(0.75 * time_step, time_step, "DN")
WEB.write_pulse(0.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(0.75 * time_step, time_step, "UP")
CSb.write_pulse(1.75 * time_step, time_step, "DN")
WEB.write_pulse(1.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(1.75 * time_step, time_step, "UP")
# read
OEb.write_pulse(3.75 * time_step, time_step, "DN")
CSb.write_pulse(3.75 * time_step, time_step, "DN")
# write
CSb.write_pulse(5.75 * time_step, time_step, "DN")
WEB.write_pulse(5.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(5.75 * time_step, time_step, "UP")
CSb.write_pulse(6.75 * time_step, time_step, "DN")
WEB.write_pulse(6.75 * time_step, time_step, "DN")
WEB_inv.write_pulse(6.75 * time_step, time_step, "UP")
# read
OEb.write_pulse(8.75 * time_step, time_step, "DN")
CSb.write_pulse(8.75 * time_step, time_step, "DN")
CSb.write_to_sp(sti_file)
WEB.write_to_sp(sti_file)
WEB_inv.write_to_sp(sti_file)
OEb.write_to_sp(sti_file)
sti_file.write("VA[0] A[0] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[1] A[1] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[2] A[2] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write("VA[3] A[3] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
sti_file.write(
"xA[0]_buff A[0] ADDR[0]_inv ADDR[0] vdd vss test_buf\n")
sti_file.write(
"xA[1]_buff A[1] ADDR[1]_inv ADDR[1] vdd vss test_buf\n")
sti_file.write(
"xA[2]_buff A[2] ADDR[2]_inv ADDR[2] vdd vss test_buf\n")
sti_file.write(
"xA[3]_buff A[3] ADDR[3]_inv ADDR[3] vdd vss test_buf\n")
VD_0 = sp_file.PWL(name="VD[0]", port=["D[0]", "0"])
VD_0.write_pulse(0, 5 * time_step, "S1")
VD_0.write_pulse(5 * time_step, 5 * time_step, "S0")
VD_0.write_to_sp(sti_file)
sti_file.write(
"xD[0]_buff D[0] DATA[0]_inv DATA[0]s vdd vss test_buf\n")
sti_file.write(
"xD[0]_gate DATA[0]s WEb WEb_inv DATA[0] vdd vss tran_gate\n")
sti_file.write("mp[0]_gate_vdd vdd write_v DATA[0] vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mn[0]_gate_vss vss write_g DATA[0] vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
Vwrite_v = sp_file.PWL(name="write_v", port=["write_vs", "0"])
Vwrite_v.write_pulse(0, 0.5 * time_step, "S1")
Vwrite_v.write_pulse(7.5 * time_step, time_step, "DN")
Vwrite_v.write_to_sp(sti_file)
sti_file.write(
"xwrite_v write_vs write_v_inv write_v vdd vss test_buf\n")
Vwrite_g = sp_file.PWL(name="write_g", port=["write_gs", "0"])
Vwrite_g.write_pulse(0, 0.5 * time_step, "S0")
Vwrite_g.write_pulse(3 * time_step, time_step, "UP")
Vwrite_g.write_to_sp(sti_file)
sti_file.write(
"xwrite_g write_gs write_g_inv write_g vdd vss test_buf\n")
sti_file.write("Vdd vdd 0 DC " +
str(tech.spice["supply_voltage"]) + "\n")
sti_file.write("Vvss vss 0 DC 0\n")
sti_file.write(".ENDS source\n")
sti_file.write("\n")
sti_file.write(".SUBCKT tran_gate in gate gate_inv out vdd vss\n")
sti_file.write("mp0 in gate out vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mn0 in gate_inv out vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write(".ENDS tran_gate\n")
sti_file.write("\n")
sti_file.write(".SUBCKT test_buf in out_inv out_buf vdd vss\n")
sti_file.write("mpinv1 out_inv in vdd vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mninv1 out_inv in vss vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mpinv2 out_buf out_inv vdd vdd " + str(tech.spice["pmos"]) +
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write("mninv2 out_buf out_inv vss vss " + str(tech.spice["nmos"]) +
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
" l=" + str(tech.drc["minlength_channel"]) + "u" +
"\n")
sti_file.write(".ENDS test_buf\n")
sti_file.write("\n")
sti_file.file.close()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,59 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
@unittest.skip("SKIPPING 26_sram_pex_test")
class sram_pex_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.analytical_delay = False
OPTS.use_pex = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import functional
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Functional test for sram with "
"{} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
tempspice = self.run_pex(s)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
(fail, error) = f.run()
self.assertTrue(fail, error)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -8,13 +8,14 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
@unittest.skip("SKIPPING 50_riscv_func_test")
class riscv_func_test(openram_test):
@ -24,6 +25,7 @@ class riscv_func_test(openram_test):
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.local_array_size = 16
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
@ -33,7 +35,7 @@ class riscv_func_test(openram_test):
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import functional, delay
from characterizer import functional
from sram_config import sram_config
c = sram_config(word_size=32,
write_size=8,
@ -53,7 +55,7 @@ class riscv_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
(fail, error) = f.run()
self.assertTrue(fail,error)
self.assertTrue(fail, error)
globals.end_openram()

View File

@ -8,14 +8,15 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
@unittest.skip("SKIPPING 50_riscv_phys_test")
#@unittest.skip("SKIPPING 50_riscv_phys_test")
class riscv_phys_test(openram_test):
def runTest(self):
@ -26,10 +27,11 @@ class riscv_phys_test(openram_test):
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
OPTS.local_array_size = 16
globals.setup_bitcell()
OPTS.route_supplies=False
OPTS.perimeter_pins=False
OPTS.route_supplies = False
OPTS.perimeter_pins = False
c = sram_config(word_size=32,
write_size=8,
num_words=256,

View File

@ -82,6 +82,8 @@ class openram_test(unittest.TestCase):
output = OPTS.openram_temp + a.name + ".pex.netlist"
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
a.gds_write(tempgds)
import verify
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)

View File

@ -295,11 +295,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global num_pex_runs
num_pex_runs += 1
#debug.warning("PEX using magic not implemented.")
#return 1
os.chdir(OPTS.openram_temp)
from tech import drc
if output == None:
output = name + ".pex.netlist"
@ -312,17 +309,11 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
# pex_fix did run the pex using a script while dev orignial method
# use batch mode.
# the dev old code using batch mode does not run and is split into functions
#pex_runset = write_batch_pex_rule(gds_name,name,sp_name,output)
pex_runset = write_script_pex_rule(gds_name,name,output)
pex_runset = write_script_pex_rule(gds_name, name, output)
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name)
# bash mode command from dev branch
#batch_cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe,
# OPTS.openram_temp,
# errfile,
# outfile)
script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
errfile,
outfile)
@ -334,8 +325,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
pex_nelist = open(output, 'r')
s = pex_nelist.read()
pex_nelist.close()
s = s.replace('pfet','p')
s = s.replace('nfet','n')
s = s.replace('pfet', 'p')
s = s.replace('nfet', 'n')
f = open(output, 'w')
f.write(s)
f.close()
@ -345,12 +336,13 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
results = f.readlines()
f.close()
out_errors = find_error(results)
debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.")
debug.check(os.path.isfile(output), "Couldn't find PEX extracted output.")
correct_port(name,output,sp_name)
correct_port(name, output, sp_name)
return out_errors
def write_batch_pex_rule(gds_name,name,sp_name,output):
def write_batch_pex_rule(gds_name, name, sp_name, output):
"""
The dev branch old batch mode runset
2. magic can perform extraction with the following:
@ -394,7 +386,8 @@ def write_batch_pex_rule(gds_name,name,sp_name,output):
f.close()
return file
def write_script_pex_rule(gds_name,cell_name,output):
def write_script_pex_rule(gds_name, cell_name, output):
global OPTS
run_file = OPTS.openram_temp + "run_pex.sh"
f = open(run_file, "w")
@ -412,23 +405,29 @@ def write_script_pex_rule(gds_name,cell_name,output):
pre = "#"
else:
pre = ""
f.write(pre+"extract\n".format(cell_name))
f.write(pre+"ext2spice hierarchy off\n")
f.write(pre+"ext2spice format ngspice\n")
f.write(pre+"ext2spice renumber off\n")
f.write(pre+"ext2spice scale off\n")
f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top on\n")
f.write(pre+"ext2spice global off\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
f.write(pre + "extract\n")
f.write(pre + "ext2sim labels on\n")
f.write(pre + "ext2sim\n")
f.write(pre + "extresist simplify off\n")
f.write(pre + "extresist all\n")
f.write(pre + "ext2spice hierarchy off\n")
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice renumber off\n")
f.write(pre + "ext2spice scale off\n")
f.write(pre + "ext2spice blackbox on\n")
f.write(pre + "ext2spice subcircuit top on\n")
f.write(pre + "ext2spice global off\n")
f.write(pre + "ext2spice extresist on\n")
f.write(pre + "ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("eof\n")
f.write("mv {0}.spice {1}\n".format(cell_name,output))
f.write("mv {0}.spice {1}\n".format(cell_name, output))
f.close()
os.system("chmod u+x {}".format(run_file))
return run_file
def find_error(results):
# Errors begin with "ERROR:"
test = re.compile("ERROR:")
@ -438,6 +437,7 @@ def find_error(results):
out_errors = len(stdouterrors)
return out_errors
def correct_port(name, output_file_name, ref_file_name):
pex_file = open(output_file_name, "r")
contents = pex_file.read()
@ -456,9 +456,9 @@ def correct_port(name, output_file_name, ref_file_name):
for bank in range(OPTS.num_banks):
for bank in range(OPTS.num_banks):
row = int(OPTS.num_words / OPTS.words_per_row) - 1
col = int(OPTS.word_size * OPTS.words_per_row) - 1
bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)
bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)
col = int(OPTS.word_size * OPTS.words_per_row) - 1
bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank, row, col)
bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank, row, col)
for col in range(OPTS.word_size * OPTS.words_per_row):
for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports):
bitcell_list += "bl{0}_{1} ".format(bank, col)
@ -484,13 +484,18 @@ def correct_port(name, output_file_name, ref_file_name):
# write the new pex file with info in the memory
output_file = open(output_file_name, "w")
output_file.write(part1)
output_file.write(circuit_title+'\n')
output_file.write(circuit_title + '\n')
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))