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): def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
""" Adds an instance of a mod to this module """ """ 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)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1])) debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons # This is commented out for runtime reasons
@ -1029,7 +1035,7 @@ class layout():
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) 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([]) self.connect_inst([])
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
@ -1038,7 +1044,7 @@ class layout():
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) 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([]) self.connect_inst([])
def add_boundary(self, ll=vector(0, 0), ur=None): 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 """ """ 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

@ -229,6 +229,8 @@ class functional(simulation):
sp_read_value = "" sp_read_value = ""
for bit in range(self.word_size + self.num_spare_cols): 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)) value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check))
try:
value = float(value)
if value > self.v_high: if value > self.v_high:
sp_read_value = "1" + sp_read_value sp_read_value = "1" + sp_read_value
elif value < self.v_low: elif value < self.v_low:
@ -240,6 +242,12 @@ class functional(simulation):
eo_period, eo_period,
self.v_low, self.v_low,
self.v_high) 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) return (0, error)
self.read_results.append([sp_read_value, dout_port, eo_period, check]) self.read_results.append([sp_read_value, dout_port, eo_period, check])
@ -348,8 +356,8 @@ class functional(simulation):
# Write important signals to stim file # Write important signals to stim file
self.sf.write("\n\n* Important signals for debug\n") self.sf.write("\n\n* Important signals for debug\n")
self.sf.write("* bl: {}\n".format(self.bl_name)) self.sf.write("* bl: {}\n".format(self.bl_name.format(port)))
self.sf.write("* br: {}\n".format(self.br_name)) self.sf.write("* br: {}\n".format(self.br_name.format(port)))
self.sf.write("* s_en: {}\n".format(self.sen_name)) self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name)) self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name)) self.sf.write("* qbar: {}\n".format(self.qbar_name))

View File

@ -66,8 +66,6 @@ class simulation():
"Number of pins generated for characterization \ "Number of pins generated for characterization \
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins)) self.pins))
#This is TODO once multiport control has been finalized.
#self.control_name = "CSB"
def set_stimulus_variables(self): def set_stimulus_variables(self):
# Clock signals # Clock signals
@ -406,7 +404,9 @@ class simulation():
return pin_names return pin_names
def add_graph_exclusions(self): 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 # other initializations can only be done during analysis when a bit has been selected
# for testing. # for testing.
@ -417,7 +417,9 @@ class simulation():
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): 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] port = self.read_ports[0]
if not OPTS.use_pex: if not OPTS.use_pex:
@ -459,7 +461,6 @@ class simulation():
self.sen_name = self.get_sen_name(self.graph.all_paths) self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(2, "s_en name = {}".format(self.sen_name)) debug.info(2, "s_en name = {}".format(self.sen_name))
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1) self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
self.br_name = "br{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))
@ -481,7 +482,9 @@ class simulation():
return sen_name return sen_name
def create_graph(self): 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.clear_exclude_bits() # Removes previous bit exclusions
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column) 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) self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
def get_bl_name_search_exclusions(self): 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 # Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward # so it makes the search awkward
@ -519,7 +524,9 @@ class simulation():
return path_net_name return path_net_name
def get_bl_name(self, paths, port): 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_mod = factory.create(module_type=OPTS.bitcell)
cell_bl = cell_mod.get_bl_name(port) cell_bl = cell_mod.get_bl_name(port)

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"
@ -271,7 +262,6 @@ class stimuli():
# end the stimulus file # end the stimulus file
self.sf.write(".end\n\n") self.sf.write(".end\n\n")
def write_include(self, circuit): def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files""" """Writes include statements, inputs are lists of model files"""
@ -291,7 +281,6 @@ class stimuli():
else: else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(self): def write_supply(self):
""" Writes supply voltage statements """ """ Writes supply voltage statements """
gnd_node_name = "0" gnd_node_name = "0"
@ -314,13 +303,15 @@ class stimuli():
xa_cfg.write("set_sim_level -level 7\n") xa_cfg.write("set_sim_level -level 7\n")
xa_cfg.write("set_powernet_level 7 -node vdd\n") xa_cfg.write("set_powernet_level 7 -node vdd\n")
xa_cfg.close() xa_cfg.close()
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe, cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe,
temp_stim, temp_stim,
OPTS.openram_temp) OPTS.openram_temp,
OPTS.num_threads)
valid_retcode=0 valid_retcode=0
elif OPTS.spice_name == "hspice": elif OPTS.spice_name == "hspice":
# TODO: Should make multithreading parameter a configuration option # TODO: Should make multithreading parameter a configuration option
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe, cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe,
OPTS.num_threads,
temp_stim, temp_stim,
OPTS.openram_temp) OPTS.openram_temp)
valid_retcode=0 valid_retcode=0
@ -328,13 +319,16 @@ class stimuli():
# ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit # 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 # Measurements can't be made with a raw file set in ngspice
# -r {2}timing.raw # -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, cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
temp_stim, temp_stim,
OPTS.openram_temp) OPTS.openram_temp)
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings # for some reason, ngspice-25 returns 1 when it only has acceptable warnings
valid_retcode=1 valid_retcode=1
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')

View File

@ -124,7 +124,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.wl_insts = [] self.wl_insts = []
self.driver_wordline_outputs = [] self.driver_wordline_outputs = []
for port in self.all_ports: 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)) mod=self.wl_array))
temp = [] temp = []
temp += [self.get_rbl_wordline_names(port)[port]] 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. # For sky130, we need magic for filtering.
magic_exe = None magic_exe = None
# Number of threads to use
num_threads = 2
# Should we print out the banner at startup # Should we print out the banner at startup
print_banner = True print_banner = True

View File

@ -414,7 +414,7 @@ class sram_1bank(sram_base):
layer_stack=self.m1_stack, layer_stack=self.m1_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.col_addr_bus_size[port] = cr.height self.col_addr_bus_size[port] = cr.height
@ -470,7 +470,7 @@ class sram_1bank(sram_base):
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap 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, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
self.add_inst("hc", cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap 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 verilog import verilog
from lef import lef from lef import lef
from sram_factory import factory from sram_factory import factory
from tech import drc
import numpy as np
import logical_effort
class sram_base(design, verilog, lef): class sram_base(design, verilog, lef):
@ -43,9 +40,6 @@ class sram_base(design, verilog, lef):
if not self.num_spare_cols: if not self.num_spare_cols:
self.num_spare_cols = 0 self.num_spare_cols = 0
# For logical effort delay calculations.
self.all_mods_except_control_done = False
def add_pins(self): def add_pins(self):
""" Add pins for entire SRAM. """ """ Add pins for entire SRAM. """
@ -95,10 +89,12 @@ class sram_base(design, verilog, lef):
Add pex labels at the sram level for spice analysis Add pex labels at the sram level for spice analysis
""" """
# 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
@ -112,32 +108,48 @@ class sram_base(design, verilog, lef):
br = [] br = []
storage_layer_name = "m1" storage_layer_name = "m1"
bitline_layer_name = "m2" bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)): 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 = [bank_offset[cell][0] + Q_offset[cell][0],
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]] 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 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) row = int(cell % (OPTS.num_words / self.words_per_row))
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) 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)): for cell in range(len(bl_offsets)):
col = bl_meta[cell][0][2] col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])): 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]) bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)): for cell in range(len(br_offsets)):
col = br_meta[cell][0][2] col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])): 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]] 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]) br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)): 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)): 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 # add pex labels for control logic
for i in range(len(self.control_logic_insts)): for i in range(len(self.control_logic_insts)):
@ -146,12 +158,11 @@ class sram_base(design, verilog, lef):
for output in instance.mod.output_list: for output in instance.mod.output_list:
pin = instance.mod.get_pin(output) pin = instance.mod.get_pin(output)
pin.transform([0, 0], instance.mirror, instance.rotate) pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]] offset = [control_logic_offset[0] + pin.center()[0],
self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset) 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): def create_netlist(self):
""" Netlist creation """ """ Netlist creation """
@ -370,10 +381,6 @@ class sram_base(design, verilog, lef):
self.bank_count = 0 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)) c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, 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("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("**************************************************\n") sp.write("**************************************************\n")
# This causes unit test mismatch # This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser())) # sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"], # sp.write(".global {0} {1}\n".format(spice["vdd_name"],

View File

@ -35,7 +35,8 @@ class hspice_pex_pinv_test(openram_test):
# generate the pinv # generate the pinv
prev_purge_value = OPTS.purge_temp 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") debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1) 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)
@ -43,10 +44,10 @@ class hspice_pex_pinv_test(openram_test):
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name) tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp) tx.sp_write(tempsp)
# make sure that the library simulation is successful\ # make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module=tempsp, sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name=tx.name) top_level_name=tx.name)
if sp_delay is "Failed": if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected') self.fail('Library Spice module did not behave as expected')
# now generate its pex file # now generate its pex file
@ -56,7 +57,7 @@ class hspice_pex_pinv_test(openram_test):
pex_delay = self.simulate_delay(test_module=pex_file, pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name=tx.name) top_level_name=tx.name)
# make sure the extracted spice simulated # make sure the extracted spice simulated
if pex_delay is "Failed": if pex_delay == "Failed":
self.fail('Pex file did not behave as expected') self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok # if pex data is bigger than original spice file then result is ok
@ -71,7 +72,6 @@ class hspice_pex_pinv_test(openram_test):
globals.end_openram() globals.end_openram()
def simulate_delay(self, test_module, top_level_name): def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list from charutils import parse_spice_list
# setup simulation # setup simulation
sim_file = OPTS.openram_temp + "stim.sp" sim_file = OPTS.openram_temp + "stim.sp"

View File

@ -45,17 +45,18 @@ class ngspice_pex_pinv_test(openram_test):
# make sure that the library simulation is successful # make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module=tempsp, sp_delay = self.simulate_delay(test_module=tempsp,
top_level_name=tx.name) top_level_name=tx.name)
if sp_delay is "Failed": if sp_delay == "Failed":
self.fail('Library Spice module did not behave as expected') self.fail('Library Spice module did not behave as expected')
# now generate its pex file # now generate its pex file
pex_file = self.run_pex(tx) 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 # generate simulation for pex, make sure the simulation is successful
pex_delay = self.simulate_delay(test_module=pex_file, pex_delay = self.simulate_delay(test_module=pex_file,
top_level_name=tx.name) top_level_name=tx.name)
# make sure the extracted spice simulated # make sure the extracted spice simulated
if pex_delay is "Failed": if pex_delay == "Failed":
self.fail('Pex file did not behave as expected') self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok # if pex data is bigger than original spice file then result is ok
@ -70,7 +71,6 @@ class ngspice_pex_pinv_test(openram_test):
globals.end_openram() globals.end_openram()
def simulate_delay(self, test_module, top_level_name): def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list from charutils import parse_spice_list
# setup simulation # setup simulation
sim_file = OPTS.openram_temp + "stim.sp" sim_file = OPTS.openram_temp + "stim.sp"
@ -100,7 +100,6 @@ class ngspice_pex_pinv_test(openram_test):
# simulation.gen_constant(sig_name = "gnd", # simulation.gen_constant(sig_name = "gnd",
# v_val = "0v") # v_val = "0v")
run_time = tech.spice["feasible_period"] * 4 run_time = tech.spice["feasible_period"] * 4
# input voltage # input voltage
clk_period = tech.spice["feasible_period"] clk_period = tech.spice["feasible_period"]

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

@ -15,6 +15,7 @@ from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
@unittest.skip("SKIPPING 50_riscv_func_test") @unittest.skip("SKIPPING 50_riscv_func_test")
class riscv_func_test(openram_test): class riscv_func_test(openram_test):
@ -24,6 +25,7 @@ class riscv_func_test(openram_test):
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.netlist_only = True OPTS.netlist_only = True
OPTS.trim_netlist = False OPTS.trim_netlist = False
OPTS.local_array_size = 16
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
@ -33,7 +35,7 @@ class riscv_func_test(openram_test):
from importlib import reload from importlib import reload
import characterizer import characterizer
reload(characterizer) reload(characterizer)
from characterizer import functional, delay from characterizer import functional
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=32, c = sram_config(word_size=32,
write_size=8, write_size=8,

View File

@ -15,7 +15,8 @@ from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug import debug
@unittest.skip("SKIPPING 50_riscv_phys_test")
#@unittest.skip("SKIPPING 50_riscv_phys_test")
class riscv_phys_test(openram_test): class riscv_phys_test(openram_test):
def runTest(self): def runTest(self):
@ -26,6 +27,7 @@ class riscv_phys_test(openram_test):
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
OPTS.local_array_size = 16
globals.setup_bitcell() globals.setup_bitcell()
OPTS.route_supplies = False OPTS.route_supplies = False
OPTS.perimeter_pins = False OPTS.perimeter_pins = False

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

@ -295,11 +295,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
#debug.warning("PEX using magic not implemented.")
#return 1
os.chdir(OPTS.openram_temp) os.chdir(OPTS.openram_temp)
from tech import drc
if output == None: if output == None:
output = name + ".pex.netlist" 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 # pex_fix did run the pex using a script while dev orignial method
# use batch mode. # use batch mode.
# the dev old code using batch mode does not run and is split into functions # 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) errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
outfile = "{0}{1}.pex.out".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, script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
errfile, errfile,
outfile) outfile)
@ -350,6 +341,7 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
correct_port(name, output, sp_name) correct_port(name, output, sp_name)
return out_errors 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 The dev branch old batch mode runset
@ -394,6 +386,7 @@ def write_batch_pex_rule(gds_name,name,sp_name,output):
f.close() f.close()
return file return file
def write_script_pex_rule(gds_name, cell_name, output): def write_script_pex_rule(gds_name, cell_name, output):
global OPTS global OPTS
run_file = OPTS.openram_temp + "run_pex.sh" run_file = OPTS.openram_temp + "run_pex.sh"
@ -412,7 +405,11 @@ def write_script_pex_rule(gds_name,cell_name,output):
pre = "#" pre = "#"
else: else:
pre = "" pre = ""
f.write(pre+"extract\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 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")
@ -420,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")
@ -429,6 +427,7 @@ def write_script_pex_rule(gds_name,cell_name,output):
os.system("chmod u+x {}".format(run_file)) os.system("chmod u+x {}".format(run_file))
return run_file return run_file
def find_error(results): def find_error(results):
# Errors begin with "ERROR:" # Errors begin with "ERROR:"
test = re.compile("ERROR:") test = re.compile("ERROR:")
@ -438,6 +437,7 @@ def find_error(results):
out_errors = len(stdouterrors) out_errors = len(stdouterrors)
return out_errors return out_errors
def correct_port(name, output_file_name, ref_file_name): def correct_port(name, output_file_name, ref_file_name):
pex_file = open(output_file_name, "r") pex_file = open(output_file_name, "r")
contents = pex_file.read() contents = pex_file.read()
@ -488,9 +488,14 @@ def correct_port(name, output_file_name, ref_file_name):
output_file.write(part2) output_file.write(part2)
output_file.close() output_file.close()
def print_drc_stats(): 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(): 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(): def print_pex_stats():
debug.info(1, "PEX runs: {0}".format(num_pex_runs)) debug.info(1, "PEX runs: {0}".format(num_pex_runs))