FPGA | Принципы написания конфигураций для ПЛИС

FPGA | Принципы написания конфигураций для ПЛИС

Часто новичкам представляется, что программируя FPGA, можно ограничиться простыми задачами, такими как "моргание светодиодом," используя платформы ALTERA или Xilinx. Однако подобные простые задачи не раскрывают всей философии параллельных вычислений, лежащей в основе FPGA. Да, вы правильно поняли, в ПЛИС все вычисления выполняются параллельно. В этой статье мы постараемся рассказать, как писать конфигурации для FPGA, не привязываясь к конкретному производителю. Мы будем использовать примеры на языке VHDL. Хотя некоторые предпочитают Verilog, выбор языка часто зависит от личных предпочтений. В любом случае, наши рекомендации будут полезны для обоих языков. После прочтения этой статьи, вы будете способны создавать конфигурации для FPGA любого производителя, используя любую среду разработки и язык программирования.

Давайте начнем с того, зачем нужно писать конфигурации на VHDL или Verilog, когда в интернете полно учебников, предоставляющих визуальные редакторы, где можно просто соединять элементы "И", "И-НЕ" и так далее, чтобы создавать необходимую схему.

На первый взгляд это кажется удобным, так как всё интуитивно понятно и наглядно. Но что произойдет, если вам вдруг потребуется добавить в проект хотя бы один FIFO? А если вам понадобится реализовать Быстрое Преобразование Фурье (БПФ)? Ваша схема разрастется до гигантских масштабов, и вы столкнетесь с проблемами. В таких случаях необходимо освоить язык описания аппаратуры (HDL), и это необходимо, чтобы создавать масштабные проекты. На предприятиях, специализирующихся на разработке для FPGA, такие графические редакторы уже не применяются уже много лет. Здесь всё пишется на VHDL или Verilog, потому что это более быстро и понятно.

Итак, мы поняли, что единственным способом создания крупных проектов является использование языков VHDL или Verilog. Теперь перейдем к рассмотрению принципов программирования, но перед этим рассмотрим некоторые ключевые моменты:

  1. Все проекты состоят из модулей, написанных на языке описания аппаратуры (HDL), и каждый модуль имеет порты для взаимодействия с другими модулями.

  2. Любые операции могут быть либо синхронными (выполняются только по сигналу тактирования), либо асинхронными (не зависят от сигнала тактирования и могут выполняться в любой момент времени).

Давайте рассмотрим пример асинхронных вычислений, где используется тактовый сигнал clk, который необходим для синхронизации других сигналов по времени. Обычно источником тактового сигнала является кварцевый генератор, хотя в данном примере он используется только для наглядности и не применяется в самом вычислении. На вход 8-битной шины data поступают 8-битные слова, такие как x"01", x"02", x"03", x"04". Источником данных для этой шины может служить любой другой модуль в этой же FPGA, или если шина подключена к реальным портам, то источником могут быть внешние устройства. Сигналы sum1 и sum2, по сути, представляют собой 8-битные регистры, в которые мы сохраняем сумму входного слова из шины data и некоторого дополнительного значения. Сигнал sum3 представляет собой результат сложения sum1 и sum2. Все вычисления выполняются асинхронно, что означает, что они происходят мгновенно при изменении сигнала data и не синхронизируются по тактовому сигналу.

Теперь давайте представим осциллограмму, где вычисления sum1, sum2 и sum3 выполняются параллельно и асинхронно. Другими словами, две разные операции, sum1 и sum2, выполняются одновременно на одном и том же значении сигнала data, и в то же время выполняется операция сложения sum1 и sum2. Осциллограмма также покажет, что за один такт на шину data приходят два слова (выделены фиолетовым), но это не влияет ни на время вычислений, ни на их результаты.

Теперь, изменим программу немного, добавив синхронизацию вычисления sum3 на фронт (переход) тактового сигнала clk, и получим пример синхронных вычислений.

Ранее, значение sum3 вычислялось одновременно с sum1 и sum2. Однако, как видно из осциллограммы ниже, значение sum3 вычисляется только на фронте тактового сигнала, что означает на следующем такте. Такие задержки в синхронных схемах должны учитываться при написании синхронных процессов. Осциллограмма также показывает, что когда за один такт на шину data приходят два слова, вычисления происходят только над последним словом, так как оно устанавливается как последнее перед фронтом тактового сигнала.

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

Логичный вопрос, который возникает, - зачем нам вообще нужна синхронизация, если все можно делать асинхронно и ускорить работу? Ответ прост: синхронизация нужна, чтобы иметь возможность выполнять определенные вычисления в определенный момент времени. Например, если перед вами стоит задача принять пакет из 100 8-битных слов и в 99-м слове вставить определенное значение перед передачей его дальше. Зная, что за один такт у вас может появиться только одно слово, вы легко можете создать счетчик и дождаться 99-го слова.

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

К списку статей