1 from collections
import OrderedDict
2 from itertools
import combinations
4 from migen
.fhdl
.structure
import *
12 self
.use_number
= False
13 self
.children
= OrderedDict()
16 def _display_tree(filename
, tree
):
17 from migen
.util
.treeviz
import RenderNode
19 def _to_render_node(name
, node
):
20 children
= [_to_render_node(k
, v
) for k
, v
in node
.children
.items()]
23 color
= (0.5, 0.9, 0.8)
25 color
= (0.8, 0.5, 0.9)
28 color
= (0.9, 0.8, 0.5)
30 color
= (0.8, 0.8, 0.8)
31 label
= "{0}\n{1} signals\n{2}".format(name
, node
.signal_count
, node
.numbers
)
32 return RenderNode(label
, children
, color
=color
)
34 top
= _to_render_node("top", tree
)
38 def _build_tree(signals
, basic_tree
=None):
40 for signal
in signals
:
41 current_b
= basic_tree
43 current
.signal_count
+= 1
44 for name
, number
in signal
.backtrace
:
45 if basic_tree
is None:
48 current_b
= current_b
.children
[name
]
49 use_number
= current_b
.use_number
55 current
= current
.children
[key
]
58 current
.children
[key
] = new
60 current
.numbers
.add(number
)
62 current
.all_numbers
= sorted(current_b
.numbers
)
63 current
.signal_count
+= 1
67 def _set_use_name(node
, node_name
=""):
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
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
)
81 if node
.signal_count
> sum(c
.signal_count
for c
in node
.children
.values()):
88 def _name_signal(tree
, signal
):
91 for step_name
, step_n
in signal
.backtrace
:
93 treepos
= treepos
.children
[(step_name
, step_n
)]
96 treepos
= treepos
.children
[step_name
]
101 elname
+= str(treepos
.all_numbers
.index(step_n
))
102 elements
.append(elname
)
103 return "_".join(elements
)
106 def _build_pnd_from_tree(tree
, signals
):
107 return dict((signal
, _name_signal(tree
, signal
)) for signal
in signals
)
110 def _invert_pnd(pnd
):
112 for k
, v
in pnd
.items():
113 inv_pnd
[v
] = inv_pnd
.get(v
, [])
118 def _list_conflicting_signals(pnd
):
119 inv_pnd
= _invert_pnd(pnd
)
121 for k
, v
in inv_pnd
.items():
127 def _set_use_number(tree
, signals
):
128 for signal
in signals
:
130 for step_name
, step_n
in signal
.backtrace
:
131 current
= current
.children
[step_name
]
132 current
.use_number
= current
.signal_count
> len(current
.numbers
) and len(current
.numbers
) > 1
137 def _build_pnd_for_group(group_n
, signals
):
138 basic_tree
= _build_tree(signals
)
139 _set_use_name(basic_tree
)
141 _display_tree("tree{0}_basic.svg".format(group_n
), basic_tree
)
142 pnd
= _build_pnd_from_tree(basic_tree
, signals
)
144 # If there are conflicts, try splitting the tree by numbers
145 # on paths taken by conflicting signals.
146 conflicting_signals
= _list_conflicting_signals(pnd
)
147 if conflicting_signals
:
148 _set_use_number(basic_tree
, conflicting_signals
)
150 print("namer: using split-by-number strategy (group {0})".format(group_n
))
151 _display_tree("tree{0}_marked.svg".format(group_n
), basic_tree
)
152 numbered_tree
= _build_tree(signals
, basic_tree
)
153 _set_use_name(numbered_tree
)
155 _display_tree("tree{0}_numbered.svg".format(group_n
), numbered_tree
)
156 pnd
= _build_pnd_from_tree(numbered_tree
, signals
)
159 print("namer: using basic strategy (group {0})".format(group_n
))
161 # ...then add number suffixes by DUID
162 inv_pnd
= _invert_pnd(pnd
)
163 duid_suffixed
= False
164 for name
, signals
in inv_pnd
.items():
167 for n
, signal
in enumerate(sorted(signals
, key
=lambda x
: x
.duid
)):
168 pnd
[signal
] += str(n
)
169 if _debug
and duid_suffixed
:
170 print("namer: using DUID suffixes (group {0})".format(group_n
))
175 def _build_signal_groups(signals
):
177 for signal
in signals
:
178 # build chain of related signals
181 while cur_signal
is not None:
182 related_list
.insert(0, cur_signal
)
183 cur_signal
= cur_signal
.related
185 for _
in range(len(related_list
) - len(r
)):
187 for target_set
, source_signal
in zip(r
, related_list
):
188 target_set
.add(source_signal
)
189 # with the algorithm above and a list of all signals,
190 # a signal appears in all groups of a lower number than its.
191 # make signals appear only in their group of highest number.
192 for s1
, s2
in zip(r
, r
[1:]):
197 def _build_pnd(signals
):
198 groups
= _build_signal_groups(signals
)
199 gpnds
= [_build_pnd_for_group(n
, gsignals
) for n
, gsignals
in enumerate(groups
)]
202 for gn
, gpnd
in enumerate(gpnds
):
203 for signal
, name
in gpnd
.items():
207 while cur_signal
.related
is not None:
208 cur_signal
= cur_signal
.related
210 result
= gpnds
[cur_gn
][cur_signal
] + "_" + result
216 def build_namespace(signals
, reserved_keywords
=set()):
217 pnd
= _build_pnd(signals
)
218 ns
= Namespace(pnd
, reserved_keywords
)
219 # register signals with name_override
220 for signal
in signals
:
221 if signal
.name_override
is not None:
227 def __init__(self
, pnd
, reserved_keywords
=set()):
228 self
.counts
= {k
: 1 for k
in reserved_keywords
}
231 self
.clock_domains
= dict()
233 def get_name(self
, sig
):
234 if isinstance(sig
, ClockSignal
):
235 sig
= self
.clock_domains
[sig
.cd
].clk
236 if isinstance(sig
, ResetSignal
):
237 sig
= self
.clock_domains
[sig
.cd
].rst
239 raise ValueError("Attempted to obtain name of non-existent "
240 "reset signal of domain "+sig
.cd
)
242 if sig
.name_override
is not None:
243 sig_name
= sig
.name_override
245 sig_name
= self
.pnd
[sig
]
250 n
= self
.counts
[sig_name
]
254 self
.counts
[sig_name
] = n
+ 1
256 return sig_name
+ "_" + str(n
)