How can I use lists of signals and still convert to verilog

Hello, new myhdl user here. I’ve hit my first hurdle that I haven’t been able to solve perusing the many excellent examples out there. In fact, I’ve seen others with similar problem but haven’t seen a resolution. I initially posted in in issue #161 and later found this forum which is probably a better place for my question:

I’ve seen in a few places (issues #161 & #181 that “Conversion of List of Signals for a top-level block is not supported.”

I’ve tried to work around this by creating a top() wrapper which doesn’t have a list of signals but conversion still fails (presumably since I have a list of signals in a lower level of hierarchy & I mis-understand what top-level block means).

Since it doesn’t appear that list of signal conversion will be supported anytime soon (I’ve seen conversations on this dating back over a decade: https://sourceforge.net/p/myhdl/mailman/message/18646379/ ), what is the best way to handle this situation? I’m really enjoying writing in myhdl but will have this situation often as my designs are hierarchical where I often need to assign to individual bits of a bus.

Here’s a simple example: I define a “dff” then want to create a bank of them in a block named “registers”. Below is the syntax that I had to use to get it to simulate correctly but am unsure how I can convert this to verilog. Calling either convert_register() or convert_top() results in the same error:

myhdl.ConversionError: in file issue_161.py, line 37:    # line 37 is the ConcatSignal line
    Not supported: extra positional arguments

A working example of how to handle this situation (for verilog conversion) would be greatly appreciated. Below is my simplified code.

@block
def dff(clk, d, q):

    @always(clk.posedge)
    def logic():
        q.next = d

    return logic


@block
def registers(clk, d, q):
    """ bank of 8 dff's """
    insts = []

    # need to create a temporary storage for q's b/c shadow sig is read-only
    qs = [Signal(bool(0)) for _ in range(len(q))]

    for i in range(len(d)):

        inst = dff(clk, d(i), qs[i])
        inst.name = 'dff%d' % i
        insts.append(inst)

    # Concatenate the qs bits and send them out on the q output.
    @always_comb
    def make_q():
        q.next = ConcatSignal(*reversed(qs))

    return insts, make_q


@block
def top():
    """ Dummy level of hierarchy to see if it will convert """
    n_bits = 8
    clk = Signal(bool(0))
    d = Signal(intbv(0)[n_bits:])
    q = Signal(intbv(0)[n_bits:])
    inst = registers(clk, d, q)
    return inst


def convert_register():
    n_bits = 8
    clk = Signal(bool(0))
    d = Signal(intbv(0)[n_bits:])
    q = Signal(intbv(0)[n_bits:])

    toVerilog(registers(clk, d, q))


def convert_top():
    toVerilog(top())

ConcatSignal() is meant to be used outside the @always_xxx generators, as the conversion doesn’t handle anonymous Signals

    qt = ConcatSignal(*reversed(qs))

    @always_comb
    def make_q():
        q.next = qt

an alternative approach:

    @always_comb
    def make_q():
        for i in range(len(q)):
            q.next[i] = qs[i]

Adding conversion of a list of Signals isn’t that hard to add, but it never got done because of differences of opinion, or simply that our BDFL didn’t find it worthwhile?
Conversion would implicate enumerating the list of Signals in the module/entity definition. At least if we want to keep Verilog conversion; in VHDL and SystemVerilog we have other possible solutions. But in order to access the elements as an array they would need to be assigned to an array (in the case of input) or assigned from an array (in case of output). I suppose this is the major blocking obstacle?

Regards,
Josy

or (not tested) :

    @always_comb
    def make_q() :
        q.next = concat(*reversed(qs)) 

@DrPi
concat is very picky, it requires an explicitly singular first argument. Perhaps q.next = concat(qs[len(qs) - 1], *reversed(qs[:len(qs) - 1]) would do the job, but man is it ugly!. Of course we could extend MyHDL to silently accept assignment list of Signal(bool) to intbv and vice versa?

Thank you both for your replies. Both of @josyb suggestions worked. I didn’t try yours @DrPi based on the followup by josyb.

I was a bit concerned after reading “A future for the MyHDL community” post but your quick responses help alleviate some of my concerns. That thread is almost 2 years old (last entry 1.5 years ago). I’ll post some follow up questions in that thread.

@etch32 Back to your original post. You asked how to use list of signals in top level module.
I suggest you have a look at PR#319.
As Josy said, the problem is with verilog which, unlike VHDL, does not allow arrays of signals in module ports. So, conversion to VHDL is easily feasible. But conversion to verilog would need to instantiate signals with special names, assign them to a internal array for inputs, the opposite for outputs. This is possible but there is a risk of name collision with other port names.
As you can see, the work is much more complex for conversion to verilog. With system verilog, there is no such limitation.
MyHDL requests the use of VHDL2008. Shall we request the use of system verilog ? I have no knowledge in verilog/system verilog world to tell.
Maybe conversion to verilog is worth the work…

Actually let’s take a step back and see why I ran into this problem in the first place. I may have intially asked the wrong question. What I really wanted to do was something like the following (note the change from qs[i] in my original post to q(i)

    for i in range(len(d)):

        inst = dff(clk, d(i), q(i))
        inst.name = 'dff%d' % i
        insts.append(inst)

I’m just trying to pass individual bits of a “bus” d & q to the dff instance. This works fine for d but for q it doesn’t work b/c shadow signals are not allowed to be assigned to.

If this were allowed I’d no longer need the qs list nor the make_q function to assign qs to q. See the full code in my original post for full context.

Maybe there’s a much better way to do this that doesn’t involve lists at all?

The shadow signal exists to let you use a signal at different places for inputs. This does not make sense to use such shadow signals for outputs.
This is like in verilog/VHDL. A signal can be used as input in all processes you want. But a signal can be assigned by only one process.
When you write inst = dff(clk, d(i), q[i]) q[i] is used by dff() as the target of the assignment.

Your small example shows a problem you try to workaround. Why using q[i] is a problem ? Can you explain your real world problem ? Maybe you have to think differently.

This is pretty close to my real world problem. Instead of a dff as the lower level hierarchy think of it as any module which I want to instantiated a number of times.

In fact it could be even more complex, instead of passing in one bit of a bus to the d & q inputs, maybe this lower level module also has bussed input of say width 4. Let’s call this input nibble. Now at a higher level of hierarchy I have an 8 bit bus (which we’ll call byte). Go up another level and we have a 32 bit bus called word. So three levels. In gibberish-pseudo-code (indexing might be off by 1) it would look something like:

module_word():
   word = Signal(intbv(0)[32:]
   byte_inst3 = module_byte(byte_bits=word[31:24])
   byte_inst2 = module_byte(byte_bits=word[23:16])
   byte_inst1 = module_byte(byte_bits=word[15:8])
   byte_inst0 = module_byte(byte_bits=word[7:0])

module_byte(byte_bits):
  nibble_inst1 = module_nibble(nibble_bits=byte_bits[7:4])
  nibble_inst0 = module_nibble(nibble_bits=byte_bits[3:0])

module_nibble(nibble_bits):
  ...
  # read a nibble bit
  value = nibble_bits[3]
  # write a nibble bit
  nibble_bits[0].next = 1

This is a pattern I will be using often as I build up my hierarchy of repeated lower level structures. So I’d like to understand the best pattern in myhdl for doing this. I’m open the thinking differently.

If I understand correctly I believe you asked why I couldn’t simply use q[i] (with square brackets)? That was the very first thing I tried and got

    q.next = d
AttributeError: 'bool' object has no attribute 'next'

After doing some searching I stumbled across the q(i) notation but then ended up with the shadow signal problem. That led me to making the temporary list which started this whole discussion.

Below is the code snippet using q[i] (not lists) which results in the AttributeError shown above. Now maybe I’m just not definiting the q bus that I pass in correctly (see the function top() for the construction of the q & d busses).

@block
def registers(clk, d, q):
    """ bank of 8 dff's """
    insts = []

    for i in range(len(d)):

        inst = dff(clk, d(i), q[i])
        inst.name = 'dff%d' % i
        insts.append(inst)

    return insts


@block
def top():
    """ Dummy level of hierarchy to see if it will convert """
    n_bits = 8
    clk = Signal(bool(0))
    d = Signal(intbv(0)[n_bits:])
    q = Signal(intbv(0)[n_bits:])
    inst = registers(clk, d, q)
    return inst

Let explain why you get this exception :
Say you declare a signal like this : my_sig = Signal(intbv(0)[8:])
To change the signal value, you write my_sig.next = new_value
To change one bit of the signal, you write my_sig.next[bit_number] = new value

Writing inst = dff(clk, d(i), q[i]) in registers and q.next = d in dff(), is in fact equivalent to writing q[i].next = d. Here, q[i] is a boolean. And a boolean has no next property.

As a workaround, you could write inst = dff(clk, d(i), q, i) in registers() and q.next[i] = d in dff(), passing i to dff(). But in this case, you’ll get an error since your signal is assigned by multiple processes (as this is the case with standard HDL).

Here we are. You try to assign bits to a vector with multiple processes. This is not correct, neither in MyHDL, nor in VHDL or verilog.
A signal can be assigned in only one process. You have no other choice than using intermediate signals and combining them in one place (whatever the language used).

I am not sure that VHDL would throw an error, as the component would only set a single bit which is not set anywhere else …
That MyHDL throws an error is due to the fact that a Signal(intbv()) does not really have actual bits, it is an integer representation. This also explains why we need ShadowSignals to feed a slice to a sub-function. And why the call with q[i] as an output argument doesn’t work; the slice/index operation returns an integer or bool value, which doesn’t have a `.next’ property.

Thanks so much for the discussion guys. I do understand why I get the error when I used q[i] as this individual bit is just a boolean.

I’m not sure I see where I’m assigning bits by multiple processes as you intimated in your text included below. The only thing I see is that in myhdl I need to construct this intermediate list but don’t believe it’s necessary in other HDLs. I was asking if there was a different or better way but it appears this is the pattern which must be used in myhdl.

Similar to what @josyb wrote about VHDL, I don’t think verilog has an issue with this either since I’m only assigning in one process. e.g. here’s functioning verilog code similar in concept to what I’m trying to do in myhdl (w/o having to resort to intermediate signals for q)

module registers (
    input clk,
    input [7:0] d,
    output [7:0] q
);

    wire [7:0] q;

    dff u0 (clk, d[0], q[0]);
    dff u1 (clk, d[1], q[1]);
    dff u2 (clk, d[2], q[2]);
    dff u3 (clk, d[3], q[3]);
    dff u4 (clk, d[4], q[4]);
    dff u5 (clk, d[5], q[5]);
    dff u6 (clk, d[6], q[6]);
    dff u7 (clk, d[7], q[7]);

endmodule // registers

module dff (
    input clk,
    input d,
    output q
);

    reg q;
    
    always @(posedge clk) begin
        q <= d;
    end

endmodule // dff

I made a quick test in VHDL (using Lattice Diamond compiler). It works. So VHDL and Verilog verify the assignment contention at the bit level.

To complete Josy answer :
There’s a concept difference between MyHDL and VHDL (and verilog ?).
VHDL basic std_logic_vector type is a bunch of bits without meaning (signed, unsigned).
signed and unsigned vector types are constructed on top of std_logic_vector and give a signification to the sign bit.
In VHDL, a vector type is a bunch of bits to which you may give a signification to the sign bit.
In MyHDL, a intbv() is a number to which you may give a size.
As a consequence, with MyHDL, you can use intbv() without size (in simulation). With MyHDL, we are modelling at a slightly higher level.
Currently, bit assignment is possible but with restrictions, as you discovered it.