Автор: 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 буфера на внутренней памяти, каждый из них со следующими характеристиками:
адрес - 8 бит (256 ячеек)
данные - 8 бит
Получается, что нам нужно 256*8 бит = 2048 бит, и таких 4 буфера. То есть 8192 бита, по идее всё это влезает в 1 ячейку M9K. Давайте проверим.
Все эксперименты будем проводить на искусственном проекте, а ПЛИС выберем как на этой отладочной плате.
Создаем пустой проект, добавляем IP ядро DP_RAM с нужными параметрами и добавляем в проект 4 таких
![](https://habrastorage.org/webt/la/uu/i-/lauui-onnsltdkikatogozjofia.png)
Собираем и смотрим результат компиляции
![](https://habrastorage.org/webt/af/hv/pb/afhvpbssvfvvshzxogbufntqcis.png)
Да, видим, что каждый наш модуль занял по 1 M9K, но задействовал не полностью и я пока не встречал, чтобы Quartus автоматических оптимизировал такое использование внутренней памяти ввиду её архитектуры.
Посмотрим теперь другой раздел по результатам компиляции
![](https://habrastorage.org/webt/ev/gn/iq/evgniqgr85q5mo3xattuuaqlepg.png)
Мы видим, что Quartus проанализировал, что мы используем 8192 бита, но задействовал 36864, то есть все эти 4 блока без вариантов дальнейшего использования.
Вариант есть, но есть и нюансы...
Одним их возможных по моему мнению вариантом по оптимизации использования внутренней памяти в данном случае может быть ручная агрегация.
Идея состоит в следующем:
Используем один модуль памяти, размером 256*4 ячеек и шиной так же, 8 бит
Старшие биты адреса будет номер канала или буфера, который мы используем
Агрегируем запросы в FIFO (в исходных кодах мы не стали использовать, но все заготовки для это есть внутри модуля, просто закомментированы, FIFO выбрано на логических элементах, с целью экономии 1 блока M9K, так как нам самим мало =)
Для простоты понимания изображу это на функциональной схеме:
![](https://habrastorage.org/webt/fy/9z/4g/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 попробуем собрать проект и посмотреть результаты компиляции:
![](https://habrastorage.org/webt/th/r-/ub/thr-ublxcl0p8rpvhavvz2vdurq.png)
![](https://habrastorage.org/webt/1j/70/uc/1j70ucagj2-1uo8reguxv9ekwow.png)
Итого, мы видим, что вложились в одну ячейку M9K.
P.S.
В этой статье просто решил поделиться опытом и общим подходом, что можно много чего интересного и нестандартного придумать, чтобы влезть в вечно ограниченные ресурсы ПЛИС.
Исходный код модуля и TestBench к нему на языке VHDL доступен на GitHub по ссылке.
Спасибо за внимание =)
Комментарии (11)
zatim
26.12.2023 09:42Может у кого то есть объяснения или точная информация, почему ячейки именно таких размеров, то комментарии приветствуются
Первое что приходит в голову - 9-й бит - это бит четности. На старых материнках 286 и 386 почти всегда использовалась оперативная память с аппаратным контролем четности (ибо техпроцесс тогда был неважнецкий, память могла дохнуть прям на ходу). Потом в эпоху 486 и выше от контроля четности стали отказываться и почти все планки пошли без доп. микросхемы. Планки поделились на серверные (с аппаратным контролем) и несерверные. Может, эхо тех времен.
fpga500
26.12.2023 09:42Нет, биты четности там ни при чем, дело не в них. Это связано с режимами конфигурации блока памяти. Сейчас посмотрел даташит на MAX10, для однопортового режима там доступны все варианты по глубине/ширине:
8192 × 1
4096 × 2
2048 × 4
1024 × 8
1024 × 9
512 × 16
512 × 18
256 × 32
256 × 36Для True dual port, всё несколько сложнее. Как я писал выше, всегда нужна смотреть даташит на конкретную микросхему
zatim
26.12.2023 09:42Не совсем понятно, причем тут режимы конфигурации.
1024 × 8 и 1024 × 9 ; 512 × 16 и 512 × 18; 256 × 32 и 256 × 36 - очевидно же что в режимах слева неиспользуемые биты просто отбрасываются логикой дешифрации и не используются. Но физически эти биты никуда не деваются, они есть. А вопрос был - для чего их изначально сделали с таким запасом.
fpga500
26.12.2023 09:42Сто процентов утверждать не могу, но скорей всего эти дополнительные биты используются при различной глубине портов адреса. Т.е. например, когда у вас один порт сконфигурирован как 4096 × 2 , а второй порт как 256 × 32. Тогда эти доп биты используются для внутренней логики, и для пользователя не доступны
wesker_96
26.12.2023 09:42+1За Альтеру не скажу, а вот в Xilinx (там такая же петрушка), например для RAMB36E1, это область памяти четности данных (шина DIPA, 4 бита шириной). В зависимости от конфигурации блока она может как использоваться, так и нет.
SIISII
26.12.2023 09:429-й бит в каждом байте -- это именно что контрольный бит. Есть применения, где надо защищаться от возможных сбоев информации в памяти -- и там контрольные разряды необходимы. Это не обязательно контроль по чётности; скажем, если использовать слово данных шириной 64 бита, т.е. 8 байтов, имеем 8 контрольных разрядов -- и этого достаточно, чтобы реализовать код Хэмминга и иметь возможность обнаруживать любые двойные ошибки и корректировать одиночные.
Но, поскольку это ПЛИС, никто не заставляет использовать "лишние" биты для контроля. Скажем, в PicoBlaze (мелкий-мелкий проц от Хилинха) команда кодируется 18 битами, если память не изменяет -- разработчик как раз использует особенность блочной памяти, где у каждого байта предусмотрен дополнительный бит.
Nikolyrus
26.12.2023 09:42Позвольте высказать свое долекое от дилетанского (В ХУДШУЮ СТОРОНУ) мнение. Если бы 9 бит был бы битом контроля, то на входе записи байтов в массив памяти стояло бы какое-то логическое устройство формирующее этот бит для записи. Но как мне кажется в архитектуре Плис этого нет, но наверное можно запрограмировать такую функцию если необходимо и использовать, так как конфигурация организации массива памяти позволяет этоорганизовать разработчиком
Brak0del
26.12.2023 09:42Но как мне кажется в архитектуре Плис этого нет, но наверное можно запрограмировать такую функцию если необходимо и использовать, так как конфигурация организации массива памяти позволяет этоорганизовать разработчиком
Во многих архитектурах ПЛИС в каждой брамке есть блок ЕСС. Скрин из мануала на брамки Xilinx:
Brak0del
26.12.2023 09:42Для начала хотелось бы выделить два основных свойства внутренней памяти ПЛИС
Важное свойство брамок low-latency доступ. Про это вспоминают всегда, когда проектируют устройства с большими требованиями к памяти и решают, что оставить в ПЛИС, а что держать в DDR и т.д. Ещё одно важное свойство -- высокий параллелизм. Ведь брамок на кристалле может быть сотня или тысяча, у каждой свои независимые порты (или порт), и при желании можно обращаться к каждой в каждом такте.
Насчёт свойства удобства, имхо, это что-то неизмеримое.
Да, видим, что каждый наш модуль занял по 1 M9K, но задействовал не полностью и я пока не встречал, чтобы Quartus автоматических оптимизировал такое использование внутренней памяти ввиду её архитектуры.
Синтезатор не умеет читать мысли. Ресурс памяти -- это не только про объём, но и про использование портов в том числе во времени. В данном случае проектировщик знает свой проект и то, что может безболезненно уменьшить производительность системы в 4 раза (ведь было 4 независимых буфера, а остался один), а синтезатор этого угадать не может. Если это знание отразить в коде, возможно, синтезатор приятно удивит.
Emil2014
26.12.2023 09:42+1IMHO, изначально BRAM использовались вместе с умножителями. Их ширина была 18 бит, соот. коэффициенты из памяти брались словами по 18-бит. По одному - два BRAM на умножитель, они и на кристалле рядом расположены. Я так думаю.
fpga500
Логика, если мне память не изменяет, там следующая: Ширина слова в блоке BRAM физически - 36 бит. Но эти 36 бит доступны только в простом режиме (один клок и один порт адреса). В таком случае память можно сконфигурировать шириной 36 бит. Если же режим двухпортовый (а тем более если он True Dual Port), то максимум ширина - 32 бита. Т.е. оставшиеся 4 бита - это вспомогательные биты для внутреннего использования, доступные пользователю только в некоторых частных случаях. Более точно - надо поднимать даташит на конкретную микросхему.