我正在尝试通过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;
这个想法是当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。
感谢您的帮助。
答案 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;