I do not understand why the following vhdl code does not simulate as I think it should.
test_pipe_1(0) is assigned at process pr1, but the simulation (both Aldec and GHDL) shows test_pipe_1(0) being 'U' all the time.
OTH, test_pipe_2(0) works differently in Aldec vs GHDL.
In Aldec, the signal is assigned as expected ('Z' @ 0 sec, '0' @ 500 ns, '1' @ 1 us, etc).
In GHDL, the wierd behaviour is seen ('Z' @ 0 sec, 'U' @ 500 ns). There is no 'U' driver for test_pipe_2(0) signal.
My feeling is that this is language definition feature, but cannot understand what, and why.
Any ideas?
library ieee;
use ieee.std_logic_1164.all;
entity vhdl_loop_assignment is
end;
architecture testing of vhdl_loop_assignment is
signal test_d0: std_logic;
signal test_pipe_1: std_logic_vector(9 downto 0);
signal test_pipe_2: std_logic_vector(9 downto 0) := (others => 'Z');
signal clk: std_logic;
begin
test_1: process(clk)
begin
if rising_edge(clk) then
l: for n in 0 to 8 loop
test_pipe_1(n+1) <= test_pipe_1(n);
end loop;
-- test_pipe_1(0) <= 'Z'; -- If this is uncommented, starts working as expected.
end if;
end process;
test_2: process(clk'delayed(300 ns))
begin
if rising_edge(clk'delayed(300 ns)) then
l: for n in 0 to 8 loop
test_pipe_2(n+1) <= test_pipe_2(n);
end loop;
end if;
end process;
pr1: process(clk)
begin
if rising_edge(clk) then
if test_d0 /= '0' then
test_d0 <= '0';
test_pipe_1(0) <= '0';
test_pipe_2(0) <= '0';
else
test_d0 <= '1';
test_pipe_1(0) <= '1';
test_pipe_2(0) <= '1';
end if;
end if;
end process;
pr2: process
begin
clk <= '0';
wait for 500 ns;
clk <= '1';
wait for 500 ns;
end process;
end;
Best Answer
This is an interesting question. Something I intuitively understood, but didn't know the reason why. Thanks to your question, I researched and learned something new!
It does appear that
test_pipe_1(0)
has only 1 driver (inpr1
). However, when I simulate your code in Modelsim (kudos for the MCVE!), I see 2 drivers:pr1
andtest_1
. For example:Now, when you uncomment the
test_pipe_1(0) <= 'Z';
, this drives all the values intest_pipe_1
. And sincetest_pipe_1
is constructed from resolved types, the resolution function is invoked, and the 'Z' resolves with the driver inpr1
. To clarify, here are your 2 cases:Without
test_pipe_1(0) <= 'Z';
, you have the example above. Both a'1'
and a'U'
are being driven ontest_pipe_1(0)
, which resolves to a'U'
.With
test_pipe_1(0) <= 'Z';
, you get the following drivers:Now, you are getting a
'1'
and a'Z'
ontest_pipe_1(0)
. And the resolution function resolves to a'1'
. And this is why your code starts working.As a quick aside, had you changes to
std_ulogic_vector
, your simulation would have failed to compile or failed during elaboration.Finally, to the root cause: multiple drivers. The problem is that drivers are created during elaboration, and the indices in
for ... loop
constructs are not determined until execution. Thus the simulators creates a driver for every single element intest_pipe_1
in thetest_1
process during elaboration. I admit, though I knew something was wrong, it wasn't clear to me why. I had to dig around to find out why.From the VHDL LRM (Section 12.4.4):
So, first the process is elaborated. At this point, the simulator doesn't know which bits of
test_pipe_1
are going to have drivers (the range of thefor ... loop
hasn't been evaluated yet). So, it creates all drivers required as per 12.4.4b.Now, in VHDL LRM (Section 12.5):
The
for ... loop
is elaborated 'prior to the execution' of the loop.So, your fix is to 1) get rid of the for loop or 2) separate out the signals.
So, here's a tweak to your original post. I added a signal
test_pipe_1_0_in
to be the value that will go intotest_pipe_1
(presuming this value andtest_d0
are somehow unrelated, though in this example they clearly are related. You could usetest_d0
instead and get the same result.).Hope that helps.