mirror of https://github.com/VLSIDA/OpenRAM.git
Lots of PEP8 cleanup. Refactor path graph to simulation class.
This commit is contained in:
parent
1eb8798bb6
commit
d7e2340e62
|
|
@ -40,9 +40,9 @@ class timing_graph():
|
||||||
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
||||||
|
|
||||||
node = node.lower()
|
node = node.lower()
|
||||||
self.graph[node] = set()
|
self.graph[node] = set()
|
||||||
|
|
||||||
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
|
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
|
||||||
"""Traverse all paths from source to destination"""
|
"""Traverse all paths from source to destination"""
|
||||||
|
|
||||||
src_node = src_node.lower()
|
src_node = src_node.lower()
|
||||||
|
|
@ -59,44 +59,44 @@ class timing_graph():
|
||||||
visited = set()
|
visited = set()
|
||||||
|
|
||||||
# Create an array to store paths
|
# Create an array to store paths
|
||||||
path = []
|
path = []
|
||||||
self.all_paths = []
|
self.all_paths = []
|
||||||
|
|
||||||
# Call the recursive helper function to print all paths
|
# Call the recursive helper function to print all paths
|
||||||
self.get_all_paths_util(src_node, dest_node, visited, path)
|
self.get_all_paths_util(src_node, dest_node, visited, path)
|
||||||
debug.info(2, "Paths found={}".format(len(self.all_paths)))
|
debug.info(2, "Paths found={}".format(len(self.all_paths)))
|
||||||
|
|
||||||
if reduce_paths:
|
if reduce_paths:
|
||||||
self.reduce_paths()
|
self.reduce_paths()
|
||||||
|
|
||||||
return self.all_paths
|
return self.all_paths
|
||||||
|
|
||||||
def reduce_paths(self):
|
def reduce_paths(self):
|
||||||
""" Remove any path that is a subset of another path """
|
""" Remove any path that is a subset of another path """
|
||||||
|
|
||||||
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
|
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
|
||||||
|
|
||||||
def get_all_paths_util(self, cur_node, dest_node, visited, path):
|
def get_all_paths_util(self, cur_node, dest_node, visited, path):
|
||||||
"""Recursive function to find all paths in a Depth First Search manner"""
|
"""Recursive function to find all paths in a Depth First Search manner"""
|
||||||
|
|
||||||
# Mark the current node as visited and store in path
|
# Mark the current node as visited and store in path
|
||||||
visited.add(cur_node)
|
visited.add(cur_node)
|
||||||
path.append(cur_node)
|
path.append(cur_node)
|
||||||
|
|
||||||
# If current vertex is same as destination, then print
|
# If current vertex is same as destination, then print
|
||||||
# current path[]
|
# current path[]
|
||||||
if cur_node == dest_node:
|
if cur_node == dest_node:
|
||||||
self.all_paths.append(copy.deepcopy(path))
|
self.all_paths.append(copy.deepcopy(path))
|
||||||
else:
|
else:
|
||||||
# If current vertex is not destination
|
# If current vertex is not destination
|
||||||
# Recur for all the vertices adjacent to this vertex
|
# Recur for all the vertices adjacent to this vertex
|
||||||
for node in self.graph[cur_node]:
|
for node in self.graph[cur_node]:
|
||||||
if node not in visited:
|
if node not in visited:
|
||||||
self.get_all_paths_util(node, dest_node, visited, path)
|
self.get_all_paths_util(node, dest_node, visited, path)
|
||||||
|
|
||||||
# Remove current vertex from path[] and mark it as unvisited
|
# Remove current vertex from path[] and mark it as unvisited
|
||||||
path.pop()
|
path.pop()
|
||||||
visited.remove(cur_node)
|
visited.remove(cur_node)
|
||||||
|
|
||||||
def get_timing(self, path, corner, slew, load):
|
def get_timing(self, path, corner, slew, load):
|
||||||
"""Returns the analytical delays in the input path"""
|
"""Returns the analytical delays in the input path"""
|
||||||
|
|
@ -106,20 +106,20 @@ 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
|
||||||
|
|
||||||
return delays
|
return delays
|
||||||
|
|
@ -127,4 +127,4 @@ class timing_graph():
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)
|
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
||||||
|
|
|
||||||
|
|
@ -500,8 +500,9 @@ 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
|
"""
|
||||||
if the nets have a connection to this mod's net (but not inst).
|
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:
|
if not exclusion_set:
|
||||||
exclusion_set = set()
|
exclusion_set = 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()
|
||||||
|
|
|
||||||
|
|
@ -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,8 +77,8 @@ 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)
|
||||||
|
|
||||||
def create_read_port_measurement_objects(self):
|
def create_read_port_measurement_objects(self):
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -217,7 +214,7 @@ class delay(simulation):
|
||||||
meas.meta_str = cycle
|
meas.meta_str = cycle
|
||||||
self.write_bit_meas[polarity].append(meas)
|
self.write_bit_meas[polarity].append(meas)
|
||||||
# Dictionary values are lists, reduce to a single list of measurements
|
# Dictionary values are lists, reduce to a single list of measurements
|
||||||
return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list]
|
return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list]
|
||||||
|
|
||||||
def get_bit_measures(self, meas_tag, probe_address, probe_data):
|
def get_bit_measures(self, meas_tag, probe_address, probe_data):
|
||||||
"""
|
"""
|
||||||
|
|
@ -231,9 +228,9 @@ class delay(simulation):
|
||||||
storage_names = cell_inst.mod.get_storage_net_names()
|
storage_names = cell_inst.mod.get_storage_net_names()
|
||||||
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)
|
||||||
|
|
@ -243,54 +240,15 @@ class delay(simulation):
|
||||||
# but they is enforced externally. {} added to names to differentiate between ports allow the
|
# but they is enforced externally. {} added to names to differentiate between ports allow the
|
||||||
# measurements are independent of the ports
|
# measurements are independent of the ports
|
||||||
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"""
|
||||||
|
|
@ -298,19 +256,19 @@ class delay(simulation):
|
||||||
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. """
|
||||||
|
|
@ -323,17 +281,16 @@ class delay(simulation):
|
||||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||||
if not OPTS.use_pex:
|
if not OPTS.use_pex:
|
||||||
self.stim.inst_model(pins=self.pins,
|
self.stim.inst_model(pins=self.pins,
|
||||||
model_name=self.sram.name)
|
model_name=self.sram.name)
|
||||||
else:
|
else:
|
||||||
self.stim.inst_sram_pex(pins=self.pins,
|
self.stim.inst_sram_pex(pins=self.pins,
|
||||||
model_name=self.sram.name)
|
model_name=self.sram.name)
|
||||||
|
|
||||||
self.sf.write("\n* SRAM output loads\n")
|
self.sf.write("\n* SRAM output loads\n")
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Creates a stimulus file for simulations to probe a bitcell at a given clock period.
|
Creates a stimulus file for simulations to probe a bitcell at a given clock period.
|
||||||
|
|
@ -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,23 +1213,23 @@ 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()
|
||||||
|
|
||||||
port = self.read_ports[0]
|
port = self.read_ports[0]
|
||||||
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))
|
||||||
|
|
||||||
# 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,41 +1238,45 @@ 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"""
|
||||||
|
|
||||||
# slews unused, only last load is used
|
# slews unused, only last load is used
|
||||||
load = loads[-1]
|
load = loads[-1]
|
||||||
power = self.sram.analytical_power(self.corner, load)
|
power = self.sram.analytical_power(self.corner, load)
|
||||||
# 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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -49,7 +46,7 @@ class functional(simulation):
|
||||||
self.add_graph_exclusions()
|
self.add_graph_exclusions()
|
||||||
self.create_graph()
|
self.create_graph()
|
||||||
self.set_internal_spice_names()
|
self.set_internal_spice_names()
|
||||||
self.q_name, self.qbar_name = self.get_bit_name()
|
self.q_name, self.qbar_name = self.get_bit_name()
|
||||||
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
|
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
|
||||||
|
|
||||||
# Number of checks can be changed
|
# Number of checks can be changed
|
||||||
|
|
@ -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,11 +246,12 @@ 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"
|
||||||
self.read_results[i][0],
|
error = str.format(self.read_results[i][1],
|
||||||
self.read_check[i][0],
|
self.read_results[i][0],
|
||||||
int((self.read_results[i][2]-self.period)/self.period),
|
self.read_check[i][0],
|
||||||
self.read_results[i][2])
|
int((self.read_results[i][2] - self.period) / self.period),
|
||||||
|
self.read_results[i][2])
|
||||||
return(0, error)
|
return(0, error)
|
||||||
return(1, "SUCCESS")
|
return(1, "SUCCESS")
|
||||||
|
|
||||||
|
|
@ -423,19 +421,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):
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
@ -142,12 +138,11 @@ class simulation():
|
||||||
if c=="0":
|
if c=="0":
|
||||||
self.addr_values[port][bit].append(0)
|
self.addr_values[port][bit].append(0)
|
||||||
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,12 +271,12 @@ 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):
|
||||||
""" Add the control values for a noop to a single port. Does not increment the period. """
|
""" Add the control values for a noop to a single port. Does not increment the period. """
|
||||||
|
|
@ -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,23 +316,23 @@ 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,
|
||||||
time_spacing,
|
time_spacing,
|
||||||
comment))
|
comment))
|
||||||
|
|
||||||
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)"
|
||||||
t_current,
|
comment = str.format(int(t_current / self.period),
|
||||||
t_current+self.period)
|
t_current,
|
||||||
|
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,40 +341,41 @@ 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)"
|
||||||
addr,
|
comment = str.format(word,
|
||||||
wmask,
|
addr,
|
||||||
port,
|
wmask,
|
||||||
int(t_current / self.period),
|
port,
|
||||||
t_current,
|
int(t_current / self.period),
|
||||||
t_current + self.period)
|
t_current,
|
||||||
|
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)"
|
||||||
addr,
|
comment = str.format(word,
|
||||||
port,
|
addr,
|
||||||
int(t_current/self.period),
|
port,
|
||||||
t_current,
|
int(t_current / self.period),
|
||||||
t_current+self.period)
|
t_current,
|
||||||
|
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"))
|
||||||
|
|
@ -425,8 +421,8 @@ class simulation():
|
||||||
|
|
||||||
port = self.read_ports[0]
|
port = self.read_ports[0]
|
||||||
if not OPTS.use_pex:
|
if not OPTS.use_pex:
|
||||||
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))
|
||||||
|
|
||||||
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
||||||
if sen_with_port.endswith(str(port)):
|
if sen_with_port.endswith(str(port)):
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
@ -482,17 +478,53 @@ class simulation():
|
||||||
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
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."""
|
||||||
|
|
||||||
cell_mod = factory.create(module_type=OPTS.bitcell)
|
cell_mod = factory.create(module_type=OPTS.bitcell)
|
||||||
cell_bl = cell_mod.get_bl_name(port)
|
cell_bl = cell_mod.get_bl_name(port)
|
||||||
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()
|
||||||
|
|
@ -501,4 +533,7 @@ class simulation():
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
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]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -279,4 +279,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
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.clear_exclude_bits()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -630,4 +630,15 @@ 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()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue