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 from collections import defaultdict
import gdsMill import gdsMill
@ -16,6 +16,7 @@ class timing_graph():
def __init__(self): def __init__(self):
self.graph = defaultdict(set) self.graph = defaultdict(set)
self.all_paths = []
def add_edge(self, src_node, dest_node): def add_edge(self, src_node, dest_node):
"""Adds edge to graph. Nodes added as well if they do not exist.""" """Adds edge to graph. Nodes added as well if they do not exist."""
@ -34,7 +35,7 @@ class timing_graph():
node = node.lower() node = node.lower()
self.graph[node] = set() 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""" """Traverse all paths from source to destination"""
src_node = src_node.lower() src_node = src_node.lower()
dest_node = dest_node.lower() dest_node = dest_node.lower()
@ -51,13 +52,15 @@ class timing_graph():
# Create an array to store paths # Create an array to store paths
path = [] path = []
self.path_count = 0 self.all_paths = []
# Call the recursive helper function to print all paths # Call the recursive helper function to print all paths
self.print_all_paths_util(src_node, dest_node, visited, path) self.get_all_paths_util(src_node, dest_node, visited, path)
debug.info(1, "Paths found={}".format(self.path_count)) 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""" """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)
@ -67,18 +70,32 @@ class timing_graph():
# current path[] # current path[]
if cur_node == dest_node: if cur_node == dest_node:
debug.info(1,"{}".format(path)) debug.info(1,"{}".format(path))
self.path_count+=1 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.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 # Remove current vertex from path[] and mark it as unvisited
path.pop() path.pop()
visited.remove(cur_node) 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): 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)

View File

@ -140,7 +140,13 @@ class spice():
debug.error("-----") debug.error("-----")
debug.error("Connections: \n"+str(conns_string),1) 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): def sp_read(self):
"""Reads the sp file (and parse the pins) from the library """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": if obj.meta_str is "read0":
obj.meta_add_delay = True obj.meta_add_delay = True
trig_name = "Xsram.s_en{}" #Sense amp enable # 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 # if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = "" # port_format = ""
else: # else:
port_format = "{}" # port_format = "{}"
bl_name = "Xsram.Xbank0.bl{}_{}".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) # 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[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)) # self.read_lib_meas.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5))
read_measures = [] read_measures = []
read_measures.append(self.read_lib_meas) read_measures.append(self.read_lib_meas)
#Other measurements associated with the read port not included in the liberty file #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()) read_measures.append(self.create_debug_measurement_objects())
return read_measures 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. """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 These measurements are only associated with read values
""" """
self.bitline_delay_meas = [] self.bitline_volt_meas = []
trig_name = "clk{0}" #Bitline voltage measures
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name self.bitline_volt_meas.append(voltage_at_measure("v_bl_name",
port_format = "" self.bl_name))
else: self.bitline_volt_meas[-1].meta_str = 'read0'
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
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit self.bitline_volt_meas.append(voltage_at_measure("v_br_name",
# 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.br_name))
# self.bitline_delay_meas[-1].meta_str = "read0" self.bitline_volt_meas[-1].meta_str = 'read1'
# 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)) return self.bitline_volt_meas
# 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
def create_write_port_measurement_objects(self): def create_write_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers""" """Create the measurements used for read ports: delays, slews, powers"""
@ -155,9 +145,11 @@ class delay(simulation):
debug_meas.parent = meas.name debug_meas.parent = meas.name
self.debug_delay_meas.append(debug_meas) self.debug_delay_meas.append(debug_meas)
#Output voltage measures
self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.meta_str), self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.meta_str),
debug_meas.targ_name_no_port)) debug_meas.targ_name_no_port))
self.debug_volt_meas[-1].meta_str = debug_meas.meta_str self.debug_volt_meas[-1].meta_str = debug_meas.meta_str
return self.debug_delay_meas+self.debug_volt_meas return self.debug_delay_meas+self.debug_volt_meas
def set_load_slew(self,load,slew): def set_load_slew(self,load,slew):
@ -183,7 +175,28 @@ class delay(simulation):
self.graph = graph_util.timing_graph() self.graph = graph_util.timing_graph()
self.sram.build_graph(self.graph,"X{}".format(self.sram.name),self.pins) self.sram.build_graph(self.graph,"X{}".format(self.sram.name),self.pins)
#debug.info(1,"{}".format(graph)) #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): def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements""" """Checks if arguments given for write_stimulus() meets requirements"""
@ -551,9 +564,6 @@ class delay(simulation):
result[port].update(read_port_dict) 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: for port in self.targ_write_ports:
write_port_dict = {} write_port_dict = {}
for measure in self.write_lib_meas: for measure in self.write_lib_meas:
@ -590,6 +600,13 @@ class delay(simulation):
debug.info(1, "Debug measurement failed. Incorrect Value found on output.") debug.info(1, "Debug measurement failed. Incorrect Value found on output.")
break 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: for meas in self.debug_volt_meas:
val = meas.retrieve_measure(port=port) val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val)) debug.info(2,"{}={}".format(meas.name, val))
@ -605,18 +622,19 @@ class delay(simulation):
debug.info(1, "Debug measurement failed. Value {}v was read on read 0 cycle.".format(val)) debug.info(1, "Debug measurement failed. Value {}v was read on read 0 cycle.".format(val))
break 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 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.""" def check_bitline_meas(self, v_bitline):
bl_delay_meas_dict = {} """Checks the value of the discharging bitline"""
values_added = 0 #For error checking
for measure in self.bitline_delay_meas: return v_bitline < self.vdd_voltage*0.9
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 run_power_simulation(self): def run_power_simulation(self):
""" """

View File

@ -10,7 +10,7 @@ class tri_gate(design.design):
netlist should be available in the technology library. 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"] type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) 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 #Insts located in control logic, exclusion function called here
for inst in self.control_logic_insts: for inst in self.control_logic_insts:
inst.mod.graph_exclude_dffs() 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) reload(characterizer)
from characterizer import delay from characterizer import delay
from sram_config import sram_config from sram_config import sram_config
c = sram_config(word_size=1, # c = sram_config(word_size=1,
num_words=16, # num_words=16,
num_banks=1)
c.words_per_row=1
# c = sram_config(word_size=32,
# num_words=256,
# num_banks=1) # num_banks=1)
# c.words_per_row=2 # c.words_per_row=1
#OPTS.use_tech_delay_chain_size = True 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() c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = factory.create(module_type="sram", sram_config=c) s = factory.create(module_type="sram", sram_config=c)