fhdl.ir: record port direction explicitly.
No point in recalculating this in the backend when writing RTLIL or Verilog port directions.
This commit is contained in:
parent
6251c95d4e
commit
90f1503c91
|
@ -231,11 +231,15 @@ class _ValueTransformer(xfrm.ValueTransformer):
|
||||||
def add_driven(self, signal, sync):
|
def add_driven(self, signal, sync):
|
||||||
self.driven[signal] = sync
|
self.driven[signal] = sync
|
||||||
|
|
||||||
def add_port(self, signal, kind=None):
|
def add_port(self, signal, kind):
|
||||||
if signal in self.driven:
|
assert kind in ("i", "o", "io")
|
||||||
self.ports[signal] = (len(self.ports), "output")
|
if kind == "i":
|
||||||
else:
|
kind = "input"
|
||||||
self.ports[signal] = (len(self.ports), "input")
|
elif kind == "o":
|
||||||
|
kind = "output"
|
||||||
|
elif kind == "io":
|
||||||
|
kind = "inout"
|
||||||
|
self.ports[signal] = (len(self.ports), kind)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def lhs(self):
|
def lhs(self):
|
||||||
|
@ -412,10 +416,11 @@ def convert_fragment(builder, fragment, name, top):
|
||||||
for domain, signal in fragment.iter_drivers():
|
for domain, signal in fragment.iter_drivers():
|
||||||
xformer.add_driven(signal, sync=domain is not None)
|
xformer.add_driven(signal, sync=domain is not None)
|
||||||
|
|
||||||
# Register all signals used as ports in the current fragment. The wires are lazily
|
# Transform all signals used as ports in the current fragment eagerly and outside of
|
||||||
# generated, so registering ports eagerly ensures they get correct direction qualifiers.
|
# any hierarchy, to make sure they get sensible (non-prefixed) names.
|
||||||
for signal in fragment.ports:
|
for signal in fragment.ports:
|
||||||
xformer.add_port(signal)
|
xformer.add_port(signal, fragment.ports[signal])
|
||||||
|
xformer(signal)
|
||||||
|
|
||||||
# Transform all clocks clocks and resets eagerly and outside of any hierarchy, to make
|
# Transform all clocks clocks and resets eagerly and outside of any hierarchy, to make
|
||||||
# sure they get sensible (non-prefixed) names. This does not affect semantics.
|
# sure they get sensible (non-prefixed) names. This does not affect semantics.
|
||||||
|
|
|
@ -516,7 +516,7 @@ class Signal(Value, DUID):
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
try:
|
try:
|
||||||
name = tracer.get_var_name()
|
name = tracer.get_var_name(depth=2 + src_loc_at)
|
||||||
except tracer.NameNotFound:
|
except tracer.NameNotFound:
|
||||||
name = "$signal"
|
name = "$signal"
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -557,7 +557,8 @@ class Signal(Value, DUID):
|
||||||
other : Value
|
other : Value
|
||||||
Object to base this Signal on.
|
Object to base this Signal on.
|
||||||
"""
|
"""
|
||||||
kw = dict(shape=cls.wrap(other).shape(), name=tracer.get_var_name())
|
kw = dict(shape=cls.wrap(other).shape(),
|
||||||
|
name=tracer.get_var_name(depth=2 + src_loc_at))
|
||||||
if isinstance(other, cls):
|
if isinstance(other, cls):
|
||||||
kw.update(reset=other.reset, reset_less=other.reset_less, attrs=other.attrs)
|
kw.update(reset=other.reset, reset_less=other.reset_less, attrs=other.attrs)
|
||||||
kw.update(kwargs)
|
kw.update(kwargs)
|
||||||
|
@ -749,9 +750,25 @@ class ValueDict(MutableMapping):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return map(lambda x: None if x is None else x.value, sorted(self._inner))
|
return map(lambda x: None if x is None else x.value, sorted(self._inner))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, ValueDict):
|
||||||
|
return False
|
||||||
|
if len(self) != len(other):
|
||||||
|
return False
|
||||||
|
for ak, bk in zip(self, other):
|
||||||
|
if ValueKey(ak) != ValueKey(bk):
|
||||||
|
return False
|
||||||
|
if self[ak] != other[bk]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._inner)
|
return len(self._inner)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
pairs = ["({!r}, {!r})".format(k, v) for k, v in self.items()]
|
||||||
|
return "ValueDict([{}])".format(", ".join(pairs))
|
||||||
|
|
||||||
|
|
||||||
class ValueSet(MutableSet):
|
class ValueSet(MutableSet):
|
||||||
def __init__(self, elements=()):
|
def __init__(self, elements=()):
|
||||||
|
|
|
@ -14,17 +14,19 @@ class DomainError(Exception):
|
||||||
|
|
||||||
class Fragment:
|
class Fragment:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ports = ValueSet()
|
self.ports = ValueDict()
|
||||||
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):
|
def add_ports(self, *ports, kind):
|
||||||
self.ports.update(flatten(ports))
|
assert kind in ("i", "o", "io")
|
||||||
|
for port in flatten(ports):
|
||||||
|
self.ports[port] = kind
|
||||||
|
|
||||||
def iter_ports(self):
|
def iter_ports(self):
|
||||||
yield from self.ports
|
yield from self.ports.keys()
|
||||||
|
|
||||||
def drive(self, signal, domain=None):
|
def drive(self, signal, domain=None):
|
||||||
if domain not in self.drivers:
|
if domain not in self.drivers:
|
||||||
|
@ -161,6 +163,7 @@ class Fragment:
|
||||||
outs |= ports & sub_outs
|
outs |= ports & sub_outs
|
||||||
|
|
||||||
# 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, outs)
|
self.add_ports(ins, kind="i")
|
||||||
|
self.add_ports(outs, kind="o")
|
||||||
|
|
||||||
return ins, outs
|
return ins, outs
|
||||||
|
|
|
@ -16,10 +16,8 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
f = Fragment()
|
f = Fragment()
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=())
|
f._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet())
|
self.assertEqual(f.ports, ValueDict([]))
|
||||||
self.assertEqual(outs, ValueSet())
|
|
||||||
self.assertEqual(f.ports, ValueSet())
|
|
||||||
|
|
||||||
def test_self_contained(self):
|
def test_self_contained(self):
|
||||||
f = Fragment()
|
f = Fragment()
|
||||||
|
@ -28,10 +26,8 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
self.s1.eq(self.c1)
|
self.s1.eq(self.c1)
|
||||||
)
|
)
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=())
|
f._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet())
|
self.assertEqual(f.ports, ValueDict([]))
|
||||||
self.assertEqual(outs, ValueSet())
|
|
||||||
self.assertEqual(f.ports, ValueSet())
|
|
||||||
|
|
||||||
def test_infer_input(self):
|
def test_infer_input(self):
|
||||||
f = Fragment()
|
f = Fragment()
|
||||||
|
@ -39,10 +35,10 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
self.c1.eq(self.s1)
|
self.c1.eq(self.s1)
|
||||||
)
|
)
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=())
|
f._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet((self.s1,)))
|
self.assertEqual(f.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet())
|
(self.s1, "i")
|
||||||
self.assertEqual(f.ports, ValueSet((self.s1,)))
|
]))
|
||||||
|
|
||||||
def test_request_output(self):
|
def test_request_output(self):
|
||||||
f = Fragment()
|
f = Fragment()
|
||||||
|
@ -50,10 +46,11 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
self.c1.eq(self.s1)
|
self.c1.eq(self.s1)
|
||||||
)
|
)
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=(self.c1,))
|
f._propagate_ports(ports=(self.c1,))
|
||||||
self.assertEqual(ins, ValueSet((self.s1,)))
|
self.assertEqual(f.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet((self.c1,)))
|
(self.s1, "i"),
|
||||||
self.assertEqual(f.ports, ValueSet((self.s1, self.c1)))
|
(self.c1, "o")
|
||||||
|
]))
|
||||||
|
|
||||||
def test_input_in_subfragment(self):
|
def test_input_in_subfragment(self):
|
||||||
f1 = Fragment()
|
f1 = Fragment()
|
||||||
|
@ -65,11 +62,11 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
self.s1.eq(0)
|
self.s1.eq(0)
|
||||||
)
|
)
|
||||||
f1.add_subfragment(f2)
|
f1.add_subfragment(f2)
|
||||||
ins, outs = f1._propagate_ports(ports=())
|
f1._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet())
|
self.assertEqual(f1.ports, ValueDict())
|
||||||
self.assertEqual(outs, ValueSet())
|
self.assertEqual(f2.ports, ValueDict([
|
||||||
self.assertEqual(f1.ports, ValueSet())
|
(self.s1, "o"),
|
||||||
self.assertEqual(f2.ports, ValueSet((self.s1,)))
|
]))
|
||||||
|
|
||||||
def test_input_only_in_subfragment(self):
|
def test_input_only_in_subfragment(self):
|
||||||
f1 = Fragment()
|
f1 = Fragment()
|
||||||
|
@ -78,11 +75,13 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
self.c1.eq(self.s1)
|
self.c1.eq(self.s1)
|
||||||
)
|
)
|
||||||
f1.add_subfragment(f2)
|
f1.add_subfragment(f2)
|
||||||
ins, outs = f1._propagate_ports(ports=())
|
f1._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet((self.s1,)))
|
self.assertEqual(f1.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet())
|
(self.s1, "i"),
|
||||||
self.assertEqual(f1.ports, ValueSet((self.s1,)))
|
]))
|
||||||
self.assertEqual(f2.ports, ValueSet((self.s1,)))
|
self.assertEqual(f2.ports, ValueDict([
|
||||||
|
(self.s1, "i"),
|
||||||
|
]))
|
||||||
|
|
||||||
def test_output_from_subfragment(self):
|
def test_output_from_subfragment(self):
|
||||||
f1 = Fragment()
|
f1 = Fragment()
|
||||||
|
@ -95,11 +94,13 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
)
|
)
|
||||||
f1.add_subfragment(f2)
|
f1.add_subfragment(f2)
|
||||||
|
|
||||||
ins, outs = f1._propagate_ports(ports=(self.c2,))
|
f1._propagate_ports(ports=(self.c2,))
|
||||||
self.assertEqual(ins, ValueSet())
|
self.assertEqual(f1.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet((self.c2,)))
|
(self.c2, "o"),
|
||||||
self.assertEqual(f1.ports, ValueSet((self.c2,)))
|
]))
|
||||||
self.assertEqual(f2.ports, ValueSet((self.c2,)))
|
self.assertEqual(f2.ports, ValueDict([
|
||||||
|
(self.c2, "o"),
|
||||||
|
]))
|
||||||
|
|
||||||
def test_input_cd(self):
|
def test_input_cd(self):
|
||||||
sync = ClockDomain()
|
sync = ClockDomain()
|
||||||
|
@ -110,10 +111,12 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
f.add_domains(sync)
|
f.add_domains(sync)
|
||||||
f.drive(self.c1, "sync")
|
f.drive(self.c1, "sync")
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=())
|
f._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet((self.s1, sync.clk, sync.rst)))
|
self.assertEqual(f.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet(()))
|
(self.s1, "i"),
|
||||||
self.assertEqual(f.ports, ValueSet((self.s1, sync.clk, sync.rst)))
|
(sync.clk, "i"),
|
||||||
|
(sync.rst, "i"),
|
||||||
|
]))
|
||||||
|
|
||||||
def test_input_cd_reset_less(self):
|
def test_input_cd_reset_less(self):
|
||||||
sync = ClockDomain(reset_less=True)
|
sync = ClockDomain(reset_less=True)
|
||||||
|
@ -124,10 +127,11 @@ class FragmentPortsTestCase(FHDLTestCase):
|
||||||
f.add_domains(sync)
|
f.add_domains(sync)
|
||||||
f.drive(self.c1, "sync")
|
f.drive(self.c1, "sync")
|
||||||
|
|
||||||
ins, outs = f._propagate_ports(ports=())
|
f._propagate_ports(ports=())
|
||||||
self.assertEqual(ins, ValueSet((self.s1, sync.clk)))
|
self.assertEqual(f.ports, ValueDict([
|
||||||
self.assertEqual(outs, ValueSet(()))
|
(self.s1, "i"),
|
||||||
self.assertEqual(f.ports, ValueSet((self.s1, sync.clk)))
|
(sync.clk, "i"),
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
class FragmentDomainsTestCase(FHDLTestCase):
|
class FragmentDomainsTestCase(FHDLTestCase):
|
||||||
|
|
Loading…
Reference in a new issue