Search code examples
vhdlclash

Why does an array update corrupt the element value?


The following standalone VHDL file is simplified from CLaSH's output, which should explain its somewhat weird structure.

The intention is to increment s.tup2_sel1(0) in the cycle where s.tup2_sel0 is "01". However, what I see in a VHDL simulator is that OUTPUT (and thus, s.tup2_sel1(0)) becomes unknoqn (its value is "XXXXXXXX") after the array update. Why does the array element become corrupted?

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.all;

entity CHIP8 is
  port(-- clock
       CLK    : in std_logic;
       -- asynchronous reset: active high
       RESET  : in std_logic;
       OUTPUT : out unsigned(7 downto 0));

  type array_of_unsigned_8 is array (integer range <>) of unsigned(7 downto 0);

  type tup2 is record
    tup2_sel0 : std_logic_vector(1 downto 0);
    tup2_sel1 : array_of_unsigned_8(0 to 1);
  end record;

end;

architecture structural of CHIP8 is
  signal y1 : array_of_unsigned_8(0 to 1);
  signal s  : tup2;
  signal s1 : tup2;
  signal y  : array_of_unsigned_8(0 to 1);
  signal x  : unsigned(7 downto 0);
begin
  y <= s.tup2_sel1;
  x <= y(0);

  process(y)
    variable ivec : array_of_unsigned_8(0 to 1);
  begin
    ivec := y;
    ivec(0) := x + 1;
    y1 <= ivec;
  end process;

  with s.tup2_sel0 select
    s1 <= (tup2_sel0 => "01", tup2_sel1 => y) when "00",
          (tup2_sel0 => "10", tup2_sel1 => y1) when "01",
          (tup2_sel0 => "10", tup2_sel1 => y) when others;

  process(CLK,RESET)
  begin
    if RESET = '1' then
      s <= (tup2_sel0 => "00", tup2_sel1 => array_of_unsigned_8'(0 to 1 => to_unsigned(0,8)));
    elsif rising_edge(CLK) then
      s <= s1;
    end if;
  end process;

  OUTPUT <= x;
end;

My toplevel testbench generates the RESET signal:

LIBRARY ieee;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.all;

ENTITY TB IS
END TB;

ARCHITECTURE behavior OF TB IS  
  signal CLK : std_logic := '0';
  signal RESET : std_logic := '0';
  signal OUTPUT : unsigned(7 downto 0);

  constant CLK_period : time := 10 ns; 
BEGIN
  uut: entity work.CHIP8 PORT MAP (
    CLK => CLK,
    RESET => RESET,
    OUTPUT => OUTPUT);

  CLK_proc :process
  begin
    CLK <= '0';
    wait for CLK_period/2;
    CLK <= '1';
    wait for CLK_period/2;
  end process;

  RESET_proc: process
  begin
    RESET <= '1';     
    wait for CLK_period * 2;
    RESET <= '0';
    wait;
  end process;
END;

Solution

  • Signal x is missing in the sensitivity list of process(y). This can be by intention, but in 99% it's a mistake.

    Because it's generated code, I cannot tell if the people who wrote the code emitter are clever VHDL programmers like me knowing that a sensitivity list is a syntactic sugar or if they just missed to add more signals to the sensitivity list...

    How is a sensitivity list translated?
    See this example:

    process(y, x)
    begin
      -- some code
    end process;
    

    Is translated to:

    process
    begin
      -- some code
      wait on y, x;
    end process;
    

    So if they are clever, they could speculate to resume a process only on y events, but not on 'x' events. Some VHDL gurus might use this to optimize process wake-ups, others would call it a tricky code-line.


    X values can be generated, when a signal contains meta values like U, -, X, W and arithmetic like + 1 is used.

    I don't see any init values for tup2_sel1, except for a reset condition.
    So, have you applied RESET in your simulation?