同步FIFO问题[VHDL]

时间:2017-02-14 20:57:35

标签: vhdl fifo synthesis

我正在尝试通过Vhdl示例(Wiley)实现Fpga Prototyping的FIFO,我遇到了一些问题。第一个poped数据实际上是推送的第二个数据。它似乎正在跳过FIFO的一个插槽。

以下是代码:

library IEEE;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL; 
use WORK.my_pkg.ALL;

entity Fifo is      
   Port(
        clk             : in  STD_LOGIC;
      rst_n             : in  STD_LOGIC;
        -- DATA 
      push_data_i   : in  STD_LOGIC_VECTOR (FIFO_WIDTH-1 downto 0);     -- Data IN.
        pop_data_o      : out STD_LOGIC_VECTOR (FIFO_WIDTH-1 downto 0);     -- Data out.
        -- CONTROL
      push_valid_i  : in  STD_LOGIC;                                                -- 1 to write push_data_i into the FIFO.
      pop_grant_i   : in  STD_LOGIC;                                                -- 1 to read from the FIFO.
        -- STATUS
        push_grant_o    : out STD_LOGIC;                                                -- 0 when full. To write push_grant_o=1 and push_valid_i=1.
        pop_valid_o     : out STD_LOGIC                                             -- 1 where there is data available in the FIFO.
        );
end Fifo;

architecture Behavioral of Fifo is
    type reg_type is array (2**FIFO_DEPTH-1 downto 0) of STD_LOGIC_VECTOR (FIFO_WIDTH-1 downto 0);              -- FIFO_WIDTH x FIFO_DEPTH 2D-array.
    signal array_reg : reg_type;                                                                                                    -- FIFO itself. Data is stored here.
    signal write_ptr_reg, write_ptr_next, write_ptr_succ : STD_LOGIC_VECTOR (FIFO_DEPTH-1 downto 0);        -- Write control registers.
    signal read_ptr_reg, read_ptr_next, read_ptr_succ : STD_LOGIC_VECTOR (FIFO_DEPTH-1 downto 0);           -- Read control registers.
    signal full_reg, full_next  : STD_LOGIC := '0';                                                                         -- Status registers
    signal empty_reg, empty_next : STD_LOGIC := '1';                                                                        -- Status registers
    signal operation : STD_LOGIC_VECTOR (1 downto 0) := "00";                                                           -- Operation 2 bit array 
    signal wr_en: STD_LOGIC;                                                                                                        -- Write possible register.

    begin
        -- ** PUSH & POP PORTS (data) ** --
        process(clk, rst_n)
        begin
            if(rst_n='0') then
                array_reg <= (others=>(others=>'0'));                                       -- Sets the entire array_reg (2D-array) to 0.
                write_ptr_reg <= (others=>'0'); -- Resets all write registers (to 0).
                read_ptr_reg <= (others=>'0');  -- Resets all read registers (to 0).
                full_reg <= '0';                        -- Full register is set to 0 as FIFO is not FULL.
                empty_reg <= '1';                       -- Empty register is set to 1 as FIFO is empty.
            elsif (clk'event and clk='1') then                                              -- Rising edge of the clock.
                if (wr_en='1') then
                    array_reg(to_integer(unsigned(write_ptr_reg))) <= push_data_i;  -- It writes the incoming data (push_data_i) to the corresponding position in the FIFO.
                                                                                                        -- It expects an intiger as the position in the array. Therefore the 'to_intiger' function.
                end if;
                write_ptr_reg <= write_ptr_next;    -- Current write position becomes the next one on clock event.
                read_ptr_reg <= read_ptr_next;  -- Current read position becomes the next one on clock event.
                full_reg <= full_next;              -- Current full position becomes the next one on clock event.
                empty_reg <= empty_next;            -- Current empty position becomes the next one on clock event.
            end if;
        end process;
        -- Input port:
        wr_en <= push_valid_i and (not full_reg);   -- If FIFO is NOT full it is possible to write.
        -- Output port:
        -- It is done differently from the input port as the output data ('first-in', pointed by read_ptr_reg)has to be available all the time.
        pop_data_o <= array_reg(to_integer(unsigned(read_ptr_reg)));

        -- Successive values to read and write when requested.
        write_ptr_succ <= STD_LOGIC_VECTOR(unsigned(write_ptr_reg)+1);
        read_ptr_succ <= STD_LOGIC_VECTOR(unsigned(read_ptr_reg)+1);

        -- ** Events and register control  ** --
        operation <= (push_valid_i & pop_grant_i);  -- Concatenates the two control inputs for the 'case, when' statement.
        process(write_ptr_reg, write_ptr_succ, read_ptr_reg, read_ptr_succ,
                  operation, full_reg, empty_reg)
        begin
            write_ptr_next <= write_ptr_reg;        -- This four lines are to assure that the current state does not
            read_ptr_next <= read_ptr_reg;      -- change in case none of the case-when statements happen.
            full_next <= full_reg;
            empty_next <= empty_reg;
            case operation is
                when "00" =>                                            -- Not write (push) or read (pop).
                when "01" =>                                            -- Read.
                    if(empty_reg /= '1') then                       -- If FIFO is NOT empty, it can be read.
                        read_ptr_next <= read_ptr_succ;         -- It points to the successive position to read.
                        full_next <= '0';                               -- As one position is read, FIFO will NOT be full.
                        if(read_ptr_succ=write_ptr_reg) then    -- Read 'reached' write. So the FIFO will be EMPTY.
                            empty_next <= '1';
                        end if;
                    end if;
                when "10" =>                                            -- Write.
                    if(full_reg /='1') then                         -- If FIFO is NOT full, it can be written.
                        write_ptr_next <= write_ptr_succ;
                        empty_next <= '0';                          -- The FIFO is written, so it will NOT be empty.
                        if(write_ptr_succ=read_ptr_reg) then    -- Write 'reached' read, so the FIFO will be full.
                            full_next <= '1';
                        end if;
                    end if;
                when others =>                                      -- Write and Read at the same time.
                    write_ptr_next <= write_ptr_succ;
                    read_ptr_next <= read_ptr_succ;
                end case;
        end process;

        -- Output STATUS
        push_grant_o <= not full_reg;
        pop_valid_o <= not empty_reg;
end Behavioral;

my_pkg.vhd:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

--use IEEE.math_real."ceil";
--use IEEE.math_real."log2";

package my_pkg is
        -- ** This should be used if math_real library available. Otherwise comment lines 24 and 25. Uncomment line 27 ** --
        --constant SLOTS : positive := 4;               -- This values has to be a power of two (2, 4, 8, 16, etc).
        --constant FIFO_DEPTH   : positive := integer(ceil(log2(real(SLOTS))));

        constant FIFO_DEPTH : positive := 2;    -- The number of SLOTS of the FIFO will be 2^FIFO_DEPTH. In this case, 4 slots.
        constant DATA_WIDTH  : positive := 3;
        constant FIFO_WIDTH  : positive := DATA_WIDTH+1;    --DATAWIDTH=WIDTH+1bitParity
        constant PARITY     : bit         := '0';   -- EVEN or ODD.
        constant PARITY_BIT : bit         := '0';   -- LSB or MSB.
end my_pkg;

这是测试平台:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY Fifo_testbench IS
END Fifo_testbench;

ARCHITECTURE behavior OF Fifo_testbench IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT Fifo
    PORT(
         clk : IN  std_logic;
         rst_n : IN  std_logic;
         push_data_i : IN  std_logic_vector(3 downto 0);
         pop_data_o : OUT  std_logic_vector(3 downto 0);
         push_valid_i : IN  std_logic;
         pop_grant_i : IN  std_logic;
         push_grant_o : OUT  std_logic;
         pop_valid_o : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal rst_n : std_logic := '0';
   signal push_data_i : std_logic_vector(3 downto 0) := (others => '0');
   signal push_valid_i : std_logic := '0';
   signal pop_grant_i : std_logic := '0';

    --Outputs
   signal pop_data_o : std_logic_vector(3 downto 0);
   signal push_grant_o : std_logic;
   signal pop_valid_o : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: Fifo PORT MAP (
          clk => clk,
          rst_n => rst_n,
          push_data_i => push_data_i,
          pop_data_o => pop_data_o,
          push_valid_i => push_valid_i,
          pop_grant_i => pop_grant_i,
          push_grant_o => push_grant_o,
          pop_valid_o => pop_valid_o
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '1';
        wait for clk_period/2;
        clk <= '0';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
      wait for 20 ns;   

        rst_n <= '1';

        push_valid_i <= '1';
        push_data_i <= "1001";
        wait for clk_period;
        push_data_i <= "1010";
        wait for clk_period;
        push_data_i <= "1011";
        wait for clk_period;
        push_data_i <= "1100";
        wait for clk_period;
        push_data_i <= "1101";
        wait for clk_period;
        push_valid_i <= '0';


      wait;
   end process;

END;

这里的模拟: Simulation

这个想法是当push_grant_i启用且FIFO未满时,所有4个初始值(1001,1010,1011和1100)都被推入FIFO。对于第5个值(1101),FIFO不能在它满时推动它。它似乎工作正常,但是在第一个值(1001)被推动的时钟的第一个上升沿(模拟中的30ns)之后,它不在输出端口(pop_data_o)上。实际上,它是第二个值,因此它跳过1001.只有当pop_grant_i为1时才应更新pop_data_o。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

您的测试平台中存在竞争条件。即你在时钟边缘改变push_data_i。由于寄存器具有建立和保持时间,因此在现实生活中也不会起作用。您应该使用与用于UUT的测试平台相同的时钟。例如。

-- Stimulus process
stim_proc: process
begin        
    -- hold reset state for 100 ns.
    wait for 100 ns;   
    rst_n <= '1';

    wait until rising_edge(clk);
    push_valid_i <= '1';
    push_data_i <= "1001";
    wait until rising_edge(clk);
    push_data_i <= "1010";
    wait until rising_edge(clk);
    push_data_i <= "1011";
    wait until rising_edge(clk);
    push_data_i <= "1100";
    wait until rising_edge(clk);
    push_data_i <= "1101";
    wait until rising_edge(clk);
    push_valid_i <= '0';

    wait;
end process;