mirror of https://github.com/VLSIDA/OpenRAM.git
Initial pex sram test.
This commit is contained in:
parent
b32c123dab
commit
1e24b780bb
|
|
@ -56,8 +56,14 @@ class delay(simulation):
|
||||||
""" Create measurement names. The names themselves currently define the type of measurement """
|
""" 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.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
|
||||||
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power",
|
self.power_meas_names = ["read0_power",
|
||||||
"disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_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.voltage_when_names = ["volt_bl", "volt_br"]
|
||||||
# self.bitline_delay_names = ["delay_bl", "delay_br"]
|
# self.bitline_delay_names = ["delay_bl", "delay_br"]
|
||||||
|
|
||||||
|
|
@ -179,7 +185,7 @@ class delay(simulation):
|
||||||
self.dout_volt_meas[-1].meta_str = meas.meta_str
|
self.dout_volt_meas[-1].meta_str = meas.meta_str
|
||||||
|
|
||||||
if not OPTS.use_pex:
|
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:
|
else:
|
||||||
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)
|
||||||
|
|
||||||
|
|
@ -191,12 +197,12 @@ class delay(simulation):
|
||||||
def create_read_bit_measures(self):
|
def create_read_bit_measures(self):
|
||||||
""" Adds bit measurements for read0 and read1 cycles """
|
""" 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)
|
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
|
||||||
for cycle in meas_cycles:
|
for cycle in meas_cycles:
|
||||||
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
|
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)
|
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
|
meas.meta_str = cycle
|
||||||
self.read_bit_meas[polarity].append(meas)
|
self.read_bit_meas[polarity].append(meas)
|
||||||
# Dictionary values are lists, reduce to a single list of measurements
|
# Dictionary values are lists, reduce to a single list of measurements
|
||||||
|
|
@ -205,12 +211,12 @@ class delay(simulation):
|
||||||
def create_write_bit_measures(self):
|
def create_write_bit_measures(self):
|
||||||
""" Adds bit measurements for write0 and write1 cycles """
|
""" 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)
|
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
|
||||||
for cycle in meas_cycles:
|
for cycle in meas_cycles:
|
||||||
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
|
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)
|
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
|
meas.meta_str = cycle
|
||||||
self.write_bit_meas[polarity].append(meas)
|
self.write_bit_meas[polarity].append(meas)
|
||||||
# Dictionary values are lists, reduce to a single list of measurements
|
# Dictionary values are lists, reduce to a single list of measurements
|
||||||
|
|
@ -279,12 +285,8 @@ class delay(simulation):
|
||||||
|
|
||||||
# instantiate the sram
|
# instantiate the sram
|
||||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||||
if not OPTS.use_pex:
|
|
||||||
self.stim.inst_model(pins=self.pins,
|
self.stim.inst_model(pins=self.pins,
|
||||||
model_name=self.sram.name)
|
model_name=self.sram.name)
|
||||||
else:
|
|
||||||
self.stim.inst_sram_pex(pins=self.pins,
|
|
||||||
model_name=self.sram.name)
|
|
||||||
|
|
||||||
self.sf.write("\n* SRAM output loads\n")
|
self.sf.write("\n* SRAM output loads\n")
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
|
|
@ -320,7 +322,6 @@ class delay(simulation):
|
||||||
self.gen_data()
|
self.gen_data()
|
||||||
self.gen_addr()
|
self.gen_addr()
|
||||||
|
|
||||||
|
|
||||||
# generate control signals
|
# generate control signals
|
||||||
self.sf.write("\n* Generation of control signals\n")
|
self.sf.write("\n* Generation of control signals\n")
|
||||||
self.gen_control()
|
self.gen_control()
|
||||||
|
|
@ -433,7 +434,7 @@ class delay(simulation):
|
||||||
|
|
||||||
# These measurements have there time further delayed to the neg. edge of the clock.
|
# These measurements have there time further delayed to the neg. edge of the clock.
|
||||||
if delay_obj.meta_add_delay:
|
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)
|
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
|
# 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_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)
|
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
|
# 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
|
# "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)
|
return (at_time, port)
|
||||||
|
|
||||||
|
|
@ -465,7 +466,7 @@ class delay(simulation):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Only checking 0 value reads for now.
|
# 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)
|
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_variant_inp_tuple = self.get_measure_variants(port, measure, "read")
|
||||||
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
||||||
|
|
||||||
|
|
||||||
def write_delay_measures_write_port(self, port):
|
def write_delay_measures_write_port(self, port):
|
||||||
"""
|
"""
|
||||||
Write the measure statements to quantify the power results for a write 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.sf.write("* Write ports {}\n".format(write_port))
|
||||||
self.write_delay_measures_write_port(write_port)
|
self.write_delay_measures_write_port(write_port)
|
||||||
|
|
||||||
|
|
||||||
def write_power_measures(self):
|
def write_power_measures(self):
|
||||||
"""
|
"""
|
||||||
Write the measure statements to quantify the leakage power only.
|
Write the measure statements to quantify the leakage power only.
|
||||||
|
|
@ -523,7 +522,7 @@ class delay(simulation):
|
||||||
|
|
||||||
# add measure statements for power
|
# add measure statements for power
|
||||||
t_initial = self.period
|
t_initial = self.period
|
||||||
t_final = 2*self.period
|
t_final = 2 * self.period
|
||||||
self.stim.gen_meas_power(meas_name="leakage_power",
|
self.stim.gen_meas_power(meas_name="leakage_power",
|
||||||
t_initial=t_initial,
|
t_initial=t_initial,
|
||||||
t_final=t_final)
|
t_final=t_final)
|
||||||
|
|
@ -543,7 +542,7 @@ class delay(simulation):
|
||||||
while True:
|
while True:
|
||||||
time_out -= 1
|
time_out -= 1
|
||||||
if (time_out <= 0):
|
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
|
# Write ports are assumed non-critical to timing, so the first available is used
|
||||||
self.targ_write_ports = [self.write_ports[0]]
|
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])
|
feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0])
|
||||||
previous_period = self.period
|
previous_period = self.period
|
||||||
|
|
||||||
|
|
||||||
# Loops through all the ports checks if the feasible period works. Everything restarts it if does not.
|
# 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.
|
# Write ports do not produce delays which is why they are not included here.
|
||||||
i = 1
|
i = 1
|
||||||
|
|
@ -630,7 +628,7 @@ class delay(simulation):
|
||||||
|
|
||||||
for port in self.targ_write_ports:
|
for port in self.targ_write_ports:
|
||||||
if not self.check_bit_measures(self.write_bit_meas, port):
|
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 = {}
|
write_port_dict = {}
|
||||||
|
|
@ -638,22 +636,21 @@ class delay(simulation):
|
||||||
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
||||||
|
|
||||||
if not check_dict_values_is_float(write_port_dict):
|
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)
|
result[port].update(write_port_dict)
|
||||||
|
|
||||||
|
|
||||||
for port in self.targ_read_ports:
|
for port in self.targ_read_ports:
|
||||||
# First, check that the memory has the right values at the right times
|
# First, check that the memory has the right values at the right times
|
||||||
if not self.check_bit_measures(self.read_bit_meas, port):
|
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))
|
debug.info(2, "Checking read delay values for port {}".format(port))
|
||||||
# Check sen timing, then bitlines, then general measurements.
|
# Check sen timing, then bitlines, then general measurements.
|
||||||
if not self.check_sen_measure(port):
|
if not self.check_sen_measure(port):
|
||||||
return (False,{})
|
return (False, {})
|
||||||
|
|
||||||
if not self.check_read_debug_measures(port):
|
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
|
# Check timing for read ports. Power is only checked if it was read correctly
|
||||||
read_port_dict = {}
|
read_port_dict = {}
|
||||||
|
|
@ -661,27 +658,26 @@ class delay(simulation):
|
||||||
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
||||||
|
|
||||||
if not self.check_valid_delays(read_port_dict):
|
if not self.check_valid_delays(read_port_dict):
|
||||||
return (False,{})
|
return (False, {})
|
||||||
|
|
||||||
if not check_dict_values_is_float(read_port_dict):
|
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)
|
result[port].update(read_port_dict)
|
||||||
|
|
||||||
return (True,result)
|
return (True, result)
|
||||||
|
|
||||||
def check_sen_measure(self, port):
|
def check_sen_measure(self, port):
|
||||||
"""Checks that the sen occurred within a half-period"""
|
"""Checks that the sen occurred within a half-period"""
|
||||||
|
|
||||||
sen_val = self.sen_meas.retrieve_measure(port=port)
|
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:
|
if self.sen_meas.meta_add_delay:
|
||||||
max_delay = self.period/2
|
max_delay = self.period / 2
|
||||||
else:
|
else:
|
||||||
max_delay = self.period
|
max_delay = self.period
|
||||||
return not (type(sen_val) != float or sen_val > max_delay)
|
return not (type(sen_val) != float or sen_val > max_delay)
|
||||||
|
|
||||||
|
|
||||||
def check_read_debug_measures(self, port):
|
def check_read_debug_measures(self, port):
|
||||||
"""Debug measures that indicate special conditions."""
|
"""Debug measures that indicate special conditions."""
|
||||||
|
|
||||||
|
|
@ -697,20 +693,20 @@ class delay(simulation):
|
||||||
elif self.br_name == meas.targ_name_no_port:
|
elif self.br_name == meas.targ_name_no_port:
|
||||||
br_vals[meas.meta_str] = val
|
br_vals[meas.meta_str] = val
|
||||||
|
|
||||||
debug.info(2,"{}={}".format(meas.name,val))
|
debug.info(2, "{}={}".format(meas.name, val))
|
||||||
|
|
||||||
dout_success = True
|
dout_success = True
|
||||||
bl_success = False
|
bl_success = False
|
||||||
for meas in self.dout_volt_meas:
|
for meas in self.dout_volt_meas:
|
||||||
val = meas.retrieve_measure(port=port)
|
val = meas.retrieve_measure(port=port)
|
||||||
debug.info(2,"{}={}".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))
|
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
|
dout_success = False
|
||||||
debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val))
|
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])
|
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
|
dout_success = False
|
||||||
debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val))
|
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])
|
bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE])
|
||||||
|
|
@ -718,11 +714,10 @@ class delay(simulation):
|
||||||
# If the bitlines have a correct value while the output does not then that is a
|
# 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.
|
# sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
|
||||||
if not dout_success and bl_success:
|
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
|
return dout_success
|
||||||
|
|
||||||
|
|
||||||
def check_bit_measures(self, bit_measures, port):
|
def check_bit_measures(self, bit_measures, port):
|
||||||
"""
|
"""
|
||||||
Checks the measurements which represent the internal storage voltages
|
Checks the measurements which represent the internal storage voltages
|
||||||
|
|
@ -732,27 +727,27 @@ class delay(simulation):
|
||||||
for polarity, meas_list in bit_measures.items():
|
for polarity, meas_list in bit_measures.items():
|
||||||
for meas in meas_list:
|
for meas in meas_list:
|
||||||
val = meas.retrieve_measure(port=port)
|
val = meas.retrieve_measure(port=port)
|
||||||
debug.info(2,"{}={}".format(meas.name, val))
|
debug.info(2, "{}={}".format(meas.name, val))
|
||||||
if type(val) != float:
|
if type(val) != float:
|
||||||
continue
|
continue
|
||||||
meas_cycle = meas.meta_str
|
meas_cycle = meas.meta_str
|
||||||
# Loose error conditions. Assume it's not metastable but account for noise during reads.
|
# 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\
|
if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\
|
||||||
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING):
|
(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\
|
elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\
|
||||||
(meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING):
|
(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\
|
elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\
|
||||||
(meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING):
|
(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\
|
elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\
|
||||||
(meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING):
|
(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:
|
if not success:
|
||||||
debug.info(1,("Wrong value detected on probe bit during read/write cycle. "
|
debug.info(1, ("Wrong value detected on probe bit during read/write cycle. "
|
||||||
"Check writes and control logic for bugs.\n measure={}, op={}, "
|
"Check writes and control logic for bugs.\n measure={}, op={}, "
|
||||||
"bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name,val))
|
"bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name, val))
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
@ -764,10 +759,10 @@ class delay(simulation):
|
||||||
# The inputs looks at discharge/charged bitline rather than left or right (bl/br)
|
# 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
|
# 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.
|
# 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_dicharge = v_discharged_bl < self.vdd_voltage * 0.9
|
||||||
min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage*0.1
|
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))
|
debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff))
|
||||||
return (min_dicharge and min_diff)
|
return (min_dicharge and min_diff)
|
||||||
|
|
||||||
def run_power_simulation(self):
|
def run_power_simulation(self):
|
||||||
|
|
@ -779,20 +774,20 @@ class delay(simulation):
|
||||||
self.write_power_stimulus(trim=False)
|
self.write_power_stimulus(trim=False)
|
||||||
self.stim.run_sim()
|
self.stim.run_sim()
|
||||||
leakage_power=parse_spice_list("timing", "leakage_power")
|
leakage_power=parse_spice_list("timing", "leakage_power")
|
||||||
debug.check(leakage_power!="Failed","Could not measure 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.info(1, "Leakage power of full array is {0} mW".format(leakage_power * 1e3))
|
||||||
# debug
|
# debug
|
||||||
# sys.exit(1)
|
# sys.exit(1)
|
||||||
|
|
||||||
self.write_power_stimulus(trim=True)
|
self.write_power_stimulus(trim=True)
|
||||||
self.stim.run_sim()
|
self.stim.run_sim()
|
||||||
trim_leakage_power=parse_spice_list("timing", "leakage_power")
|
trim_leakage_power=parse_spice_list("timing", "leakage_power")
|
||||||
debug.check(trim_leakage_power!="Failed","Could not measure 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.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power * 1e3))
|
||||||
|
|
||||||
# For debug, you sometimes want to inspect each simulation.
|
# For debug, you sometimes want to inspect each simulation.
|
||||||
# key=raw_input("press return to continue")
|
# 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):
|
def check_valid_delays(self, result_dict):
|
||||||
""" Check if the measurements are defined and if they are valid. """
|
""" Check if the measurements are defined and if they are valid. """
|
||||||
|
|
@ -802,28 +797,29 @@ class delay(simulation):
|
||||||
delay_lh = result_dict["delay_lh"]
|
delay_lh = result_dict["delay_lh"]
|
||||||
slew_hl = result_dict["slew_hl"]
|
slew_hl = result_dict["slew_hl"]
|
||||||
slew_lh = result_dict["slew_lh"]
|
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 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:
|
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)
|
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)
|
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,
|
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,
|
delays_str,
|
||||||
slews_str))
|
slews_str))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
|
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)
|
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
|
# 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 \
|
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:
|
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,
|
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,
|
delays_str,
|
||||||
slews_str))
|
slews_str))
|
||||||
return False
|
return False
|
||||||
else:
|
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,
|
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,
|
delays_str,
|
||||||
slews_str))
|
slews_str))
|
||||||
|
|
||||||
|
|
@ -870,7 +866,7 @@ class delay(simulation):
|
||||||
while True:
|
while True:
|
||||||
time_out -= 1
|
time_out -= 1
|
||||||
if (time_out <= 0):
|
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
|
self.period = target_period
|
||||||
debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(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_ones = "1" * self.word_size
|
||||||
data_zeros = "0" * self.word_size
|
data_zeros = "0" * self.word_size
|
||||||
wmask_ones = "1" * self.num_wmasks
|
wmask_ones = "1" * self.num_wmasks
|
||||||
wmask_zeroes = "0" * self.num_wmasks
|
|
||||||
|
|
||||||
if self.t_current == 0:
|
if self.t_current == 0:
|
||||||
self.add_noop_all_ports("Idle cycle (no positive clock edge)")
|
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.add_noop_clock_one_port(read_port)
|
||||||
self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1
|
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
|
# 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),
|
self.add_read("R data 0 address {} to clear dout caps".format(inverse_address),
|
||||||
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.
|
# 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_read_port = self.get_available_port(get_read_port=True)
|
||||||
cur_write_port = self.get_available_port(get_read_port=False)
|
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_read_port != None,
|
||||||
debug.check(cur_write_port != None, "Characterizer requires at least 1 write port")
|
"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.
|
# Create test cycles for specified target ports.
|
||||||
write_pos = 0
|
write_pos = 0
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import tech
|
||||||
import debug
|
import debug
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
@ -40,32 +39,26 @@ class stimuli():
|
||||||
debug.info(2, "Not using spice library")
|
debug.info(2, "Not using spice library")
|
||||||
self.device_models = tech.spice["fet_models"][self.process]
|
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):
|
def inst_model(self, pins, model_name):
|
||||||
""" Function to instantiate a generic model with a set of pins """
|
""" Function to instantiate a generic model with a set of pins """
|
||||||
|
|
||||||
|
if OPTS.use_pex:
|
||||||
|
self.inst_pex_model(pins, model_name)
|
||||||
|
else:
|
||||||
self.sf.write("X{0} ".format(model_name))
|
self.sf.write("X{0} ".format(model_name))
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
self.sf.write("{0} ".format(pin))
|
self.sf.write("{0} ".format(pin))
|
||||||
self.sf.write("{0}\n".format(model_name))
|
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))
|
self.sf.write("X{0} ".format(model_name))
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
self.sf.write("{0} ".format(pin))
|
self.sf.write("{0} ".format(pin))
|
||||||
for bank in range(OPTS.num_banks):
|
for bank in range(OPTS.num_banks):
|
||||||
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
row = int(OPTS.num_words / OPTS.words_per_row) - 1
|
||||||
col = int(OPTS.word_size * 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_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))
|
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
|
# 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 row in range(int(OPTS.num_words / OPTS.words_per_row)):
|
||||||
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
|
# for col in range(int(OPTS.word_size * OPTS.words_per_row)):
|
||||||
|
|
@ -77,7 +70,6 @@ class stimuli():
|
||||||
self.sf.write("bl{0}_{1} ".format(port, col))
|
self.sf.write("bl{0}_{1} ".format(port, col))
|
||||||
self.sf.write("br{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("s_en{0} ".format(bank))
|
||||||
self.sf.write("{0}\n".format(model_name))
|
self.sf.write("{0}\n".format(model_name))
|
||||||
|
|
||||||
|
|
@ -94,8 +86,7 @@ class stimuli():
|
||||||
self.tx_length))
|
self.tx_length))
|
||||||
self.sf.write(".ENDS test_inv\n")
|
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
|
Generates buffer for top level signals (only for sim
|
||||||
purposes). Size is pair for PMOS, NMOS width multiple.
|
purposes). Size is pair for PMOS, NMOS width multiple.
|
||||||
|
|
@ -122,8 +113,6 @@ class stimuli():
|
||||||
self.tx_length))
|
self.tx_length))
|
||||||
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
|
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):
|
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
|
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,
|
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||||
period))
|
period))
|
||||||
|
|
||||||
|
|
||||||
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
|
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.
|
Generate a PWL stimulus given a signal name and data values at each period.
|
||||||
|
|
@ -149,18 +137,22 @@ class stimuli():
|
||||||
to the initial value.
|
to the initial value.
|
||||||
"""
|
"""
|
||||||
# the initial value is not a clock time
|
# 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
|
# 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
|
values = np.array(data_values) * self.voltage
|
||||||
half_slew = 0.5 * slew
|
half_slew = 0.5 * slew
|
||||||
self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values))))
|
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]))
|
self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
|
||||||
for i in range(1,len(times)):
|
for i in range(1, len(times)):
|
||||||
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
|
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i] - half_slew,
|
||||||
values[i-1],
|
values[i - 1],
|
||||||
times[i]+half_slew,
|
times[i] + half_slew,
|
||||||
values[i]))
|
values[i]))
|
||||||
self.sf.write(")\n")
|
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))
|
self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
|
||||||
|
|
||||||
def get_inverse_voltage(self, value):
|
def get_inverse_voltage(self, value):
|
||||||
if value > 0.5*self.voltage:
|
if value > 0.5 * self.voltage:
|
||||||
return 0
|
return 0
|
||||||
elif value <= 0.5*self.voltage:
|
elif value <= 0.5 * self.voltage:
|
||||||
return self.voltage
|
return self.voltage
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||||
|
|
@ -184,7 +176,6 @@ class stimuli():
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
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):
|
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 """
|
""" 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"
|
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"
|
||||||
|
|
@ -246,7 +237,7 @@ class stimuli():
|
||||||
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
|
# 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))
|
self.sf.write(".TEMP {}\n".format(self.temperature))
|
||||||
if OPTS.spice_name == "ngspice":
|
if OPTS.spice_name == "ngspice":
|
||||||
# ngspice sometimes has convergence problems if not using gear method
|
# ngspice sometimes has convergence problems if not using gear method
|
||||||
|
|
@ -260,7 +251,7 @@ class stimuli():
|
||||||
# create plots for all signals
|
# create plots for all signals
|
||||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||||
if OPTS.debug_level>0:
|
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")
|
self.sf.write(".probe V(*)\n")
|
||||||
else:
|
else:
|
||||||
self.sf.write(".plot V(*)\n")
|
self.sf.write(".plot V(*)\n")
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class sram_base(design, verilog, lef):
|
||||||
# add pex labels for bitcells
|
# add pex labels for bitcells
|
||||||
for bank_num in range(len(self.bank_insts)):
|
for bank_num in range(len(self.bank_insts)):
|
||||||
bank = self.bank_insts[bank_num]
|
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
|
bank_offset = pex_data[0] # offset bank relative to sram
|
||||||
Q_offset = pex_data[1] # offset of storage relative to bank
|
Q_offset = pex_data[1] # offset of storage relative to bank
|
||||||
|
|
@ -107,7 +107,7 @@ class sram_base(design, verilog, lef):
|
||||||
bl = []
|
bl = []
|
||||||
br = []
|
br = []
|
||||||
|
|
||||||
storage_layer_name = self.bitcell.get_pin("Q").layer
|
storage_layer_name = "m1"
|
||||||
bitline_layer_name = self.bitcell.get_pin("bl").layer
|
bitline_layer_name = self.bitcell.get_pin("bl").layer
|
||||||
|
|
||||||
for cell in range(len(bank_offset)):
|
for cell in range(len(bank_offset)):
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ class openram_test(unittest.TestCase):
|
||||||
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
|
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
|
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
|
||||||
|
|
||||||
|
a.gds_write(tempgds)
|
||||||
|
|
||||||
import verify
|
import verify
|
||||||
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)
|
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,10 @@ def write_script_pex_rule(gds_name, cell_name, output):
|
||||||
else:
|
else:
|
||||||
pre = ""
|
pre = ""
|
||||||
f.write(pre + "extract\n")
|
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 hierarchy off\n")
|
||||||
f.write(pre + "ext2spice format ngspice\n")
|
f.write(pre + "ext2spice format ngspice\n")
|
||||||
f.write(pre + "ext2spice renumber off\n")
|
f.write(pre + "ext2spice renumber off\n")
|
||||||
|
|
@ -413,6 +417,7 @@ def write_script_pex_rule(gds_name, cell_name, output):
|
||||||
f.write(pre + "ext2spice blackbox on\n")
|
f.write(pre + "ext2spice blackbox on\n")
|
||||||
f.write(pre + "ext2spice subcircuit top on\n")
|
f.write(pre + "ext2spice subcircuit top on\n")
|
||||||
f.write(pre + "ext2spice global off\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(pre + "ext2spice {}\n".format(cell_name))
|
||||||
f.write("quit -noprompt\n")
|
f.write("quit -noprompt\n")
|
||||||
f.write("eof\n")
|
f.write("eof\n")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue