Lots of PEP8 cleanup. Refactor path graph to simulation class.

This commit is contained in:
mrg 2020-09-29 10:26:31 -07:00
parent 1eb8798bb6
commit d7e2340e62
13 changed files with 305 additions and 275 deletions

View File

@ -106,18 +106,18 @@ class timing_graph():
delays = []
cur_slew = slew
for i in range(len(path)-1):
for i in range(len(path) - 1):
path_edge_mod = self.edge_mods[(path[i], path[i+1])]
path_edge_mod = self.edge_mods[(path[i], path[i + 1])]
# On the output of the current stage, get COUT from all other mods connected
cout = 0
for node in self.graph[path[i+1]]:
output_edge_mod = self.edge_mods[(path[i+1], node)]
for node in self.graph[path[i + 1]]:
output_edge_mod = self.edge_mods[(path[i + 1], node)]
cout+=output_edge_mod.get_cin()
# If at the last output, include the final output load
if i == len(path)-2:
cout+=load
if i == len(path) - 2:
cout += load
delays.append(path_edge_mod.analytical_delay(corner, slew, cout))
cur_slew = delays[-1].slew

View File

@ -12,6 +12,7 @@ import os
from globals import OPTS
import tech
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
Design Class for all modules to inherit the base features.
@ -137,12 +138,16 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
os.remove(tempgds)
def init_graph_params(self):
"""Initializes parameters relevant to the graph creation"""
"""
Initializes parameters relevant to the graph creation
"""
# Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets):
"""Recursively create graph from instances in module."""
"""
Recursively create graph from instances in module.
"""
# Translate port names to external nets
if len(port_nets) != len(self.pins):
@ -159,7 +164,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets):
"""Collects all the nets and the parent inst of that net."""
"""
Collects all the nets and the parent inst of that net.
"""
# Translate port names to external nets
if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
@ -178,7 +185,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
def translate_nets(self, subinst_ports, port_dict, inst_name):
"""Converts connection names to their spice hierarchy equivalent"""
"""
Converts connection names to their spice hierarchy equivalent
"""
converted_conns = []
for conn in subinst_ports:
if conn in port_dict:
@ -188,8 +197,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
return converted_conns
def add_graph_edges(self, graph, port_nets):
"""For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules."""
"""
For every input, adds an edge to every output.
Only intended to be used for gates and other simple modules.
"""
# The final pin names will depend on the spice hierarchy, so
# they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}

View File

@ -500,7 +500,8 @@ class spice():
return power_data(dynamic, leakage)
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
"""Given a list of nets, will compare the internal alias of a mod to determine
"""
Given a list of nets, will compare the internal alias of a mod to determine
if the nets have a connection to this mod's net (but not inst).
"""
if not exclusion_set:
@ -520,7 +521,9 @@ class spice():
return aliases
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
"""Checks if the alias_net in input mod is the same as the input net for this mod (self)."""
"""
Checks if the alias_net in input mod is the same as the input net for this mod (self).
"""
if self in exclusion_set:
return False
# Check ports of this mod
@ -540,7 +543,9 @@ class spice():
return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
"""Utility function for checking single net alias."""
"""
Utility function for checking single net alias.
"""
return self == mod and \
child_net.lower() == alias_net.lower() and \
parent_net.lower() == alias_net.lower()

View File

@ -5,7 +5,7 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys,re,shutil,copy
import shutil
import debug
import tech
import math
@ -14,13 +14,10 @@ from .trim_spice import *
from .charutils import *
from .sram_op import *
from .bit_polarity import *
import utils
from globals import OPTS
from .simulation import simulation
from .measurements import *
import logical_effort
import graph_util
from sram_factory import factory
class delay(simulation):
"""
@ -50,7 +47,7 @@ class delay(simulation):
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
self.set_load_slew(0,0)
self.set_load_slew(0, 0)
self.set_corner(corner)
self.create_signal_names()
self.add_graph_exclusions()
@ -69,7 +66,7 @@ class delay(simulation):
self.read_meas_lists = self.create_read_port_measurement_objects()
self.write_meas_lists = self.create_write_port_measurement_objects()
self.check_meas_names(self.read_meas_lists+self.write_meas_lists)
self.check_meas_names(self.read_meas_lists + self.write_meas_lists)
def check_meas_names(self, measures_lists):
"""
@ -80,7 +77,7 @@ class delay(simulation):
for meas_list in measures_lists:
for meas in meas_list:
name = meas.name.lower()
debug.check(name not in name_set,("SPICE measurements must have unique names. "
debug.check(name not in name_set, ("SPICE measurements must have unique names. "
"Duplicate name={}").format(name))
name_set.add(name)
@ -89,7 +86,7 @@ class delay(simulation):
self.read_lib_meas = []
self.clk_frmt = "clk{0}" # Unformatted clock name
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) # Empty values are the port and probe data bit
targ_name = "{0}{1}_{2}".format(self.dout_name, "{}", self.probe_data) # Empty values are the port and probe data bit
self.delay_meas = []
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9))
self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file.
@ -232,8 +229,8 @@ class delay(simulation):
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
"supported for characterization. Storage nets={}").format(storage_names))
if not OPTS.use_pex:
q_name = cell_name+'.'+str(storage_names[0])
qbar_name = cell_name+'.'+str(storage_names[1])
q_name = cell_name + '.' + str(storage_names[0])
qbar_name = cell_name + '.' + str(storage_names[1])
else:
bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col)
q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col)
@ -245,72 +242,33 @@ class delay(simulation):
q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name)
qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name)
return {bit_polarity.NONINVERTING:q_meas, bit_polarity.INVERTING:qbar_meas}
return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
def set_load_slew(self,load,slew):
def set_load_slew(self, load, slew):
""" Set the load and slew """
self.load = load
self.slew = slew
def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.bank.bitcell_array.bitcell_array.init_graph_params() # Removes previous bit exclusions
self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_spc_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph,self.sram_spc_name,self.pins)
def get_bl_name_search_exclusions(self):
"""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
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
"""
Finds a single alias for the int_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(int_net),1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(int_net),1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(int_net),1)
return path_net_name
def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements"""
try:
int(self.probe_address, 2)
except ValueError:
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1)
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address), 1)
if len(self.probe_address) != self.addr_size:
debug.error("Probe Address's number of bits does not correspond to given SRAM",1)
debug.error("Probe Address's number of bits does not correspond to given SRAM", 1)
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
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
if len(self.read_ports) == 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:
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):
""" Create the instance, supplies, loads, and access transistors. """
@ -331,8 +289,7 @@ class delay(simulation):
self.sf.write("\n* SRAM output loads\n")
for port in self.read_ports:
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))
def write_delay_stimulus(self):
"""
@ -385,7 +342,6 @@ class delay(simulation):
self.sf.close()
def write_power_stimulus(self, trim):
""" Creates a stimulus file to measure leakage power only.
This works on the *untrimmed netlist*.
@ -410,11 +366,11 @@ class delay(simulation):
self.sf.write("\n* Generation of data and address signals\n")
for write_port in self.write_ports:
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)
for port in self.all_ports:
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)
# generate control signals
@ -431,7 +387,7 @@ class delay(simulation):
self.write_power_measures()
# run until the end of the cycle time
self.stim.write_control(2*self.period)
self.stim.write_control(2 * self.period)
self.sf.close()
@ -1257,7 +1213,7 @@ class delay(simulation):
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0'*self.addr_size, 0)
self.set_probe('0' * self.addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()
@ -1267,13 +1223,13 @@ class delay(simulation):
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
# Select the path with the bitline (bl)
bl_name,br_name = self.get_bl_name(self.graph.all_paths, port)
bl_name, br_name = self.get_bl_name(self.graph.all_paths, port)
bl_path = [path for path in self.graph.all_paths if bl_name in path][0]
# Set delay/power for slews and loads
port_data = self.get_empty_measure_data_dict()
power = self.analytical_power(slews, loads)
debug.info(1,'Slew, Load, Delay(ns), Slew(ns)')
debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)')
max_delay = 0.0
for slew in slews:
for load in loads:
@ -1282,29 +1238,33 @@ class delay(simulation):
total_delay = self.sum_delays(path_delays)
max_delay = max(max_delay, total_delay.delay)
debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3))
debug.info(1,
'{}, {}, {}, {}'.format(slew,
load,
total_delay.delay / 1e3,
total_delay.slew / 1e3))
# Delay is only calculated on a single port and replicated for now.
for port in self.all_ports:
for mname in self.delay_meas_names+self.power_meas_names:
for mname in self.delay_meas_names + self.power_meas_names:
if "power" in mname:
port_data[port][mname].append(power.dynamic)
elif "delay" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.delay/1e3)
port_data[port][mname].append(total_delay.delay / 1e3)
elif "slew" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.slew/1e3)
port_data[port][mname].append(total_delay.slew / 1e3)
else:
debug.error("Measurement name not recognized: {}".format(mname),1)
debug.error("Measurement name not recognized: {}".format(mname), 1)
# Estimate the period as double the delay with margin
period_margin = 0.1
sram_data = { "min_period":(max_delay/1e3)*2*period_margin,
sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
"leakage_power": power.leakage}
debug.info(2,"SRAM Data:\n{}".format(sram_data))
debug.info(2,"Port Data:\n{}".format(port_data))
debug.info(2, "SRAM Data:\n{}".format(sram_data))
debug.info(2, "Port Data:\n{}".format(port_data))
return (sram_data,port_data)
return (sram_data, port_data)
def analytical_power(self, slews, loads):
"""Get the dynamic and leakage power from the SRAM"""
@ -1315,8 +1275,8 @@ class delay(simulation):
# convert from nW to mW
power.dynamic /= 1e6
power.leakage /= 1e6
debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1,"Leakage Power: {0} mW".format(power.leakage))
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power
def gen_data(self):
@ -1324,7 +1284,7 @@ class delay(simulation):
for write_port in self.write_ports:
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)
def gen_addr(self):
@ -1335,7 +1295,7 @@ class delay(simulation):
for port in self.all_ports:
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)
def gen_control(self):
@ -1346,11 +1306,10 @@ class delay(simulation):
if port in self.readwrite_ports:
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
def get_empty_measure_data_dict(self):
"""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
# 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 self.all_ports]
measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports]
return measure_data

View File

@ -13,9 +13,6 @@ from .stimuli import *
from .charutils import *
from globals import OPTS
from .simulation import simulation
# from .delay import delay
import graph_util
from sram_factory import factory
class functional(simulation):
@ -39,7 +36,7 @@ class functional(simulation):
if not self.num_spare_cols:
self.num_spare_cols = 0
self.probe_address, self.probe_data = '0'*self.addr_size,0
self.probe_address, self.probe_data = '0' * self.addr_size, 0
self.set_corner(corner)
self.set_spice_constants()
self.set_stimulus_variables()
@ -60,7 +57,7 @@ class functional(simulation):
self.read_results = []
def run(self, feasible_period=None):
if feasible_period: #period defaults to tech.py feasible period otherwise.
if feasible_period: # period defaults to tech.py feasible period otherwise.
self.period = feasible_period
# Generate a random sequence of reads and writes
self.create_random_memory_sequence()
@ -249,10 +246,11 @@ class functional(simulation):
def check_stim_results(self):
for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]:
error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_results[i][1],
str = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n"
error = str.format(self.read_results[i][1],
self.read_results[i][0],
self.read_check[i][0],
int((self.read_results[i][2]-self.period)/self.period),
int((self.read_results[i][2] - self.period) / self.period),
self.read_results[i][2])
return(0, error)
return(1, "SUCCESS")
@ -424,19 +422,6 @@ class functional(simulation):
self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close()
# FIXME: refactor to share with delay.py
def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions
# Does wordline=0 and column=0 just for debug names
self.sram.bank.bitcell_array.graph_exclude_bits(0, 0)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_spc_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph, self.sram_spc_name, self.pins)
#FIXME: Similar function to delay.py, refactor this
def get_bit_name(self):
""" Get a bit cell name """
@ -449,31 +434,4 @@ class functional(simulation):
return (q_name, qbar_name)
def get_bl_name_search_exclusions(self):
"""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
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
"""
Finds a single alias for the int_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(int_net), 1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(int_net), 1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(int_net), 1)
return path_net_name

View File

@ -5,17 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys,re,shutil
from design import design
import debug
import math
import tech
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS
from sram_factory import factory
import graph_util
class simulation():
@ -39,11 +35,11 @@ class simulation():
self.write_ports = self.sram.write_ports
self.words_per_row = self.sram.words_per_row
if self.write_size:
self.num_wmasks = int(math.ceil(self.word_size/self.write_size))
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
def set_corner(self,corner):
def set_corner(self, corner):
""" Set the corner values """
self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner
@ -51,8 +47,8 @@ class simulation():
def set_spice_constants(self):
""" sets feasible timing parameters """
self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2
self.load = tech.spice["dff_in_cap"]*4
self.slew = tech.spice["rise_time"] * 2
self.load = tech.spice["dff_in_cap"] * 4
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"]
@ -79,20 +75,20 @@ class simulation():
self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = {port:[] for port in self.all_ports}
self.web_values = {port:[] for port in self.readwrite_ports}
self.csb_values = {port: [] for port in self.all_ports}
self.web_values = {port: [] for port in self.readwrite_ports}
# Raw values added as a bit vector
self.addr_value = {port:[] for port in self.all_ports}
self.data_value = {port:[] for port in self.write_ports}
self.wmask_value = {port:[] for port in self.write_ports}
self.spare_wen_value = {port:[] for port in self.write_ports}
self.addr_value = {port: [] for port in self.all_ports}
self.data_value = {port: [] for port in self.write_ports}
self.wmask_value = {port: [] for port in self.write_ports}
self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port:[[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port:[[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
# For generating comments in SPICE stimulus
self.cycle_comments = []
@ -109,7 +105,7 @@ class simulation():
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)
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)
@ -129,7 +125,7 @@ class simulation():
elif c=="1":
self.data_values[port][bit].append(1)
else:
debug.error("Non-binary data string",1)
debug.error("Non-binary data string", 1)
bit -= 1
def add_address(self, address, port):
@ -144,10 +140,9 @@ class simulation():
elif c=="1":
self.addr_values[port][bit].append(1)
else:
debug.error("Non-binary address string",1)
debug.error("Non-binary address string", 1)
bit -= 1
def add_wmask(self, wmask, port):
""" Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
@ -191,9 +186,9 @@ class simulation():
self.t_current += self.period
self.add_control_one_port(port, "write")
self.add_data(data,port)
self.add_address(address,port)
self.add_wmask(wmask,port)
self.add_data(data, port)
self.add_address(address, port)
self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, port)
#Add noops to all other ports.
@ -221,11 +216,11 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
#Add noops to all other ports.
@ -276,11 +271,11 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_one_port(self, port):
@ -290,7 +285,7 @@ class simulation():
try:
self.add_address(self.addr_value[port][-1], port)
except:
self.add_address("0"*self.addr_size, port)
self.add_address("0" * self.addr_size, port)
# If the port is also a readwrite then add
# the same value as previous cycle
@ -298,11 +293,11 @@ class simulation():
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
self.add_data("0" * (self.word_size + self.num_spare_cols), port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
self.add_wmask("0" * self.num_wmasks, port)
self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_clock_one_port(self, port):
@ -321,12 +316,11 @@ class simulation():
if unselected_port != port:
self.add_noop_one_port(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
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,
@ -335,9 +329,10 @@ class simulation():
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop":
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
comment = str.format(int(t_current / self.period),
t_current,
t_current+self.period)
t_current + self.period)
elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
@ -346,7 +341,8 @@ class simulation():
t_current,
t_current+self.period)
elif op == "partial_write":
comment = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)".format(word,
str = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)"
comment = str.format(word,
addr,
wmask,
port,
@ -354,32 +350,32 @@ class simulation():
t_current,
t_current + self.period)
else:
comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
str = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)"
comment = str.format(word,
addr,
port,
int(t_current/self.period),
int(t_current / self.period),
t_current,
t_current+self.period)
t_current + self.period)
return comment
def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
"""Creates the pins names of the SRAM based on the no. of ports."""
#This may seem redundant as the pin names are already defined in the sram. However, it is difficult
#to extract the functionality from the names, so they are recreated. As the order is static, changing
#the order of the pin names will cause issues here.
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
# to extract the functionality from the names, so they are recreated. As the order is static, changing
# the order of the pin names will cause issues here.
pin_names = []
(addr_name, din_name, dout_name) = port_signal_names
(total_ports, write_index, read_index) = port_info
for write_input in write_index:
for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(din_name,write_input, i))
pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
for port in range(total_ports):
for i in range(abits):
pin_names.append("{0}{1}_{2}".format(addr_name,port,i))
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
#Control signals not finalized.
for port in range(total_ports):
@ -394,16 +390,16 @@ class simulation():
if self.write_size:
for port in write_index:
for bit in range(self.num_wmasks):
pin_names.append("WMASK{0}_{1}".format(port,bit))
pin_names.append("WMASK{0}_{1}".format(port, bit))
if self.num_spare_cols:
for port in write_index:
for bit in range(self.num_spare_cols):
pin_names.append("SPARE_WEN{0}_{1}".format(port,bit))
pin_names.append("SPARE_WEN{0}_{1}".format(port, bit))
for read_output in read_index:
for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i))
pin_names.append("{0}{1}_{2}".format(dout_name, read_output, i))
pin_names.append("{0}".format("vdd"))
pin_names.append("{0}".format("gnd"))
@ -435,38 +431,38 @@ class simulation():
self.sen_name = sen_with_port
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
debug.info(2,"s_en name = {}".format(self.sen_name))
debug.info(2, "s_en name = {}".format(self.sen_name))
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
port_pos = -1-len(str(self.probe_data))-len(str(port))
port_pos = -1 - len(str(self.probe_data)) - len(str(port))
if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)):
self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):]
if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)):
self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):]
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
self.bl_name = bl_name_port
else:
self.bl_name = bl_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
if br_name_port.endswith(str(port)+"_"+str(self.probe_data)):
self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):]
if br_name_port.endswith(str(port) + "_" + str(self.probe_data)):
self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):]
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
self.br_name = br_name_port
else:
self.br_name = br_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
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))
else:
self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
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.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))
self.bl_name = "bl{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))
def get_sen_name(self, paths, assumed_port=None):
"""
@ -484,6 +480,43 @@ class simulation():
sen_name = sen_name.split('.')[-1]
return sen_name
def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.clear_exclude_bits() # Removes previous bit exclusions
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
# Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram_instance_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
def get_bl_name_search_exclusions(self):
"""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
# so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_alias_in_path(self, paths, internal_net, mod, exclusion_set=None):
"""
Finds a single alias for the internal_net in given paths.
More or less hits cause an error
"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_instance_name, self.pins, path, internal_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(internal_net), 1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(internal_net), 1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(internal_net), 1)
return path_net_name
def get_bl_name(self, paths, port):
"""Gets the signal name associated with the bitlines in the bank."""
@ -492,7 +525,6 @@ class simulation():
cell_bl = cell_mod.get_bl_name(port)
cell_br = cell_mod.get_br_name(port)
bl_found = False
# Only a single path should contain a single s_en name. Anything else is an error.
bl_names = []
exclude_set = self.get_bl_name_search_exclusions()
@ -502,3 +534,6 @@ class simulation():
for i in range(len(bl_names)):
bl_names[i] = bl_names[i].split('.')[-1]
return bl_names[0], bl_names[1]

View File

@ -1076,14 +1076,30 @@ class bank(design.design):
offset=control_pos)
def graph_exclude_precharge(self):
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
"""
Precharge adds a loop between bitlines, can be excluded to reduce complexity
"""
for port in self.read_ports:
if self.port_data[port]:
self.port_data[port].graph_exclude_precharge()
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
"""
Gets the spice name of the target bitcell.
"""
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
row,
col)
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -105,7 +105,9 @@ class bitcell_array(bitcell_base_array):
return bl_wire
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
"""
Excludes bits in column from being added to graph except target
"""
# Function is not robust with column mux configurations
for row in range(self.row_size):
for col in range(self.column_size):

View File

@ -297,6 +297,15 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
for i, local_col in enumerate(self.col_offsets):
if local_col > col:
break
else:
# In this case, we it should be in the last bitcell array
i = len(self.col_offsets)
return self.local_mods[i - 1].get_cell_name(inst_name + '.x' + self.local_insts[i - 1].name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
for mod in self.local_mods:
mod.clear_exclude_bits()

View File

@ -280,3 +280,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
"""Gets the spice name of the target bitcell."""
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()

View File

@ -529,15 +529,27 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
return bl_wire
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self):
"""Exclude all replica/dummy cells in the replica columns except the replica bit."""
"""
Exclude all replica/dummy cells in the replica columns except the replica bit.
"""
for port in self.left_rbl + self.right_rbl:
self.replica_columns[port].exclude_all_but_replica()
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
"""
Gets the spice name of the target bitcell.
"""
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.init_graph_params()

View File

@ -200,8 +200,10 @@ class replica_column(bitcell_base_array):
return self.bitline_names[port]
def get_bitcell_pins(self, row, col):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = []
for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
@ -212,8 +214,10 @@ class replica_column(bitcell_base_array):
return bitcell_pins
def get_bitcell_pins_col_cap(self, row, col):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = []
for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
@ -223,7 +227,9 @@ class replica_column(bitcell_base_array):
return bitcell_pins
def exclude_all_but_replica(self):
"""Excludes all bits except the replica cell (self.replica_bit)."""
"""
Excludes all bits except the replica cell (self.replica_bit).
"""
for row, cell in self.cell_inst.items():
if row != self.replica_bit:

View File

@ -631,3 +631,14 @@ class sram_base(design, verilog, lef):
def lvs_write(self, sp_name):
self.sp_write(sp_name, lvs_netlist=True)
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bank.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bank.clear_exclude_bits()