Added smarter name checking for the characterizer.

This commit is contained in:
Hunter Nichols 2019-05-27 13:08:59 -07:00
parent d08181455c
commit e2d1f7ab0a
5 changed files with 142 additions and 36 deletions

View File

@ -8,7 +8,7 @@ import globals
import debug
from vector import vector
from pin_layout import pin_layout
class timing_graph():
"""Implements a directed graph
Nodes are currently just Strings.

View File

@ -104,17 +104,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
total_lvs_errors += num_errors
debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors))
os.remove(tempspice)
os.remove(tempgds)
#Example graph run
# import graph_util
# graph = graph_util.graph()
# pins = ['A','Z','vdd','gnd']
# d.build_graph(graph,"Xpdriver",pins)
# graph.remove_edges('vdd')
# graph.remove_edges('gnd')
# debug.info(1,"{}".format(graph))
# graph.print_all_paths('A', 'Z')
os.remove(tempgds)
def init_graph_params(self):
"""Initializes parameters relevant to the graph creation"""
@ -136,6 +126,71 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets):
"""Collects all the nets and the parent inst of that net."""
#Translate port names to external nets
if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1)
port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
debug.info(3, "Instance name={}".format(inst_name))
for subinst, conns in zip(self.insts, self.conns):
subinst_name = inst_name+'.X'+subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
for si_port, conn in zip(subinst_ports, conns):
#Only add for first occurrence
if si_port.lower() not in name_dict:
mod_info = {'mod':self, 'int_net':conn}
name_dict[si_port.lower()] = mod_info
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod):
"""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).
"""
try:
self.name_dict
except AttributeError:
self.name_dict = {}
self.build_names(self.name_dict, inst_name, port_nets)
aliases = []
for net in path_nets:
net = net.lower()
int_net = self.name_dict[net]['int_net']
int_mod = self.name_dict[net]['mod']
if int_mod.is_net_alias(int_net, alias, alias_mod):
aliases.append(net)
debug.info(1,"Aliases Found={}".format(aliases))
return aliases
def is_net_alias(self, known_net, net_alias, mod):
"""Checks if the alias_net in mod is the same as the input net."""
#Check ports of this mod
for pin in self.pins:
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
return True
#Check connections of all other subinsts
for subinst, inst_conns in zip(self.insts, self.conns):
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
return True
elif inst_conn.lower() == known_net.lower():
return subinst.mod.is_net_alias(mod_pin, net_alias, mod)
return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
"""Utility function for checking single net alias."""
return self == mod and \
child_net.lower() == alias_net.lower() and \
parent_net.lower() == alias_net.lower()
def get_mod_net(self, parent_net, child_inst, child_conns):
"""Given an instance and net, returns the internal net in the mod
corresponding to input net."""
for conn, pin in zip(child_conns, child_inst.mod.pins):
if parent_net.lower() == conn.lower():
return pin
return None
def translate_nets(self, subinst_ports, port_dict, inst_name):
"""Converts connection names to their spice hierarchy equivalent"""
converted_conns = []
@ -159,7 +214,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for out in output_pins+inout_pins:
if inp != out: #do not add self loops
graph.add_edge(pin_dict[inp], pin_dict[out])
def __str__(self):
""" override print function output """
pins = ",".join(self.pins)

View File

@ -18,6 +18,7 @@ from .simulation import simulation
from .measurements import *
import logical_effort
import graph_util
from sram_factory import factory
class delay(simulation):
"""Functions to measure the delay and power of an SRAM at a given address and
@ -99,17 +100,6 @@ class delay(simulation):
for obj in self.read_lib_meas:
if obj.meta_str is sram_op.READ_ZERO:
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 = "{}"
# 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)
@ -230,19 +220,20 @@ class delay(simulation):
#Generate new graph every analysis as edges might change depending on test bit
self.graph = graph_util.timing_graph()
self.sram.build_graph(self.graph,"X{}".format(self.sram.name),self.pins)
self.sram_spc_name = "X{}".format(self.sram.name)
self.sram.build_graph(self.graph,self.sram_spc_name,self.pins)
#debug.info(1,"{}".format(self.graph))
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.")
sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(1,"s_en name = {}".format(sen_name))
self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths)
import sys
sys.exit(1)
preconv_names = []
for path in self.graph.all_paths:
if not path is sen_path:
@ -258,7 +249,52 @@ class delay(simulation):
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, self.wordline_row, self.bitline_column)
debug.info(1, "cell_name={}".format(cell_name))
def get_sen_name(self, paths):
"""Gets the signal name associated with the sense amp enable from input paths.
Only expects a single path to contain the sen signal name."""
sa_mods = factory.get_mods(OPTS.sense_amp)
#Any sense amp instantiated should be identical, any change to that
#will require some identification to determine the mod desired.
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
enable_name = sa_mods[0].get_enable_name()
sen_found = False
#Only a single path should contain a single s_en name. Anything else is an error.
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, enable_name, sa_mods[0])
if sen_found and len(aliases) >= 1:
debug.error('Found multiple paths with SA enable.',1)
elif len(aliases) > 1:
debug.error('Found multiple S_EN points in single path. Cannot distinguish between them.',1)
elif not sen_found and len(aliases) == 1:
sen_name = aliases[0]
sen_found = True
if not sen_found:
debug.error("Could not find S_EN name.",1)
return sen_name
def get_bl_name(self, paths):
"""Gets the signal name associated with the bitlines in the bank."""
sa_mods = factory.get_mods(OPTS.sense_amp)
#Any sense amp instantiated should be identical, any change to that
#will require some identification to determine the mod desired.
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
enable_name = sa_mods[0].get_enable_name()
sen_found = False
#Only a single path should contain a single s_en name. Anything else is an error.
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, enable_name, sa_mods[0])
if sen_found and len(aliases) >= 1:
debug.error('Found multiple paths with SA enable.',1)
elif len(aliases) > 1:
debug.error('Found multiple S_EN points in single path. Cannot distinguish between them.',1)
elif not sen_found and len(aliases) == 1:
sen_name = aliases[0]
sen_found = True
if not sen_found:
debug.error("Could not find S_EN name.",1)
return sen_name
def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements"""

View File

@ -61,7 +61,14 @@ class sense_amp(design.design):
nmos_cin = parameter["sa_en_nmos_size"]/drc("minwidth_tx")
#sen is connected to 2 pmos isolation TX and 1 nmos per sense amp.
return 2*pmos_cin + nmos_cin
def get_enable_name(self):
"""Returns name used for enable net"""
#FIXME: A better programmatic solution to designate pins
enable_name = "en"
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
return enable_name
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -83,7 +83,15 @@ class sram_factory:
self.objects[module_type].append((kwargs,obj))
return obj
def get_mods(self, module_type):
"""Returns list of all objects of module name's type."""
try:
mod_tuples = self.objects[module_type]
mods = [mod for kwargs,mod in mod_tuples]
except KeyError:
mods = []
return mods
# Make a factory
factory = sram_factory()