mirror of https://github.com/VLSIDA/OpenRAM.git
Made delay.py a child of simulation.py. Removed duplicate code in delay and changed some in simulation
This commit is contained in:
parent
a3bec5518c
commit
3ac2d29940
|
|
@ -7,8 +7,9 @@ from .trim_spice import *
|
||||||
from .charutils import *
|
from .charutils import *
|
||||||
import utils
|
import utils
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from .simulation import simulation
|
||||||
|
|
||||||
class delay():
|
class delay(simulation):
|
||||||
"""Functions to measure the delay and power of an SRAM at a given address and
|
"""Functions to measure the delay and power of an SRAM at a given address and
|
||||||
data bit.
|
data bit.
|
||||||
|
|
||||||
|
|
@ -26,27 +27,14 @@ class delay():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner):
|
def __init__(self, sram, spfile, corner):
|
||||||
self.sram = sram
|
simulation.__init__(self, sram, spfile, corner)
|
||||||
self.name = sram.name
|
|
||||||
self.word_size = self.sram.word_size
|
|
||||||
self.addr_size = self.sram.addr_size
|
|
||||||
self.num_cols = self.sram.num_cols
|
|
||||||
self.num_rows = self.sram.num_rows
|
|
||||||
self.num_banks = self.sram.num_banks
|
|
||||||
self.sp_file = spfile
|
|
||||||
|
|
||||||
self.total_ports = self.sram.total_ports
|
|
||||||
self.total_write = self.sram.total_write
|
|
||||||
self.total_read = self.sram.total_read
|
|
||||||
self.read_index = self.sram.read_index
|
|
||||||
self.write_index = self.sram.write_index
|
|
||||||
self.port_id = self.sram.port_id
|
|
||||||
|
|
||||||
# These are the member variables for a simulation
|
# These are the member variables for a simulation
|
||||||
|
self.targ_read_ports = []
|
||||||
|
self.targ_write_ports = []
|
||||||
self.period = 0
|
self.period = 0
|
||||||
self.set_load_slew(0,0)
|
self.set_load_slew(0,0)
|
||||||
self.set_corner(corner)
|
self.set_corner(corner)
|
||||||
self.create_port_names()
|
|
||||||
self.create_signal_names()
|
self.create_signal_names()
|
||||||
|
|
||||||
#Create global measure names. Should maybe be an input at some point.
|
#Create global measure names. Should maybe be an input at some point.
|
||||||
|
|
@ -66,34 +54,6 @@ class delay():
|
||||||
#This is TODO once multiport control has been finalized.
|
#This is TODO once multiport control has been finalized.
|
||||||
#self.control_name = "CSB"
|
#self.control_name = "CSB"
|
||||||
|
|
||||||
def create_port_names(self):
|
|
||||||
"""Generates the port names to be used in characterization and sets default simulation target ports"""
|
|
||||||
self.write_ports = []
|
|
||||||
self.read_ports = []
|
|
||||||
self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
|
|
||||||
|
|
||||||
#save a member variable to avoid accessing global. readwrite ports have different control signals.
|
|
||||||
self.readwrite_port_num = OPTS.num_rw_ports
|
|
||||||
|
|
||||||
#Generate the port names. readwrite ports are required to be added first for this to work.
|
|
||||||
for readwrite_port_num in range(OPTS.num_rw_ports):
|
|
||||||
self.read_ports.append(readwrite_port_num)
|
|
||||||
self.write_ports.append(readwrite_port_num)
|
|
||||||
#This placement is intentional. It makes indexing input data easier. See self.data_values
|
|
||||||
for write_port_num in range(OPTS.num_rw_ports, OPTS.num_rw_ports+OPTS.num_w_ports):
|
|
||||||
self.write_ports.append(write_port_num)
|
|
||||||
for read_port_num in range(OPTS.num_rw_ports+OPTS.num_w_ports, OPTS.num_rw_ports+OPTS.num_w_ports+OPTS.num_r_ports):
|
|
||||||
self.read_ports.append(read_port_num)
|
|
||||||
|
|
||||||
#Set the default target ports for simulation. Default is all the ports.
|
|
||||||
self.targ_read_ports = self.read_ports
|
|
||||||
self.targ_write_ports = self.write_ports
|
|
||||||
|
|
||||||
def set_corner(self,corner):
|
|
||||||
""" Set the corner values """
|
|
||||||
self.corner = corner
|
|
||||||
(self.process, self.vdd_voltage, self.temperature) = corner
|
|
||||||
|
|
||||||
def set_load_slew(self,load,slew):
|
def set_load_slew(self,load,slew):
|
||||||
""" Set the load and slew """
|
""" Set the load and slew """
|
||||||
self.load = load
|
self.load = load
|
||||||
|
|
@ -113,9 +73,9 @@ class delay():
|
||||||
debug.error("Given probe_data is not an integer to specify a data bit",1)
|
debug.error("Given probe_data is not an integer to specify a data bit",1)
|
||||||
|
|
||||||
#Adding port options here which the characterizer cannot handle. Some may be added later like ROM
|
#Adding port options here which the characterizer cannot handle. Some may be added later like ROM
|
||||||
if len(self.read_ports) == 0:
|
if len(self.read_index) == 0:
|
||||||
debug.error("Characterizer does not currently support SRAMs without read ports.",1)
|
debug.error("Characterizer does not currently support SRAMs without read ports.",1)
|
||||||
if len(self.write_ports) == 0:
|
if len(self.write_index) == 0:
|
||||||
debug.error("Characterizer does not currently support SRAMs without write ports.",1)
|
debug.error("Characterizer does not currently support SRAMs without write ports.",1)
|
||||||
|
|
||||||
def write_generic_stimulus(self):
|
def write_generic_stimulus(self):
|
||||||
|
|
@ -129,12 +89,12 @@ class delay():
|
||||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||||
self.stim.inst_sram(sram=self.sram,
|
self.stim.inst_sram(sram=self.sram,
|
||||||
port_signal_names=(self.addr_name,self.din_name,self.dout_name),
|
port_signal_names=(self.addr_name,self.din_name,self.dout_name),
|
||||||
port_info=(self.total_port_num,self.write_ports,self.read_ports),
|
port_info=(self.total_ports,self.write_index,self.read_index),
|
||||||
abits=self.addr_size,
|
abits=self.addr_size,
|
||||||
dbits=self.word_size,
|
dbits=self.word_size,
|
||||||
sram_name=self.name)
|
sram_name=self.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_index:
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load))
|
self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load))
|
||||||
|
|
||||||
|
|
@ -172,7 +132,7 @@ class delay():
|
||||||
self.gen_control()
|
self.gen_control()
|
||||||
|
|
||||||
self.sf.write("\n* Generation of Port clock signal\n")
|
self.sf.write("\n* Generation of Port clock signal\n")
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
self.stim.gen_pulse(sig_name="CLK{0}".format(port),
|
self.stim.gen_pulse(sig_name="CLK{0}".format(port),
|
||||||
v1=0,
|
v1=0,
|
||||||
v2=self.vdd_voltage,
|
v2=self.vdd_voltage,
|
||||||
|
|
@ -214,24 +174,24 @@ class delay():
|
||||||
|
|
||||||
# generate data and addr signals
|
# generate data and addr signals
|
||||||
self.sf.write("\n* Generation of data and address signals\n")
|
self.sf.write("\n* Generation of data and address signals\n")
|
||||||
for write_port in self.write_ports:
|
for write_port in self.write_index:
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i),
|
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i),
|
||||||
v_val=0)
|
v_val=0)
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
for i in range(self.addr_size):
|
for i in range(self.addr_size):
|
||||||
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i),
|
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i),
|
||||||
v_val=0)
|
v_val=0)
|
||||||
|
|
||||||
# generate control signals
|
# generate control signals
|
||||||
self.sf.write("\n* Generation of control signals\n")
|
self.sf.write("\n* Generation of control signals\n")
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage)
|
self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage)
|
||||||
if port in self.write_ports and port in self.read_ports:
|
if port in self.write_index and port in self.read_index:
|
||||||
self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage)
|
self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage)
|
||||||
|
|
||||||
self.sf.write("\n* Generation of global clock signal\n")
|
self.sf.write("\n* Generation of global clock signal\n")
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
|
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
|
||||||
|
|
||||||
self.write_power_measures()
|
self.write_power_measures()
|
||||||
|
|
@ -360,10 +320,9 @@ class delay():
|
||||||
double the period until we find a valid period to use as a
|
double the period until we find a valid period to use as a
|
||||||
starting point.
|
starting point.
|
||||||
"""
|
"""
|
||||||
debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.")
|
debug.check(port in self.read_index, "Characterizer requires a read port to determine a period.")
|
||||||
|
|
||||||
feasible_period = float(tech.spice["feasible_period"])
|
feasible_period = float(tech.spice["feasible_period"])
|
||||||
#feasible_period = float(2.5)#What happens if feasible starting point is wrong?
|
|
||||||
time_out = 9
|
time_out = 9
|
||||||
while True:
|
while True:
|
||||||
time_out -= 1
|
time_out -= 1
|
||||||
|
|
@ -406,19 +365,18 @@ class delay():
|
||||||
Loops through all read ports determining the feasible period and collecting
|
Loops through all read ports determining the feasible period and collecting
|
||||||
delay information from each port.
|
delay information from each port.
|
||||||
"""
|
"""
|
||||||
feasible_delays = [{} for i in range(self.total_port_num)]
|
feasible_delays = [{} for i in range(self.total_ports)]
|
||||||
self.period = float(tech.spice["feasible_period"])
|
|
||||||
|
|
||||||
#Get initial feasible delays from first port
|
#Get initial feasible delays from first port
|
||||||
feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0])
|
feasible_delays[self.read_index[0]] = self.find_feasible_period_one_port(self.read_index[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
|
||||||
while i < len(self.read_ports):
|
while i < len(self.read_index):
|
||||||
port = self.read_ports[i]
|
port = self.read_index[i]
|
||||||
#Only extract port values from the specified port, not the entire results.
|
#Only extract port values from the specified port, not the entire results.
|
||||||
feasible_delays[port].update(self.find_feasible_period_one_port(port))
|
feasible_delays[port].update(self.find_feasible_period_one_port(port))
|
||||||
#Function sets the period. Restart the entire process if period changes to collect accurate delays
|
#Function sets the period. Restart the entire process if period changes to collect accurate delays
|
||||||
|
|
@ -461,7 +419,7 @@ class delay():
|
||||||
#Sanity Check
|
#Sanity Check
|
||||||
debug.check(self.period > 0, "Target simulation period non-positive")
|
debug.check(self.period > 0, "Target simulation period non-positive")
|
||||||
|
|
||||||
result = [{} for i in range(self.total_port_num)]
|
result = [{} for i in range(self.total_ports)]
|
||||||
# Checking from not data_value to data_value
|
# Checking from not data_value to data_value
|
||||||
self.write_delay_stimulus()
|
self.write_delay_stimulus()
|
||||||
|
|
||||||
|
|
@ -563,7 +521,7 @@ class delay():
|
||||||
|
|
||||||
#Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position.
|
#Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position.
|
||||||
#For testing purposes, only checks read ports.
|
#For testing purposes, only checks read ports.
|
||||||
for port in self.read_ports:
|
for port in self.read_index:
|
||||||
target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period)
|
target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period)
|
||||||
#The min period of one port becomes the new lower bound. Reset the upper_bound.
|
#The min period of one port becomes the new lower bound. Reset the upper_bound.
|
||||||
lb_period = target_period
|
lb_period = target_period
|
||||||
|
|
@ -728,8 +686,8 @@ class delay():
|
||||||
"""Simulate all specified output loads and input slews pairs of all ports"""
|
"""Simulate all specified output loads and input slews pairs of all ports"""
|
||||||
measure_data = self.get_empty_measure_data_dict()
|
measure_data = self.get_empty_measure_data_dict()
|
||||||
#Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways.
|
#Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways.
|
||||||
self.targ_read_ports = self.read_ports
|
self.targ_read_ports = self.read_index
|
||||||
self.targ_write_ports = self.write_ports
|
self.targ_write_ports = self.write_index
|
||||||
for slew in slews:
|
for slew in slews:
|
||||||
for load in loads:
|
for load in loads:
|
||||||
self.set_load_slew(load,slew)
|
self.set_load_slew(load,slew)
|
||||||
|
|
@ -738,7 +696,7 @@ class delay():
|
||||||
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
|
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
|
||||||
debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load))
|
debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load))
|
||||||
#The results has a dict for every port but dicts can be empty (e.g. ports were not targeted).
|
#The results has a dict for every port but dicts can be empty (e.g. ports were not targeted).
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
for mname,value in delay_results[port].items():
|
for mname,value in delay_results[port].items():
|
||||||
if "power" in mname:
|
if "power" in mname:
|
||||||
# Subtract partial array leakage and add full array leakage for the power measures
|
# Subtract partial array leakage and add full array leakage for the power measures
|
||||||
|
|
@ -747,118 +705,7 @@ class delay():
|
||||||
measure_data[port][mname].append(value)
|
measure_data[port][mname].append(value)
|
||||||
return measure_data
|
return measure_data
|
||||||
|
|
||||||
def add_data(self, data, port):
|
|
||||||
""" Add the array of data values """
|
|
||||||
debug.check(len(data)==self.word_size, "Invalid data word size.")
|
|
||||||
debug.check(port < len(self.data_values), "Port number cannot index data values.")
|
|
||||||
index = 0
|
|
||||||
for c in data:
|
|
||||||
if c=="0":
|
|
||||||
self.data_values[port][index].append(0)
|
|
||||||
elif c=="1":
|
|
||||||
self.data_values[port][index].append(1)
|
|
||||||
else:
|
|
||||||
debug.error("Non-binary data string",1)
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
def add_address(self, address, port):
|
|
||||||
""" Add the array of address values """
|
|
||||||
debug.check(len(address)==self.addr_size, "Invalid address size.")
|
|
||||||
index = 0
|
|
||||||
for c in address:
|
|
||||||
if c=="0":
|
|
||||||
self.addr_values[port][index].append(0)
|
|
||||||
elif c=="1":
|
|
||||||
self.addr_values[port][index].append(1)
|
|
||||||
else:
|
|
||||||
debug.error("Non-binary address string",1)
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
def add_noop_one_port(self, address, data, port):
|
|
||||||
""" Add the control values for a noop to a single port. """
|
|
||||||
#This is to be used as a helper function for the other add functions. Cycle and comments are omitted.
|
|
||||||
self.add_control_one_port(port, "noop")
|
|
||||||
if port in self.write_ports:
|
|
||||||
self.add_data(data,port)
|
|
||||||
self.add_address(address, port)
|
|
||||||
|
|
||||||
def add_noop_all_ports(self, comment, address, data):
|
|
||||||
""" Add the control values for a noop to all ports. """
|
|
||||||
self.add_comment("All", comment)
|
|
||||||
self.cycle_times.append(self.t_current)
|
|
||||||
self.t_current += self.period
|
|
||||||
|
|
||||||
for port in range(self.total_port_num):
|
|
||||||
self.add_noop_one_port(address, data, port)
|
|
||||||
|
|
||||||
|
|
||||||
def add_read(self, comment, address, data, port):
|
|
||||||
""" Add the control values for a read cycle. """
|
|
||||||
debug.check(port in self.read_ports, "Cannot add read cycle to a write port.")
|
|
||||||
self.add_comment(port, comment)
|
|
||||||
self.cycle_times.append(self.t_current)
|
|
||||||
self.t_current += self.period
|
|
||||||
self.add_control_one_port(port, "read")
|
|
||||||
|
|
||||||
#If the port is also a readwrite then add data.
|
|
||||||
if port in self.write_ports:
|
|
||||||
self.add_data(data,port)
|
|
||||||
self.add_address(address, port)
|
|
||||||
|
|
||||||
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
|
|
||||||
noop_data = "0"*self.word_size
|
|
||||||
#Add noops to all other ports.
|
|
||||||
for unselected_port in range(self.total_port_num):
|
|
||||||
if unselected_port != port:
|
|
||||||
self.add_noop_one_port(address, noop_data, unselected_port)
|
|
||||||
|
|
||||||
def add_write(self, comment, address, data, port):
|
|
||||||
""" Add the control values for a write cycle. """
|
|
||||||
debug.check(port in self.write_ports, "Cannot add read cycle to a read port.")
|
|
||||||
self.add_comment(port, comment)
|
|
||||||
self.cycle_times.append(self.t_current)
|
|
||||||
self.t_current += self.period
|
|
||||||
|
|
||||||
self.add_control_one_port(port, "write")
|
|
||||||
self.add_data(data,port)
|
|
||||||
self.add_address(address,port)
|
|
||||||
|
|
||||||
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
|
|
||||||
noop_data = "0"*self.word_size
|
|
||||||
#Add noops to all other ports.
|
|
||||||
for unselected_port in range(self.total_port_num):
|
|
||||||
if unselected_port != port:
|
|
||||||
self.add_noop_one_port(address, noop_data, unselected_port)
|
|
||||||
|
|
||||||
def add_control_one_port(self, port, op):
|
|
||||||
"""Appends control signals for operation to a given port"""
|
|
||||||
#Determine values to write to port
|
|
||||||
web_val = 1
|
|
||||||
csb_val = 1
|
|
||||||
if op == "read":
|
|
||||||
csb_val = 0
|
|
||||||
elif op == "write":
|
|
||||||
csb_val = 0
|
|
||||||
web_val = 0
|
|
||||||
elif op != "noop":
|
|
||||||
debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1)
|
|
||||||
|
|
||||||
#Append the values depending on the type of port
|
|
||||||
self.csb_values[port].append(csb_val)
|
|
||||||
#If port is in both lists, add rw control signal. Condition indicates its a RW port.
|
|
||||||
if port in self.write_ports and port in self.read_ports:
|
|
||||||
self.web_values[port].append(web_val)
|
|
||||||
|
|
||||||
def add_comment(self, port, comment):
|
|
||||||
"""Add comment to list to be printed in stimulus file"""
|
|
||||||
#Clean up time before appending. Make spacing dynamic as well.
|
|
||||||
time = "{0:.2f} ns:".format(self.t_current)
|
|
||||||
time_spacing = len(time)+6
|
|
||||||
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
|
|
||||||
port,
|
|
||||||
time,
|
|
||||||
time_spacing,
|
|
||||||
comment))
|
|
||||||
def gen_test_cycles_one_port(self, read_port, write_port):
|
def gen_test_cycles_one_port(self, read_port, write_port):
|
||||||
"""Intended but not implemented: Returns a list of key time-points [ns] of the waveform (each rising edge)
|
"""Intended but not implemented: Returns a list of key time-points [ns] of the waveform (each rising edge)
|
||||||
of the cycles to do a timing evaluation of a single port. Current: Values overwritten for multiple calls"""
|
of the cycles to do a timing evaluation of a single port. Current: Values overwritten for multiple calls"""
|
||||||
|
|
@ -925,43 +772,29 @@ class delay():
|
||||||
|
|
||||||
def get_available_port(self,get_read_port):
|
def get_available_port(self,get_read_port):
|
||||||
"""Returns the first accessible read or write port. """
|
"""Returns the first accessible read or write port. """
|
||||||
if get_read_port and len(self.read_ports) > 0:
|
if get_read_port and len(self.read_index) > 0:
|
||||||
return self.read_ports[0]
|
return self.read_index[0]
|
||||||
elif not get_read_port and len(self.write_ports) > 0:
|
elif not get_read_port and len(self.write_index) > 0:
|
||||||
return self.write_ports[0]
|
return self.write_index[0]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def set_stimulus_variables(self):
|
||||||
|
simulation.set_stimulus_variables(self)
|
||||||
|
self.measure_cycles = {}
|
||||||
|
|
||||||
def create_test_cycles(self):
|
def create_test_cycles(self):
|
||||||
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
||||||
of the cycles to do a timing evaluation. The last time is the end of the simulation
|
of the cycles to do a timing evaluation. The last time is the end of the simulation
|
||||||
and does not need a rising edge."""
|
and does not need a rising edge."""
|
||||||
#Using this requires setting at least one port to target for simulation.
|
#Using this requires setting at least one port to target for simulation.
|
||||||
if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0:
|
if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0:
|
||||||
debug.error("No ports selected for characterization.",1)
|
debug.error("No port selected for characterization.",1)
|
||||||
|
self.set_stimulus_variables()
|
||||||
# Start at time 0
|
|
||||||
self.t_current = 0
|
|
||||||
|
|
||||||
# Cycle times (positive edge) with comment
|
|
||||||
self.cycle_comments = []
|
|
||||||
self.cycle_times = []
|
|
||||||
self.measure_cycles = {}
|
|
||||||
|
|
||||||
# Control signals for ports. These are not the final signals and will likely be changed later.
|
|
||||||
#web is the enable for write ports. Dicts used for simplicity as ports are not necessarily incremental.
|
|
||||||
self.web_values = {port:[] for port in self.write_ports}
|
|
||||||
#csb acts as an enable for the read ports.
|
|
||||||
self.csb_values = {port:[] for port in range(self.total_port_num)}
|
|
||||||
|
|
||||||
# Address and data values for each address/data bit. A 3d list of size #ports x bits x cycles.
|
|
||||||
self.data_values=[[[] for bit in range(self.word_size)] for port in range(len(self.write_ports))]
|
|
||||||
self.addr_values=[[[] for bit in range(self.addr_size)] for port in range(self.total_port_num)]
|
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
#These checks should be superceded by check_arguments which should have been called earlier, so this is a double check.
|
|
||||||
debug.check(cur_read_port != None, "Characterizer requires at least 1 read port")
|
debug.check(cur_read_port != None, "Characterizer requires at least 1 read port")
|
||||||
debug.check(cur_write_port != None, "Characterizer requires at least 1 write port")
|
debug.check(cur_write_port != None, "Characterizer requires at least 1 write port")
|
||||||
|
|
||||||
|
|
@ -1026,7 +859,7 @@ class delay():
|
||||||
|
|
||||||
def gen_data(self):
|
def gen_data(self):
|
||||||
""" Generates the PWL data inputs for a simulation timing test. """
|
""" Generates the PWL data inputs for a simulation timing test. """
|
||||||
for write_port in self.write_ports:
|
for write_port in self.write_index:
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i)
|
sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i)
|
||||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05)
|
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05)
|
||||||
|
|
@ -1036,16 +869,16 @@ class delay():
|
||||||
Generates the address inputs for a simulation timing test.
|
Generates the address inputs for a simulation timing test.
|
||||||
This alternates between all 1's and all 0's for the address.
|
This alternates between all 1's and all 0's for the address.
|
||||||
"""
|
"""
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
for i in range(self.addr_size):
|
for i in range(self.addr_size):
|
||||||
sig_name = "{0}{1}_{2}".format(self.addr_name,port,i)
|
sig_name = "{0}{1}_{2}".format(self.addr_name,port,i)
|
||||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)
|
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)
|
||||||
|
|
||||||
def gen_control(self):
|
def gen_control(self):
|
||||||
""" Generates the control signals """
|
""" Generates the control signals """
|
||||||
for port in range(self.total_port_num):
|
for port in range(self.total_ports):
|
||||||
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
|
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
|
||||||
if port in self.read_ports and port in self.write_ports:
|
if port in self.read_index and port in self.write_index:
|
||||||
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
|
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1053,5 +886,5 @@ class delay():
|
||||||
"""Make a dict of lists for each type of delay and power measurement to append results to"""
|
"""Make a dict of lists for each type of delay and power measurement to append results to"""
|
||||||
measure_names = self.delay_meas_names + self.power_meas_names
|
measure_names = self.delay_meas_names + self.power_meas_names
|
||||||
#Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
|
#Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
|
||||||
measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_port_num)]
|
measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_ports)]
|
||||||
return measure_data
|
return measure_data
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,6 @@ class simulation():
|
||||||
def add_data(self, data, port):
|
def add_data(self, data, port):
|
||||||
""" Add the array of data values """
|
""" Add the array of data values """
|
||||||
debug.check(len(data)==self.word_size, "Invalid data word size.")
|
debug.check(len(data)==self.word_size, "Invalid data word size.")
|
||||||
#debug.check(port < len(self.data_values), "Port number cannot index data values.")
|
|
||||||
|
|
||||||
bit = self.word_size - 1
|
bit = self.word_size - 1
|
||||||
for c in data:
|
for c in data:
|
||||||
|
|
@ -109,12 +108,9 @@ class simulation():
|
||||||
|
|
||||||
def add_write(self, comment, address, data, port):
|
def add_write(self, comment, address, data, port):
|
||||||
""" Add the control values for a write cycle. """
|
""" Add the control values for a write cycle. """
|
||||||
debug.info(1, comment)
|
debug.info(2, comment)
|
||||||
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index))
|
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index))
|
||||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
self.append_cycle_comment(port, comment)
|
||||||
self.t_current,
|
|
||||||
comment,
|
|
||||||
port))
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
|
|
@ -131,12 +127,9 @@ class simulation():
|
||||||
|
|
||||||
def add_read(self, comment, address, data, port):
|
def add_read(self, comment, address, data, port):
|
||||||
""" Add the control values for a read cycle. """
|
""" Add the control values for a read cycle. """
|
||||||
debug.info(1, comment)
|
debug.info(2, comment)
|
||||||
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index))
|
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index))
|
||||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
self.append_cycle_comment(port, comment)
|
||||||
self.t_current,
|
|
||||||
comment,
|
|
||||||
port))
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
self.add_control_one_port(port, "read")
|
self.add_control_one_port(port, "read")
|
||||||
|
|
@ -153,12 +146,21 @@ class simulation():
|
||||||
if unselected_port != port:
|
if unselected_port != port:
|
||||||
self.add_noop_one_port(address, noop_data, unselected_port)
|
self.add_noop_one_port(address, noop_data, unselected_port)
|
||||||
|
|
||||||
|
def append_cycle_comment(self, port, comment):
|
||||||
|
"""Add comment to list to be printed in stimulus file"""
|
||||||
|
#Clean up time before appending. Make spacing dynamic as well.
|
||||||
|
time = "{0:.2f} ns:".format(self.t_current)
|
||||||
|
time_spacing = len(time)+6
|
||||||
|
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
|
||||||
|
port,
|
||||||
|
time,
|
||||||
|
time_spacing,
|
||||||
|
comment))
|
||||||
|
|
||||||
def add_noop_all_ports(self, comment, address, data):
|
def add_noop_all_ports(self, comment, address, data):
|
||||||
""" Add the control values for a noop to all ports. """
|
""" Add the control values for a noop to all ports. """
|
||||||
debug.info(1, comment)
|
debug.info(2, comment)
|
||||||
self.cycle_comments.append("Cycle {0:2d}\tPort All\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times),
|
self.append_cycle_comment("All", comment)
|
||||||
self.t_current,
|
|
||||||
comment))
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ class worst_case(delay):
|
||||||
test_bits = self.get_test_bits()
|
test_bits = self.get_test_bits()
|
||||||
bit_delays = self.simulate_for_bit_delays(test_bits)
|
bit_delays = self.simulate_for_bit_delays(test_bits)
|
||||||
|
|
||||||
for delay in bit_delays:
|
for i in range(len(test_bits)):
|
||||||
debug.info(1, "{}".format(delay))
|
debug.info(1, "Bit tested: addr {0[0]} data_pos {0[1]}\n Values {1}".format(test_bits[i], bit_delays[i]))
|
||||||
|
|
||||||
def simulate_for_bit_delays(self, test_bits):
|
def simulate_for_bit_delays(self, test_bits):
|
||||||
"""Simulates the delay of the sram of over several bits."""
|
"""Simulates the delay of the sram of over several bits."""
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ output_path = "temp"
|
||||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
||||||
|
|
||||||
#Setting for multiport
|
#Setting for multiport
|
||||||
# netlist_only = True
|
netlist_only = True
|
||||||
# bitcell = "pbitcell"
|
bitcell = "pbitcell"
|
||||||
# replica_bitcell="replica_pbitcell"
|
replica_bitcell="replica_pbitcell"
|
||||||
# num_rw_ports = 1
|
num_rw_ports = 1
|
||||||
# num_r_ports = 1
|
num_r_ports = 1
|
||||||
# num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,14 @@ class worst_case_timing_sram_test(openram_test):
|
||||||
if not OPTS.spice_exe:
|
if not OPTS.spice_exe:
|
||||||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||||
|
|
||||||
word_size, num_words, num_banks = 32, 32, 1
|
word_size, num_words, num_banks = 2, 16, 1
|
||||||
from sram import sram
|
from sram import sram
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
c = sram_config(word_size=word_size,
|
c = sram_config(word_size=word_size,
|
||||||
num_words=num_words,
|
num_words=num_words,
|
||||||
num_banks=num_banks)
|
num_banks=num_banks)
|
||||||
#c.words_per_row=1
|
c.words_per_row=1
|
||||||
c.compute_sizes()
|
#c.compute_sizes()
|
||||||
debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format(
|
debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format(
|
||||||
word_size, num_words, num_banks))
|
word_size, num_words, num_banks))
|
||||||
s = sram(c, name="sram1")
|
s = sram(c, name="sram1")
|
||||||
|
|
@ -56,8 +56,10 @@ class worst_case_timing_sram_test(openram_test):
|
||||||
sp_pex_file = OPTS.output_path + s.name + "_pex.sp"
|
sp_pex_file = OPTS.output_path + s.name + "_pex.sp"
|
||||||
verify.run_pex(s.name, gdsname, sp_netlist_file, output=sp_pex_file)
|
verify.run_pex(s.name, gdsname, sp_netlist_file, output=sp_pex_file)
|
||||||
sp_sim_file = sp_pex_file
|
sp_sim_file = sp_pex_file
|
||||||
|
debug.info(1, "Performing spice simulations with backannotated spice file.")
|
||||||
else:
|
else:
|
||||||
sp_sim_file = sp_netlist_file
|
sp_sim_file = sp_netlist_file
|
||||||
|
debug.info(1, "Performing spice simulations with spice netlist.")
|
||||||
|
|
||||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
wc = worst_case(s.s, sp_sim_file, corner)
|
wc = worst_case(s.s, sp_sim_file, corner)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue