Внутренняя память ПЛИС, которой всегда не хватает

Автор: https://github.com/iwaniwaniwan012

Возможно конечно не всем… =)

Если у вас её в избытке, то лишних знаний не бывает, возможно стоит почитать.

А если вам её не хватает, то есть варианты. Но обо всём по порядку.

Введение

Для начала хотелось бы выделить два основных свойства внутренней памяти ПЛИС:

Вот со вторым приходится всегда бороться, особенно если есть необходимость буферизации каких-то данных в достаточном количестве.

В этой статье мы рассмотрим какая внутренняя память есть в ПЛИС фирмы Intel/Altera и возможные варианты оптимизации её использования.

Общая информация

В ПЛИС фирмы Intel/Altera в основном используются следующие типа встроенной памяти:

  • M9K, в таких как Stratix IV, Arria II, Cyclone IV, Cyclone III

  • M20K, в таких как Stratix 10, Stratix V, Arria 10, Arria V

Объём каждой из них примерно соответствует названию, но не точно.

А именно, реальный их размер следующий:

  • M9K — 9216 бит

  • M20K — 20480 бит

Это я к чему… К тому, что честно не понятно для меня, и этой информации я не нашел, почему производители ПЛИС выбрали объём используемой памяти не степень 2.

У меня есть пара предположений. О них сейчас расскажу.

Если вы используете например 2^7 адресов с шиной 64 бита, то получаем 8192 бита задействованной памяти ПЛИС, то есть одну ячейку M9K, но не полностью, ведь её размер 9216 бит.

Для примера возьмём шину XGMII, у которой 64 бита данных и 8 бит служебной информации, то есть сумма 72 бита. Вот если использовать 2^7 адресов по 72 бита данных, то получается 9216 бит, то есть мы использовали всю память ячейки.

Есть ещё один вариант, заключается он в следующем. 1024 ячейки памяти, 8 бит данных + 1 бит чётности, получается 1024×9 = 9216.

Для M20K можно получить размер ячейки следующим способом 4096×5.

Может у кого то есть объяснения или точная информация, почему ячейки именно таких размеров, то комментарии приветствуются.

Проблематика

Давайте представим, что нам необходимо 4 буфера на внутренней памяти, каждый из них со следующими характеристиками:

Получается, что нам нужно 256×8 бит = 2048 бит, и таких 4 буфера. То есть 8192 бита, по идее всё это влезает в 1 ячейку M9K. Давайте проверим.

Все эксперименты будем проводить на искусственном проекте, а ПЛИС выберем как на этой отладочной плате.

Создаем пустой проект, добавляем IP ядро DP_RAM с нужными параметрами и добавляем в проект 4 таких

lauui-onnsltdkikatogozjofia.png

Собираем и смотрим результат компиляции

afhvpbssvfvvshzxogbufntqcis.png

Да, видим, что каждый наш модуль занял по 1 M9K, но задействовал не полностью и я пока не встречал, чтобы Quartus автоматических оптимизировал такое использование внутренней памяти ввиду её архитектуры.

Посмотрим теперь другой раздел по результатам компиляции

evgniqgr85q5mo3xattuuaqlepg.png

Мы видим, что Quartus проанализировал, что мы используем 8192 бита, но задействовал 36864, то есть все эти 4 блока без вариантов дальнейшего использования.

Вариант есть, но есть и нюансы…

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

Идея состоит в следующем:

  • Используем один модуль памяти, размером 256×4 ячеек и шиной так же, 8 бит

  • Старшие биты адреса будет номер канала или буфера, который мы используем

  • Агрегируем запросы в FIFO (в исходных кодах мы не стали использовать, но все заготовки для это есть внутри модуля, просто закомментированы, FIFO выбрано на логических элементах, с целью экономии 1 блока M9K, так как нам самим мало =)

Для простоты понимания изображу это на функциональной схеме:

fy9z4g09bxesfuxnwavv1fbd4sk.png

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

Модуль ручной агрегации сделан в нашем случае для 4 независимых каналов, каждый из которых имеет следующий интерфейс:

  • En_In — разрешающий сигнал

  • Cmd_In — тип команды (чтение/запись)

  • Addr_In — адрес, по которому идёт чтение/запись

  • Data_In — данные для записи по адресу

А это описание интерфейса модуля агрегации на языке VHDL, изображённый на функциональной схеме. Он включает в себя FIFO, Dual Port RAM и саму логику, названную модулем взаимодействия.

entity Conv_OnChip_RAM is
    port (
        Clk 			: in std_logic;				-- Тактовый сигнал
        Reset 			: in std_logic;				-- сигнал сброса
        En_In			: in std_logic_vector(3 downto 0);	-- запрос для работы с памятью 4 канала по 1 биту
        Cmd_In			: in std_logic_vector(3 downto 0);	-- команда (1 - запись, 0 - чтение), 4 канала по 1 биту
        Addr_In			: in std_logic_vector(31 downto 0);	-- адрес для чтения/записи в память 4 канала по 8 бит
        Data_In			: in std_logic_vector(31 downto 0);	-- данные для записи в память 4 канала по 8 бит
        Read_Data_Valid_Out	: out std_logic_vector(3 downto 0);
        Read_Data_Out		: out std_logic_vector(31 downto 0);	-- выходные данные из памяти 4 канала по 8 бит
        Busy_Out		: out std_logic				-- сигнал о том, что модуль занят				
    );
end entity;

При необходимости модуль можно сделать параметризированным.

После описание всей схемы на VHDL попробуем собрать проект и посмотреть результаты компиляции:

thr-ublxcl0p8rpvhavvz2vdurq.png1j70ucagj2-1uo8reguxv9ekwow.png

Итого, мы видим, что вложились в одну ячейку M9K.

P.S.

В этой статье просто решил поделиться опытом и общим подходом, что можно много чего интересного и нестандартного придумать, чтобы влезть в вечно ограниченные ресурсы ПЛИС.

Исходный код модуля и TestBench к нему на языке VHDL доступен на GitHub по ссылке.

Спасибо за внимание =)

© Habrahabr.ru