At first glance you would expect the VHDL source code below to behave as a shift register. In that q, over time would be
"UUUU0", "UUU00", "UU000", "U0000", "00000", ....
but instead it is always U
after five (or more) consecutive clock cycles.
Why is this?
This code is actually a much simplified version of a far more complicated simulation. But it demonstrates the symptoms that I see.
It exhibits this interesting and unexpected result during simulation under both ModelSim and ActiveHDL, I have not tried other simulators and would (secondly to an explanation of the cause) like to know if others act in the same way.
To answer this question properly you must understand that:
- I know this is not the best way of implementing a shift register
- I know for RTL synthesis this should have a reset.
- I know an array of std_logic is a std_logic_vector.
- I know of the aggregation operator,
&
.
What I have also found:
- If the assignment
temp(0)<='0';
is moved inside the process, it works. - If the loop is unwrapped (see commented code), it works.
I will reiterate that this is a very simplified version of a much more complicated design (for a pipelined CPU), configured to purely show the unexpected simulation results. The actual signal types are just a simplification. For this reason you must consider your answers with the code in the form as it is.
My guess is that the VHDL simulation engine's optimiser is mistakenly (or perhaps as per specification) not bothering to run the expressions inside the loop as no signals outside change, though I can disprove this by placing the unwrapped loop in a loop.
So I expect that the answer to this question is more to do with the standards for VHDL simulation of inexplicit VHDL syntax and how VHDL simulation engines do their optimisations, rather than if given code example is the best way of doing something or not.
And now to the code I am simulating:
library ieee;
use ieee.std_logic_1164.all;
entity test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end entity;
architecture example of test_simple is
type t_temp is array(4 downto 0) of std_logic;
signal temp : t_temp;
begin
temp(0) <= '0';
p : process (clk)
begin
if rising_edge(clk) then
for i in 1 to 4 loop
temp(i) <= temp(i - 1);
end loop;
--temp(1) <= temp(0);
--temp(2) <= temp(1);
--temp(3) <= temp(2);
--temp(4) <= temp(3);
end if;
end process p;
q <= temp(4);
end architecture;
And the test bench:
library ieee;
use ieee.std_logic_1164.all;
entity Bench is
end entity;
architecture tb of bench is
component test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end component;
signal clk:std_logic:='0';
signal q:std_logic;
signal rst:std_logic;
constant freq:real:=100.0e3;
begin
clk<=not clk after 0.5 sec / freq;
TB:process
begin
rst<='1';
wait for 10 us;
rst<='0';
wait for 100 us;
wait;
end process;
--Note: rst is not connected
UUT:test_simple port map (clk=>clk,q=>q) ;
end architecture;
temp(0)
because there are no "events" associated with the literal constant. Putting the assignment inside theprocess
creates an association with the clock events that causes it to work. I wonder if adding anafter
clause to the assignment would be a potential workaround. \$\endgroup\$ – Dave Tweed Dec 11 '12 at 14:57