Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 5 commits
  • 2 files changed
  • 0 comments
  • 1 contributor
251  migen/fhdl/namer.py
... ...
@@ -1,122 +1,163 @@
  1
+from collections import OrderedDict
1 2
 from itertools import combinations
2  
-from collections import defaultdict
3 3
 
4 4
 from migen.fhdl.structure import *
5  
-from migen.fhdl.tracer import index_id
6  
-
7  
-def _bin(sig_iters):
8  
-	# advance by one in the trace of each signal
9  
-	status = []
10  
-	for signal, it in sig_iters:
11  
-		step, last = next(it)
12  
-		status.append((signal, it, step, last))
  5
+
  6
+class _Node:
  7
+	def __init__(self):
  8
+		self.signal_count = 0
  9
+		self.numbers = set()
  10
+		self.use_name = False
  11
+		self.use_number = False
  12
+		self.children = OrderedDict()
  13
+
  14
+def _display_tree(filename, tree):
  15
+	from migen.graph.treeviz import RenderNode
13 16
 	
14  
-	# build bins accordingly
15  
-	bins = defaultdict(lambda: defaultdict(list))
16  
-	for signal, it, (stepname, stepidx), last in status:
17  
-		if last:
18  
-			it = None
19  
-		bins[stepname][stepidx].append((signal, it))
20  
-
21  
-	r = []
22  
-	# merge bins when all step indices differ
23  
-	for stepname, stepname_d in bins.items():
24  
-		if all(len(content) == 1 for content in stepname_d.values()):
25  
-			r.append((stepname, [(stepidx, signal, it)
26  
-				for stepidx, stepidx_d in stepname_d.items()
27  
-				for signal, it in stepidx_d]))
  17
+	def _to_render_node(name, node):
  18
+		children = [_to_render_node(k, v) for k, v in node.children.items()]
  19
+		if node.use_name:
  20
+			if node.use_number:
  21
+				color = (0.5, 0.9, 0.8)
  22
+			else:
  23
+				color = (0.8, 0.5, 0.9)
28 24
 		else:
29  
-			for stepidx, stepidx_d in stepname_d.items():
30  
-				r.append((stepname, [(stepidx, signal, it)
31  
-					for signal, it in stepidx_d]))
32  
-	
33  
-	#for stepname, content in r:
34  
-		#print("Bin: " + stepname)
35  
-		#for stepidx, signal, it in content:
36  
-			#print("   stepidx:" + str(stepidx) + " " + str(signal) + " " + str(it))
37  
-	#print("++++++++++")
38  
-	
39  
-	return r
  25
+			if node.use_number:
  26
+				color = (0.9, 0.8, 0.5)
  27
+			else:
  28
+				color = (0.8, 0.8, 0.8)
  29
+		label = "{0}\n{1} signals\n{2}".format(name, node.signal_count, node.numbers)
  30
+		return RenderNode(label, children, color=color)
40 31
 
41  
-def _sets_disjoint(l):
42  
-	for s1, s2 in combinations(l, 2):
43  
-		if not s1.isdisjoint(s2):
44  
-			return False
45  
-	return True
  32
+	top = _to_render_node("top", tree)
  33
+	top.to_svg(filename)
46 34
 
47  
-# sig_iters contains a list of tuples (signal, iterator on the current trace position)
48  
-def _r_build_pnd(sig_iters):
49  
-	bins = _bin(sig_iters)
50  
-	
51  
-	subnames = {}
52  
-	mentions = defaultdict(list)
53  
-	bins_named = []
54  
-	stepindices = {}
55  
-	
56  
-	for stepname, next_steps in bins:
57  
-		bin_content = []
58  
-		for stepidx, signal, it in next_steps:
59  
-			if it is None:
60  
-				mentions[stepname].append(signal)
  35
+def _build_tree(signals, basic_tree=None):
  36
+	root = _Node()
  37
+	for signal in signals:
  38
+		current_b = basic_tree
  39
+		current = root
  40
+		current.signal_count += 1
  41
+		for name, number in signal.backtrace:
  42
+			if basic_tree is None:
  43
+				use_number = False
61 44
 			else:
62  
-				bin_content.append((signal, it))
63  
-			stepindices[signal] = stepidx
64  
-		if bin_content:
65  
-			bins_named.append((stepname, _r_build_pnd(bin_content)))
66  
-	
67  
-	name_sets = [set(sub_pnd.values()) for prefix, sub_pnd in bins_named]
68  
-	if not _sets_disjoint(name_sets):
69  
-		for prefix, sub_pnd in bins_named:
70  
-			for signal, subname in sub_pnd.items():
71  
-				subname = (prefix, subname)
72  
-				subnames[signal] = subname
73  
-				mentions[subname].append(signal)
74  
-	else:
75  
-		for prefix, sub_pnd in bins_named:
76  
-			for signal, subname in sub_pnd.items():
77  
-				subname = ("", subname)
78  
-				subnames[signal] = subname
79  
-				mentions[subname].append(signal)
80  
-	
81  
-	# Sort lists of mentions by step indices
82  
-	for v in mentions.values():
83  
-		v.sort(key=lambda x: stepindices[x])
84  
-	
85  
-	r = {}
86  
-	for stepname, next_steps in bins:
87  
-		for stepidx, signal, it in next_steps:
88  
-			if it is None:
89  
-				name = stepname
90  
-				prefix = ""
  45
+				current_b = current_b.children[name]
  46
+				use_number = current_b.use_number
  47
+			if use_number:
  48
+				key = (name, number)
91 49
 			else:
92  
-				prefix = subnames[signal][0]
93  
-				name = subnames[signal][1]
94  
-			mention = mentions[(prefix, name)]
95  
-			if prefix:
96  
-				if len(mention) > 1:
97  
-					r[signal] = prefix + str(index_id(mention, signal)) + "_" + name
98  
-				else:
99  
-					r[signal] = prefix + "_" + name
  50
+				key = name
  51
+			try:
  52
+				current = current.children[key]
  53
+			except KeyError:
  54
+				new = _Node()
  55
+				current.children[key] = new
  56
+				current = new
  57
+			current.numbers.add(number)
  58
+			if use_number:
  59
+				current.all_numbers = sorted(current_b.numbers)
  60
+			current.signal_count += 1
  61
+	return root
  62
+
  63
+def _set_use_name(node, node_name=""):
  64
+	if not node.children:
  65
+		node.use_name = True
  66
+		return {(node_name, )}
  67
+	else:
  68
+		cnames = [(k, _set_use_name(v, k)) for k, v in node.children.items()]
  69
+		for (c1_prefix, c1_names), (c2_prefix, c2_names) in combinations(cnames, 2):
  70
+			if not c1_names.isdisjoint(c2_names):
  71
+				node.children[c1_prefix].use_name = True
  72
+				node.children[c2_prefix].use_name = True
  73
+		r = set()
  74
+		for c_prefix, c_names in cnames:
  75
+			if node.children[c_prefix].use_name:
  76
+				for c_name in c_names:
  77
+					r.add((c_prefix, ) + c_name)
100 78
 			else:
101  
-				if len(mention) > 1:
102  
-					r[signal] = name + str(index_id(mention, signal))
103  
-				else:
104  
-					r[signal] = name
105  
-	
  79
+				r |= c_names
  80
+		return r
  81
+
  82
+def _name_signal(tree, signal):
  83
+	elements = []
  84
+	treepos = tree
  85
+	for step_name, step_n in signal.backtrace:
  86
+		try:
  87
+			treepos = treepos.children[(step_name, step_n)]
  88
+			use_number = True
  89
+		except KeyError:
  90
+			treepos = treepos.children[step_name]
  91
+			use_number = False
  92
+		if treepos.use_name:
  93
+			elname = step_name
  94
+			if use_number:
  95
+				elname += str(treepos.all_numbers.index(step_n))
  96
+			elements.append(elname)
  97
+	return "_".join(elements)
  98
+
  99
+def _build_pnd(tree, signals):
  100
+	return dict((signal, _name_signal(tree, signal)) for signal in signals)
  101
+
  102
+def _invert_pnd(pnd):
  103
+	inv_pnd = dict()
  104
+	for k, v in pnd.items():
  105
+		inv_pnd[v] = inv_pnd.get(v, [])
  106
+		inv_pnd[v].append(k)
  107
+	return inv_pnd
  108
+
  109
+def _list_conflicting_signals(pnd):
  110
+	inv_pnd = _invert_pnd(pnd)
  111
+	r = set()
  112
+	for k, v in inv_pnd.items():
  113
+		if len(v) > 1:
  114
+			r.update(v)
106 115
 	return r
107 116
 
108  
-def last_flagged(seq):
109  
-	seq = iter(seq)
110  
-	a = next(seq)
111  
-	for b in seq:
112  
-		yield a, False
113  
-		a = b
114  
-	yield a, True
  117
+def _set_use_number(tree, signals):
  118
+	for signal in signals:
  119
+		current = tree
  120
+		for step_name, step_n in signal.backtrace:
  121
+			current = current.children[step_name]
  122
+			current.use_number = current.signal_count > len(current.numbers) and len(current.numbers) > 1
  123
+
  124
+_debug = False
115 125
 
116 126
 def build_namespace(signals):
117  
-	sig_iters = [(signal, last_flagged(signal.backtrace))
118  
-	  for signal in signals if signal.name_override is None]
119  
-	pnd = _r_build_pnd(sig_iters)
  127
+	basic_tree = _build_tree(signals)
  128
+	_set_use_name(basic_tree)
  129
+	if _debug:
  130
+		_display_tree("tree_basic.svg", basic_tree)
  131
+	pnd = _build_pnd(basic_tree, signals)
  132
+
  133
+	# If there are conflicts, try splitting the tree by numbers
  134
+	# on paths taken by conflicting signals.
  135
+	conflicting_signals = _list_conflicting_signals(pnd)
  136
+	if conflicting_signals:
  137
+		_set_use_number(basic_tree, conflicting_signals)
  138
+		if _debug:
  139
+			print("namer: using split-by-number strategy")
  140
+			_display_tree("tree_marked.svg", basic_tree)
  141
+		numbered_tree = _build_tree(signals, basic_tree)
  142
+		_set_use_name(numbered_tree)
  143
+		if _debug:
  144
+			_display_tree("tree_numbered.svg", numbered_tree)
  145
+		pnd = _build_pnd(numbered_tree, signals)
  146
+	else:
  147
+		if _debug:
  148
+			print("namer: using basic strategy")
  149
+	
  150
+	# ...then add number suffixes by HUID
  151
+	inv_pnd = _invert_pnd(pnd)
  152
+	huid_suffixed = False
  153
+	for name, signals in inv_pnd.items():
  154
+		if len(signals) > 1:
  155
+			huid_suffixed = True
  156
+			for n, signal in enumerate(sorted(signals, key=lambda x: x.huid)):
  157
+				pnd[signal] += str(n)
  158
+	if _debug and huid_suffixed:
  159
+		print("namer: using HUID suffixes")
  160
+
120 161
 	ns = Namespace(pnd)
121 162
 	# register signals with name_override
122 163
 	for signal in signals:
58  migen/graph/treeviz.py
... ...
@@ -1,9 +1,11 @@
1 1
 import cairo
2 2
 import math
3 3
 
4  
-def _cairo_draw_node(ctx, radius, color, outer_color, s):
  4
+def _cairo_draw_node(ctx, dx, radius, color, outer_color, s):
5 5
 	ctx.save()
6 6
 
  7
+	ctx.translate(dx, 0)
  8
+
7 9
 	ctx.set_line_width(0.0)
8 10
 	gradient_color = cairo.RadialGradient(0, 0, 0, 0, 0, radius)
9 11
 	gradient_color.add_color_stop_rgb(0, *color)
@@ -12,10 +14,20 @@ def _cairo_draw_node(ctx, radius, color, outer_color, s):
12 14
 	ctx.arc(0, 0, radius, 0, 2*math.pi)
13 15
 	ctx.fill()
14 16
 
15  
-	ctx.set_source_rgb(0, 0, 0)
16  
-	x_bearing, y_bearing, textw, texth, x_advance, y_advance = ctx.text_extents(s)
17  
-	ctx.translate(-textw/2, texth/2)
18  
-	ctx.show_text(s)
  17
+	lines = s.split("\n")
  18
+	textws = []
  19
+	texths = []
  20
+	for line in lines:
  21
+		x_bearing, y_bearing, w, h, x_advance, y_advance = ctx.text_extents(line)
  22
+		textws.append(w)
  23
+		texths.append(h + 2)
  24
+	ctx.translate(0, -sum(texths[1:])/2)
  25
+	for line, w, h in zip(lines, textws, texths):
  26
+		ctx.translate(-w/2, h/2)
  27
+		ctx.move_to(0, 0)
  28
+		ctx.set_source_rgb(0, 0, 0)
  29
+		ctx.show_text(line)
  30
+		ctx.translate(w/2, h/2)
19 31
 
20 32
 	ctx.restore()
21 33
 
@@ -40,36 +52,44 @@ def __init__(self, label, children=None, color=(0.8, 0.8, 0.8), radius=40):
40 52
 		self.radius = radius
41 53
 		self.pitch = self.radius*3
42 54
 
43  
-	def get_extents(self):
  55
+	def get_dimensions(self):
44 56
 		if self.children:
45  
-			cw, ch = zip(*[c.get_extents() for c in self.children])
46  
-			w = max(cw)*len(self.children)
47  
-			h = self.pitch + max(ch)
  57
+			cws, chs, cdxs = zip(*[c.get_dimensions() for c in self.children])
  58
+			w = sum(cws)
  59
+			h = self.pitch + max(chs)
  60
+			dx = cws[0]/4 - cws[-1]/4
48 61
 		else:
49 62
 			w = h = self.pitch
50  
-		return w, h
  63
+			dx = 0
  64
+		return w, h, dx
51 65
 
52 66
 	def render(self, ctx):
53  
-		_cairo_draw_node(ctx, self.radius, self.color, self.outer_color, self.label)
54 67
 		if self.children:
55  
-			cpitch = max([c.get_extents()[0] for c in self.children])
56  
-			first_child_x = -(cpitch*(len(self.children) - 1))/2
  68
+			cws, chs, cdxs = zip(*[c.get_dimensions() for c in self.children])
  69
+			first_child_x = -sum(cws)/2
57 70
 
58 71
 			ctx.save()
59 72
 			ctx.translate(first_child_x, self.pitch)
60  
-			for c in self.children:
  73
+			for c, w in zip(self.children, cws):
  74
+				ctx.translate(w/2, 0)
61 75
 				c.render(ctx)
62  
-				ctx.translate(cpitch, 0)
  76
+				ctx.translate(w/2, 0)
63 77
 			ctx.restore()
64 78
 
  79
+			dx = cws[0]/4 - cws[-1]/4
  80
+
65 81
 			current_x = first_child_x
66  
-			for c in self.children:
  82
+			for c, w, cdx in zip(self.children, cws, cdxs):
67 83
 				current_y = self.pitch - c.radius
68  
-				_cairo_draw_connection(ctx, 0, self.radius, self.outer_color, current_x, current_y, c.outer_color)
69  
-				current_x += cpitch
  84
+				current_x += w/2
  85
+				_cairo_draw_connection(ctx, dx, self.radius, self.outer_color, current_x+cdx, current_y, c.outer_color)
  86
+				current_x += w/2
  87
+		else:
  88
+			dx = 0
  89
+		_cairo_draw_node(ctx, dx, self.radius, self.color, self.outer_color, self.label)
70 90
 
71 91
 	def to_svg(self, name):
72  
-		w, h = self.get_extents()
  92
+		w, h, dx = self.get_dimensions()
73 93
 		surface = cairo.SVGSurface(name, w, h)
74 94
 		ctx = cairo.Context(surface)
75 95
 		ctx.translate(w/2, self.pitch/2)

No commit comments for this range

Something went wrong with that request. Please try again.