litex/migen/fhdl/tools.py

249 lines
6.4 KiB
Python
Raw Normal View History

2011-12-16 15:30:14 -05:00
from migen.fhdl.structure import *
2013-04-14 07:50:26 -04:00
from migen.fhdl.structure import _Slice, _Assign
from migen.fhdl.visit import NodeVisitor, NodeTransformer
2013-12-03 16:12:40 -05:00
from migen.fhdl.bitcontainer import value_bits_sign
from migen.util.misc import flat_iteration
2013-01-05 08:18:15 -05:00
2012-11-26 15:40:23 -05:00
class _SignalLister(NodeVisitor):
def __init__(self):
self.output_list = set()
def visit_Signal(self, node):
self.output_list.add(node)
class _TargetLister(NodeVisitor):
def __init__(self):
self.output_list = set()
self.target_context = False
def visit_Signal(self, node):
if self.target_context:
self.output_list.add(node)
def visit_Assign(self, node):
self.target_context = True
self.visit(node.l)
self.target_context = False
def visit_ArrayProxy(self, node):
for choice in node.choices:
self.visit(choice)
2012-11-26 15:40:23 -05:00
2011-12-16 10:02:55 -05:00
def list_signals(node):
2012-11-26 15:40:23 -05:00
lister = _SignalLister()
lister.visit(node)
return lister.output_list
2011-12-04 16:41:50 -05:00
2011-12-16 10:02:55 -05:00
def list_targets(node):
2012-11-26 15:40:23 -05:00
lister = _TargetLister()
lister.visit(node)
return lister.output_list
2011-12-04 16:41:50 -05:00
def _resort_statements(ol):
return [statement for i, statement in
sorted(ol, key=lambda x: x[0])]
def group_by_targets(sl):
groups = []
for statement_order, statement in enumerate(flat_iteration(sl)):
targets = list_targets(statement)
chk_groups = [(targets.isdisjoint(g[0]), g) for g in groups]
merge_groups = [g for dj, g in chk_groups if not dj]
groups = [g for dj, g in chk_groups if dj]
new_group = (set(targets), [(statement_order, statement)])
for g in merge_groups:
new_group[0].update(g[0])
new_group[1].extend(g[1])
groups.append(new_group)
return [(target, _resort_statements(stmts))
for target, stmts in groups]
2013-02-22 11:56:35 -05:00
def list_special_ios(f, ins, outs, inouts):
r = set()
for special in f.specials:
r |= special.list_ios(ins, outs, inouts)
return r
2013-02-14 18:17:24 -05:00
class _ClockDomainLister(NodeVisitor):
def __init__(self):
self.clock_domains = set()
def visit_ClockSignal(self, node):
self.clock_domains.add(node.cd)
def visit_ResetSignal(self, node):
self.clock_domains.add(node.cd)
def visit_clock_domains(self, node):
for clockname, statements in node.items():
self.clock_domains.add(clockname)
self.visit(statements)
def list_clock_domains_expr(f):
cdl = _ClockDomainLister()
cdl.visit(f)
return cdl.clock_domains
2013-02-22 11:56:35 -05:00
def list_clock_domains(f):
r = list_clock_domains_expr(f)
2013-02-22 11:56:35 -05:00
for special in f.specials:
r |= special.list_clock_domains()
2013-03-15 13:17:33 -04:00
for cd in f.clock_domains:
r.add(cd.name)
2013-02-22 11:56:35 -05:00
return r
2011-12-16 10:02:55 -05:00
def is_variable(node):
2011-12-05 16:00:06 -05:00
if isinstance(node, Signal):
return node.variable
2011-12-21 16:57:07 -05:00
elif isinstance(node, _Slice):
2011-12-16 10:02:55 -05:00
return is_variable(node.value)
2011-12-05 16:00:06 -05:00
elif isinstance(node, Cat):
2011-12-16 10:02:55 -05:00
arevars = list(map(is_variable, node.l))
2011-12-05 16:00:06 -05:00
r = arevars[0]
for x in arevars:
if x != r:
raise TypeError
return r
else:
raise TypeError
2013-04-23 05:53:37 -04:00
def generate_reset(rst, sl):
2011-12-16 10:02:55 -05:00
targets = list_targets(sl)
2013-04-23 05:53:37 -04:00
return [t.eq(t.reset) for t in sorted(targets, key=lambda x: x.huid)]
def insert_reset(rst, sl):
return [If(rst, *generate_reset(rst, sl)).Else(*sl)]
2013-08-08 05:32:58 -04:00
def insert_resets(f):
newsync = dict()
for k, v in f.sync.items():
if f.clock_domains[k].rst is not None:
newsync[k] = insert_reset(ResetSignal(k), v)
else:
newsync[k] = v
f.sync = newsync
class _Lowerer(NodeTransformer):
def __init__(self):
self.target_context = False
self.extra_stmts = []
self.comb = []
def visit_Assign(self, node):
old_target_context, old_extra_stmts = self.target_context, self.extra_stmts
self.extra_stmts = []
self.target_context = True
lhs = self.visit(node.l)
self.target_context = False
rhs = self.visit(node.r)
r = _Assign(lhs, rhs)
if self.extra_stmts:
r = [r] + self.extra_stmts
self.target_context, self.extra_stmts = old_target_context, old_extra_stmts
return r
# Basics are FHDL structure elements that back-ends are not required to support
# but can be expressed in terms of other elements (lowered) before conversion.
class _BasicLowerer(_Lowerer):
def __init__(self, clock_domains):
self.clock_domains = clock_domains
_Lowerer.__init__(self)
def visit_ArrayProxy(self, node):
2013-06-30 14:14:20 -04:00
# TODO: rewrite without variables
array_muxed = Signal(value_bits_sign(node), variable=True)
if self.target_context:
k = self.visit(node.key)
cases = {}
for n, choice in enumerate(node.choices):
cases[n] = [self.visit_Assign(_Assign(choice, array_muxed))]
self.extra_stmts.append(Case(k, cases).makedefault())
else:
cases = dict((n, _Assign(array_muxed, self.visit(choice)))
for n, choice in enumerate(node.choices))
self.comb.append(Case(self.visit(node.key), cases).makedefault())
return array_muxed
def visit_ClockSignal(self, node):
return self.clock_domains[node.cd].clk
2012-12-14 17:56:16 -05:00
def visit_ResetSignal(self, node):
return self.clock_domains[node.cd].rst
class _ComplexSliceLowerer(_Lowerer):
def visit_Slice(self, node):
if not isinstance(node.value, Signal):
slice_proxy = Signal(value_bits_sign(node.value))
if self.target_context:
a = _Assign(node.value, slice_proxy)
else:
a = _Assign(slice_proxy, node.value)
self.comb.append(self.visit_Assign(a))
node = _Slice(slice_proxy, node.start, node.stop)
return NodeTransformer.visit_Slice(self, node)
def _apply_lowerer(l, f):
f = l.visit(f)
f.comb += l.comb
for special in f.specials:
for obj, attr, direction in special.iter_expressions():
if direction != SPECIAL_INOUT:
# inouts are only supported by Migen when connected directly to top-level
# in this case, they are Signal and never need lowering
l.comb = []
l.target_context = direction != SPECIAL_INPUT
l.extra_stmts = []
expr = getattr(obj, attr)
expr = l.visit(expr)
setattr(obj, attr, expr)
f.comb += l.comb + l.extra_stmts
return f
def lower_basics(f):
return _apply_lowerer(_BasicLowerer(f.clock_domains), f)
def lower_complex_slices(f):
return _apply_lowerer(_ComplexSliceLowerer(), f)
class _ClockDomainRenamer(NodeVisitor):
def __init__(self, old, new):
self.old = old
self.new = new
def visit_ClockSignal(self, node):
if node.cd == self.old:
node.cd = self.new
def visit_ResetSignal(self, node):
if node.cd == self.old:
node.cd = self.new
def rename_clock_domain_expr(f, old, new):
cdr = _ClockDomainRenamer(old, new)
cdr.visit(f)
def rename_clock_domain(f, old, new):
rename_clock_domain_expr(f, old, new)
if new in f.sync:
f.sync[new].extend(f.sync[old])
else:
f.sync[new] = f.sync[old]
del f.sync[old]
for special in f.specials:
special.rename_clock_domain(old, new)
try:
cd = f.clock_domains[old]
except KeyError:
pass
else:
cd.rename(new)