diff --git a/compiler/base/graph_util.py b/compiler/base/graph_util.py index 25c79d3b..af4d0888 100644 --- a/compiler/base/graph_util.py +++ b/compiler/base/graph_util.py @@ -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. diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 1bb9f44e..f6b10530 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -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) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index e92663d9..3f97f885 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -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""" diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index e28f3efa..8e0ff112 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -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) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 325261a0..90478f4c 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -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()