Initial pex sram test.

This commit is contained in:
mrg 2020-10-02 13:32:52 -07:00
parent b32c123dab
commit 1e24b780bb
6 changed files with 128 additions and 134 deletions

View File

@ -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"]
@ -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()
@ -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.
@ -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
@ -641,7 +639,6 @@ class delay(simulation):
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):
@ -681,7 +678,6 @@ class delay(simulation):
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."""
@ -722,7 +718,6 @@ class delay(simulation):
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
@ -815,7 +810,8 @@ class delay(simulation):
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,
@ -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

View File

@ -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,24 +39,18 @@ 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))
@ -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,7 +86,6 @@ 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
@ -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,7 +137,11 @@ 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
@ -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"

View File

@ -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)):

View File

@ -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:

View File

@ -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")