hdl.ir: rework named port handling for Instances.
The main purpose of this rework is cleanup, to avoid specifying the direction of input ports in an implicit, ad-hoc way using the named ports and ports dictionaries. While working on this I realized that output ports can be connected to anything that is valid on LHS, so this is now supported too.
This commit is contained in:
parent
aed2062101
commit
585514e6ed
|
@ -669,7 +669,7 @@ class _StatementCompiler(xfrm.StatementVisitor):
|
||||||
def convert_fragment(builder, fragment, hierarchy):
|
def convert_fragment(builder, fragment, hierarchy):
|
||||||
if isinstance(fragment, ir.Instance):
|
if isinstance(fragment, ir.Instance):
|
||||||
port_map = OrderedDict()
|
port_map = OrderedDict()
|
||||||
for port_name, value in fragment.named_ports.items():
|
for port_name, (value, dir) in fragment.named_ports.items():
|
||||||
port_map["\\{}".format(port_name)] = value
|
port_map["\\{}".format(port_name)] = value
|
||||||
|
|
||||||
if fragment.type[0] == "$":
|
if fragment.type[0] == "$":
|
||||||
|
|
|
@ -344,16 +344,19 @@ class Fragment:
|
||||||
# Collect all signals we're driving (on LHS of statements), and signals we're using
|
# Collect all signals we're driving (on LHS of statements), and signals we're using
|
||||||
# (on RHS of statements, or in clock domains).
|
# (on RHS of statements, or in clock domains).
|
||||||
if isinstance(self, Instance):
|
if isinstance(self, Instance):
|
||||||
# Named ports contain signals for input, output and bidirectional ports. Output
|
|
||||||
# and bidirectional ports are already added to the main port dict, however, for
|
|
||||||
# input ports this has to be done lazily as any expression is valid there, including
|
|
||||||
# ones with deferred resolution to signals, such as ClockSignal().
|
|
||||||
self_driven = SignalSet()
|
self_driven = SignalSet()
|
||||||
self_used = SignalSet()
|
self_used = SignalSet()
|
||||||
for named_port_used in union((p._rhs_signals() for p in self.named_ports.values()),
|
for port_name, (value, dir) in self.named_ports.items():
|
||||||
start=SignalSet()):
|
if dir == "i":
|
||||||
if named_port_used not in self.ports:
|
for signal in value._rhs_signals():
|
||||||
self_used.add(named_port_used)
|
self_used.add(signal)
|
||||||
|
self.add_ports(signal, dir="i")
|
||||||
|
if dir == "o":
|
||||||
|
for signal in value._lhs_signals():
|
||||||
|
self_driven.add(signal)
|
||||||
|
self.add_ports(signal, dir="o")
|
||||||
|
if dir == "io":
|
||||||
|
self.add_ports(value, dir="io")
|
||||||
else:
|
else:
|
||||||
self_driven = union((s._lhs_signals() for s in self.statements), start=SignalSet())
|
self_driven = union((s._lhs_signals() for s in self.statements), start=SignalSet())
|
||||||
self_used = union((s._rhs_signals() for s in self.statements), start=SignalSet())
|
self_used = union((s._rhs_signals() for s in self.statements), start=SignalSet())
|
||||||
|
@ -415,24 +418,19 @@ class Instance(Fragment):
|
||||||
def __init__(self, type, **kwargs):
|
def __init__(self, type, **kwargs):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.type = type
|
self.type = type
|
||||||
self.parameters = OrderedDict()
|
self.parameters = OrderedDict()
|
||||||
self.named_ports = OrderedDict()
|
self.named_ports = OrderedDict()
|
||||||
|
|
||||||
for kw, arg in kwargs.items():
|
for kw, arg in kwargs.items():
|
||||||
if kw.startswith("p_"):
|
if kw.startswith("p_"):
|
||||||
self.parameters[kw[2:]] = arg
|
self.parameters[kw[2:]] = arg
|
||||||
elif kw.startswith("i_"):
|
elif kw.startswith("i_"):
|
||||||
self.named_ports[kw[2:]] = arg
|
self.named_ports[kw[2:]] = (arg, "i")
|
||||||
# Unlike with "o_" and "io_", "i_" ports can be assigned an arbitrary value;
|
|
||||||
# this includes unresolved ClockSignals etc. We rely on Fragment.prepare to
|
|
||||||
# populate fragment ports for these named ports.
|
|
||||||
elif kw.startswith("o_"):
|
elif kw.startswith("o_"):
|
||||||
self.named_ports[kw[2:]] = arg
|
self.named_ports[kw[2:]] = (arg, "o")
|
||||||
self.add_ports(arg, dir="o")
|
|
||||||
elif kw.startswith("io_"):
|
elif kw.startswith("io_"):
|
||||||
self.named_ports[kw[3:]] = arg
|
self.named_ports[kw[3:]] = (arg, "io")
|
||||||
self.add_ports(arg, dir="io")
|
|
||||||
else:
|
else:
|
||||||
raise NameError("Instance argument '{}' does not start with p_, i_, o_, or io_"
|
raise NameError("Instance argument '{}' does not start with p_, i_, o_, or io_"
|
||||||
.format(arg))
|
.format(arg))
|
||||||
|
|
|
@ -244,8 +244,8 @@ class FragmentTransformer:
|
||||||
|
|
||||||
def map_named_ports(self, fragment, new_fragment):
|
def map_named_ports(self, fragment, new_fragment):
|
||||||
if hasattr(self, "on_value"):
|
if hasattr(self, "on_value"):
|
||||||
for name, value in fragment.named_ports.items():
|
for name, (value, dir) in fragment.named_ports.items():
|
||||||
new_fragment.named_ports[name] = self.on_value(value)
|
new_fragment.named_ports[name] = self.on_value(value), dir
|
||||||
else:
|
else:
|
||||||
new_fragment.named_ports = OrderedDict(fragment.named_ports.items())
|
new_fragment.named_ports = OrderedDict(fragment.named_ports.items())
|
||||||
|
|
||||||
|
|
|
@ -531,11 +531,14 @@ class InstanceTestCase(FHDLTestCase):
|
||||||
self.rst = Signal()
|
self.rst = Signal()
|
||||||
self.stb = Signal()
|
self.stb = Signal()
|
||||||
self.pins = Signal(8)
|
self.pins = Signal(8)
|
||||||
|
self.datal = Signal(4)
|
||||||
|
self.datah = Signal(4)
|
||||||
self.inst = Instance("cpu",
|
self.inst = Instance("cpu",
|
||||||
p_RESET=0x1234,
|
p_RESET=0x1234,
|
||||||
i_clk=ClockSignal(),
|
i_clk=ClockSignal(),
|
||||||
i_rst=self.rst,
|
i_rst=self.rst,
|
||||||
o_stb=self.stb,
|
o_stb=self.stb,
|
||||||
|
o_data=Cat(self.datal, self.datah),
|
||||||
io_pins=self.pins
|
io_pins=self.pins
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -544,22 +547,18 @@ class InstanceTestCase(FHDLTestCase):
|
||||||
f = self.inst
|
f = self.inst
|
||||||
self.assertEqual(f.type, "cpu")
|
self.assertEqual(f.type, "cpu")
|
||||||
self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
|
self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
|
||||||
self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "pins"])
|
self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "data", "pins"])
|
||||||
self.assertEqual(f.ports, SignalDict([
|
self.assertEqual(f.ports, SignalDict([]))
|
||||||
(self.stb, "o"),
|
|
||||||
(self.pins, "io"),
|
|
||||||
]))
|
|
||||||
|
|
||||||
def test_prepare(self):
|
def test_prepare(self):
|
||||||
self.setUp_cpu()
|
self.setUp_cpu()
|
||||||
f = self.inst.prepare()
|
f = self.inst.prepare()
|
||||||
clk = f.domains["sync"].clk
|
clk = f.domains["sync"].clk
|
||||||
self.assertEqual(f.type, "cpu")
|
|
||||||
self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
|
|
||||||
self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "pins"])
|
|
||||||
self.assertEqual(f.ports, SignalDict([
|
self.assertEqual(f.ports, SignalDict([
|
||||||
(clk, "i"),
|
(clk, "i"),
|
||||||
(self.rst, "i"),
|
(self.rst, "i"),
|
||||||
(self.stb, "o"),
|
(self.stb, "o"),
|
||||||
|
(self.datal, "o"),
|
||||||
|
(self.datah, "o"),
|
||||||
(self.pins, "io"),
|
(self.pins, "io"),
|
||||||
]))
|
]))
|
||||||
|
|
Loading…
Reference in a new issue