fhdl/namer: new namer with explicit tree
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Wed, 7 Aug 2013 15:13:52 +0000 (17:13 +0200)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Wed, 7 Aug 2013 15:13:52 +0000 (17:13 +0200)
migen/fhdl/namer.py

index d27f303feddc4ad306b5935cf16e59d4350162fb..dfa9bd5ff8a1c1567e0b659b637492ae5d8812cb 100644 (file)
+from collections import OrderedDict
 from itertools import combinations
-from collections import defaultdict
 
 from migen.fhdl.structure import *
-from migen.fhdl.tracer import index_id
 
-def _bin(sig_iters):
-       # advance by one in the trace of each signal
-       status = []
-       for signal, it in sig_iters:
-               step, last = next(it)
-               status.append((signal, it, step, last))
-       
-       # build bins accordingly
-       bins = defaultdict(lambda: defaultdict(list))
-       for signal, it, (stepname, stepidx), last in status:
-               if last:
-                       it = None
-               bins[stepname][stepidx].append((signal, it))
-
-       r = []
-       # merge bins when all step indices differ
-       for stepname, stepname_d in bins.items():
-               if all(len(content) == 1 for content in stepname_d.values()):
-                       r.append((stepname, [(stepidx, signal, it)
-                               for stepidx, stepidx_d in stepname_d.items()
-                               for signal, it in stepidx_d]))
-               else:
-                       for stepidx, stepidx_d in stepname_d.items():
-                               r.append((stepname, [(stepidx, signal, it)
-                                       for signal, it in stepidx_d]))
-       
-       #for stepname, content in r:
-               #print("Bin: " + stepname)
-               #for stepidx, signal, it in content:
-                       #print("   stepidx:" + str(stepidx) + " " + str(signal) + " " + str(it))
-       #print("++++++++++")
-       
-       return r
+class _Node:
+       def __init__(self):
+               self.use_name = False
+               self.children = OrderedDict()
 
-def _sets_disjoint(l):
-       for s1, s2 in combinations(l, 2):
-               if not s1.isdisjoint(s2):
-                       return False
-       return True
+def _build_tree(signals):
+       root = _Node()
+       for signal in signals:
+               current = root
+               for name, number in signal.backtrace:
+                       try:
+                               current = current.children[name]
+                       except KeyError:
+                               new = _Node()
+                               current.children[name] = new
+                               current = new
+       return root
 
-# sig_iters contains a list of tuples (signal, iterator on the current trace position)
-def _r_build_pnd(sig_iters):
-       bins = _bin(sig_iters)
-       
-       subnames = {}
-       mentions = defaultdict(list)
-       bins_named = []
-       stepindices = {}
-       
-       for stepname, next_steps in bins:
-               bin_content = []
-               for stepidx, signal, it in next_steps:
-                       if it is None:
-                               mentions[stepname].append(signal)
-                       else:
-                               bin_content.append((signal, it))
-                       stepindices[signal] = stepidx
-               if bin_content:
-                       bins_named.append((stepname, _r_build_pnd(bin_content)))
-       
-       name_sets = [set(sub_pnd.values()) for prefix, sub_pnd in bins_named]
-       if not _sets_disjoint(name_sets):
-               for prefix, sub_pnd in bins_named:
-                       for signal, subname in sub_pnd.items():
-                               subname = (prefix, subname)
-                               subnames[signal] = subname
-                               mentions[subname].append(signal)
+def _set_use_name(node, node_name=""):
+       if not node.children:
+               node.use_name = True
+               return {(node_name, )}
        else:
-               for prefix, sub_pnd in bins_named:
-                       for signal, subname in sub_pnd.items():
-                               subname = ("", subname)
-                               subnames[signal] = subname
-                               mentions[subname].append(signal)
-       
-       # Sort lists of mentions by step indices
-       for v in mentions.values():
-               v.sort(key=lambda x: stepindices[x])
-       
-       r = {}
-       for stepname, next_steps in bins:
-               for stepidx, signal, it in next_steps:
-                       if it is None:
-                               name = stepname
-                               prefix = ""
-                       else:
-                               prefix = subnames[signal][0]
-                               name = subnames[signal][1]
-                       mention = mentions[(prefix, name)]
-                       if prefix:
-                               if len(mention) > 1:
-                                       r[signal] = prefix + str(index_id(mention, signal)) + "_" + name
-                               else:
-                                       r[signal] = prefix + "_" + name
+               cnames = [(k, _set_use_name(v, k)) for k, v in node.children.items()]
+               for (c1_prefix, c1_names), (c2_prefix, c2_names) in combinations(cnames, 2):
+                       if not c1_names.isdisjoint(c2_names):
+                               node.children[c1_prefix].use_name = True
+                               node.children[c2_prefix].use_name = True
+               r = set()
+               for c_prefix, c_names in cnames:
+                       if node.children[c_prefix].use_name:
+                               for c_name in c_names:
+                                       r.add((c_prefix, ) + c_name)
                        else:
-                               if len(mention) > 1:
-                                       r[signal] = name + str(index_id(mention, signal))
-                               else:
-                                       r[signal] = name
+                               r |= c_names
+               return r
+
+def _display_tree(tree):
+       from migen.graph.treeviz import RenderNode
        
-       return r
+       def _to_render_node(name, node):
+               children = [_to_render_node(k, v) for k, v in node.children.items()]
+               if node.use_name:
+                       color = (0.8, 0.5, 0.9)
+               else:
+                       color = (0.8, 0.8, 0.8)
+               return RenderNode(name, children, color=color)
+
+       top = _to_render_node("top", tree)
+       top.to_svg("names.svg")
 
-def last_flagged(seq):
-       seq = iter(seq)
-       a = next(seq)
-       for b in seq:
-               yield a, False
-               a = b
-       yield a, True
+def _name_signal(tree, signal):
+       elements = []
+       treepos = tree
+       for step_name, step_n in signal.backtrace:
+               treepos = treepos.children[step_name]
+               if treepos.use_name:
+                       elements.append(step_name)
+       return "_".join(elements)
 
+def _build_pnd(tree, signals):
+       return dict((signal, _name_signal(tree, signal)) for signal in signals)
+       
 def build_namespace(signals):
-       sig_iters = [(signal, last_flagged(signal.backtrace))
-         for signal in signals if signal.name_override is None]
-       pnd = _r_build_pnd(sig_iters)
+       tree = _build_tree(signals)
+       _set_use_name(tree)
+       _display_tree(tree)
+       pnd = _build_pnd(tree, signals)
+       
        ns = Namespace(pnd)
        # register signals with name_override
        for signal in signals: