Плис

Архитектура ПЛИС (FPGA)

  • E-mail

Подробности Категория: Разное Создано 20 Январь 2014 Николай Ковач Просмотров: 137907

FPGA – это сокращение от английского словосочетания Field Programmable Gate Array.

ПЛИС – это сокращение от словосочетания «Программируемая Логическая Интегральная Схема». Слово ПЛИС встречается в русскоязычных документациях и описаниях вместо слова FPGA. Далее по тексту в основном будет использоваться этот термин — ПЛИС.
ПЛИС и FPGA – это аббревиатуры, обозначающие один и тот же класс электронных компонентов, микросхем. Это микросхемы, применяемые для создания собственной структуры цифровых интегральных схем.

Логика работы ПЛИС определяется не на фабрике изготовителем микросхемы, а путем дополнительного программирования (в полевых условиях, field-programmable) с помощью специальных средств: программаторов и программного обеспечения.

Микросхемы ПЛИС – это не микропроцессоры, в которых пользовательская программа выполняется последовательно, команда за командой. В ПЛИС реализуется именно электронная схема, состоящая из логики и триггеров.

Проект для ПЛИС может быть разработан, например, в виде принципиальной схемы. Еще существуют специальные языки описания аппаратуры типа Verilog или VHDL.

В любом случае, и графическое и текстовое описание проекта реализует цифровую электронную схему, которая в конечном счете будет «встроена» в ПЛИС.

Обычно, сама микросхема ПЛИС состоит из:

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

Строго говоря это не полный список. В современных ПЛИС часто бывают встроены дополнительно блоки памяти, блоки DSP или умножители, PLL и другие компоненты. Здесь, в этой статье я их рассматривать не буду.

Разработчик проекта для ПЛИС обычно абстрагируется от внутреннего устройства конкретной микросхемы. Он просто описывает желаемую логику работы «своей» будещей микросхемы в виде схемы или текста на Verilog/ VHDL. Компилятор, зная внутреннее устройство ПЛИС сам пытается разместить требуемую схему по имеющимся конфигурируемым логическим блокам и пытается соединить эти блоки с помощью имеющихся программируемых электронных связей. В общем случае размещение и трассировка связей между логическими блоками в ПЛИС остается за компилятором.

Классификация ПЛИС по типу хранения конфигурации.

SRAM-Based.
Это одна из самых распространенных разновидностей ПЛИС. Конфигурация ПЛИС хранится ячейках статической памяти, изготовленной по стандартной технологии CMOS.
Достоинство этой технологии – возможность многократного перепрограммирования ПЛИС. Недостатки – не самое высокое быстродействие, после включения питания прошивку нужно вновь загружать. Значит на плате должен еще стоять загрузчик, специальная микросхема FLASH или микроконтроллер – все это удорожает конечное изделие.

Flash-based.
В таких микросхемах хранение конфигурации происходит во внутренней FLASH памяти или памяти типа EEPROM. Такие ПЛИС лучше тем, что при выключении питания прошивка не пропадает. После подачи питания микросхема опять готова к работе. Однако, у этого типа ПЛИС есть и свои недостатки. Реализация FLASH памяти внутри CMOS микросхемы – это не очень просто. Требуется совместить два разных техпроцесса для производства таких микросхем. Значит они получаются дороже. Кроме того, такие микросхемы, как правило, имеют ограниченное количество циклов перезаписи конфигурации.

Antifuse.
Специальная технология по которой выполняются однократно программируемые ПЛИС. Программирование такой ПЛИС заключается в расплавлении в нужных местах чипа специальных перемычек для образования нужной схемы.
Недостаток – собственно программировать / прошивать чип можно только один раз. После этого исправить уже ничего нельзя. Сам процесс прошивки довольно не быстрый. Зато есть масса достоинств у таких ПЛИС: они довольно быстрые (могут работать на больших частотах), меньше подвержены сбоям при радиации – все из-за того, что конфигурация получается в виде перемычек, а не в виде дополнительной логики, как у SRAM-based.

Конфигурируемые логические блоки.

В документации компании Альтера встречается выражение Logic Array Block (LAB) – массив логики. У компании Xilinx в микросхемах ПЛИС есть примерно такие же блоки — Configurable Logic Block (CLB). Конфигурируемый логический блок – это базовый элемент в ПЛИС, в нем может быть выполнена какая-то простая логическая функция или реализовано хранение результата вычисления в регистрах (триггерах).

Сложность и структура конфигурируемого логического блока (CLB) определяется производителем.

Теоретически, конфигурируемый логический блок может быть, например, очень простым, просто как отдельный транзистор. Или он может быть очень сложным, как целый процессор. Это крайние точки реализации.

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

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

Таким образом, выбор структуры конфигурируемого логического блока производителем ПЛИС – это всегда поиск компромиса по площади кристалла, по быстродействию, энергопотреблению и так далее.

Конфигурируемый логический блок может состоять из одного или нескольких базовых логических элементов. В англоязычной литературе это Basic Logic Element (BLE) или просто Logic Element (LE). В ПЛИС обычно используются так называемые LUT-based базовые логические элементы. Что-то вроде этого:


Рис. 1. Пример традиционного базового логического элемента.

LUT – это Look-Up Table, таблица преобразования. Например, на Рис.1 показан четырехбитный LUT в составе базового логического блока. Здесь четырехбитному числу на входе логической функции ставится в соответствие однобитный результат. Красные квадратики на Рис. 1 обозначают программируемый элемент, регистр – это та память, где хранится прошивка для ПЛИС. Видно, что для конфигурации 4-х битного LUT требуется 16 конфигурационных регистра. Содержимое этих регистров определяют логическую функцию, реализованную внутри базового логического элемента.
Еще один конфигурационный регистр (на Рис. 1 это одиночный красный квадратик справа) определяет нужно ли на выход базового логического элемента выдавать прямо значение с LUT или нужно выдать зафиксированное в D-триггере значение с LUT. Фиксация и хранение данных в цифровых схемах нужна практически в любом проекте.

Примерно такой логический элемент использовался в моем экспериментальном проекте «ПЛИС внутри ПЛИС».

Рассматривая Рис. 1 как пример традиционного базового логического элемента понимаешь какая избыточность заложена внутрь современного кристалла ПЛИС (SRAM-based). Ведь в самом деле, конфигурационные регистры (красные квадратики) прямо не доступны для использования в цифровом проекте. Они только служат для формирования пользовательской функции. Для одного D-триггера в пользовательском проекте требуется более 16 (иногда много больше) триггеров для хранения конфигурации ПЛИС.

На самом деле базовый логический элемент в разных ПЛИС оказывается гораздо сложнее, чем показано на Рис. 1. Ниже есть некоторые примеры из документации на разные типы ПЛИС.

Рис. 2. Базовый логический элемент CPLD MAX II компании Альтера.

Здесь хорошо видны LUT и D-Триггер хранения результата. Ниже, на Рис. 3 представлен базовый элемент Cyclone III.

Рис. 3. Базовый логический элемент FPGA Cyclone III компании Альтера.

В микросхемах Альтеры в одном LAB может содержаться 10-16 LE.

В микросхемах компании Xilinx Virtex-6 базовый логический элемент – это так называемый Slice. В одном CLB всего два Slice. Зато один Slice – это довольно сложное устройство:

Рис. 4. Базовый элемент Xilinx Virtex-6 Slice.

В одном CLB Virtex-6 имеется 8 LUT и 16 D-Триггеров и еще кое-что плюс к этому. Вот так все сложно.

Другая крайность – микросхемы FPGA компании Microsemi (бывшая Actel).
Например, в микросхемах серии 40MX базовый логический элемент выглядит вот так:

Рис. 5. Logic Module of Microsemi 40MX serie.

Восемь входов и один выход.
Здесь нет ни Look-Up Table, ни даже D-Триггера. Триггера, как и остальная логика, формируются где нужно из вот таких крошечных кирпичиков – Logic Module.

Почему у разных компаний получилась такая большая разница в реализации базового логического элемента? Видимо в микросхемах Microsemi связь между базовыми блоками обходится гораздо дешевле: серия 40MX является однократно программируемой. В ней межблочные связи «проплавляются» между соединяющими дорожками и позже не могут быть изменены. Нет никаких регистров для временного хранения прошивки. Здесь нет программируемых переключателей, мультиплексоров, как в FPGA других типов. Ну микросхемы компании Microsemi — это несколько особый случай. Это технология называется antifuse – для производства таких микросхем используется модифицированный техпроцесс CMOS с дополнительными слоями для организации межблочных связей.

Программируемые связи между логическими блоками.

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

Для этого в ПЛИС имеются специальные конфигурируемые коммутаторы.

В англоязычной документации встречаются следующие термины: FPGA Routing Architecture и Programmable Routing Interconnect. Это все об этом, о программируемых связях между логическими блоками.

Известно две основных методики построения ПЛИС по типу архитектуры связей: островная и иерархическая.

Рис. 6. Островная ПЛИС.

Островная ПЛИС называется так потому, что конфигурируемые блоки все равны между собой и находятся, как острова в океане, между узлами коммутации и линиями связи.

Здесь, на Рис. 6 обозначаются CB – Connection Box и SB – Switch Box. В сущности это программируемые мультиплексоры, подключающие тот или иной CLB к другому CLB через цепочки проводов в ПЛИС.

Это island-style FPGA или mesh-based FPGA. Типичный пример таких микросхем – это серии Altera Cyclone и Stratix.

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

Рис. 7. Иерархическая ПЛИС.

Здесь близлежащие CLB соединить довольно просто, нужно не много коммутаторов и получающиеся связи работают быстро. Вот если нужен более крупный блок вычислителей, то сигнал должен выйти на более высокий уровень иерархии и потом зайти вглубь в соседнюю «комнату».

Нельзя сказать, что это существенно хуже, чем island-style. Просто каждый метод имеет свои плюсы и минусы.

Типичные представители иерархических ПЛИС – это микросхемы компании Альтера серии Flex10K, APEX.

Программное обеспечение для проектирования ПЛИС.

Программное обеспечение для проектирования ПЛИС, а именно компилятор (синтезатор логики и фиттер и ассемблер) – это, возможно, самая сложная часть всей ПЛИС технологии.

Компилятор должен проанализировать пользовательский проект (схемы и текстовые описания на Verilog HDL или VHDL ) и сгенерировать нетлист (netlist) – список всех элементов схемы и связи между ними. Netlist должен быть оптимизирован – логические функции нужно минимизировать, возможные дублированные регистры нужно удалить.

Затем компилятор должен вместить всю логику из netlist в имеющуюся архитектуру ПЛИС. Это делает фиттер (fitter). Он размещает логические элементы и выполняет трассировку связей между ними (процесс place and route). Сложность состоит в том, что один и тот же проект может быть размещен в ПЛИС разными способами и этих способов миллионы. Некоторое размещение и трассировка оказываются лучше, другие хуже. Главный критерий качества полученной системы – максимальная частота, на которой сможет работать проект при данном размещении элементов и при данной трассировке связей. Здесь оказывает влияние длина связей между логическими блоками и количество программируемых коммутаторов между ними.

Компилятор, зная архитектуру ПЛИС по результатам работы дополнительно выдает отчет о времени прохождении сигналов от регистра до регистра. Эта информация часто бывает полезной для разработчика высокопроизводительных систем. Разработчик для ПЛИС имеет возможность давать некоторые советы компилятору где, в каком месте кристалла лучше разместить тот или иной модуль проекта.

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

Программное обеспечение компании Альтера: Quartus II.
ПО Xilinx для проектирования для ПЛИС: ISE Suite, Vivaldo Design Suite.
ПО компании Microsemi: Libero IDE, Libero SoC.

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

На страницах нашего сайта https://marsohod.org мы уделяем внимание прежде всего проектированию систем на базе ПЛИС компании Альтера и пользуемся средой разработки Altera Quartus II.

ПЛИС — мои первые шаги

Недавно я все-таки сделал свой первый шаг к ПЛИС и призвал вас за собой. Мое фанатическое увлечение ПЛИС и идея о том, что ПЛИС является лучшей платформой для создания любых устройств приобрела религиозный характер. Моя секта ПЛИСоводов проповедует полный отказ от микроконтроллеров, а особо экстремистская ветвь проповедует отказ не только от софт процессоров, но и вообще от последовательных вычислений!
Как всегда, постижению истин помогло решение реальных задач. В сегодняшней проповеди я хотел бы рассказать об испытаниях, которые выпадают на долю молодого ПЛИСовода. Преодолевая испытания мы постигаем истину. Но остаются вопросы, на которые я не нашел ответов. Поэтому я бы очень хотел, чтобы братья-хабровчане — ПЛИСоводы с опытом, поучаствовали в обсуждении, протянули руку помощи своим младшим собратьям.
Эта статья для новичков. В ней я опишу типичные проблемы, вопросы, заблуждения, ошибки, которые могут появиться в самом начале обучения (потому что они появились у меня). Однако, контекст статьи ограничен тем, что разработка ведется на ПЛИС от Altera в среде Quartus на языке Verilog.

Трудно жить ничего не делая, но мы не боимся трудностей!

Одна из причин, по которой многие не начинают изучать Verilog вот прямо сейчас — это отсутствие реальной ПЛИС. Кто-то не может заказать, потому что дорого, а кто-то потому, что не знает, что именно взять (вопрос выбора рассмотрен в предыдущей статье). Кому-то ПЛИС пока еще едет по почте.
Но в своих разработках я пришел к тому, что наличие реальной ПЛИС мне требуется уже на финальном этапе разработки, когда нужно протестировать проект «в железе». Речь о том, что бóльшую часть времени я провожу в отладке своего кода с помощью симуляторов.
Поэтому мой совет: отсутствие ПЛИС — это не повод бездействовать. Пишите и отлаживайте модули для ПЛИС в симуляторах!

Симулятор для Verilog

Итак, чем же развлечь себя скучными длинными рабочими днями (если они таковыми являются)? Конечно же освоеним ПЛИС! Но как же затащить на работу целую среду разработки от Altera, если она весит 3 ежемесячных рабочих лимита интернета? Можно принести на флешке! Но если предметом изучения является Verilog, то можно ограничиться блокнотом, компилятором IcarusVerilog, а результат смотреть в GTK Wave.
Попробовать прямо сейчасДля начала работы в среде Windows, достаточно скачать по ссылке http://bleyer.org/icarus/ файл установки iverilog-20130827_setup.exe (development snapshot)
Установка трудностей не вызывает. Теперь немного забежим вперед: создадим папку для проекта и в ней пару файлов с пока что не понятным содержимым:

Файл-модуль с кодом для тестирования модулей — bench.v`timescale 1ns / 100 ps module testbench(); reg clk; initial begin $display(«start»); $dumpfile(«test.vcd»); $dumpvars(0,testbench); clk <= 0; repeat (100) begin #10; clk <= 1; #10; clk <= 0; end $display(«finish»); end
В файле bench.v описан тестовый модуль testbench, в нем создан тестовый источник сигнала clk (меандр). Другие модули будут создаваться в отдельных файлах, либо логику можно протестировать сначала в этом модуле, а потом вынести в отдельный модуль. Потом в модуль testbench будут добавляться экземпляры этих модулей, где мы будем подавать на их входы тестовые сигналы и получать из них результаты. Из модулей мы можем строить иерархию, думаю это понятно всем.
BAT Файл, который скомпилирует и просимулирует главный модуль, добавив другие модули из текущей папки — makev.bativerilog -o test -I./ -y./ bench.v vvp test pause
После запуска этого файла мы увидим на экране текст, заданный в $display (это отладочный вывод), значение же сигналов и регистров схемы будут находиться в файле test.vcd. Кликаем по файлу и выбираем программу для просмотра — GTKWave (в моем случае D:\iverilog\gtkwave\bin\gtkwave.exe). Еще пару кликов и мы увидим наш clk.

Практически, каждый свой новый модуль я создаю в блокноте и отлаживаю IcarusVerilog. Следующим этапом после такой отладки идет проверка модулей в Quartus. Хотя в Quartus тоже есть свой симулятор, но я его использую реже. Причина в простоте обновления кода и просмотра результата в IcarusVerilog: сохранил изменения в файле, запустил BAT, нажал кнопку «обновить» в GTKWave — все! В ModelSim для этого требуется чуть больше движений, но он тоже не плох, особенно на данных сложных структур.
После симуляции наступает пора запуска Quartus. Но загружать прошивку в ПЛИС пока еще рано. Нужно убедиться, что божественная вычислительная машина правильно поняла, какую схему мы хотим получить, изложив свои мысли в виде Verilog’а.

Разница между симуляцией и работой в реальном железе

Первое время я, подобно слепому котенку, бился головой об косяки. Казалось бы правильный код, не работает совсем, либо работает не так, как предполагаешь. Либо вот только что работал, а теперь внезапно перестал!
Пытливый котенок, начинает искать взаимосвязь между своими действиями и результатом («голубиное суеверием»).

Самая большая драма

Ниже будет список странностей, но сначала самая большая драма, с которой я столкнулся: не все конструкции Verilog могут быть синтезированы в железе. Это связано с тем, что на Verilog описывается не только аппаратная логика, которая объединяется в модули и работает в железе. На том же Verilog описываются тестовые модули, которые объединяют тестируемые модули, подают на их входы тестовые сигналы и в целом существуют только для проверки на компьютере. Изменение значений сигналов во времени задается конструкциями, содержащим знак «#» в тексте Verilog. Такой знак означает задержку во времени. В примере выше именно так генерируется сигнал CLK. И я грешным делом думал, что таким же образом внутри настоящей ПЛИС можно генерировать, к примеру, последовательность бит для отправки сообщения по RS232. Ведь на вход ПЛИС подан сигнал от генератора 50 МГц! Может быть она как-то на него ориентируется. Как оказалось, я не единственный, кто надеялся на чудо: 1, 2, 3, 4, 5. Реальность как всегда оказывается более суровой: ПЛИС это набор логики и временная задержка в ней может появиться при использовании счетчика, значение которого увеличивается тактами от генератора до заданной величины, либо как-то иначе (но всегда аппаратно).

Список найденных странностей

Удивительные вещи, однако, прочтение книг проливает свет на эту бесовщину. Более того, обретается благодать.

Если обозначить reg, то не факт, что он будет создан

Как я пришел к проблеме? Допустим есть один модуль, на вход которого я должен подавать значение (по типу параметра). В перспективе, этот параметр должен будет изменяться во времени в зависимости от каких-то внешних событий. Поэтому значение должно хранится в регистре (reg). Но реализация приема внешних событий пока не реализована, поэтому я регистр не меняю, а просто задаю ему изначальное значение, которое в дальнейшем не меняется.
//задаю 8 битный регистр reg val; //инициирую его значением initial val <= 8’d0240; //wire к которому подключим выход из модуля wire out_data; //неведомый модуль, называется bbox //экземпляр этого модуля называется bb_01 //будем считать, что в модуле есть входной порт in_data и выходной out_data //во входной порт подаем значение с регистра val, а выход подключаем к wire — out_data bbox bb_01(.in_data(val), .out_data(out_data));
Казалось бы в чем подвох? В императивных ЯП мы часто задаем переменные в качестве констант и потом ни разу их не меняем и все работает. Что же мы видим в железе?

Во-первых, мы не видим регистра. Во-вторых, на вход модуля подано 8’hFF вместо наших 8’d0240! И этого уже достаточно для того, чтобы схема заработала не так, как мы планировали. То, что регистра нет — это нормально. В Verilog можно описывать логику разными способами, в то же время, синтезатор всегда оптимизирует аппаратную реализацию. Даже если написать блок always и в нем работать с регистрами, но при этом выходное значение всегда будет определяться входными, то применение регистра тут окажется лишним и синтезатор его не поставит. И наоборот, если при каких то значениях входных данных выходное значение не меняется, то тут никак не обойтись без регистра-защелки и синтезатор его создаст. (Книга 1 стр. 88-89). Что из этого следует? Если мы начнем менять значение регистра, например, в зависимости от нажатии кнопок, то геристр уже будет создан и все будет работать так, как нужно. Если же окажется, что кнопки ничего не меняют, то синтезатор его опять же выкинет и опять все поломается. Что же делать с константой? Нужно подать ее напрямую на вход модуля:
bbox bb_01(.in_data(8’d0240), .out_data(out_data));
Теперь на входе модуля мы имеем правильное значение:

Остается загадкой, почему при сокращении регистра, его значение в initial не подставляется на вход модуля.

Размерность wire лучше задавать самому.

При разработке в среде Quartus, допускается не задавать линии wire заранее. В этом случае они будут созданы автоматически, но об этом будет выдано предупреждение (warning). Проблема заключается в том, что разрядность wire будет 1-бит, а если порты будут иметь разрядность больше 1 бита, то значение не будет передано.
bbox bb_01(.in_data(8’d0240), .out_data(int_data)); other_bbox bb_02(.in_data(int_data), .out_data(out_data));
Предупреждение
Warning (10236): Verilog HDL Implicit Net warning at test.v(15): created implicit net for «int_data»

Результат:

Как видим, один бит подключен, а остальные 7 бит получаются не подключены (NC). Чтобы такой проблемы не было — нужно создать wire самостоятельно. Не зря компилятор IcarusVerilog выдает не warning, а ошибку, если wire не задан заранее.
wire int_data; bbox bb_01(.in_data(8’d0240), .out_data(int_data)); other_bbox bb_02(.in_data(int_data), .out_data(out_data));

Компьютер не будет лазать по модулям, смотреть, какая разрядность портов. К тому же, разрядность может оказаться разной, а на вход модуля или с выхода берутся не все биты, а какие-то определенные.

Нельзя использовать выход логической функции, в качестве тактового сигнала

Иногда в проекте требуется снизить тактовую частоту, либо ввести временную задержку в N тактов. Новичёк может применить счетчик и дополнительную схему определения достижения счетчиком определенного значения (схема сравнения). Однако, если напрямую использовать выход со схемы сравнения в качестве тактового, то могут возникнуть проблемы. Это связано с тем, что логической схеме требуется некоторое время для установки стабильного значения на выходе. Эта задержка смещает фронт сигнала, проходящего через разные части логической схемы относительно тактового, в итоге получаются гонки, метастабильность, асинхронщина. Даже довелось однажды услышать реплику об этом в качестве критики ПЛИС: «с ПЛИС постоянные проблемы — гонки сигналов».
Если прочитать хотя бы парочку статей:
Метастабильность триггера и межтактовая синхронизация
Пару слов о конвейерах в FPGA
то становится ясно, каким образом разрабатываются устройства на ПЛИС: вся задача делится на аппаратные блоки, а данные между ними движутся по конвеерам, синхронно защелкиваясь в регистрах по тактовому сигналу. Таким образом, зная общую тактовую частоту, синтезатор рассчитывает максимальную частоту работы всех комбинаторных схем, определяет, укладывается ли их скорость к период такта и делает вывод — будет или не будет работать схема в ПЛИС. Все это происходит на этапе синтеза. Если схемы укладываются в параметры, то можно прошивать ПЛИС.
Для полного понимания, стоит прочитать Altera handbook на предмет «clock domains», а так же разобраться с тем, как задавать параметры рассчета TimeQuest для проекта.
Таким образом, для разработчиков устройств на базе ПЛИС созданы все необходимые методологии, и если их придерживаться, то проблем не будет.

А что, если я хочу пойти против системы?

Порядок разработки и поведение синтезатора схем подводит нас к выводу о том, что же такое ПЛИС на аппаратном уровне. Это синхронные схемы. Поэтому, среди целей синтезатора — уложиться во временные интервалы. Для этого он, к примеру, упрощает логические выражения, выбрасывает из синтеза части схем, которые не используются другими схемами и не привязаны к физическим выводам ПЛИС. Асинхронные решения и аналоговые трюки не приветствуются, потому что их работа может быть непредсказуемой и зависеть от чего угодно (напряжение, температура, техпроцесс, партия, поколение ПЛИС), а поэтому не дает гарантированного, повторяемого, переносимого результата. А всем же нужен стабильный результат и общие подходы к проектированию!

Но что же делать, если вы не согласны с мнением синтезатора о том, что нужно выкидывать неизменяемые регистры, сокращать логические схемы? Как быть, если хотите делать схемы с асинхронной логикой? Нужна тонкая настройка? А может быть вы сами хотите собрать схему на низкоуровневых компонентах ПЛИС? Легко! Спасибо разработчикам Altera за такую возможность и подробную документацию!
Как это сделать? Можно попробовать графический редактор схем. Вы, возможно, слышали о том, что Quartus позволяет рисовать схемы? Можно самому выбрать стандартные блоки и соединить их. Но это не решение! Даже нарисованная схема будет оптимизирована синтезатором, если на это будет возможность.
В итоге мы приходим к старой истине: если ничего не помогает — прочитайте инструкцию. А именно «Altera Handbook» часть под названием «Quartus II Synthesis Options».
Начнем с того, что описывая архитектуру на Verilog определенным образом, можно получить определенный результат. Вот примеры кода для получения синхронного и асинхронного RS триггера:
//модуль синхронного RS триггера module rs(clk, r, s, q); input wire clk, r,s; output reg q; always @(posedge clk) begin if (r) begin q <= 0; end else if (s) begin q <= 1; end end endmodule
В этом случае получится синхронный триггер.

Если не брать во внимание тактовый сигнал и переключаться в зависимости от любых изменений r и s, то в результате получится элемент с асинхронной установной значений — защелка (latch).
//пример модуль асинхронного RS триггера module ModuleTester(clk, r, s, q); input wire clk, r,s; output reg q; always @(r or s) begin if (r) begin q <= 0; end else if (s) begin q <= 1; end end endmodule

Но можно пойти еще дальше и самому создать защелку из примитива (примитивы доступны так же, как любой другой модуль Verilog):
module ModuleTester(clk, r, s, q); input wire clk, r,s; output reg q; DLATCH lt(.q(q), .clrn(~r), .prn(~s)); endmodule
В итоге, весь «обвес» на входе защелки, который синтезатор посчитал нужным, исчезнет и мы получим именно то, что хотели:

Список существующих примитивов можно посмотреть на сайте Altera.
А теперь небольшой пример про асинхронность и сокращение. Задумал я, к примеру, сделать генератор по тому же принципу, как это было принято делать раньше, но только на ПЛИС:

Но для увеличения периода я возьму 4 элемента, но только один из них будет с инверсией:
module ModuleTester(q); output wire q; wire a,b,c,d; assign a = b; assign b = c; assign c = d; assign d = ~a; assign q = a; endmodule
Но получается сокращение (1 элемент, вместо четырех). Что логично. Но мы то задумывали линию задержки.

Но если поставить синтезатору условие, что линии a,b,c,d не сокращать, то получится то, что мы задумали. Для подсказки синтезатору применяются директивы. Один из способов указания — это текст в комментарии:
module ModuleTester(q); output wire q; wire a,b,c,d /* synthesis keep */; // ^^^— это директива для синтезатора assign a = b; assign b = c; assign c = d; assign d = ~a; assign q = a; endmodule
А вот и результат — цепочка из четырех элементов:


И это далеко не все! Оставлю на радость самостоятельного изучения: работу с case и директиву для реализации его в качестве RAM/ROM или логической схемой; работу со встроенными блоками памяти (RAM/ROM); выбор реализации умножения — аппаратным умножителем или логической схемой.

Выводы

Цитируя статью, хочу сказать, что «ПЛИС-ы / FPGA — не процессоры, «программируя» ПЛИС (заполняя конфигурационную память ПЛИС-а) вы создаете электронную схему (хардвер), в то время как при программировании процессора (фиксированного хардвера) вы подсовываете ему цепочку написанных в память последовательных инструкций программы (софтвер)».
Причем, как бы мне изначально не хотелось сильно не привязываться к конкретной железяке, но иногда, чтобы более эффективно и экономно использовать ресурсы, приходится работать на низком уровне. Часто этого можно избежать, если правильно разрабатывать синхронные схемы. Однако совсем забыть, что это железо — не получается.
Еще хочу сказать, что фанатизма и максимализма со временем поубавилось. Сначала я стремился все действия и рассчеты на ПЛИС выполнять за один такт, потому что ПЛИС это позволяет. Однако, это требуется далеко не всегда. Вычислительные ядра софт процессоров мне пока не довелось использовать, однако применение state machines для работы по определенному алгоритму — стало нормой. Вычисления не за 1 такт, временные задержки в несколько тактов из за применения конвееров — это норма.

Книги, которые мне очень помогли

1. В.В. Соловьев — Основы языка проектирования цифровой аппаратуры Verilog. 2014
2. Altera: Quartus II Handbook
3. Altera: Advanced Synthesis Cookbook
4. Altera: Designing with Low-Level Primitives

Статьи по тематике ПЛИС, Altera и Verilog

Новости ПЛИС индустрии
Microsoft переходит на процессоры собственной разработки
Intel собирается выпустить серверные процессоры Xeon со встроенной FPGA
Intel планирует выкупить Altera
РБК:Intel купила производителя чипов Altera за $16,7 млрд
Поиск Bing оптимизировали с помощью нейросети на FPGA
Поиск Bing оптимизировали с помощью нейросети на FPGA
Процессоры Intel Xeon оснастили FPGA Altera
Теория
Разработка цифровых устройств на базе СБИС программируемой логики
Аппаратные особенности
Метастабильность триггера и межтактовая синхронизация
Временной анализ FPGA или как я осваивала Timequest
Пару слов о конвейерах в FPGA
Verilog. Обертки RAM и зачем это нужно
Проектирование синхронных схем. Быстрый старт с Verilog HDL
Примеры
Делаем таймер или первый проект на ПЛИС
Часы на ПЛИС с применением Quartus II и немного Verilog
Как я делал USB устройство
Цветомузыка на базе ПЛИС
Программирование ПЛИС. Изучение явления «дребезг контактов» и метод избавления от него(VHDL!)
Реализация на Verilog цифрового БИХ-фильтра
Verilog. Цифровой фильтр на RAM
ПЛИС это просто или АЛУ своими руками
VGA адаптер на ПЛИС Altera Cyclone III
Исследование процессора и его функциональная симуляция
NES, реализация на FPGA
Генерация видео математической функцией на ПЛИС
Аппаратный сортировщик чисел на verilog-е
Простой SDR приёмник на ПЛИС
Автономный SDR приёмник на ПЛИС
Взгляд на 10G Ethernet со стороны FPGA разработчика
Простой радиопередатчик FM диапазона на основе ПЛИС
Делаем тетрис под FPGA