Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> VHDL

Реализация AXI FIFO в блочной памяти с готовым/валидным рукопожатием

Когда я впервые столкнулся с интерфейсом AXI при проектировании модуля, я удивился его упрощенному подходу к управлению потоком. В отличие от традиционных протоколов, полагающихся на пары busy/valid, full/valid или empty/valid, AXI использует только два сигнала: ready и valid. Что начиналось как фрустрация, быстро превратилось в восхищение.

AXI встраивает управление потоком прямо в свое рукопожатие, устраняя необходимость в дополнительных статусных линиях. Хотя базовые правила просты, при отображении протокола на FPGA-аппаратное обеспечение требуется внимательность. В этом туториале я проведу через VHDL-реализацию AXI FIFO, работающей на блочной памяти.

AXI решает проблему задержки в один цикл

Предотвращение перечтения и перезаписи — частая задача, когда два тактово-синхронизированных модуля обмениваются потоками данных. Суть проблемы в том, что каждый модуль может наблюдать выход другого только на один тактовый цикл после его возникновения.

Реализация AXI FIFO в блочной памяти с готовым/валидным рукопожатием

Диаграмма выше иллюстрирует последовательный модуль, записывающий в FIFO, использующий классическую схему сигнализации write enable/full. Писатель утверждает wr_en для помещения данных в FIFO, пока FIFO поднимает full, когда места больше нет. Проблема в том, что full утверждается именно на фронте тактового сигнала, поэтому писатель больше не может на него среагировать в том же цикле. Решение — предоставлять опережающий сигнал, такой как almost_empty, давая вышестоящей логике фору.

Рукопожатие ready/valid

Потоковые интерфейсы AXI решают эту временную дилемму двухсигнальным рукопожатием: ready и valid. Приемник управляет ready; высокое значение указывает, что он готов принять новый датум. Отправитель управляет valid; высокое значение означает, что данные на шине стабильны и могут быть засемплированы. Передача происходит только когда оба сигнала высоки на одном и том же нарастающем фронте.

Реализация AXI FIFO в блочной памяти с готовым/валидным рукопожатием

Волновая форма демонстрирует передачу единственного элемента данных. Данные сэмплируются на фронте тактового сигнала, как и в обычных синхронных дизайнах.

Реализация

Существует несколько способов построить AXI FIFO в VHDL. Хотя сдвиговый регистр сработал бы, кольцевой буфер на базе блочной памяти — самый эффективный и масштабируемый выбор. Ниже я представляю модульный дизайн, разделяющий логику на выделенные процессы для ясности и дружелюбности к синтезатору.

Сущность

Сущность определяет два generic-параметра – ram_width и ram_depth – задающие ширину слова и глубину FIFO. Емкость эффективно равна ram_depth - 1, потому что один слот зарезервирован для различения полного FIFO от пустого.

entity axi_fifo is generic ( ram_width : natural; ram_depth : natural ); port ( clk : in std_logic; rst : in std_logic; -- AXI input interface in_ready : out std_logic; in_valid : in std_logic; in_data : in std_logic_vector(ram_width - 1 downto 0); -- AXI output interface out_ready : in std_logic; out_valid : out std_logic; out_data : out std_logic_vector(ram_width - 1 downto 0) ); end axi_fifo; 

Интерфейс следует соглашениям AXI: входная сторона использует in_ready и in_valid, а выходная зеркалит сигналы с обратными направлениями. Эта симметрия позволяет соединять несколько FIFO в цепочку бесшовно.

Объявления сигналов

Сначала определяем тип RAM, соответствующий generic-ширине и глубине, затем инстанцируем массив:

-- The FIFO is full when the RAM contains ram_depth - 1 elements type ram_type is array (0 to ram_depth - 1) of std_logic_vector(in_data'range); signal ram : ram_type; 

Далее объявляем подтип индекса, способный представить любой адрес в RAM, и четыре основных сигнала: head, tail, count и отложенную копию count_p1:

subtype index_type is natural range ram_type'range; signal head : index_type; signal tail : index_type; signal count : index_type; signal count_p1 : index_type; 

Также храним внутренние версии выходов ready и valid, чтобы избежать чтения out сигналов внутри архитектуры:

signal in_ready_i : std_logic; signal out_valid_i : std_logic; 

Вспомогательный сигнал отслеживает одновременное чтение и запись в предыдущем цикле, что критично для правильной установки out_valid, когда FIFO почти пуст:

signal read_while_write_p1 : std_logic; 

Подпрограммы

Функция next_index определяет следующий адрес на основе того, завершилось ли чтение или запись. Она заворачивается при достижении конца массива.

function next_index( index : index_type; ready : std_logic; valid : std_logic) return index_type is begin if ready = '1' and valid = '1' then if index = index_type'high then return index_type'low; else return index + 1; end if; end if; return index; end function; 

Процедура index_proc инкапсулирует синхронную логику обновления либо указателя head, либо tail. Это избегает дублирования кода и сохраняет архитектуру чистой.

procedure index_proc( signal clk : in std_logic; signal rst : in std_logic; signal index : inout index_type; signal ready : in std_logic; signal valid : in std_logic) is begin if rising_edge(clk) then if rst = '1' then index <= index_type'low; else index <= next_index(index, ready, valid); end if; end if; end procedure; 

Копирование внутренних сигналов на выход

in_ready <= in_ready_i; out_valid <= out_valid_i; 

Обновление head и tail

Инстанцируем вспомогательную процедуру дважды — один раз для указателя записи (head) и один раз для указателя чтения (tail):

PROC_HEAD : index_proc(clk, rst, head, in_ready_i, in_valid); PROC_TAIL : index_proc(clk, rst, tail, out_ready, out_valid_i); 

Вывод блочной памяти

Блочная память в большинстве FPGA синхронна. Чтобы позволить синтезатору вывести примитив RAM, держим операции чтения и записи внутри тактового процесса и избегаем явных сигналов enable:

PROC_RAM : process(clk) begin if rising_edge(clk) then ram(head) <= in_data; out_data <= ram(next_index(tail, out_ready, out_valid_i)); end if; end process; 

Запись происходит безусловно в цикле, когда in_valid высок, а данные чтения обновляются каждый цикл, чтобы потребитель мог наблюдать валидное слово, когда FIFO содержит данные.

Подсчет элементов в FIFO

Сигнал count отражает текущую заполненность. Когда head опережает tail, счетчик — просто разница; если head завернул, добавляем общую глубину:

PROC_COUNT : process(head, tail) begin if head < tail then count <= head - tail + ram_depth; else count <= head - tail; end if; end process; 

Отложенная копия count_p1 нужна для обнаружения, когда FIFO был пуст в предыдущем цикле, что влияет на out_valid:

PROC_COUNT_P1 : process(clk) begin if rising_edge(clk) then if rst = '1' then count_p1 <= 0; else count_p1 <= count; end if; end if; end process; 

Обновление выхода ready

Писателю нужно знать, когда FIFO может принять больше данных. Сигнал ready высок, когда счетчик меньше ram_depth - 1:

PROC_IN_READY : process(count) begin if count < ram_depth - 1 then in_ready_i <= '1'; else in_ready_i <= '0'; end if; end process; 

Обнаружение одновременного чтения и записи

Когда и чтение, и запись происходят в одном цикле, заполненность FIFO не меняется, но внутреннее состояние должно отражать, что значение станет доступно только через два цикла, когда FIFO почти пуст. Поэтому мы фиксируем это событие в read_while_write_p1:

PROC_READ_WHILE_WRITE_P1: process(clk) begin if rising_edge(clk) then if rst = '1' then read_while_write_p1 <= '0'; else read_while_write_p1 <= '0'; if in_ready_i = '1' and in_valid = '1' and out_ready = '1' and out_valid_i = '1' then read_while_write_p1 <= '1'; end if; end if; end if; end process; 

Обновление выхода valid

Сигнал out_valid сообщает нижестоящей логике, когда out_data содержит пригодное слово. Комбинационный процесс ниже устанавливает его высоким по умолчанию, но сбрасывает в двух пограничных случаях:

PROC_OUT_VALID : process(count, count_p1, read_while_write_p1) begin out_valid_i <= '1'; -- If the FIFO is empty or was empty in the previous cycle if count = 0 or count_p1 = 0 then out_valid_i <= '0'; end if; -- If a simultaneous read/write occurred when the FIFO had only one item if count = 1 and read_while_write_p1 = '1' then out_valid_i <= '0'; end if; end process; 

Первое условие гарантирует, что out_valid остается низким во время задержки распространения после первой записи. Второе защищает от ситуации, когда FIFO кажется полным на один цикл, но на самом деле не содержит данных для вывода.

Синтез в Vivado

Для синтеза этого дизайна в Xilinx Vivado предоставьте конкретные значения для generic-параметров. В диалоге Settings (General → Generics/Parameters) выберите глубину, соответствующую примитиву RAMB36E1 для целевого Zynq-устройства. После реализации использование ресурсов скромно — один блок памяти плюс несколько LUT и FF.

Реализация AXI FIFO в блочной памяти с готовым/валидным рукопожатием

Реализация AXI FIFO в блочной памяти с готовым/валидным рукопожатием

AXI — больше чем ready/valid

AXI (Advanced eXtensible Interface) — компонент шины AMBA архитектуры ARM и предлагает богатый набор функций за пределами простого рукопожатия ready/valid. Для глубокого понимания рассмотрите следующие ресурсы:

VHDL

  1. Облачная революция: как облачные вычисления трансформируют IT, безопасность и карьеры
  2. Разблокировка IoT-данных: как Business Rules Management драйвит enterprise value
  3. Инициализация блочной памяти FPGA из текстовых файлов с помощью библиотеки TEXTIO в VHDL
  4. Подготовка производственных операций к ИИ с помощью IoT
  5. Промышленный интернет: революция в управлении активами через связность, аналитику и UX
  6. Лучшие практики отслеживания активов: как извлечь ценность из данных оборудования
  7. Углубление понимания Интернета вещей: использование визуализации данных и графовых баз данных
  8. Максимизация потенциала IoT для ресторанов: повышение эффективности, сокращение отходов и повышение удовлетв…
  9. Дата-драйвенные цепочки поставок: топливо логистики завтрашнего дня
  10. Обеспечение надежных данных цепочки поставок: проверенная стратегия