Search code examples
vhdlsimulationfifodigital-logic

Index constraint violation in vhdl


I've a problem with the simulation of my code. I have an asynchronous FIFO that is composed by a dual port memory. The write are performed synchronous to the writing clock, the read are performed providing the address of the location I want to read. The synchronization is performed by read and write pointer.

Basically I have something like this :

architecture rtl of async_memory is 
     type RAM is array (MEM_DEPTH - 1 downto 0) of std_logic_vector(DATAWIDTH - 1 downto 0);
     signal memory : RAM;

begin
     MEM_WRITE:process(clk,rstn)
     begin
     .....
        memory(to_integer(unsigned(addr_wr_i))) <= data_i;
     .....
     end process;

     MEM_READ:data_o <= memory(to_integer(unsigned(addr_rd_i)));

When MEM_DEPTH is a power of 2 I don't get any problem. When the MEM_DEPTH isn't a power of 2, I have some problem when I'm simulating a random delay for each wire of the addr_rd_i ( the address signal for the read ).

Just to be clear, if I have set a MEM_DEPTH of 10, the width of the addr_rd_i is 4 bits. The allowed value of addr_rd_i are :

  1. 0000
  2. 0001
  3. 0010
  4. 0011
  5. 0100
  6. 0101
  7. 0110
  8. 0111
  9. 1000
  10. 1001

Other values cause an error in the simulation of course (index constraint violation). The problem is I can have number bigger than 1001 due the delay. For example if addr_rd_i is 0111 and I want to read 1000 it's possible that for a short time I have 1111 :

  • 0111 -> 1111 -> 1000

Now the question : is there a way to avoid the simulation error ? I thought something like this :

MEM_READ:data_o <= memory(to_integer(unsigned(addr_rd_i)) mod MEM_DEPTH);

My only ( big ) concern is that I probably can't keep the same version of the file for the synthesis, so I'll need 2 files, one for the synthesis and one for simulations.


Solution

  • Keep the same file for synth and sim!!!

    Synthesise with and without "mod MEM_DEPTH". If they are the same size, then synthesis optimisation has removed the MOD operator ... then, no problem.

    My preferred approach: write a "to_address" function performing all the type conversions, returning a valid address. Wrap a return statement involving the MOD operator between --pragma translate off and --pragma translate on (consult your synth tool for the actual accepted syntax). Follow it with a simple return statement...

    Note that the read and write addresses should probably be declared as unsigned in the first place. Any time you're cascading type conversions, there's probably something wrong with the design...

    function to_address(addr : unsigned) return natural is
       temp : natural := to_integer(addr);
    begin
       --pragma translate off
       return temp mod MEM_DEPTH;
       --pragma translate on
       return temp;
    end to_address.
    

    Then simulation will hit the first return while synthesis will fall through to the second. Comment it out and insist on manual inspection of this function come code review time...