Автор: 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 таких

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

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

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

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

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

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

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

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

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

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

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

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

Модуль ручной агрегации сделан в нашем случае для 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 попробуем собрать проект и посмотреть результаты компиляции:

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

P.S.

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

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

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

Комментарии (11)


  1. fpga500
    26.12.2023 09:42

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

    Логика, если мне память не изменяет, там следующая: Ширина слова в блоке BRAM физически - 36 бит. Но эти 36 бит доступны только в простом режиме (один клок и один порт адреса). В таком случае память можно сконфигурировать шириной 36 бит. Если же режим двухпортовый (а тем более если он True Dual Port), то максимум ширина - 32 бита. Т.е. оставшиеся 4 бита - это вспомогательные биты для внутреннего использования, доступные пользователю только в некоторых частных случаях. Более точно - надо поднимать даташит на конкретную микросхему.


  1. zatim
    26.12.2023 09:42

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

    Первое что приходит в голову - 9-й бит - это бит четности. На старых материнках 286 и 386 почти всегда использовалась оперативная память с аппаратным контролем четности (ибо техпроцесс тогда был неважнецкий, память могла дохнуть прям на ходу). Потом в эпоху 486 и выше от контроля четности стали отказываться и почти все планки пошли без доп. микросхемы. Планки поделились на серверные (с аппаратным контролем) и несерверные. Может, эхо тех времен.


    1. 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, всё несколько сложнее. Как я писал выше, всегда нужна смотреть даташит на конкретную микросхему


      1. zatim
        26.12.2023 09:42

        Не совсем понятно, причем тут режимы конфигурации.

        1024 × 8 и 1024 × 9 ; 512 × 16 и 512 × 18; 256 × 32 и 256 × 36 - очевидно же что в режимах слева неиспользуемые биты просто отбрасываются логикой дешифрации и не используются. Но физически эти биты никуда не деваются, они есть. А вопрос был - для чего их изначально сделали с таким запасом.


        1. fpga500
          26.12.2023 09:42

          Сто процентов утверждать не могу, но скорей всего эти дополнительные биты используются при различной глубине портов адреса. Т.е. например, когда у вас один порт сконфигурирован как 4096 × 2 , а второй порт как 256 × 32. Тогда эти доп биты используются для внутренней логики, и для пользователя не доступны


  1. wesker_96
    26.12.2023 09:42
    +1

    За Альтеру не скажу, а вот в Xilinx (там такая же петрушка), например для RAMB36E1, это область памяти четности данных (шина DIPA, 4 бита шириной). В зависимости от конфигурации блока она может как использоваться, так и нет.


  1. SIISII
    26.12.2023 09:42

    9-й бит в каждом байте -- это именно что контрольный бит. Есть применения, где надо защищаться от возможных сбоев информации в памяти -- и там контрольные разряды необходимы. Это не обязательно контроль по чётности; скажем, если использовать слово данных шириной 64 бита, т.е. 8 байтов, имеем 8 контрольных разрядов -- и этого достаточно, чтобы реализовать код Хэмминга и иметь возможность обнаруживать любые двойные ошибки и корректировать одиночные.

    Но, поскольку это ПЛИС, никто не заставляет использовать "лишние" биты для контроля. Скажем, в PicoBlaze (мелкий-мелкий проц от Хилинха) команда кодируется 18 битами, если память не изменяет -- разработчик как раз использует особенность блочной памяти, где у каждого байта предусмотрен дополнительный бит.


    1. Nikolyrus
      26.12.2023 09:42

      Позвольте высказать свое долекое от дилетанского (В ХУДШУЮ СТОРОНУ) мнение. Если бы 9 бит был бы битом контроля, то на входе записи байтов в массив памяти стояло бы какое-то логическое устройство формирующее этот бит для записи. Но как мне кажется в архитектуре Плис этого нет, но наверное можно запрограмировать такую функцию если необходимо и использовать, так как конфигурация организации массива памяти позволяет этоорганизовать разработчиком


      1. Brak0del
        26.12.2023 09:42

         Но как мне кажется в архитектуре Плис этого нет, но наверное можно запрограмировать такую функцию если необходимо и использовать, так как конфигурация организации массива памяти позволяет этоорганизовать разработчиком

        Во многих архитектурах ПЛИС в каждой брамке есть блок ЕСС. Скрин из мануала на брамки Xilinx:


  1. Brak0del
    26.12.2023 09:42

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

    Важное свойство брамок low-latency доступ. Про это вспоминают всегда, когда проектируют устройства с большими требованиями к памяти и решают, что оставить в ПЛИС, а что держать в DDR и т.д. Ещё одно важное свойство -- высокий параллелизм. Ведь брамок на кристалле может быть сотня или тысяча, у каждой свои независимые порты (или порт), и при желании можно обращаться к каждой в каждом такте.

    Насчёт свойства удобства, имхо, это что-то неизмеримое.

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

    Синтезатор не умеет читать мысли. Ресурс памяти -- это не только про объём, но и про использование портов в том числе во времени. В данном случае проектировщик знает свой проект и то, что может безболезненно уменьшить производительность системы в 4 раза (ведь было 4 независимых буфера, а остался один), а синтезатор этого угадать не может. Если это знание отразить в коде, возможно, синтезатор приятно удивит.


  1. Emil2014
    26.12.2023 09:42
    +1

    IMHO, изначально BRAM использовались вместе с умножителями. Их ширина была 18 бит, соот. коэффициенты из памяти брались словами по 18-бит. По одному - два BRAM на умножитель, они и на кристалле рядом расположены. Я так думаю.