myhdl.AlwaysCombError: signal used as inout in always_comb function argument

I’m new to myHDL and VDHL. I’m trying to duplicate the combinatorial logic of a 74154 4:16 decoder. When I run my unit test, I get this error:

testDisable (__main__.TestLVC154) ... ERROR

======================================================================
ERROR: testDisable (__main__.TestLVC154)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "74LVC154.py", line 44, in testDisable
    self.runTest(test)
  File "74LVC154.py", line 48, in runTest
    dut = LVC154(signals.g1, signals.g2, signals.i, signals.q)
  File "/usr/local/lib/python3.6/dist-packages/myhdl/_block.py", line 196, in __call__
    self.srcline, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/myhdl/_block.py", line 218, in __init__
    self.subs = _flatten(func(*args, **kwargs))
  File "74LVC154.py", line 14, in LVC154
    @always_comb
  File "/usr/local/lib/python3.6/dist-packages/myhdl/_always_comb.py", line 50, in always_comb
    c = _AlwaysComb(func, callinfo=callinfo)
  File "/usr/local/lib/python3.6/dist-packages/myhdl/_always_comb.py", line 62, in __init__
    raise AlwaysCombError(_error.SignalAsInout % inouts)
myhdl.AlwaysCombError: signal ({'q'}) used as inout in always_comb function argument

I’m not sure what this means, or what I’ve done wrong:

import unittest
from myhdl import *

@block
def LVC154(g1, g2, i, q):
    """
    Args:
        g1 (Signal(bool)): Active low enable.
        g2 (Signal(bool)): Active low enable.
        i (intbv): Which output to activate.
        q ([Signal(bool)]): Active low outputs.
    """

    @always_comb
    def logic():
        for elem in q:
            elem.next = True
        if g1 or g2:
            return
        q[i].next = False

    return logic


class Signals(object):
    def __init__(self):
        self.g1 = Signal(bool())
        self.g2 = Signal(bool())
        self.i = intbv(val=0, min=0, max=15)
        self.q = [Signal(bool())] * 16


class TestLVC154(unittest.TestCase):
    def testDisable(self):
        def test(signals):
            signals.g1.next = False
            signals.g2.next = True
            signals.i.next = 0
            yield delay(1)
            actual = signals.q
            expected = [Signal(bool(True))] * 16
            self.assertListEqual(actual, expected)

        self.runTest(test)

    def runTest(self, test):
        signals = Signals()
        dut = LVC154(signals.g1, signals.g2, signals.i, signals.q)
        check = test(signals)
        sim = Simulation(dut, check)
        sim.run(quiet=1)


if __name__ == '__main__':
    unittest.main(verbosity=2)

Any help is appreciated! Thanks,

–Rob

I think I have a sort of answer: I changed the code to assign to each output once only:

@always_comb
def logic():
    for o, elem in enumerate(q):
        if g1 or g2:
            elem.next = True
        elif o == i:
            elem.next = False
        else:
            elem.next = True

I’m still not sure why the previous version failed port direction inference. The only difference appears to be that an output’s .next was assigned to twice, and I’m not sure why that would make a difference, since I never read the output?

In your first version, q was read in the for elem in q and assigned, the signal q, I think this might be the source of the inout error. Also note, for elem in q is not converitible.

Maybe try a form like

for x in range(16):
    q[x].next = 0 if (i == x) and not g1 and not g2 else 1

I haven’t tried this in my environment yet.