amaranth/nmigen/fhdl/ir.py
2018-12-13 03:31:13 +00:00

88 lines
3.2 KiB
Python

from collections import defaultdict, OrderedDict
from ..tools import *
from .ast import *
__all__ = ["Fragment"]
class Fragment:
def __init__(self):
self.ports = ValueSet()
self.drivers = OrderedDict()
self.statements = []
self.subfragments = []
def add_ports(self, *ports):
self.ports.update(flatten(ports))
def iter_ports(self):
yield from self.ports
def drive(self, signal, cd_name=None):
if cd_name not in self.drivers:
self.drivers[cd_name] = ValueSet()
self.drivers[cd_name].add(signal)
def iter_domains(self):
yield from self.drivers.items()
def iter_drivers(self):
for cd_name, signals in self.drivers.items():
for signal in signals:
yield cd_name, signal
def iter_comb(self):
yield from self.drivers[None]
def iter_sync(self):
for cd_name, signals in self.drivers.items():
if cd_name is None:
continue
for signal in signals:
yield cd_name, signal
def add_statements(self, *stmts):
self.statements += Statement.wrap(stmts)
def add_subfragment(self, subfragment, name=None):
assert isinstance(subfragment, Fragment)
self.subfragments.append((subfragment, name))
def _propagate_ports(self, ports, clock_domains):
# Collect all signals we're driving (on LHS of statements), and signals we're using
# (on RHS of statements, or in clock domains).
self_driven = union(s._lhs_signals() for s in self.statements)
self_used = union(s._rhs_signals() for s in self.statements)
for cd_name, _ in self.iter_sync():
cd = clock_domains[cd_name]
self_used.add(cd.clk)
if cd.reset is not None:
self_used.add(cd.reset)
# Our input ports are all the signals we're using but not driving. This is an over-
# approximation: some of these signals may be driven by our subfragments.
ins = self_used - self_driven
# Our output ports are all the signals we're asked to provide that we're driving. This is
# an underapproximation: some of these signals may be driven by subfragments.
outs = ports & self_driven
# Go through subfragments and refine our approximation for ports.
for subfrag, name in self.subfragments:
# Always ask subfragments to provide all signals we're using and signals we're asked
# to provide. If the subfragment is not driving it, it will silently ignore it.
sub_ins, sub_outs = subfrag._propagate_ports(ports=self_used | ports,
clock_domains=clock_domains)
# Refine the input port approximation: if a subfragment is driving a signal,
# it is definitely not our input.
ins -= sub_outs
# Refine the output port approximation: if a subfragment is driving a signal,
# and we're asked to provide it, we can provide it now.
outs |= ports & sub_outs
# We've computed the precise set of input and output ports.
self.add_ports(ins, outs)
return ins, outs