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

View File

@ -12,6 +12,7 @@ import os
from globals import OPTS from globals import OPTS
import tech import tech
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
""" """
Design Class for all modules to inherit the base features. 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) os.remove(tempgds)
def init_graph_params(self): 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 # Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set() self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets): 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 # Translate port names to external nets
if len(port_nets) != len(self.pins): 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) subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets): 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 # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, 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) subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
def translate_nets(self, subinst_ports, port_dict, inst_name): 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 = [] converted_conns = []
for conn in subinst_ports: for conn in subinst_ports:
if conn in port_dict: if conn in port_dict:
@ -188,8 +197,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
return converted_conns return converted_conns
def add_graph_edges(self, graph, port_nets): 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 # The final pin names will depend on the spice hierarchy, so
# they are passed as an input. # they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} 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) return power_data(dynamic, leakage)
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): 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 the nets have a connection to this mod's net (but not inst).
""" """
if not exclusion_set: if not exclusion_set:
@ -520,7 +521,9 @@ class spice():
return aliases return aliases
def is_net_alias(self, known_net, net_alias, mod, exclusion_set): 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: if self in exclusion_set:
return False return False
# Check ports of this mod # Check ports of this mod
@ -540,7 +543,9 @@ class spice():
return False return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): 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 \ return self == mod and \
child_net.lower() == alias_net.lower() and \ child_net.lower() == alias_net.lower() and \
parent_net.lower() == alias_net.lower() parent_net.lower() == alias_net.lower()

View File

@ -5,7 +5,7 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys,re,shutil,copy import shutil
import debug import debug
import tech import tech
import math import math
@ -14,13 +14,10 @@ from .trim_spice import *
from .charutils import * from .charutils import *
from .sram_op import * from .sram_op import *
from .bit_polarity import * from .bit_polarity import *
import utils
from globals import OPTS from globals import OPTS
from .simulation import simulation from .simulation import simulation
from .measurements import * from .measurements import *
import logical_effort
import graph_util
from sram_factory import factory
class delay(simulation): class delay(simulation):
""" """
@ -50,7 +47,7 @@ class delay(simulation):
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: else:
self.num_wmasks = 0 self.num_wmasks = 0
self.set_load_slew(0,0) self.set_load_slew(0, 0)
self.set_corner(corner) self.set_corner(corner)
self.create_signal_names() self.create_signal_names()
self.add_graph_exclusions() self.add_graph_exclusions()
@ -69,7 +66,7 @@ class delay(simulation):
self.read_meas_lists = self.create_read_port_measurement_objects() self.read_meas_lists = self.create_read_port_measurement_objects()
self.write_meas_lists = self.create_write_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): def check_meas_names(self, measures_lists):
""" """
@ -80,7 +77,7 @@ class delay(simulation):
for meas_list in measures_lists: for meas_list in measures_lists:
for meas in meas_list: for meas in meas_list:
name = meas.name.lower() 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)) "Duplicate name={}").format(name))
name_set.add(name) name_set.add(name)
@ -89,7 +86,7 @@ class delay(simulation):
self.read_lib_meas = [] self.read_lib_meas = []
self.clk_frmt = "clk{0}" # Unformatted clock name 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 = []
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9)) 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. 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" debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
"supported for characterization. Storage nets={}").format(storage_names)) "supported for characterization. Storage nets={}").format(storage_names))
if not OPTS.use_pex: if not OPTS.use_pex:
q_name = cell_name+'.'+str(storage_names[0]) q_name = cell_name + '.' + str(storage_names[0])
qbar_name = cell_name+'.'+str(storage_names[1]) qbar_name = cell_name + '.' + str(storage_names[1])
else: else:
bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col) 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) 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) q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name)
qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_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 """ """ Set the load and slew """
self.load = load self.load = load
self.slew = slew 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): def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements""" """Checks if arguments given for write_stimulus() meets requirements"""
try: try:
int(self.probe_address, 2) int(self.probe_address, 2)
except ValueError: 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: 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: 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 # 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_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: 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): def write_generic_stimulus(self):
""" Create the instance, supplies, loads, and access transistors. """ """ Create the instance, supplies, loads, and access transistors. """
@ -331,8 +289,7 @@ class delay(simulation):
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:
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))
def write_delay_stimulus(self): def write_delay_stimulus(self):
""" """
@ -385,7 +342,6 @@ class delay(simulation):
self.sf.close() self.sf.close()
def write_power_stimulus(self, trim): def write_power_stimulus(self, trim):
""" Creates a stimulus file to measure leakage power only. """ Creates a stimulus file to measure leakage power only.
This works on the *untrimmed netlist*. This works on the *untrimmed netlist*.
@ -410,11 +366,11 @@ class delay(simulation):
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_ports:
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 self.all_ports: for port in self.all_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
@ -431,7 +387,7 @@ class delay(simulation):
self.write_power_measures() self.write_power_measures()
# run until the end of the cycle time # 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() 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.") 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. # 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.create_graph()
self.set_internal_spice_names() self.set_internal_spice_names()
self.create_measurement_names() self.create_measurement_names()
@ -1267,13 +1223,13 @@ class delay(simulation):
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
# Select the path with the bitline (bl) # 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] bl_path = [path for path in self.graph.all_paths if bl_name in path][0]
# Set delay/power for slews and loads # Set delay/power for slews and loads
port_data = self.get_empty_measure_data_dict() port_data = self.get_empty_measure_data_dict()
power = self.analytical_power(slews, loads) 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 max_delay = 0.0
for slew in slews: for slew in slews:
for load in loads: for load in loads:
@ -1282,29 +1238,33 @@ class delay(simulation):
total_delay = self.sum_delays(path_delays) total_delay = self.sum_delays(path_delays)
max_delay = max(max_delay, total_delay.delay) 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. # Delay is only calculated on a single port and replicated for now.
for port in self.all_ports: 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: if "power" in mname:
port_data[port][mname].append(power.dynamic) port_data[port][mname].append(power.dynamic)
elif "delay" in mname and port in self.read_ports: 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: 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: 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 # Estimate the period as double the delay with margin
period_margin = 0.1 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} "leakage_power": power.leakage}
debug.info(2,"SRAM Data:\n{}".format(sram_data)) debug.info(2, "SRAM Data:\n{}".format(sram_data))
debug.info(2,"Port Data:\n{}".format(port_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): def analytical_power(self, slews, loads):
"""Get the dynamic and leakage power from the SRAM""" """Get the dynamic and leakage power from the SRAM"""
@ -1315,8 +1275,8 @@ class delay(simulation):
# convert from nW to mW # convert from nW to mW
power.dynamic /= 1e6 power.dynamic /= 1e6
power.leakage /= 1e6 power.leakage /= 1e6
debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic)) debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1,"Leakage Power: {0} mW".format(power.leakage)) debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power return power
def gen_data(self): def gen_data(self):
@ -1324,7 +1284,7 @@ class delay(simulation):
for write_port in self.write_ports: for write_port in self.write_ports:
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)
def gen_addr(self): def gen_addr(self):
@ -1335,7 +1295,7 @@ class delay(simulation):
for port in self.all_ports: for port in self.all_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):
@ -1346,11 +1306,10 @@ class delay(simulation):
if port in self.readwrite_ports: 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) 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): def get_empty_measure_data_dict(self):
"""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 self.all_ports] measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports]
return measure_data return measure_data

View File

@ -13,9 +13,6 @@ from .stimuli import *
from .charutils import * from .charutils import *
from globals import OPTS from globals import OPTS
from .simulation import simulation from .simulation import simulation
# from .delay import delay
import graph_util
from sram_factory import factory
class functional(simulation): class functional(simulation):
@ -39,7 +36,7 @@ class functional(simulation):
if not self.num_spare_cols: if not self.num_spare_cols:
self.num_spare_cols = 0 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_corner(corner)
self.set_spice_constants() self.set_spice_constants()
self.set_stimulus_variables() self.set_stimulus_variables()
@ -60,7 +57,7 @@ class functional(simulation):
self.read_results = [] self.read_results = []
def run(self, feasible_period=None): 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 self.period = feasible_period
# Generate a random sequence of reads and writes # Generate a random sequence of reads and writes
self.create_random_memory_sequence() self.create_random_memory_sequence()
@ -249,10 +246,11 @@ class functional(simulation):
def check_stim_results(self): def check_stim_results(self):
for i in range(len(self.read_check)): for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]: 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_results[i][0],
self.read_check[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]) self.read_results[i][2])
return(0, error) return(0, error)
return(1, "SUCCESS") return(1, "SUCCESS")
@ -424,19 +422,6 @@ class functional(simulation):
self.stim.write_control(self.cycle_times[-1] + self.period) self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close() 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 #FIXME: Similar function to delay.py, refactor this
def get_bit_name(self): def get_bit_name(self):
""" Get a bit cell name """ """ Get a bit cell name """
@ -449,31 +434,4 @@ class functional(simulation):
return (q_name, qbar_name) 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys,re,shutil
from design import design
import debug import debug
import math import math
import tech import tech
from .stimuli import *
from .trim_spice import *
from .charutils import *
import utils
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import graph_util
class simulation(): class simulation():
@ -39,11 +35,11 @@ class simulation():
self.write_ports = self.sram.write_ports self.write_ports = self.sram.write_ports
self.words_per_row = self.sram.words_per_row self.words_per_row = self.sram.words_per_row
if self.write_size: 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: else:
self.num_wmasks = 0 self.num_wmasks = 0
def set_corner(self,corner): def set_corner(self, corner):
""" Set the corner values """ """ Set the corner values """
self.corner = corner self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner (self.process, self.vdd_voltage, self.temperature) = corner
@ -51,8 +47,8 @@ class simulation():
def set_spice_constants(self): def set_spice_constants(self):
""" sets feasible timing parameters """ """ sets feasible timing parameters """
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2 self.slew = tech.spice["rise_time"] * 2
self.load = tech.spice["dff_in_cap"]*4 self.load = tech.spice["dff_in_cap"] * 4
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"] self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["nom_threshold"] self.v_low = tech.spice["nom_threshold"]
@ -79,20 +75,20 @@ class simulation():
self.t_current = 0 self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port # 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.csb_values = {port: [] for port in self.all_ports}
self.web_values = {port:[] for port in self.readwrite_ports} self.web_values = {port: [] for port in self.readwrite_ports}
# Raw values added as a bit vector # Raw values added as a bit vector
self.addr_value = {port:[] for port in self.all_ports} self.addr_value = {port: [] for port in self.all_ports}
self.data_value = {port:[] for port in self.write_ports} self.data_value = {port: [] for port in self.write_ports}
self.wmask_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.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 # 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.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.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.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.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
# For generating comments in SPICE stimulus # For generating comments in SPICE stimulus
self.cycle_comments = [] self.cycle_comments = []
@ -109,7 +105,7 @@ class simulation():
csb_val = 0 csb_val = 0
web_val = 0 web_val = 0
elif op != "noop": 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 # Append the values depending on the type of port
self.csb_values[port].append(csb_val) self.csb_values[port].append(csb_val)
@ -129,7 +125,7 @@ class simulation():
elif c=="1": elif c=="1":
self.data_values[port][bit].append(1) self.data_values[port][bit].append(1)
else: else:
debug.error("Non-binary data string",1) debug.error("Non-binary data string", 1)
bit -= 1 bit -= 1
def add_address(self, address, port): def add_address(self, address, port):
@ -144,10 +140,9 @@ class simulation():
elif c=="1": elif c=="1":
self.addr_values[port][bit].append(1) self.addr_values[port][bit].append(1)
else: else:
debug.error("Non-binary address string",1) debug.error("Non-binary address string", 1)
bit -= 1 bit -= 1
def add_wmask(self, wmask, port): def add_wmask(self, wmask, port):
""" Add the array of address values """ """ Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.") debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
@ -191,9 +186,9 @@ class simulation():
self.t_current += self.period self.t_current += self.period
self.add_control_one_port(port, "write") self.add_control_one_port(port, "write")
self.add_data(data,port) self.add_data(data, port)
self.add_address(address,port) self.add_address(address, port)
self.add_wmask(wmask,port) self.add_wmask(wmask, port)
self.add_spare_wen("1" * self.num_spare_cols, port) self.add_spare_wen("1" * self.num_spare_cols, port)
#Add noops to all other ports. #Add noops to all other ports.
@ -221,11 +216,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: 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: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: 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) self.add_spare_wen("0" * self.num_spare_cols, port)
#Add noops to all other ports. #Add noops to all other ports.
@ -276,11 +271,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: 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: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: 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) self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_one_port(self, port): def add_noop_one_port(self, port):
@ -290,7 +285,7 @@ class simulation():
try: try:
self.add_address(self.addr_value[port][-1], port) self.add_address(self.addr_value[port][-1], port)
except: 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 # If the port is also a readwrite then add
# the same value as previous cycle # the same value as previous cycle
@ -298,11 +293,11 @@ class simulation():
try: try:
self.add_data(self.data_value[port][-1], port) self.add_data(self.data_value[port][-1], port)
except: 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: try:
self.add_wmask(self.wmask_value[port][-1], port) self.add_wmask(self.wmask_value[port][-1], port)
except: 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) self.add_spare_wen("0" * self.num_spare_cols, port)
def add_noop_clock_one_port(self, port): def add_noop_clock_one_port(self, port):
@ -321,12 +316,11 @@ class simulation():
if unselected_port != port: if unselected_port != port:
self.add_noop_one_port(unselected_port) self.add_noop_one_port(unselected_port)
def append_cycle_comment(self, port, comment): def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file""" """Add comment to list to be printed in stimulus file"""
#Clean up time before appending. Make spacing dynamic as well. #Clean up time before appending. Make spacing dynamic as well.
time = "{0:.2f} ns:".format(self.t_current) 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), self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
port, port,
time, time,
@ -335,9 +329,10 @@ class simulation():
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current): def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop": 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,
t_current+self.period) t_current + self.period)
elif op == "write": elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr, addr,
@ -346,7 +341,8 @@ class simulation():
t_current, t_current,
t_current+self.period) t_current+self.period)
elif op == "partial_write": 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, addr,
wmask, wmask,
port, port,
@ -354,32 +350,32 @@ class simulation():
t_current, t_current,
t_current + self.period) t_current + self.period)
else: 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, addr,
port, port,
int(t_current/self.period), int(t_current / self.period),
t_current, t_current,
t_current+self.period) t_current + self.period)
return comment return comment
def gen_pin_names(self, port_signal_names, port_info, abits, dbits): 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.""" """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 # 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 # 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. # the order of the pin names will cause issues here.
pin_names = [] pin_names = []
(addr_name, din_name, dout_name) = port_signal_names (addr_name, din_name, dout_name) = port_signal_names
(total_ports, write_index, read_index) = port_info (total_ports, write_index, read_index) = port_info
for write_input in write_index: for write_input in write_index:
for i in range(dbits): 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 port in range(total_ports):
for i in range(abits): 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. #Control signals not finalized.
for port in range(total_ports): for port in range(total_ports):
@ -394,16 +390,16 @@ class simulation():
if self.write_size: if self.write_size:
for port in write_index: for port in write_index:
for bit in range(self.num_wmasks): 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: if self.num_spare_cols:
for port in write_index: for port in write_index:
for bit in range(self.num_spare_cols): 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 read_output in read_index:
for i in range(dbits): 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("vdd"))
pin_names.append("{0}".format("gnd")) pin_names.append("{0}".format("gnd"))
@ -435,38 +431,38 @@ class simulation():
self.sen_name = sen_with_port self.sen_name = sen_with_port
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") 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) 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)): 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)):] 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 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 self.bl_name = bl_name_port
else: else:
self.bl_name = bl_name_port self.bl_name = bl_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
if br_name_port.endswith(str(port)+"_"+str(self.probe_data)): 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)):] 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 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 self.br_name = br_name_port
else: else:
self.br_name = br_name_port self.br_name = br_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") 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: else:
self.graph.get_all_paths('{}{}'.format("clk", port), self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
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))
def get_sen_name(self, paths, assumed_port=None): def get_sen_name(self, paths, assumed_port=None):
""" """
@ -484,6 +480,43 @@ class simulation():
sen_name = sen_name.split('.')[-1] sen_name = sen_name.split('.')[-1]
return sen_name 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): 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."""
@ -492,7 +525,6 @@ class simulation():
cell_bl = cell_mod.get_bl_name(port) cell_bl = cell_mod.get_bl_name(port)
cell_br = cell_mod.get_br_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. # Only a single path should contain a single s_en name. Anything else is an error.
bl_names = [] bl_names = []
exclude_set = self.get_bl_name_search_exclusions() exclude_set = self.get_bl_name_search_exclusions()
@ -502,3 +534,6 @@ class simulation():
for i in range(len(bl_names)): for i in range(len(bl_names)):
bl_names[i] = bl_names[i].split('.')[-1] bl_names[i] = bl_names[i].split('.')[-1]
return bl_names[0], bl_names[1] return bl_names[0], bl_names[1]

View File

@ -1076,14 +1076,30 @@ class bank(design.design):
offset=control_pos) offset=control_pos)
def graph_exclude_precharge(self): 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: for port in self.read_ports:
if self.port_data[port]: if self.port_data[port]:
self.port_data[port].graph_exclude_precharge() self.port_data[port].graph_exclude_precharge()
def get_cell_name(self, inst_name, row, col): 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, return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
row, row,
col) 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 return bl_wire
def graph_exclude_bits(self, targ_row, targ_col): 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 # Function is not robust with column mux configurations
for row in range(self.row_size): for row in range(self.row_size):
for col in range(self.column_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): for i, local_col in enumerate(self.col_offsets):
if local_col > col: if local_col > col:
break 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) 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.""" """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) 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 return bl_wire
def graph_exclude_bits(self, targ_row, targ_col): 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) self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self): 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: for port in self.left_rbl + self.right_rbl:
self.replica_columns[port].exclude_all_but_replica() self.replica_columns[port].exclude_all_but_replica()
def get_cell_name(self, inst_name, row, col): 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) 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] return self.bitline_names[port]
def get_bitcell_pins(self, row, col): 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 = [] bitcell_pins = []
for port in self.all_ports: for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) 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 return bitcell_pins
def get_bitcell_pins_col_cap(self, row, col): 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 = [] bitcell_pins = []
for port in self.all_ports: for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) 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 return bitcell_pins
def exclude_all_but_replica(self): 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(): for row, cell in self.cell_inst.items():
if row != self.replica_bit: if row != self.replica_bit:

View File

@ -631,3 +631,14 @@ class sram_base(design, verilog, lef):
def lvs_write(self, sp_name): def lvs_write(self, sp_name):
self.sp_write(sp_name, lvs_netlist=True) 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()