fhdl.ir: add black-box fragments, fragment parameters, and Instance.
This commit is contained in:
parent
de6c12af77
commit
c7f9386eab
26
examples/inst.py
Normal file
26
examples/inst.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from nmigen import *
|
||||||
|
from nmigen.back import rtlil, verilog
|
||||||
|
|
||||||
|
|
||||||
|
class System:
|
||||||
|
def __init__(self):
|
||||||
|
self.adr = Signal(16)
|
||||||
|
self.dat_r = Signal(8)
|
||||||
|
self.dat_w = Signal(8)
|
||||||
|
self.we = Signal()
|
||||||
|
|
||||||
|
def get_fragment(self, platform):
|
||||||
|
m = Module()
|
||||||
|
m.submodules += Instance("CPU",
|
||||||
|
p_RESET_ADDR=0xfff0,
|
||||||
|
i_d_adr =self.adr,
|
||||||
|
i_d_dat_r=self.dat_r,
|
||||||
|
o_d_dat_w=self.dat_w,
|
||||||
|
)
|
||||||
|
return m.lower(platform)
|
||||||
|
|
||||||
|
|
||||||
|
sys = System()
|
||||||
|
frag = sys.get_fragment(platform=None)
|
||||||
|
# print(rtlil.convert(frag, ports=[sys.adr, sys.dat_r, sys.dat_w, sys.we]))
|
||||||
|
print(verilog.convert(frag, ports=[sys.adr, sys.dat_r, sys.dat_w, sys.we]))
|
|
@ -1,7 +1,7 @@
|
||||||
from .hdl.ast import Value, Const, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
|
from .hdl.ast import Value, Const, Mux, Cat, Repl, Array, Signal, ClockSignal, ResetSignal
|
||||||
from .hdl.dsl import Module
|
from .hdl.dsl import Module
|
||||||
from .hdl.cd import ClockDomain
|
from .hdl.cd import ClockDomain
|
||||||
from .hdl.ir import Fragment
|
from .hdl.ir import Fragment, Instance
|
||||||
from .hdl.xfrm import ResetInserter, CEInserter
|
from .hdl.xfrm import ResetInserter, CEInserter
|
||||||
|
|
||||||
from .lib.cdc import MultiReg
|
from .lib.cdc import MultiReg
|
||||||
|
|
|
@ -567,6 +567,16 @@ class _StatementCompiler(xfrm.AbstractStatementTransformer):
|
||||||
|
|
||||||
|
|
||||||
def convert_fragment(builder, fragment, name, top):
|
def convert_fragment(builder, fragment, name, top):
|
||||||
|
if fragment.black_box is not None:
|
||||||
|
port_map = OrderedDict()
|
||||||
|
for signal in fragment.ports:
|
||||||
|
port_map["\\{}".format(fragment.port_names[signal])] = signal
|
||||||
|
|
||||||
|
return "\\{}".format(fragment.black_box), port_map
|
||||||
|
else:
|
||||||
|
assert not fragment.port_names
|
||||||
|
assert not fragment.parameters
|
||||||
|
|
||||||
with builder.module(name or "anonymous", attrs={"top": 1} if top else {}) as module:
|
with builder.module(name or "anonymous", attrs={"top": 1} if top else {}) as module:
|
||||||
compiler_state = _ValueCompilerState(module)
|
compiler_state = _ValueCompilerState(module)
|
||||||
rhs_compiler = _RHSValueCompiler(compiler_state)
|
rhs_compiler = _RHSValueCompiler(compiler_state)
|
||||||
|
@ -601,7 +611,7 @@ def convert_fragment(builder, fragment, name, top):
|
||||||
module.cell(sub_name, name=sub_name, ports={
|
module.cell(sub_name, name=sub_name, ports={
|
||||||
port: compiler_state.resolve_curr(signal, prefix=sub_name)
|
port: compiler_state.resolve_curr(signal, prefix=sub_name)
|
||||||
for port, signal in sub_port_map.items()
|
for port, signal in sub_port_map.items()
|
||||||
})
|
}, params=subfragment.parameters)
|
||||||
|
|
||||||
with module.process() as process:
|
with module.process() as process:
|
||||||
with process.case() as case:
|
with process.case() as case:
|
||||||
|
|
|
@ -887,11 +887,11 @@ class Passive(Statement):
|
||||||
class _MappedKeyCollection(metaclass=ABCMeta):
|
class _MappedKeyCollection(metaclass=ABCMeta):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _map_key(self, key):
|
def _map_key(self, key):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _unmap_key(self, key):
|
def _unmap_key(self, key):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
|
|
||||||
class _MappedKeyDict(MutableMapping, _MappedKeyCollection):
|
class _MappedKeyDict(MutableMapping, _MappedKeyCollection):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .ast import *
|
||||||
from .cd import *
|
from .cd import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Fragment", "DriverConflict"]
|
__all__ = ["Fragment", "Instance", "DriverConflict"]
|
||||||
|
|
||||||
|
|
||||||
class DriverConflict(UserWarning):
|
class DriverConflict(UserWarning):
|
||||||
|
@ -15,19 +15,28 @@ class DriverConflict(UserWarning):
|
||||||
|
|
||||||
class Fragment:
|
class Fragment:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.black_box = None
|
||||||
|
self.port_names = SignalDict()
|
||||||
|
self.parameters = OrderedDict()
|
||||||
|
|
||||||
self.ports = SignalDict()
|
self.ports = SignalDict()
|
||||||
self.drivers = OrderedDict()
|
self.drivers = OrderedDict()
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.domains = OrderedDict()
|
self.domains = OrderedDict()
|
||||||
self.subfragments = []
|
self.subfragments = []
|
||||||
|
|
||||||
def add_ports(self, *ports, kind):
|
def add_ports(self, *ports, dir):
|
||||||
assert kind in ("i", "o", "io")
|
assert dir in ("i", "o", "io")
|
||||||
for port in flatten(ports):
|
for port in flatten(ports):
|
||||||
self.ports[port] = kind
|
self.ports[port] = dir
|
||||||
|
|
||||||
def iter_ports(self):
|
def iter_ports(self, dir=None):
|
||||||
yield from self.ports.keys()
|
if dir is None:
|
||||||
|
yield from self.ports
|
||||||
|
else:
|
||||||
|
for port, port_dir in self.ports.items():
|
||||||
|
if port_dir == dir:
|
||||||
|
yield port
|
||||||
|
|
||||||
def add_driver(self, signal, domain=None):
|
def add_driver(self, signal, domain=None):
|
||||||
if domain not in self.drivers:
|
if domain not in self.drivers:
|
||||||
|
@ -78,6 +87,9 @@ class Fragment:
|
||||||
assert isinstance(subfragment, Fragment)
|
assert isinstance(subfragment, Fragment)
|
||||||
self.subfragments.append((subfragment, name))
|
self.subfragments.append((subfragment, name))
|
||||||
|
|
||||||
|
def get_fragment(self, platform):
|
||||||
|
return self
|
||||||
|
|
||||||
def _resolve_driver_conflicts(self, hierarchy=("top",), mode="warn"):
|
def _resolve_driver_conflicts(self, hierarchy=("top",), mode="warn"):
|
||||||
assert mode in ("silent", "warn", "error")
|
assert mode in ("silent", "warn", "error")
|
||||||
|
|
||||||
|
@ -248,7 +260,7 @@ class Fragment:
|
||||||
for subfrag, name in self.subfragments:
|
for subfrag, name in self.subfragments:
|
||||||
# Always ask subfragments to provide all signals we're using and signals we're asked
|
# 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.
|
# 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)
|
sub_ins, sub_outs, sub_inouts = subfrag._propagate_ports(ports=self_used | ports)
|
||||||
# Refine the input port approximation: if a subfragment is driving a signal,
|
# Refine the input port approximation: if a subfragment is driving a signal,
|
||||||
# it is definitely not our input. But, if a subfragment requires a signal as an input,
|
# it is definitely not our input. But, if a subfragment requires a signal as an input,
|
||||||
# and we aren't driving it, it has to be our input as well.
|
# and we aren't driving it, it has to be our input as well.
|
||||||
|
@ -257,12 +269,17 @@ class Fragment:
|
||||||
# Refine the output port approximation: if a subfragment is driving a signal,
|
# Refine the output port approximation: if a subfragment is driving a signal,
|
||||||
# and we're asked to provide it, we can provide it now.
|
# and we're asked to provide it, we can provide it now.
|
||||||
outs |= ports & sub_outs
|
outs |= ports & sub_outs
|
||||||
|
# All of our subfragments' bidirectional ports are also our bidirectional ports,
|
||||||
|
# since these are only used for pins.
|
||||||
|
self.add_ports(sub_inouts, dir="io")
|
||||||
|
|
||||||
# We've computed the precise set of input and output ports.
|
# We've computed the precise set of input and output ports.
|
||||||
self.add_ports(ins, kind="i")
|
self.add_ports(ins, dir="i")
|
||||||
self.add_ports(outs, kind="o")
|
self.add_ports(outs, dir="o")
|
||||||
|
|
||||||
return ins, outs
|
return (SignalSet(self.iter_ports("i")),
|
||||||
|
SignalSet(self.iter_ports("o")),
|
||||||
|
SignalSet(self.iter_ports("io")))
|
||||||
|
|
||||||
def prepare(self, ports=(), ensure_sync_exists=True):
|
def prepare(self, ports=(), ensure_sync_exists=True):
|
||||||
from .xfrm import FragmentTransformer
|
from .xfrm import FragmentTransformer
|
||||||
|
@ -274,3 +291,24 @@ class Fragment:
|
||||||
fragment = fragment._lower_domain_signals()
|
fragment = fragment._lower_domain_signals()
|
||||||
fragment._propagate_ports(ports)
|
fragment._propagate_ports(ports)
|
||||||
return fragment
|
return fragment
|
||||||
|
|
||||||
|
|
||||||
|
class Instance(Fragment):
|
||||||
|
def __init__(self, type, **kwargs):
|
||||||
|
super().__init__()
|
||||||
|
self.black_box = type
|
||||||
|
for kw, arg in kwargs.items():
|
||||||
|
if kw.startswith("p_"):
|
||||||
|
self.parameters[kw[2:]] = arg
|
||||||
|
elif kw.startswith("i_"):
|
||||||
|
self.port_names[arg] = kw[2:]
|
||||||
|
self.add_ports(arg, dir="i")
|
||||||
|
elif kw.startswith("o_"):
|
||||||
|
self.port_names[arg] = kw[2:]
|
||||||
|
self.add_ports(arg, dir="o")
|
||||||
|
elif kw.startswith("io_"):
|
||||||
|
self.port_names[arg] = kw[3:]
|
||||||
|
self.add_ports(arg, dir="io")
|
||||||
|
else:
|
||||||
|
raise NameError("Instance argument '{}' does not start with p_, i_, o_, or io_"
|
||||||
|
.format(arg))
|
||||||
|
|
|
@ -18,43 +18,43 @@ __all__ = ["AbstractValueTransformer", "ValueTransformer",
|
||||||
class AbstractValueTransformer(metaclass=ABCMeta):
|
class AbstractValueTransformer(metaclass=ABCMeta):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Const(self, value):
|
def on_Const(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Signal(self, value):
|
def on_Signal(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_ClockSignal(self, value):
|
def on_ClockSignal(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_ResetSignal(self, value):
|
def on_ResetSignal(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Operator(self, value):
|
def on_Operator(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Slice(self, value):
|
def on_Slice(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Part(self, value):
|
def on_Part(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Cat(self, value):
|
def on_Cat(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Repl(self, value):
|
def on_Repl(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_ArrayProxy(self, value):
|
def on_ArrayProxy(self, value):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
def on_unknown_value(self, value):
|
def on_unknown_value(self, value):
|
||||||
raise TypeError("Cannot transform value '{!r}'".format(value)) # :nocov:
|
raise TypeError("Cannot transform value '{!r}'".format(value)) # :nocov:
|
||||||
|
@ -126,15 +126,15 @@ class ValueTransformer(AbstractValueTransformer):
|
||||||
class AbstractStatementTransformer(metaclass=ABCMeta):
|
class AbstractStatementTransformer(metaclass=ABCMeta):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Assign(self, stmt):
|
def on_Assign(self, stmt):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_Switch(self, stmt):
|
def on_Switch(self, stmt):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_statements(self, stmt):
|
def on_statements(self, stmt):
|
||||||
pass
|
pass # :nocov:
|
||||||
|
|
||||||
def on_unknown_statement(self, stmt):
|
def on_unknown_statement(self, stmt):
|
||||||
raise TypeError("Cannot transform statement '{!r}'".format(stmt)) # :nocov:
|
raise TypeError("Cannot transform statement '{!r}'".format(stmt)) # :nocov:
|
||||||
|
@ -173,6 +173,10 @@ class FragmentTransformer:
|
||||||
for subfragment, name in fragment.subfragments:
|
for subfragment, name in fragment.subfragments:
|
||||||
new_fragment.add_subfragment(self(subfragment), name)
|
new_fragment.add_subfragment(self(subfragment), name)
|
||||||
|
|
||||||
|
def map_ports(self, fragment, new_fragment):
|
||||||
|
for port, dir in fragment.ports.items():
|
||||||
|
new_fragment.add_ports(port, dir=dir)
|
||||||
|
|
||||||
def map_domains(self, fragment, new_fragment):
|
def map_domains(self, fragment, new_fragment):
|
||||||
for domain in fragment.iter_domains():
|
for domain in fragment.iter_domains():
|
||||||
new_fragment.add_domains(fragment.domains[domain])
|
new_fragment.add_domains(fragment.domains[domain])
|
||||||
|
@ -189,6 +193,10 @@ class FragmentTransformer:
|
||||||
|
|
||||||
def on_fragment(self, fragment):
|
def on_fragment(self, fragment):
|
||||||
new_fragment = Fragment()
|
new_fragment = Fragment()
|
||||||
|
new_fragment.black_box = fragment.black_box
|
||||||
|
new_fragment.parameters = OrderedDict(fragment.parameters)
|
||||||
|
new_fragment.port_names = SignalDict(fragment.port_names.items())
|
||||||
|
self.map_ports(fragment, new_fragment)
|
||||||
self.map_subfragments(fragment, new_fragment)
|
self.map_subfragments(fragment, new_fragment)
|
||||||
self.map_domains(fragment, new_fragment)
|
self.map_domains(fragment, new_fragment)
|
||||||
self.map_statements(fragment, new_fragment)
|
self.map_statements(fragment, new_fragment)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from ..hdl.ast import *
|
from ..hdl.ast import *
|
||||||
from ..hdl.cd import *
|
from ..hdl.cd import *
|
||||||
from ..hdl.ir import *
|
from ..hdl.ir import *
|
||||||
|
@ -29,7 +31,7 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
|
|
||||||
def test_iter_signals(self):
|
def test_iter_signals(self):
|
||||||
f = Fragment()
|
f = Fragment()
|
||||||
f.add_ports(self.s1, self.s2, kind="io")
|
f.add_ports(self.s1, self.s2, dir="io")
|
||||||
self.assertEqual(SignalSet((self.s1, self.s2)), f.iter_signals())
|
self.assertEqual(SignalSet((self.s1, self.s2)), f.iter_signals())
|
||||||
|
|
||||||
def test_self_contained(self):
|
def test_self_contained(self):
|
||||||
|
@ -146,6 +148,18 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
(sync.clk, "i"),
|
(sync.clk, "i"),
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
def test_inout(self):
|
||||||
|
s = Signal()
|
||||||
|
f1 = Fragment()
|
||||||
|
f2 = Fragment()
|
||||||
|
f2.add_ports(s, dir="io")
|
||||||
|
f1.add_subfragment(f2)
|
||||||
|
|
||||||
|
f1._propagate_ports(ports=())
|
||||||
|
self.assertEqual(f1.ports, SignalDict([
|
||||||
|
(s, "io")
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
class FragmentDomainsTestCase(FHDLTestCase):
|
class FragmentDomainsTestCase(FHDLTestCase):
|
||||||
def test_iter_signals(self):
|
def test_iter_signals(self):
|
||||||
|
@ -391,3 +405,18 @@ class FragmentDriverConflictTestCase(FHDLTestCase):
|
||||||
(eq (sig c2) (const 1'd1))
|
(eq (sig c2) (const 1'd1))
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceTestCase(FHDLTestCase):
|
||||||
|
def test_init(self):
|
||||||
|
rst = Signal()
|
||||||
|
stb = Signal()
|
||||||
|
pins = Signal(8)
|
||||||
|
inst = Instance("cpu", p_RESET=0x1234, i_rst=rst, o_stb=stb, io_pins=pins)
|
||||||
|
self.assertEqual(inst.black_box, "cpu")
|
||||||
|
self.assertEqual(inst.parameters, OrderedDict([("RESET", 0x1234)]))
|
||||||
|
self.assertEqual(inst.ports, SignalDict([
|
||||||
|
(rst, "i"),
|
||||||
|
(stb, "o"),
|
||||||
|
(pins, "io"),
|
||||||
|
]))
|
||||||
|
|
Loading…
Reference in a new issue