Задача
Буферизовать поток данных между источником и приёмником.
Тип схемы: Синхронная память с управляющей логикой, параметры разрядности и глубины.
Модуль: fifo. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования.
Теорию по языку смотрите в статье VHDL — язык описания аппаратуры,
по самим микросхемам — на странице ПЛИС.
Алгоритм
- Хранить данные в массиве, адресуемом двумя указателями.
- При записи класть слово по указателю записи и сдвигать его по кругу.
- При чтении выдавать слово по указателю чтения.
- Вести счётчик заполнения и формировать флаги пустоты и переполнения.
Код на 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.