5

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.

mbschenkel
  • 1,865
  • 1
  • 18
  • 40

2 Answers2

6

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;
  • Sorry for shuffling the source-lines, the question is corrected now. Your solution would defenitely work for the simple example I've posted, but in reality I have less homogeneous arrays of structures and not just a single `std_logic_vector`. Also variables wouldn't work because I intend to loop some signals back into the procedure for verification (not shown in the question). I will probably need to separate the signal-selection from the sequence control and approach this in a less procedural and more RTL-like fashion. However, I still don't fully understand why non-static `i` shouldn't work. – mbschenkel Jun 25 '15 at 11:17
  • `i` being non-static isn't the problem. `foo(i)` being non-static is. I appreciate this isn't synthesisable anyway, but it may help to visualise hardware here : the original reconnects formal `s` to a different actual in each iteration. Dynamic rewiring is not part of what the language permits. The altered form connects formal `s` to the same signal (all of `foo`) permanently. NB you can pass arrays of structures as parameters; can you make the internal selection work? –  Jun 25 '15 at 13:05
  • It seems like I would need a `for ... generate` inside the process to avoide the 'rewiring'... And, unfortunately I planned to call this procedure on the members of a rather irregular structure, so the approach you've suggested can't be easily applied then. – mbschenkel Jun 28 '15 at 22:09
  • for ... generate is a concurrent construct, meaning it's not available in a sequential code region ... i.e a process. Expect an update to my answer. –  Jun 28 '15 at 22:18
  • 1
    ... or not. I don't understand what you're trying to do well enough to help. Waits in procedures just killed the latest attempt, sorry. It's unclear to me why passing the whole of foo to the procedure doesn't do what you want. –  Jun 28 '15 at 22:33
  • Well, I'm trying to excite a number of signals (some of them in vectors and structs) with a given pattern sequentially, to one at a time and record the effect they have on another signal. And I know that `generate` inside in sequential code is not available, that's why I wrote that "I would need [it]". – mbschenkel Jun 29 '15 at 10:57
  • @BrianDrummond It's generally not a problem if a process doesn't explicitly drive the whole of a vector all the time. You can conditionally drive one bit of a vector using a different signal as the selector without any issues, also for synthesis. The other bits will keep their old value by default. There is perhaps a somewhat sensible reason for this this limitation, but that isn't it. – pc3e Mar 04 '16 at 16:03
  • @pc3e : I suspect you are thinking of a clocked process which drives the whole vector (or a statically determined subset of it) but only *updates* a selected bit according to a signal. There, the undriven (in this cycle) bits do indeed retain their previous value. But I'd need to see the example you have in mind to be sure. –  Mar 04 '16 at 16:21
  • @BrianDrummond If you drive an array element in a process using a non-static selector, the compiler will assume that the whole vector is driven by the process. I don't see why it should make any difference if the signal is driven directly in the process or through a procedure like in the original code. My understanding is that procedures are simply extensions of the calling process, such that copying the code in the procedures into the process always yield the same result. As long as that holds there is no reason why this shouldn't be allowed. – pc3e Mar 04 '16 at 17:06
  • @pc3e There is something (I cannot quite put my finger on exactly what atm) about passing parameters to procedures that interferes with this. I agree that it's a language limitation/simplification and not fundamental to HW design (clearly! because the simple modifications work) but I can't see how to relax the rule without infringing on the un-implementable. As I have logged bugs against a synth tool (ISE : eventually fixed!) in exactly the area of parameter passing to procedures, they have enough difficulty with the existing rules, which makes me cautious. –  Mar 04 '16 at 18:57
1

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.

pc3e
  • 1,299
  • 11
  • 18
  • Interesting ... but the modified object is not a bit in `foo` so I'm not clear how it answers the question. –  Mar 04 '16 at 18:39
  • It seems I was a bit quick to post this answer! In my situation I had a library procedure with a signal of direction `in`, which obviously did not translate well to this situation. I also had no updates to the signal while the procedure was running, which would also have broken the solution. I have updated my answer to something that should work better for this particular example. – pc3e Mar 06 '16 at 15:07