Lowering of Special expressions + support ClockSignal/ResetSignal
This commit is contained in:
parent
dc55289323
commit
7a06e9457c
|
@ -3,7 +3,7 @@ from itertools import combinations
|
|||
|
||||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.specials import Special
|
||||
from migen.fhdl.tools import flat_iteration
|
||||
from migen.fhdl.tools import flat_iteration, rename_clock_domain
|
||||
|
||||
class FinalizeError(Exception):
|
||||
pass
|
||||
|
@ -158,7 +158,7 @@ class Module:
|
|||
for mod_name, f in subfragments:
|
||||
for cd in f.clock_domains:
|
||||
if cd.name in needs_renaming:
|
||||
f.rename_clock_domain(cd.name, mod_name + "_" + cd.name)
|
||||
rename_clock_domain(f, cd.name, mod_name + "_" + cd.name)
|
||||
# sum subfragments
|
||||
for mod_name, f in subfragments:
|
||||
self._fragment += f
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.tools import list_signals, value_bits_sign
|
||||
from migen.fhdl.tools import *
|
||||
from migen.fhdl.tracer import get_obj_var_name
|
||||
from migen.fhdl.verilog import _printexpr as verilog_printexpr
|
||||
|
||||
class Special(HUID):
|
||||
def rename_clock_domain(self, old, new):
|
||||
pass
|
||||
def iter_expressions(self):
|
||||
for x in []:
|
||||
yield x
|
||||
|
||||
def get_clock_domains(self):
|
||||
return set()
|
||||
def rename_clock_domain(self, old, new):
|
||||
for obj, attr, direction in self.iter_expressions():
|
||||
rename_clock_domain_expr(getattr(obj, attr), old, new)
|
||||
|
||||
def list_clock_domains(self):
|
||||
r = set()
|
||||
for obj, attr, direction in self.iter_expressions():
|
||||
r |= list_clock_domains_expr(getattr(obj, attr))
|
||||
return r
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
r = set()
|
||||
for obj, attr, direction in self.iter_expressions():
|
||||
if (direction == SPECIAL_INPUT and ins) \
|
||||
or (direction == SPECIAL_OUTPUT and outs) \
|
||||
or (direction == SPECIAL_INOUT and inouts):
|
||||
signals = list_signals(getattr(obj, attr))
|
||||
r.update(signals)
|
||||
return r
|
||||
|
||||
class Tristate(Special):
|
||||
def __init__(self, target, o, oe, i=None):
|
||||
|
@ -18,16 +36,13 @@ class Tristate(Special):
|
|||
self.oe = oe
|
||||
self.i = i
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
r = set()
|
||||
if inouts:
|
||||
r.update(list_signals(self.target))
|
||||
if ins:
|
||||
r.update(list_signals(self.o))
|
||||
r.update(list_signals(self.oe))
|
||||
if outs:
|
||||
r.update(list_signals(self.i))
|
||||
return r
|
||||
def iter_expressions(self):
|
||||
for attr, target_context in [
|
||||
("target", SPECIAL_INOUT),
|
||||
("o", SPECIAL_INPUT),
|
||||
("oe", SPECIAL_INPUT),
|
||||
("i", SPECIAL_OUTPUT)]:
|
||||
yield self, attr, target_context
|
||||
|
||||
@staticmethod
|
||||
def emit_verilog(tristate, ns, clock_domains):
|
||||
|
@ -79,40 +94,19 @@ class Instance(Special):
|
|||
self.name = name
|
||||
self.value = value
|
||||
|
||||
class _CR:
|
||||
def __init__(self, name_inst, domain="sys", invert=False):
|
||||
self.name_inst = name_inst
|
||||
self.domain = domain
|
||||
self.invert = invert
|
||||
class ClockPort(_CR):
|
||||
pass
|
||||
class ResetPort(_CR):
|
||||
pass
|
||||
|
||||
def get_io(self, name):
|
||||
for item in self.items:
|
||||
if isinstance(item, Instance._IO) and item.name == name:
|
||||
return item.expr
|
||||
|
||||
def rename_clock_domain(self, old, new):
|
||||
for cr in filter(lambda x: isinstance(x, Instance._CR), self.items):
|
||||
if cr.domain == old:
|
||||
cr.domain = new
|
||||
|
||||
def get_clock_domains(self):
|
||||
return set(cr.domain
|
||||
for cr in filter(lambda x: isinstance(x, Instance._CR), self.items))
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
subsets = [list_signals(item.expr) for item in filter(lambda x:
|
||||
(ins and isinstance(x, Instance.Input))
|
||||
or (outs and isinstance(x, Instance.Output))
|
||||
or (inouts and isinstance(x, Instance.InOut)),
|
||||
self.items)]
|
||||
if subsets:
|
||||
return set.union(*subsets)
|
||||
else:
|
||||
return set()
|
||||
def iter_expressions(self):
|
||||
for item in self.items:
|
||||
if isinstance(item, Instance.Input):
|
||||
yield item, "expr", SPECIAL_INPUT
|
||||
elif isinstance(item, Instance.Output):
|
||||
yield item, "expr", SPECIAL_OUTPUT
|
||||
elif isinstance(item, Instance.InOut):
|
||||
yield item, "expr", SPECIAL_INOUT
|
||||
|
||||
@staticmethod
|
||||
def emit_verilog(instance, ns, clock_domains):
|
||||
|
@ -144,20 +138,10 @@ class Instance(Special):
|
|||
if isinstance(p, Instance._IO):
|
||||
name_inst = p.name
|
||||
name_design = verilog_printexpr(ns, p.expr)[0]
|
||||
elif isinstance(p, Instance.ClockPort):
|
||||
name_inst = p.name_inst
|
||||
name_design = ns.get_name(clock_domains[p.domain].clk)
|
||||
if p.invert:
|
||||
name_design = "~" + name_design
|
||||
elif isinstance(p, Instance.ResetPort):
|
||||
name_inst = p.name_inst
|
||||
name_design = ns.get_name(clock_domains[p.domain].rst)
|
||||
else:
|
||||
continue
|
||||
if not firstp:
|
||||
r += ",\n"
|
||||
firstp = False
|
||||
r += "\t." + name_inst + "(" + name_design + ")"
|
||||
if not firstp:
|
||||
r += ",\n"
|
||||
firstp = False
|
||||
r += "\t." + name_inst + "(" + name_design + ")"
|
||||
if not firstp:
|
||||
r += "\n"
|
||||
r += ");\n\n"
|
||||
|
@ -214,27 +198,26 @@ class Memory(Special):
|
|||
self.ports.append(mp)
|
||||
return mp
|
||||
|
||||
def iter_expressions(self):
|
||||
for p in self.ports:
|
||||
for attr, target_context in [
|
||||
("adr", SPECIAL_INPUT),
|
||||
("we", SPECIAL_INPUT),
|
||||
("dat_w", SPECIAL_INPUT),
|
||||
("re", SPECIAL_INPUT),
|
||||
("dat_r", SPECIAL_OUTPUT)]:
|
||||
yield p, attr, target_context
|
||||
|
||||
def rename_clock_domain(self, old, new):
|
||||
# port expressions are always signals - no need to call Special.rename_clock_domain
|
||||
for port in self.ports:
|
||||
if port.clock_domain == old:
|
||||
port.clock_domain = new
|
||||
|
||||
def get_clock_domains(self):
|
||||
def list_clock_domains(self):
|
||||
# port expressions are always signals - no need to call Special.list_clock_domains
|
||||
return set(port.clock_domain for port in self.ports)
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
s = set()
|
||||
def add(*sigs):
|
||||
for sig in sigs:
|
||||
if sig is not None:
|
||||
s.add(sig)
|
||||
for p in self.ports:
|
||||
if ins:
|
||||
add(p.adr, p.we, p.dat_w, p.re)
|
||||
if outs:
|
||||
add(p.dat_r)
|
||||
return s
|
||||
|
||||
@staticmethod
|
||||
def emit_verilog(memory, ns, clock_domains):
|
||||
r = ""
|
||||
|
@ -315,9 +298,6 @@ class SynthesisDirective(Special):
|
|||
self.template = template
|
||||
self.signals = signals
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
return set()
|
||||
|
||||
@staticmethod
|
||||
def emit_verilog(directive, ns, clock_domains):
|
||||
name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items())
|
||||
|
|
|
@ -170,6 +170,16 @@ class Signal(Value):
|
|||
def __repr__(self):
|
||||
return "<Signal " + (self.backtrace[-1][0] or "anonymous") + " at " + hex(id(self)) + ">"
|
||||
|
||||
class ClockSignal(Value):
|
||||
def __init__(self, cd="sys"):
|
||||
Value.__init__(self)
|
||||
self.cd = cd
|
||||
|
||||
class ResetSignal(Value):
|
||||
def __init__(self, cd="sys"):
|
||||
Value.__init__(self)
|
||||
self.cd = cd
|
||||
|
||||
# statements
|
||||
|
||||
class _Assign:
|
||||
|
@ -260,6 +270,8 @@ class _ClockDomainList(list):
|
|||
else:
|
||||
return list.__getitem__(self, key)
|
||||
|
||||
(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3)
|
||||
|
||||
class Fragment:
|
||||
def __init__(self, comb=None, sync=None, specials=None, clock_domains=None, sim=None):
|
||||
if comb is None: comb = []
|
||||
|
@ -287,15 +299,3 @@ class Fragment:
|
|||
self.specials | other.specials,
|
||||
self.clock_domains + other.clock_domains,
|
||||
self.sim + other.sim)
|
||||
|
||||
def rename_clock_domain(self, old, new):
|
||||
self.sync[new] = self.sync[old]
|
||||
del self.sync[old]
|
||||
for special in self.specials:
|
||||
special.rename_clock_domain(old, new)
|
||||
try:
|
||||
cd = self.clock_domains[old]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
cd.rename(new)
|
||||
|
|
|
@ -4,6 +4,11 @@ from migen.fhdl.structure import *
|
|||
from migen.fhdl.structure import _Operator, _Slice, _Assign, _ArrayProxy
|
||||
from migen.fhdl.visit import NodeVisitor, NodeTransformer
|
||||
|
||||
def bitreverse(s):
|
||||
length, signed = value_bits_sign(s)
|
||||
l = [s[i] for i in reversed(range(length))]
|
||||
return Cat(*l)
|
||||
|
||||
def flat_iteration(l):
|
||||
for element in l:
|
||||
if isinstance(element, collections.Iterable):
|
||||
|
@ -64,10 +69,30 @@ def list_special_ios(f, ins, outs, inouts):
|
|||
r |= special.list_ios(ins, outs, inouts)
|
||||
return r
|
||||
|
||||
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
|
||||
|
||||
def list_clock_domains(f):
|
||||
r = set(f.sync.keys())
|
||||
r = list_clock_domains_expr(f)
|
||||
for special in f.specials:
|
||||
r |= special.get_clock_domains()
|
||||
r |= special.list_clock_domains()
|
||||
for cd in f.clock_domains:
|
||||
r.add(cd.name)
|
||||
return r
|
||||
|
@ -99,6 +124,8 @@ def value_bits_sign(v):
|
|||
return bits_for(v), v < 0
|
||||
elif isinstance(v, Signal):
|
||||
return v.nbits, v.signed
|
||||
elif isinstance(v, (ClockSignal, ResetSignal)):
|
||||
return 1, False
|
||||
elif isinstance(v, _Operator):
|
||||
obs = list(map(value_bits_sign, v.operands))
|
||||
if v.op == "+" or v.op == "-":
|
||||
|
@ -168,11 +195,14 @@ def value_bits_sign(v):
|
|||
else:
|
||||
raise TypeError
|
||||
|
||||
class _ArrayLowerer(NodeTransformer):
|
||||
def __init__(self):
|
||||
# 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(NodeTransformer):
|
||||
def __init__(self, clock_domains):
|
||||
self.comb = []
|
||||
self.target_context = False
|
||||
self.extra_stmts = []
|
||||
self.clock_domains = clock_domains
|
||||
|
||||
def visit_Assign(self, node):
|
||||
old_target_context, old_extra_stmts = self.target_context, self.extra_stmts
|
||||
|
@ -203,13 +233,58 @@ class _ArrayLowerer(NodeTransformer):
|
|||
self.comb.append(Case(self.visit(node.key), cases).makedefault())
|
||||
return array_muxed
|
||||
|
||||
def lower_arrays(f):
|
||||
al = _ArrayLowerer()
|
||||
tf = al.visit(f)
|
||||
tf.comb += al.comb
|
||||
return tf
|
||||
def visit_ClockSignal(self, node):
|
||||
return self.clock_domains[node.cd].clk
|
||||
|
||||
def bitreverse(s):
|
||||
length, signed = value_bits_sign(s)
|
||||
l = [s[i] for i in reversed(range(length))]
|
||||
return Cat(*l)
|
||||
def visit_ResetSignal(self, node):
|
||||
return self.clock_domains[node.cd].rst
|
||||
|
||||
def lower_basics(f):
|
||||
bl = _BasicLowerer(f.clock_domains)
|
||||
f = bl.visit(f)
|
||||
f.comb += bl.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
|
||||
bl.comb = []
|
||||
bl.target_context = direction != SPECIAL_INPUT
|
||||
bl.extra_stmts = []
|
||||
expr = getattr(obj, attr)
|
||||
expr = bl.visit(expr)
|
||||
setattr(obj, attr, expr)
|
||||
f.comb += bl.comb + bl.extra_stmts
|
||||
|
||||
return 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)
|
||||
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)
|
||||
|
|
|
@ -203,7 +203,7 @@ def _printcomb(f, ns, display_run):
|
|||
def _insert_resets(f):
|
||||
newsync = dict()
|
||||
for k, v in f.sync.items():
|
||||
newsync[k] = insert_reset(f.clock_domains[k].rst, v)
|
||||
newsync[k] = insert_reset(ResetSignal(k), v)
|
||||
f.sync = newsync
|
||||
|
||||
def _printsync(f, ns):
|
||||
|
@ -263,10 +263,7 @@ def convert(f, ios=None, name="top",
|
|||
f = f.get_fragment()
|
||||
if ios is None:
|
||||
ios = set()
|
||||
|
||||
f = lower_arrays(f) # this also copies f
|
||||
fs, lowered_specials = _lower_specials(special_overrides, f.specials)
|
||||
f += fs
|
||||
|
||||
for cd_name in list_clock_domains(f):
|
||||
try:
|
||||
f.clock_domains[cd_name]
|
||||
|
@ -274,7 +271,11 @@ def convert(f, ios=None, name="top",
|
|||
cd = ClockDomain(cd_name)
|
||||
f.clock_domains.append(cd)
|
||||
ios |= {cd.clk, cd.rst}
|
||||
|
||||
_insert_resets(f)
|
||||
f = lower_basics(f)
|
||||
fs, lowered_specials = _lower_specials(special_overrides, f.specials)
|
||||
f += lower_basics(fs)
|
||||
|
||||
ns = build_namespace(list_signals(f) \
|
||||
| list_special_ios(f, True, True, True) \
|
||||
|
|
|
@ -9,6 +9,10 @@ class NodeVisitor:
|
|||
self.visit_constant(node)
|
||||
elif isinstance(node, Signal):
|
||||
self.visit_Signal(node)
|
||||
elif isinstance(node, ClockSignal):
|
||||
self.visit_ClockSignal(node)
|
||||
elif isinstance(node, ResetSignal):
|
||||
self.visit_ResetSignal(node)
|
||||
elif isinstance(node, _Operator):
|
||||
self.visit_Operator(node)
|
||||
elif isinstance(node, _Slice):
|
||||
|
@ -39,6 +43,12 @@ class NodeVisitor:
|
|||
|
||||
def visit_Signal(self, node):
|
||||
pass
|
||||
|
||||
def visit_ClockSignal(self, node):
|
||||
pass
|
||||
|
||||
def visit_ResetSignal(self, node):
|
||||
pass
|
||||
|
||||
def visit_Operator(self, node):
|
||||
for o in node.operands:
|
||||
|
@ -89,7 +99,7 @@ class NodeVisitor:
|
|||
pass
|
||||
|
||||
# Default methods always copy the node, except for:
|
||||
# - Signals
|
||||
# - Signals, ClockSignals and ResetSignals
|
||||
# - Unknown objects
|
||||
# - All fragment fields except comb and sync
|
||||
# In those cases, the original node is returned unchanged.
|
||||
|
@ -99,6 +109,10 @@ class NodeTransformer:
|
|||
return self.visit_constant(node)
|
||||
elif isinstance(node, Signal):
|
||||
return self.visit_Signal(node)
|
||||
elif isinstance(node, ClockSignal):
|
||||
return self.visit_ClockSignal(node)
|
||||
elif isinstance(node, ResetSignal):
|
||||
return self.visit_ResetSignal(node)
|
||||
elif isinstance(node, _Operator):
|
||||
return self.visit_Operator(node)
|
||||
elif isinstance(node, _Slice):
|
||||
|
@ -132,6 +146,12 @@ class NodeTransformer:
|
|||
def visit_Signal(self, node):
|
||||
return node
|
||||
|
||||
def visit_ClockSignal(self, node):
|
||||
return node
|
||||
|
||||
def visit_ResetSignal(self, node):
|
||||
return node
|
||||
|
||||
def visit_Operator(self, node):
|
||||
return _Operator(node.op, [self.visit(o) for o in node.operands])
|
||||
|
||||
|
|
|
@ -30,19 +30,18 @@ class MultiReg(Special):
|
|||
self.odomain = odomain
|
||||
self.n = n
|
||||
|
||||
def iter_expressions(self):
|
||||
yield self, "i", SPECIAL_INPUT
|
||||
yield self, "o", SPECIAL_OUTPUT
|
||||
|
||||
def rename_clock_domain(self, old, new):
|
||||
Special.rename_clock_domain(self, old, new)
|
||||
if self.odomain == old:
|
||||
self.odomain = new
|
||||
|
||||
def get_clock_domains(self):
|
||||
return {self.odomain}
|
||||
|
||||
def list_ios(self, ins, outs, inouts):
|
||||
r = set()
|
||||
if ins:
|
||||
r.update(list_signals(self.i))
|
||||
if outs:
|
||||
r.update(list_signals(self.o))
|
||||
def list_clock_domains(self):
|
||||
r = Special.list_clock_domains(self)
|
||||
r.add(self.odomain)
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
|
|
Loading…
Reference in New Issue