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:
whitequark 2018-12-13 13:12:31 +00:00
parent 6251c95d4e
commit 90f1503c91
4 changed files with 83 additions and 54 deletions

View file

@ -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.

View file

@ -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=()):

View file

@ -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

View file

@ -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):