MyHDL Discourse

Combinational tree like accumulatioin


#1

Hello,
I want to create a (convertible) structure that will accumulate a list of signals in always_comb, here is a code that works perfectly fine (calc_elems is a list of signals, filled from 0 to in_size):

    calc_elems_len = 2 * in_size - 1
    @block
    def add(z, x, y):
            @always_comb
            def logic():
                    z.next = x + y
            return logic

    add_inst = list()
    for i in range(in_size, calc_elems_len):
            a = 2 * i - 2 * in_size
            b = a + 1
            add_inst.append(add(calc_elems[i], calc_elems[a], calc_elems[b]))

I can use it in simulation and it converts into correct VHDL. But its not very nice to use, I would like to do something like this:

    calc_elems_len = 2 * in_size - 1
    for i in range(in_size, calc_elems_len):
            a = 2 * i - 2 * in_size
            b = a + 1
            @always_comb
            def add_me():
                calc_elems[i].next = calc_elems[a] + calc_elems[b]

The problem is that I get:
myhdl.AlwaysCombError: signal ({'calc_elems'}) used as inout in always_comb function argument
Which is not true, because each signal in the list is used exactly in one direction in one always_comb. Unless list of signals is treated as a whole, which is wrong.
Is there a way to achieve this without the need to create an extra add block?


#2

The option is to create a and b outside the @always, make them a list of ints, and then move the loop inside the @always.


#3

The error you get is a problem already discussed in other threads.
You can try this (not tested) :

    calc_elems_len = 2 * in_size - 1
    for i in range(in_size, calc_elems_len):
            a = 2 * i - 2 * in_size
            b = a + 1
            c = calc_elems[a] + calc_elems[b]
            @always_comb
            def add_me():
                calc_elems[i].next = c

#4

This one will more likely work (still not tested) :

    calc_elems_len = 2 * in_size - 1
    c = [Signal(calc_elems[0].val) for _ in range(len(calc_elems))]
    for i in range(in_size, calc_elems_len):
            a = 2 * i - 2 * in_size
            b = a + 1
            @always_comb
            def add_me():
                c[i].next = calc_elems[a] + calc_elems[b]
            @always_comb
            def assign_me():
                calc_elems[i].next = c[i]

#5

IMO, and certainly if when converting to VHDL, that check in the code can be removed. The only issue is that when that signal is a top-level signal (which in this case I presume it isn’t) you would get an inout port …


#6

Thanks for help, its not top-level signal, what worked at the end is:

    @always_comb
    def add_me():
        for i in range(in_size, calc_elems_len):
            calc_elems_helper[i].next = calc_elems[2 * i - 2 * in_size] + calc_elems[2 * i - 2 * in_size + 1]

    @always_comb
    def assign_me():
        for i in range(in_size, calc_elems_len):
            calc_elems[i].next = calc_elems_helper[i]

For unknown to me reason when I moved loop outside always_comb I didn’t get any warnings/errors but signals were not assigned.


#7

That makes sense.
With loop outside always_comb, you have to add_inst.append(add_me) and add_inst.append(assign_me) inside the loop… if it works.