Procedure call in loop with non-static signal name

4.4k views Asked by At

In some testbench code I use a procedure to do something with a signal. I then use this procedure multiple times in sequence on different signals. This works fine as long as I explicitly define the signal; as soon as I index signals in a loop it fails with

(vcom-1450) Actual (indexed name) for formal "s" is not a static signal name.

Why is this not possible and how can I work around it? Probably I could move this to a for ... generate, but then I want do_something to be called in a nicely defined sequence.

library ieee;
use ieee.std_logic_1164.all;

entity test is
end test;

architecture tb of test is
    signal foo : std_logic_vector(1 downto 0);
begin
    dummy: process is
        procedure do_something (
            signal s : out std_logic
        ) is begin
            s <= '1';
            report "tic";
            wait for 1 ns;
            -- actually we would do something more interesting here
            s <= '0';
            report "toc";
        end procedure;
    begin
        -- This works well, but requires manual loop-unrolling
        do_something(foo(0));
        do_something(foo(1));

        -- This should do the same 
        for i in foo'range loop
            -- This is the offending line:
            do_something(foo(i));
        end loop;
        wait; -- for ever
    end process dummy;
end architecture tb;

I'm using ModelSim 10.4 PE.

2

There are 2 answers

10
AudioBubble On BEST ANSWER

Interestingly, if foo is a variable local to the process, (and s is adjusted to suit) ghdl compiles this. Which highlights the problem in the original version. The "for" loop is required to drive the whole of foo all the time because you can't make signal drivers appear or disappear at will - it can't be ambivalent about which bits it's driving, (and as you can see, the procedure tries to drive different bits at different times).

So if you can readjust your application to allow variable update semantics, and make foo a variable local to the process, that will work. (You would have to copy its value to a signal before every "wait" if you wanted to see the effect!)

Alternatively, pass the entire foo signal and the index to the subprogram, so that the latter always drives all of foo as follows... (I've also added the missing bits and fixed the spurious concurrent "wait" : in future, PLEASE check your code example actually compiles before posting!)

library ieee;
use ieee.std_logic_1164.all;

entity test is
end test;

architecture tb of test is
    signal foo : std_logic_vector(1 downto 0);
begin
    dummy: process is
        procedure do_something (
            signal s : out std_logic_vector(1 downto 0); 
            constant i : in natural
        ) is begin
            s <= (others => '0');
            s(i) <= '1';
            report "tic";
            wait for 1 ns;
            -- actually we would do something more interesting here
            s(i) <= '0';
            report "toc";
        end procedure;

    begin
        -- This works well, but requires manual loop-unrolling
        do_something(foo,0);
        do_something(foo,1);

        -- This should do the same 
        for i in foo'range loop
            -- This is the offending line:
            do_something(foo,i);
        end loop;
        wait; -- for ever
    end process dummy;

end architecture tb;
2
pc3e On

I share your feelings about this being a silly limitation of the language. Minus the wait and report statements your example certainly has a valid hardware implementation, let alone well defined simulation behavior.

I think this situation can be avoided in most cases. For example, in your simple example you could just copy the contents of the procedure into the process body, or pass the whole vector as Brian proposed. If you really need to do it, this is one workaround:

architecture tb of test is
    signal foo : std_logic_vector(1 downto 0);
    signal t : std_logic;
    signal p : integer := 0;
begin
    foo(p) <= t;

    dummy: process is
        procedure do_something (
            signal s : out std_logic
        ) is begin
            s <= '1';
            wait for 1 ns;
            s <= '0';
        end procedure;
    begin
        for i in foo'range loop
            p <= idx;
            do_something(t);
            wait for 0 ns;
        end loop;
        wait;
    end process dummy;
end architecture tb;

This only works in simulation and will result in one delta cycle delay per iteration, compared to unrolling the loop which finishes in zero time when the procedure contains no wait statements.