mirror of https://github.com/VLSIDA/OpenRAM.git
Added smarter name checking for the characterizer.
This commit is contained in:
parent
d08181455c
commit
e2d1f7ab0a
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue