1.
Зачем это нужно? Встроенная синхронная память ПЛИС имеет свои нехорошие особенности.
- Во-первых, время выборки данных по заданному адресу составляет два такта, а не один, как хотелось бы.
- Во-вторых, при записи в память ПЛИС и считывании данных из того же адреса, куда ведется запись,
правильные данные на выходе появляются даже не через два, а через три такта (увы - экспериментальный факт для Альтеры).
Тем не менее, при последовательном считывании, даже если адрес меняется каждый такт,
данные на выходе блока памяти, хоть и с опозданием на два такта, но все же считываются с максимальной скоростью.
Подобное поведение встроенной памяти в ПЛИС мешает прямому построению стека на встроенной памяти в ПЛИС.
Прямо построенный стек, работает правильно, но команды стека (PUSH, POP, NOP и SWAP) исполняются только за три(даже не два!) такта.
В то же время, скорость работы встроенной памяти не столь высока, как хотелось бы. Она, как минимум, в два раза ниже скорости работы
логической ячейки ПЛИС (например, для EP2C20F484C7 тригеры LCELL способны работать на частоте 380MHz, а память имеет предельную частоту 195MHz).
Это обстоятельство еще более усугубляет положение со скоростью работы стека на встроенной памяти ПЛИС ф. ALTERA.
Чтобы обойти столь пакостное положение, уже давно появилась идея использовать схему построения стека на встроенной памяти, но с кэшем в логических
ячейках. Первая реализация подобного стека не заработала как надо и была заброшена в долгий ящик.
Однако, мысль не стояла на месте, и в настоящее время появилась новая реализация этой идеи, которая прошла успешную проверку в симуляторе и показала
довольно высокие результаты по предельным частотам работы.
2.
Принцип работы. Следуююий рисунок показывает упрощенную структурную схему стека.
Четыре регистра представляют собой тот самый КЭШ вершины стека.
Выбрано именно четыре регистра, а не два, как было в первой реализации,
потому что задержка появления правильных данных на выходе памяти составляет до 3-х тактов.
Запись в память производится каждый раз, при появлении команды PUSH для стека.
Адресный регистр является указателем стека и меняется при появлении соответствующих команд так как указано в тексте на рисунке (язык AHDL).
Данные со входа стека пишутся по команде PUSH как в пямять, так и в регистры RGi.
номер регистра, в который ведется запись выбирается в соответствии с двумя младшими битами регистра адреса.
Эти же два бита записываются вместе с данными в память стека, которая на 3 бита шире, чем данные.
Три вспомогательных бита используются для выбора, в какой из регистров должны быть записаны данные, считанные из памяти.
Два бита выбирают регистр, третий разрешает/запрещает запись.
При работе стека в регистрах оказываются копии данных, сохраненных в памяти, в четырех верхних ячейках, соответствующих указателю стека.
При записи это происходит потому, что данные пишутся и в память и в регистры, при чтении - потому что данные в регистры перезаписываются из памяти. Для того, чтобы данные считывались из стека правильно, чтение из памяти производится не прямо по адресу указателя, а по адресу на 2 меньше указателя.
Так, что при считывании в регистрах оказывается два верхних элемента стека, один из которых передается на выход, а один готов для считывания на следующем такте. третий элемент в этот момент считывается из памяти, и копируется в соответствующий регистр, чтобы его можно было считать в дальнейшем.
Команды стека меняются каждый такт. И для того, чтобы в регистрах сохранялись актуальные на каждый момент времени элементы стека, запрещаются какие-либо
перепрыгивания указателя стека более чем на единицу.
3.
Реализация. Далее представлена реализация данной идеи на языке AHDL. Кроме описанной выше схемы она содержит еще два элемента стека на регистрах, которые являются фактическими верхними элементами, с которыми можно производить действия независимо от остальной части схемы. В частности, команда SWAP выполняется за один такт без каких-либо проблем, связанных с невозможностью за один такт считать/записать в память два значения (если только эта память не двухпортовая).
В данной реализации использована двухпортовая пямять, но в ней один порт используется только на запись, второй только на чтение для того, чтобы эту конструкцию можно было использовать для более старых ПЛИС (таких как ACEX).
Код:
TITLE "Stack LCELL RAM";
Код затерт, так как последующие тесты показали его неадекватность.
Более правильная схема - ниже в этой же теме .
END;
Элемент RAM4Stack создается через Quartus MegaWizard с такими параметрами:
Название элемента в MegaWizard - "RAM: 2-PORT",
Ширина данных - 19 бит,
Количество слов в памяти 256 бит.
4.
Результат компиляции. для EP2C20F484C7
занятый объем: 248 LCELL менее 1% от объема ПЛИС
занятая память: 4864 BIT 2% от объема ПЛИС
предельная рабочая частота: 195.01MHz
5.
Переход на VHDL. Попытка не удалась (уперлось в отсутствие опыта работы с VHDL).
Для Альтеры оно как бы и не нужно. AHDL достаточно.
Для Xilinx - вопрос лишь в том - а надо ли это там при условии наличия встроенной памяти другого типа?