mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'wlbuffer' into dev
This commit is contained in:
commit
da83824a70
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue