AFON // радиотехник affonya@mail.ru
← Проекты на VHDL

Проект 17. Буфер FIFO

Очередь «первым пришёл — первым вышел» сглаживает разницу в скорости источника и приёмника данных. Реализована кольцевым буфером с двумя указателями.

Задача

Буферизовать поток данных между источником и приёмником.

Тип схемы: Синхронная память с управляющей логикой, параметры разрядности и глубины.
Модуль: fifo. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования. Теорию по языку смотрите в статье VHDL — язык описания аппаратуры, по самим микросхемам — на странице ПЛИС.

Алгоритм

  1. Хранить данные в массиве, адресуемом двумя указателями.
  2. При записи класть слово по указателю записи и сдвигать его по кругу.
  3. При чтении выдавать слово по указателю чтения.
  4. Вести счётчик заполнения и формировать флаги пустоты и переполнения.

Код на VHDL

Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v17.vhd

--=====================================================
-- Проект 17. Буфер FIFO
-- Кольцевая очередь на массиве с указателями записи
-- и чтения. Основа любой буферизации потока данных.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity fifo is
    generic (
        WIDTH : integer := 8;                           -- разрядность слова
        DEPTH : integer := 16                           -- глубина очереди
    );
    port (
        clk     : in  std_logic;
        rst     : in  std_logic;
        wr      : in  std_logic;                        -- запись
        rd      : in  std_logic;                        -- чтение
        din     : in  std_logic_vector(WIDTH-1 downto 0);
        dout    : out std_logic_vector(WIDTH-1 downto 0);
        empty   : out std_logic;
        full    : out std_logic
    );
end entity fifo;

architecture rtl of fifo is
    type mem_t is array (0 to DEPTH-1) of std_logic_vector(WIDTH-1 downto 0);
    signal mem    : mem_t := (others => (others => '0'));
    signal wr_ptr : integer range 0 to DEPTH-1 := 0;
    signal rd_ptr : integer range 0 to DEPTH-1 := 0;
    signal count  : integer range 0 to DEPTH := 0;      -- сколько слов в очереди
begin
    process (clk, rst)
    begin
        if rst = '1' then
            wr_ptr <= 0; rd_ptr <= 0; count <= 0;
        elsif rising_edge(clk) then
            -- запись, если есть место
            if wr = '1' and count < DEPTH then
                mem(wr_ptr) <= din;
                if wr_ptr = DEPTH-1 then wr_ptr <= 0; else wr_ptr <= wr_ptr + 1; end if;
            end if;

            -- чтение, если есть данные
            if rd = '1' and count > 0 then
                dout <= mem(rd_ptr);
                if rd_ptr = DEPTH-1 then rd_ptr <= 0; else rd_ptr <= rd_ptr + 1; end if;
            end if;

            -- счётчик заполнения меняется только при одиночной операции
            if wr = '1' and rd = '0' and count < DEPTH then
                count <= count + 1;
            elsif rd = '1' and wr = '0' and count > 0 then
                count <= count - 1;
            end if;
        end if;
    end process;

    empty <= '1' when count = 0     else '0';
    full  <= '1' when count = DEPTH else '0';
end architecture rtl;

Пояснения

  • Счётчик заполнения меняется только при одиночной операции: если запись и чтение происходят одновременно, число слов не меняется.
  • Проверка флагов обязательна: запись в полный буфер потеряет данные, чтение из пустого вернёт мусор.
  • Синтезатор распознаёт такой массив и размещает его в блочной памяти кристалла, а не в логических ячейках.

Что дальше

Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.