이 글을 PDF로 보기
사전 지식
시작하기에 앞서 이전 글을 안보신 분들은 사전 지식 습득을 위해 참고 바랍니다.
[FPGA 강의3] FIFO 개념과 용도
https://johnnyjay.tistory.com/127
레지스터 기반의 FIFO
오늘 배울 모듈은 레지스터 기반의 FIFO입니다. 레지스터 기반 FIFO의 의미는 FPGA 전반에 분산된 논리 또는 레지스터를 사용하여 FIFO가 생성된다는 것입니다. 이는 FIFO를 저장하기 위해 Block RAM을 사용하는 것과는 다릅니다. 일반적으로, 레지스터 기반 FIFO는 작은 FIFO(예: 32 words 이하)에 사용해야 하며, Block RAM 기반 FIFO는 더 큰 FIFO에 사용해야 합니다.
이 글에서 두 가지 버전의 코드를 보여줍니다.
첫 번째 버전은 programmable 한 Almost Full(AF) 와 Almost Empty(AE) 플래그를 사용하지 않습니다.
두 번째 버전은 이러한 플래그를 포함합니다.
이 플래그가 필요한지는 FPGA 설계자에게 달려 있습니다. 이 글 아래에서 테스트벤치 코드로 시뮬레이션 하여 기본 기능을 확인했습니다. 또한, 사용자가 FIFO를 오버플로우하거나 언더플로우하지 않도록 보장하기 위해 assert 문을 사용합니다.
참고: assert 문은 합성할 수 없으며, 시뮬레이션에서만 유용합니다.
FIFO의 첫 번째 버전에서는 더 많은 데이터를 수용할 준비가 되었는지 확인하는 유일한 방법이 가득 차지 않았는지를 확인하는 것입니다. 더 많은 데이터를 받을 준비가 되었는지 알기 위해서는 빈 플래그를 확인해야 합니다. 두 번째 FIFO는 더 다재다능한 '거의' 플래그의 사용을 허용합니다.
FIFO (without Almost-Full & Almost-Empty 플래그)
module_fifo_regs_no_flags.vhd
코드 (출처: nandland)
-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
--
-- Description: Creates a Synchronous FIFO made out of registers.
-- Generic: g_WIDTH sets the width of the FIFO created.
-- Generic: g_DEPTH sets the depth of the FIFO created.
--
-- Total FIFO register usage will be width * depth
-- Note that this fifo should not be used to cross clock domains.
-- (Read and write clocks NEED TO BE the same clock domain)
--
-- FIFO Full Flag will assert as soon as last word is written.
-- FIFO Empty Flag will assert as soon as last word is read.
--
-- FIFO is 100% synthesizable. It uses assert statements which do
-- not synthesize, but will cause your simulation to crash if you
-- are doing something you shouldn't be doing (reading from an
-- empty FIFO or writing to a full FIFO).
--
-- No Flags = No Almost Full (AF)/Almost Empty (AE) Flags
-- There is a separate module that has programmable AF/AE flags.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity module_fifo_regs_no_flags is
generic (
g_WIDTH : natural := 8;
g_DEPTH : integer := 32
);
port (
i_rst_sync : in std_logic;
i_clk : in std_logic;
-- FIFO Write Interface
i_wr_en : in std_logic;
i_wr_data : in std_logic_vector(g_WIDTH-1 downto 0);
o_full : out std_logic;
-- FIFO Read Interface
i_rd_en : in std_logic;
o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0);
o_empty : out std_logic
);
end module_fifo_regs_no_flags;
architecture rtl of module_fifo_regs_no_flags is
type t_FIFO_DATA is array (0 to g_DEPTH-1) of std_logic_vector(g_WIDTH-1 downto 0);
signal r_FIFO_DATA : t_FIFO_DATA := (others => (others => '0'));
signal r_WR_INDEX : integer range 0 to g_DEPTH-1 := 0;
signal r_RD_INDEX : integer range 0 to g_DEPTH-1 := 0;
-- # Words in FIFO, has extra range to allow for assert conditions
signal r_FIFO_COUNT : integer range -1 to g_DEPTH+1 := 0;
signal w_FULL : std_logic;
signal w_EMPTY : std_logic;
begin
p_CONTROL : process (i_clk) is
begin
if rising_edge(i_clk) then
if i_rst_sync = '1' then
r_FIFO_COUNT <= 0;
r_WR_INDEX <= 0;
r_RD_INDEX <= 0;
else
-- Keeps track of the total number of words in the FIFO
if (i_wr_en = '1' and i_rd_en = '0') then
r_FIFO_COUNT <= r_FIFO_COUNT + 1;
elsif (i_wr_en = '0' and i_rd_en = '1') then
r_FIFO_COUNT <= r_FIFO_COUNT - 1;
end if;
-- Keeps track of the write index (and controls roll-over)
if (i_wr_en = '1' and w_FULL = '0') then
if r_WR_INDEX = g_DEPTH-1 then
r_WR_INDEX <= 0;
else
r_WR_INDEX <= r_WR_INDEX + 1;
end if;
end if;
-- Keeps track of the read index (and controls roll-over)
if (i_rd_en = '1' and w_EMPTY = '0') then
if r_RD_INDEX = g_DEPTH-1 then
r_RD_INDEX <= 0;
else
r_RD_INDEX <= r_RD_INDEX + 1;
end if;
end if;
-- Registers the input data when there is a write
if i_wr_en = '1' then
r_FIFO_DATA(r_WR_INDEX) <= i_wr_data;
end if;
end if; -- sync reset
end if; -- rising_edge(i_clk)
end process p_CONTROL;
o_rd_data <= r_FIFO_DATA(r_RD_INDEX);
w_FULL <= '1' when r_FIFO_COUNT = g_DEPTH else '0';
w_EMPTY <= '1' when r_FIFO_COUNT = 0 else '0';
o_full <= w_FULL;
o_empty <= w_EMPTY;
-- ASSERTION LOGIC - Not synthesized
-- synthesis translate_off
p_ASSERT : process (i_clk) is
begin
if rising_edge(i_clk) then
if i_wr_en = '1' and w_FULL = '1' then
report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS FULL AND BEING WRITTEN " severity failure;
end if;
if i_rd_en = '1' and w_EMPTY = '1' then
report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS EMPTY AND BEING READ " severity failure;
end if;
end if;
end process p_ASSERT;
-- synthesis translate_on
end rtl;
[테스트벤치 코드] module_fifo_regs_no_flags_tb.vhd
-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity module_fifo_regs_no_flags_tb is
end module_fifo_regs_no_flags_tb;
architecture behave of module_fifo_regs_no_flags_tb is
constant c_DEPTH : integer := 4;
constant c_WIDTH : integer := 8;
signal r_RESET : std_logic := '0';
signal r_CLOCK : std_logic := '0';
signal r_WR_EN : std_logic := '0';
signal r_WR_DATA : std_logic_vector(c_WIDTH-1 downto 0) := X"A5";
signal w_FULL : std_logic;
signal r_RD_EN : std_logic := '0';
signal w_RD_DATA : std_logic_vector(c_WIDTH-1 downto 0);
signal w_EMPTY : std_logic;
component module_fifo_regs_no_flags is
generic (
g_WIDTH : natural := 8;
g_DEPTH : integer := 32
);
port (
i_rst_sync : in std_logic;
i_clk : in std_logic;
-- FIFO Write Interface
i_wr_en : in std_logic;
i_wr_data : in std_logic_vector(g_WIDTH-1 downto 0);
o_full : out std_logic;
-- FIFO Read Interface
i_rd_en : in std_logic;
o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0);
o_empty : out std_logic
);
end component module_fifo_regs_no_flags;
begin
MODULE_FIFO_REGS_NO_FLAGS_INST : module_fifo_regs_no_flags
generic map (
g_WIDTH => c_WIDTH,
g_DEPTH => c_DEPTH
)
port map (
i_rst_sync => r_RESET,
i_clk => r_CLOCK,
i_wr_en => r_WR_EN,
i_wr_data => r_WR_DATA,
o_full => w_FULL,
i_rd_en => r_RD_EN,
o_rd_data => w_RD_DATA,
o_empty => w_EMPTY
);
r_CLOCK <= not r_CLOCK after 5 ns;
p_TEST : process is
begin
wait until r_CLOCK = '1';
r_WR_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_WR_EN <= '0';
r_RD_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_RD_EN <= '0';
r_WR_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_RD_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_WR_EN <= '0';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
end process;
end behave;
FIFO (with Almost-Full & Almost-Empty 플래그)
module_fifo_regs_with_flags.vhd
-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
--
-- Description: Creates a Synchronous FIFO made out of registers.
-- Generic: g_WIDTH sets the width of the FIFO created.
-- Generic: g_DEPTH sets the depth of the FIFO created.
--
-- Total FIFO register usage will be width * depth
-- Note that this fifo should not be used to cross clock domains.
-- (Read and write clocks NEED TO BE the same clock domain)
--
-- FIFO Full Flag will assert as soon as last word is written.
-- FIFO Empty Flag will assert as soon as last word is read.
--
-- FIFO is 100% synthesizable. It uses assert statements which do
-- not synthesize, but will cause your simulation to crash if you
-- are doing something you shouldn't be doing (reading from an
-- empty FIFO or writing to a full FIFO).
--
-- With Flags = Has Almost Full (AF)/Almost Empty (AE) Flags
-- These are settable via Generics: g_AF_LEVEL and g_AE_LEVEL
-- g_AF_LEVEL: Goes high when # words in FIFO is > this number.
-- g_AE_LEVEL: Goes high when # words in FIFO is < this number. ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity module_fifo_regs_with_flags is generic ( g_WIDTH : natural := 8; g_DEPTH : integer := 32; g_AF_LEVEL : integer := 28; g_AE_LEVEL : integer := 4 ); port ( i_rst_sync : in std_logic; i_clk : in std_logic; -- FIFO Write Interface i_wr_en : in std_logic; i_wr_data : in std_logic_vector(g_WIDTH-1 downto 0); o_af : out std_logic; o_full : out std_logic; -- FIFO Read Interface i_rd_en : in std_logic; o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0); o_ae : out std_logic; o_empty : out std_logic ); end module_fifo_regs_with_flags; architecture rtl of module_fifo_regs_with_flags is type t_FIFO_DATA is array (0 to g_DEPTH-1) of std_logic_vector(g_WIDTH-1 downto 0); signal r_FIFO_DATA : t_FIFO_DATA := (others => (others => '0'));
signal r_WR_INDEX : integer range 0 to g_DEPTH-1 := 0;
signal r_RD_INDEX : integer range 0 to g_DEPTH-1 := 0;
-- # Words in FIFO, has extra range to allow for assert conditions
signal r_FIFO_COUNT : integer range -1 to g_DEPTH+1 := 0;
signal w_FULL : std_logic;
signal w_EMPTY : std_logic;
begin
p_CONTROL : process (i_clk) is
begin
if rising_edge(i_clk) then
if i_rst_sync = '1' then
r_FIFO_COUNT <= 0;
r_WR_INDEX <= 0;
r_RD_INDEX <= 0;
else
-- Keeps track of the total number of words in the FIFO
if (i_wr_en = '1' and i_rd_en = '0') then
r_FIFO_COUNT <= r_FIFO_COUNT + 1;
elsif (i_wr_en = '0' and i_rd_en = '1') then
r_FIFO_COUNT <= r_FIFO_COUNT - 1;
end if;
-- Keeps track of the write index (and controls roll-over)
if (i_wr_en = '1' and w_FULL = '0') then
if r_WR_INDEX = g_DEPTH-1 then
r_WR_INDEX <= 0;
else
r_WR_INDEX <= r_WR_INDEX + 1;
end if;
end if;
-- Keeps track of the read index (and controls roll-over)
if (i_rd_en = '1' and w_EMPTY = '0') then
if r_RD_INDEX = g_DEPTH-1 then
r_RD_INDEX <= 0;
else
r_RD_INDEX <= r_RD_INDEX + 1;
end if;
end if;
-- Registers the input data when there is a write
if i_wr_en = '1' then
r_FIFO_DATA(r_WR_INDEX) <= i_wr_data;
end if;
end if; -- sync reset
end if; -- rising_edge(i_clk)
end process p_CONTROL;
o_rd_data <= r_FIFO_DATA(r_RD_INDEX);
w_FULL <= '1' when r_FIFO_COUNT = g_DEPTH else '0';
w_EMPTY <= '1' when r_FIFO_COUNT = 0 else '0';
o_af <= '1' when r_FIFO_COUNT > g_AF_LEVEL else '0';
o_ae <= '1' when r_FIFO_COUNT < g_AE_LEVEL else '0';
o_full <= w_FULL;
o_empty <= w_EMPTY;
-----------------------------------------------------------------------------
-- ASSERTION LOGIC - Not synthesized
-----------------------------------------------------------------------------
-- synthesis translate_off
p_ASSERT : process (i_clk) is
begin
if rising_edge(i_clk) then
if i_wr_en = '1' and w_FULL = '1' then
report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS FULL AND BEING WRITTEN " severity failure;
end if;
if i_rd_en = '1' and w_EMPTY = '1' then
report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS EMPTY AND BEING READ " severity failure;
end if;
end if;
end process p_ASSERT;
-- synthesis translate_on
end rtl;
[테스트벤치 코드] module_fifo_regs_with_flags_tb.vhd
-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity module_fifo_regs_with_flags_tb is
end module_fifo_regs_with_flags_tb;
architecture behave of module_fifo_regs_with_flags_tb is
constant c_DEPTH : integer := 4;
constant c_WIDTH : integer := 8;
constant c_AF_LEVEL : integer := 2;
constant c_AE_LEVEL : integer := 2;
signal r_RESET : std_logic := '0';
signal r_CLOCK : std_logic := '0';
signal r_WR_EN : std_logic := '0';
signal r_WR_DATA : std_logic_vector(c_WIDTH-1 downto 0) := X"A5";
signal w_AF : std_logic;
signal w_FULL : std_logic;
signal r_RD_EN : std_logic := '0';
signal w_RD_DATA : std_logic_vector(c_WIDTH-1 downto 0);
signal w_AE : std_logic;
signal w_EMPTY : std_logic;
component module_fifo_regs_with_flags is
generic (
g_WIDTH : natural := 8;
g_DEPTH : integer := 32;
g_AF_LEVEL : integer := 28;
g_AE_LEVEL : integer := 4
);
port (
i_rst_sync : in std_logic;
i_clk : in std_logic;
-- FIFO Write Interface
i_wr_en : in std_logic;
i_wr_data : in std_logic_vector(g_WIDTH-1 downto 0);
o_af : out std_logic;
o_full : out std_logic;
-- FIFO Read Interface
i_rd_en : in std_logic;
o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0);
o_ae : out std_logic;
o_empty : out std_logic
);
end component module_fifo_regs_with_flags;
begin
MODULE_FIFO_REGS_WITH_FLAGS_INST : module_fifo_regs_with_flags
generic map (
g_WIDTH => c_WIDTH,
g_DEPTH => c_DEPTH,
g_AF_LEVEL => c_AF_LEVEL,
g_AE_LEVEL => c_AE_LEVEL
)
port map (
i_rst_sync => r_RESET,
i_clk => r_CLOCK,
i_wr_en => r_WR_EN,
i_wr_data => r_WR_DATA,
o_af => w_AF,
o_full => w_FULL,
i_rd_en => r_RD_EN,
o_rd_data => w_RD_DATA,
o_ae => w_AE,
o_empty => w_EMPTY
);
r_CLOCK <= not r_CLOCK after 5 ns;
p_TEST : process is
begin
wait until r_CLOCK = '1';
r_WR_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_WR_EN <= '0';
r_RD_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_RD_EN <= '0';
r_WR_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_RD_EN <= '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
r_WR_EN <= '0';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
wait until r_CLOCK = '1';
end process;
end behave;
자, 이제 이 코드들을 분석하고 시뮬레이션 하면서 FIFO 개념과 설계를 확실하게 익혀봅시다.
어려우신가요?
화이팅!
'전자 | 제어 | 항공우주 > FPGA & HDL' 카테고리의 다른 글
[FPGA 강의3] FIFO 개념과 용도 (0) | 2024.08.11 |
---|---|
[FPGA 강의2] Flip-Flop (Register) (6) | 2024.08.10 |
[FPGA 강의1] LUT (Look Up Table) (0) | 2024.08.10 |
VS Code 에서 TerosHDL Linter를 GHDL로 사용하는 방법 (0) | 2024.04.07 |
Verilig & VHDL 무료 프로그램 TOP 4 (2) | 2023.05.28 |
댓글