Added graph to characterizer to get net names and perform s_en checks. Graph not working with column mux.

This commit is contained in:
Hunter Nichols 2019-05-14 14:44:49 -07:00
parent b30c20ffb5
commit 178d3df5f5
6 changed files with 121 additions and 62 deletions

View File

@ -1,4 +1,4 @@
import os
import os, copy
from collections import defaultdict
import gdsMill
@ -16,6 +16,7 @@ class timing_graph():
def __init__(self):
self.graph = defaultdict(set)
self.all_paths = []
def add_edge(self, src_node, dest_node):
"""Adds edge to graph. Nodes added as well if they do not exist."""
@ -34,7 +35,7 @@ class timing_graph():
node = node.lower()
self.graph[node] = set()
def print_all_paths(self, src_node, dest_node, rmv_rail_nodes=True):
def get_all_paths(self, src_node, dest_node, rmv_rail_nodes=True):
"""Traverse all paths from source to destination"""
src_node = src_node.lower()
dest_node = dest_node.lower()
@ -51,13 +52,15 @@ class timing_graph():
# Create an array to store paths
path = []
self.path_count = 0
self.all_paths = []
# Call the recursive helper function to print all paths
self.print_all_paths_util(src_node, dest_node, visited, path)
debug.info(1, "Paths found={}".format(self.path_count))
self.get_all_paths_util(src_node, dest_node, visited, path)
debug.info(1, "Paths found={}".format(len(self.all_paths)))
def print_all_paths_util(self, cur_node, dest_node, visited, path):
return self.all_paths
def get_all_paths_util(self, cur_node, dest_node, visited, path):
"""Recursive function to find all paths in a Depth First Search manner"""
# Mark the current node as visited and store in path
visited.add(cur_node)
@ -67,18 +70,32 @@ class timing_graph():
# current path[]
if cur_node == dest_node:
debug.info(1,"{}".format(path))
self.path_count+=1
self.all_paths.append(copy.deepcopy(path))
else:
# If current vertex is not destination
#Recur for all the vertices adjacent to this vertex
for node in self.graph[cur_node]:
if node not in visited:
self.print_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
path.pop()
visited.remove(cur_node)
def get_path_preconvergence_point(self, path1, path2):
"""Assuming the inputs paths have the same starting point and end point, the
paths should split and converge at some point before/at the last stage. Finds the
point before convergence."""
debug.check(path1[0] == path2[0], "Paths must start from the same point.")
debug.check(path1[-1] == path2[-1], "Paths must end from the same point.")
#Paths must end at the same point, so the paths are traversed backwards to find
#point of convergence. There could be multiple points, only finds first.
for point1,point2 in zip(reversed(path1), reversed(path2)):
if point1 != point2:
return (point1,point2)
debug.info(1,"Pre-convergence point not found, paths are equals.")
return path1[0],path2[0]
def __str__(self):
""" override print function output """
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)

View File

@ -140,7 +140,13 @@ class spice():
debug.error("-----")
debug.error("Connections: \n"+str(conns_string),1)
def get_conns(self, inst):
"""Returns the connections of a given instance."""
for i in range(len(self.insts)):
if inst is self.insts[i]:
return self.conns[i]
#If not found, returns None
return None
def sp_read(self):
"""Reads the sp file (and parse the pins) from the library

View File

@ -81,49 +81,39 @@ class delay(simulation):
if obj.meta_str is "read0":
obj.meta_add_delay = True
trig_name = "Xsram.s_en{}" #Sense amp enable
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = ""
else:
port_format = "{}"
# trig_name = "Xsram.s_en{}" #Sense amp enable
# if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
# port_format = ""
# else:
# port_format = "{}"
bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
# bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
# br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
# self.read_lib_meas.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5))
# self.read_lib_meas.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5))
read_measures = []
read_measures.append(self.read_lib_meas)
#Other measurements associated with the read port not included in the liberty file
read_measures.append(self.create_bitline_delay_measurement_objects())
read_measures.append(self.create_bitline_measurement_objects())
read_measures.append(self.create_debug_measurement_objects())
return read_measures
def create_bitline_delay_measurement_objects(self):
def create_bitline_measurement_objects(self):
"""Create the measurements used for bitline delay values. Due to unique error checking, these are separated from other measurements.
These measurements are only associated with read values
"""
self.bitline_delay_meas = []
trig_name = "clk{0}"
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = ""
else:
port_format = "{}"
bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
targ_val = (self.vdd_voltage - tech.spice["v_threshold_typical"])/self.vdd_voltage #Calculate as a percentage of vdd
self.bitline_volt_meas = []
#Bitline voltage measures
self.bitline_volt_meas.append(voltage_at_measure("v_bl_name",
self.bl_name))
self.bitline_volt_meas[-1].meta_str = 'read0'
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
# self.bitline_delay_meas.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_meas[-1].meta_str = "read0"
# self.bitline_delay_meas.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_meas[-1].meta_str = "read1"
#Enforces the time delay on the bitline measurements for read0 or read1
for obj in self.bitline_delay_meas:
obj.meta_add_delay = True
return self.bitline_delay_meas
self.bitline_volt_meas.append(voltage_at_measure("v_br_name",
self.br_name))
self.bitline_volt_meas[-1].meta_str = 'read1'
return self.bitline_volt_meas
def create_write_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers"""
@ -155,9 +145,11 @@ class delay(simulation):
debug_meas.parent = meas.name
self.debug_delay_meas.append(debug_meas)
#Output voltage measures
self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.meta_str),
debug_meas.targ_name_no_port))
self.debug_volt_meas[-1].meta_str = debug_meas.meta_str
return self.debug_delay_meas+self.debug_volt_meas
def set_load_slew(self,load,slew):
@ -183,7 +175,28 @@ class delay(simulation):
self.graph = graph_util.timing_graph()
self.sram.build_graph(self.graph,"X{}".format(self.sram.name),self.pins)
#debug.info(1,"{}".format(graph))
#graph.print_all_paths('clk0', 'DOUT0[0]')
port = 0
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), \
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
sen_name = self.sram.get_sen_name(self.sram.name, port).lower()
debug.info(1, "Sen name={}".format(sen_name))
sen_path = []
for path in self.graph.all_paths:
if sen_name in path:
sen_path = path
break
debug.check(len(sen_path)!=0, "Could not find s_en timing path.")
preconv_names = []
for path in self.graph.all_paths:
if not path is sen_path:
sen_preconv = self.graph.get_path_preconvergence_point(path, sen_path)
if sen_preconv not in preconv_names:
preconv_names.append(sen_preconv[0]) #only save non-sen_path names
#Not an good way to separate inverting and non-inverting bitlines...
self.bl_name = [bl for bl in preconv_names if 'bl' in bl][0]
self.br_name = [bl for bl in preconv_names if 'br' in bl][0]
debug.info(1,"bl_name={}".format(self.bl_name))
def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements"""
@ -551,9 +564,6 @@ class delay(simulation):
result[port].update(read_port_dict)
bitline_delay_dict = self.evaluate_bitline_delay(port)
result[port].update(bitline_delay_dict)
for port in self.targ_write_ports:
write_port_dict = {}
for measure in self.write_lib_meas:
@ -589,7 +599,14 @@ class delay(simulation):
success = False
debug.info(1, "Debug measurement failed. Incorrect Value found on output.")
break
#FIXME: these checks need to be re-done to be more robust against possible errors
bitline_results = {}
for meas in self.bitline_volt_meas:
val = meas.retrieve_measure(port=port)
bitline_results[meas.meta_str] = val
for meas in self.debug_volt_meas:
val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val))
@ -604,19 +621,20 @@ class delay(simulation):
success = False
debug.info(1, "Debug measurement failed. Value {}v was read on read 0 cycle.".format(val))
break
#If the bitlines have a correct value while the output does not then that is a
#sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
if not success and self.check_bitline_meas(bitline_results[meas.meta_str]):
debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.",1)
return success
def evaluate_bitline_delay(self, port):
"""Parse and check the bitline delay. One of the measurements is expected to fail which warrants its own function."""
bl_delay_meas_dict = {}
values_added = 0 #For error checking
for measure in self.bitline_delay_meas:
bl_delay_val = measure.retrieve_measure(port=port)
if type(bl_delay_val) != float or 0 > bl_delay_val or bl_delay_val > self.period/2: #Only add if value is valid, do not error.
debug.error("Bitline delay measurement failed: half-period={}, {}={}".format(self.period/2, measure.name, bl_delay_val),1)
bl_delay_meas_dict[measure.name] = bl_delay_val
return bl_delay_meas_dict
def check_bitline_meas(self, v_bitline):
"""Checks the value of the discharging bitline"""
return v_bitline < self.vdd_voltage*0.9
def run_power_simulation(self):
"""

View File

@ -10,7 +10,7 @@ class tri_gate(design.design):
netlist should be available in the technology library.
"""
pin_names = ["in", "out", "en", "en_bar", "gnd", "vdd"]
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])

View File

@ -327,3 +327,21 @@ class sram_1bank(sram_base):
#Insts located in control logic, exclusion function called here
for inst in self.control_logic_insts:
inst.mod.graph_exclude_dffs()
def get_sen_name(self, sram_name, port=0):
"""Returns the s_en spice name."""
#Naming scheme is hardcoded using this function, should be built into the
#graph in someway.
sen_name = "s_en{}".format(port)
control_conns = self.get_conns(self.control_logic_insts[port])
#Sanity checks
if sen_name not in control_conns:
debug.error("Signal={} not contained in control logic connections={}"\
.format(sen_name, control_conns))
if sen_name in self.pins:
debug.error("Internal signal={} contained in port list. Name defined by the parent.")
debug.info(1,"pins={}".format(self.pins))
debug.info(1,"cl conns={}".format(control_conns))
return "X{}.{}".format(sram_name, sen_name)

View File

@ -26,15 +26,15 @@ class timing_sram_test(openram_test):
reload(characterizer)
from characterizer import delay
from sram_config import sram_config
c = sram_config(word_size=1,
num_words=16,
num_banks=1)
c.words_per_row=1
# c = sram_config(word_size=32,
# num_words=256,
# c = sram_config(word_size=1,
# num_words=16,
# num_banks=1)
# c.words_per_row=2
#OPTS.use_tech_delay_chain_size = True
# c.words_per_row=1
c = sram_config(word_size=32,
num_words=256,
num_banks=1)
c.words_per_row=2
OPTS.use_tech_delay_chain_size = True
c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = factory.create(module_type="sram", sram_config=c)