Технология FPGA (ПЛИС) в настоящее время обретает большую популярность. Растёт количество сфер применения: помимо обработки цифровых сигналов, FPGA используются для ускорения машинного обучения, в blockchain технологиях, обработке видео и в IoT.

Данная технология имеет один существенный минус: для программирования используются довольно сложные и специфичные языки описания цифровой аппаратуры Verilog и VHDL. Это осложняет вхождение новичка в FPGA и для работодателя найти специалиста с этими специфичными знаниями на рынке труда сложно. С другой стороны популярный высокоуровневый язык программирования Python с фреймворком MyHDL делают программирование FPGA простым и приятным. Тем более людей знающих Python на порядок больше специалистов владеющих Verilog/VHDL. Серией статей я хочу показать как легко и просто войти в область FPGA зная Python и начать делать настоящие сложные FPGA проекты на этом языке. Отмечу, что на ПЛИС python непосредственно не исполняется, а является инструментом для генерации прошивки.

Во первых нам понадобится сам python версии 3.6 (здесь и далее все операции производятся в ОС Ubuntu 18.04).

Устанавливаем myhdl:

pip3 install myhdl

В качестве «Hello World!» Напишем простую программу, которая заставляет загораться светодиоды от нажатия на кнопку. В мире микропроцессоров «Hello World!» это программа, которая мигает одним светодиодом, в мире FPGA Hello World — это мигание тысячи светодиодов. На плате есть только четыре светодиода, так что мигать будем только ими в зависимости от нажатия кнопки. Важно отметить, что весь код в FPGA в отличие от микроконтроллеров выполняется одновременно, все диоды зажигаются и гаснут одновременно. А не последовательно в случае микроконтроллеров. В роли подопытного используется плата WaveShare OpenEPM1270 с плис Altera Max II EPM1270T144C5 на борту.



Создаём новый python файл:

from myhdl import *
from random import randrange
def led_blinker(input1, led1, led2, led3, led4):
    @always_comb
    def on_off_led():
        if input1 == 1:
            led1.next = 1
            led2.next = 1
            led3.next = 0
            led4.next = 0
        else:
            led1.next = 0
            led2.next = 0
            led3.next = 1
            led4.next = 1
    return on_off_led

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

sudo apt-get install gtkwave

Далее в файле с прошивкой следует описать тестовое окружение. Это тоже питоновская функция:

def test():
    input1, led1, led2, led3, led4 = [Signal(bool(0)) for i in range(5)]
    test = led_blinker(input1, led1, led2, led3, led4)
    @always(delay(10))
    def gen():
        input1.next = randrange(2)
return test, gen

Здесь тестовое окружение генерирует случайную последовательность из нулей и единиц (используется питоновский модуль random).

def simulate(timesteps):
    tb = traceSignals(test)
    sim = Simulation(tb)
    sim.run(timesteps)

И инициализируем симулятор, протаскивая туда функцию окружения test_inverter. Таким образом получается матрёшка inverter > test_inverter > simulate(время в условных единицах).

После запуска скрипта в рабочей папке создаться .vcd файл и его следует пропустить через gtkwave, в терминале: gtkwave test_invereter.vcd.



В итоге сгенерировалась случайная последовательность входных сигналов input1, и то как обработала эти сигналы функция led_blinker.

После того как мы убедились, что логика отработал ровно так как мы хотели, далее следует эту функцию закинуть в FPGA. Я привык работать с микросхемами фирмы Intel (ранее Altera), данная последовательность действий аналогична и для микросхем других производителей с соответствующими САПР. На микросхему ПЛИС заливается бинарный файл, который создаёт компилятор производителя микросхемы, для Intel это Quartus, для Xilinx Vivado. Компиляторы умеют работать только с кодом в VHDL/Verilog, поэтому питоновский код следует транслировать в любой из этих языков (не принципиально в какой).

def convert():
    input1, led1, led2, led3, led4 = [Signal(bool(0)) for i in range(5)]
    toVerilog(led_blinker, input1, led1, led2, led3, led4)
convert()

В этом примере код транслируется в Verilog. Результат в файле led_blinker.v, его и надо будет дать Quartus для генерации прошивки FPGA:

module led_blinker (
    input1,
    led1,
    led2,
    led3,
    led4
);

input input1;
output led1;
reg led1;
output led2;
reg led2;
output led3;
reg led3;
output led4;
reg led4;

always @(input1) begin: LED_BLINKER_ON_OFF_LED
    if ((input1 == 1)) begin
        led1 = 1;
        led2 = 1;
        led3 = 0;
        led4 = 0;
    end
    else begin
        led1 = 0;
        led2 = 0;
        led3 = 1;
        led4 = 1;
    end
end
endmodule

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

Скачать Quartus можно с сайта fpgasoftware.intel.com, нужна бесплатная версия Lite, её нам будет достаточно. Качаем базовую версию размер 9 GB.

Установка Quartus не должна вызвать сложностей для обычного пользователя Linux. После установки необходимо задать некоторые параметры в системе, чтобы можно было пользоваться устройством для зашивки программы ПЛИС — программатором:

1. Создаем udev правило. Для этого создаем новый файл /etc/udev/rules.d/51-altera-usb-blaster.rules со следующим с содержанием:


# USB-Blaster
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6001", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6002", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6003", MODE="0666"
# USB-Blaster II
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6010", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6810", MODE="0666"

Перезагружаем udev, используя udevadm:

sudo udevadm control --reload

2. Разрешаем non-root доступ к устройству USB-Blaster. Для этого создаем файл /etc/udev/rules.d/altera-usb-blaster.rules со строкой:

ATTR{idVendor}=="09fb", ATTR{idProduct}=="6001", MODE="666"

Это даёт rw-rw-rw- доступ к программатору.

3. Конфигурируем jtagd. Quartus использует для работы демона jtagd, который связывет софт с устройством программатора. Копируем описание из вашей директории с Quartus:

sudo mkdir /etc/jtagd
sudo cp <Quartus install path>/quartus/linux64/pgm_parts.txt /etc/jtagd/jtagd.pgm_parts

Запускаем Quartus и создаём новый проект «File» — «New project wizard», набираем имя проекта.

Далее нажимаем Next. И в меню Add Files подключаем сгенерированный verilog файл с расширением .v. Таким образом, если verilog файл будет редактироваться из python файла то он автоматически будет подхватываться Quartus, Далее попадаем в меню выбора девайса, в нашем случае это MAX II EMP1270T144C5 и ещё пару раз next. Проект создан.

В Project Navigator заходим в меню файлов и правой кнопкой мыши задаем наш verilog файл «Set as top-level entity».

Компилируем проект. Теперь в меню «Assignments-Pin Planner» конфигурируем пины на микросхеме:



Ещё раз компилируем. Теперь всё готово к программированию: Tools-Programmer. Подключаем программатор и питание к плате, в Hardware Setup выбираем свой USB-Blaster, задаём галочки как изображено на рисунке и Start.



После того как Programmer отрапортовал Successful. Можно посмотреть результат на плате:



Заключение


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

Рассмотрено:

  • как устанавливать:
    • myHDL;
    • GTKWave;
    • Quartus;
  • Произведена настройка программатора USB Blaster в Ubuntu;
  • Разработан проект на ПЛИС на python;
  • Произведено тестирование и верификация проекта;
  • Скомпилирован проект под ПЛИС;
  • Загружен проект на ПЛИС.

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


  1. dedyshka
    09.02.2019 16:00
    +1

    Было бы полезно:
    — описать (хотя бы, парой предложений) декораторы использованые в ваших примерах кода

    @always_comb, @always

    — добавить ссылку на официальную доку по myHDL


  1. laviol
    09.02.2019 16:07
    +2

    Для поиграться на первых порах (и понять, нужно ли вам это вообще), и, возможно, простецких тестов, сойдет.
    А вот как у myHDL обстоят дела со скоростью (и избыточностью) сгенерированного кода?
    По мне так, если захотелось опробовать FPGA, то лучше сразу привыкать к синтаксису VHDL и/или Verilog (а лучше SystemVerilog), если вы хотите лаконичный и производительный код, тем более что, судя по его вставкам, нет даже отличий в названии блоков логики, аля always и always_comb.


    1. alter_fritz Автор
      09.02.2019 16:41

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


      1. DmitrySpb79
        09.02.2019 22:43
        -3

        Если будет возможность, проверьте мигание светодиодом в цикле из Python и напрямую из Verilog — какая разница в скорости будет? Просто в Python overhead гигантский бывает, и некоторые вещи в нем реально медленные, да и память жрут (sys.sizeof(1) = 28 к примеру — 28 байт на один Int! хотя я понимаю что это объект и все такое, но в FPGA памяти-то не так много).

        И кстати, какие функции питона 3,6 поддерживаются, все или только ограниченное множество? Можно например numpy запустить?


        1. masai
          10.02.2019 14:49
          +1

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


          1. DmitrySpb79
            10.02.2019 15:43

            Так и интересно, какое качество этой кодогенерации в плане скорости.

            Что транслируется в Verilog — исходный текст python-программы, или уже байт-код питона?


            1. proton17
              10.02.2019 21:50
              +1

              Вы, видимо, совсем не понимаете как устроены ПЛИС. В них никакой код не выполняется. В них на уровне триггеров и логических элементов реализуется нужная вам схема, при этом никакой памяти не используется. Т.е. если вам надо с частотой 1Гц поморгать светодиодом, то внутри будет сформирован счетчик строго на необходимое количество разрядов и некая логическая схема декодера которая при одном значении счетчика будет зажигать светодиод, при другом гасить. Термин быстродействие тут вообще не применим. Хотя вопрос эффективности структур сформированных с MyHDL интересен.


              1. DmitrySpb79
                10.02.2019 22:05

                Спасибо. Если на ПЛИС уже и линукс запускают, то может кто-то и интерпретатор Питона сделал, почему бы нет. Собственно интерес как раз в том, имеем мы полностью полноценный Python или просто какое-то урезанное подмножество языка (типа Micropython для ESP32), пригодное только для самых простых операций.

                Про моргание светодиодом пример был неудачный, да. Переформулирую вопрос, вот написали мы какую-то функцию, например, перемножения матриц — одну на Verilog, другую на этом Python, какая будет разница в их быстродействии?

                А в целом, если для FPGA появился полноценный Python — то это реально круто, и действительно снижает порог входа в разы (уж больно лениво этот Verilog изучать, вещь совсем узкоспециализированная). Если Python сильно урезанный, то конечно не так интересно, хотя все лучше чем ничего.


                1. proton17
                  10.02.2019 22:20
                  +1

                  На ПЛИСе можно без проблем собрать какой-нибудь простой микропроцессор или использовать более дорогую ПЛИС со встроенным аппаратным ARMом. А на нем, пожалуйста, можно Linux или RTOS поднять без проблем, но это из пушки по воробьям, проще одноплатник взять типа RPi. А если говорить о чисто аппаратной реализации функций ЦОС, то можно сделать хоть за один так частоты работы ПЛИС, но это займет очень большой процент логики и не будет работать на больших частотах, а можно разбить на много тактов — сделать конвейер. Вообще почти во всех ПЛИСах есть встроенные аппаратные блоки ускоряющие такие операции, например умножители размером 18х18. Кстати Matlab давно умеет формировать годный HDL-код для ПЛИС. Т.е. можно запилить какой-нибудь фильтр в Matlab и получить готовый код для прошивки в ПЛИС.


            1. masai
              11.02.2019 21:34

              Что транслируется в Verilog — исходный текст python-программы, или уже байт-код питона?
              Ни код, ни байт-код не транслируются в Verilog. Код для Verilog — результат исполнения программы на Python.


    1. vasiliev
      10.02.2019 09:08
      +1

      Вы правильно заметили, что отличий в названий блоков по сравнению с традиционными HDL нет. За счёт этого сгенерированный код получается достаточно компактный и вполне себе человекочитаемый по сравнению с высокоуровневыми генерилками типа C-to-HDL.

      Основной плюс myHDL — как раз тесты. Поскольку он работает в экосистеме python, тесты достаточно легко организуются и запускаются. Кроме того, можно подключить множество библиотек для проверки правильности работы HDL-модели: например, в обработке сигналов пригодится numpy. Ещё есть режим ко-симуляции в случае разработки тестов на MyHDL, а кода — на verilog/systemverilog.

      Пример организации тестов на MyHDL в более-менее серьёзном проекте можно посмотреть здесь.


  1. Goron_Dekar
    09.02.2019 16:19
    +1

    А я на несколько секунд поверил, что можно перестать запускать этот монстр Quartus с его сложным гуём.


    1. bugdesigner
      10.02.2019 04:37

      Можно, в принципе, не запускать гуй квартуса, а написать скриптик для компиляции с использованием quartus_map, quartus_fit, quartus_asm. Для этого нужно будет ещё 2 текстовых файла (.qpf и .qsf) с описанием проекта, ну и сами исходники на HDL (verilog/vhdl). Если проект простой, без использования мегакорок, то можно всё проделать в текстовом редакторе, но это для настоящих джедаев командной строки.


    1. Alatarum
      10.02.2019 13:55

      В своё время делал интеграцию кодблокса с квартусом: писал код, ситезировал и заливал чип в C::B, а квартус запускал только для конфигурирование проекта.
      http://we.easyelectronics.ru/CADSoft/integraciya-ide-codeblocks-s-programmnym-kompleksom-altera-quartus-ii-chast-i.html


  1. Daddy_Cool
    09.02.2019 16:44

    Можно чайниковский вопрос?
    Еще давно, когда узнал про FPGA сразу возникла мысль, что надо заставить его решать системы линейных уравнений. Но я так понимаю, что дело упирается в количество входов-выходов. Чтобы было интересно — надо загружать сразу много данных, а входов-выходов всего лишь порядка тысячи.
    Нашел несколько работ по решению СЛАУ на FPGA, пишут — ускорение есть — по сравнению с софтовым решателем, но как-то в массе использования не видно. Ну и да — есть CUDA, суперкомпьютеры, и т.п… Но вот FPGA тоже хочется.


    1. Goron_Dekar
      09.02.2019 17:03

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


      1. NumLock
        09.02.2019 19:19

        Но математикой придётся обойтись целочисленной.

        Python прекрасно работает с дробями. Интересно, можно ли использовать питоновские функции при его конвертации в Verilog/VHDL.


        1. NumLock
          09.02.2019 19:53

          Нашёл в документации, что как бы может конвертировать питоновские функции.
          http://docs.myhdl.org/en/stable/manual/conversion.html

          Function calls are mapped to Verilog or VHDL subprograms
          The converter analyzes function calls and function code. Each function is mapped to an appropriate subprogram in the target HDL: a function or task in Verilog, and a function or procedure in VHDL. In order to support the full power of Python functions, a unique subprogram is generated per Python function call.


          гугл перевод:

          Преобразователь анализирует вызовы функций и код функции. Каждая функция сопоставляется с соответствующей подпрограммой в целевом HDL: функция или задача в Verilog и функция или процедура в VHDL. Чтобы поддерживать всю мощь функций Python, для каждого вызова функции Python генерируется уникальная подпрограмма.


          Так что системы линейных уравнений в FPGA с решением и ответом в виде дробей вполне даже привлекательно.


      1. Daddy_Cool
        09.02.2019 20:13

        Как это? Т.е. имеем дело с integer и всё?
        Поискал — нашел про плавающую точку на FPGA.
        www.eetimes.com/document.asp?doc_id=1275350
        вдруг кого заинтересует.


    1. DmitrySpb79
      09.02.2019 22:37
      +1

      Когда узнал про FPGA сразу возникла мысль, что надо заставить его решать системы линейных уравнений

      Сейчас есть специализированные штуки типа Intel Neural Compute Stick — только не знаю, заточена она только под нейросети, или на ней можно и обычную математику считать.

      А так да, проще на Cuda, там все довольно просто, главное придумать как распараллелить задачу.


    1. AVDerov
      09.02.2019 23:39

      Про СЛАУ надо сразу указывать, какая размерность системы вам нужна, разреженная или нет. Вообще то не стоит самому городить огород про линейные системы, профессионально написанного кода — вагон и маленькая тележка, пусть даже и для CPU.


  1. xztau
    09.02.2019 18:42
    +1

    А как же chisel?

    Вопрос, раз уж в тему
    Есть iverilog, где можно смоделировать Verilog модель.
    А есть ли свободные инструменты (не ModelSim) для моделирования VHDL?


  1. aamonster
    09.02.2019 21:23
    +1

    Э… Это Python или это просто аналог Verilog/VHDL с синтаксисом Python (вместо C/Pascal)?


    1. alter_fritz Автор
      09.02.2019 21:41

      Чистый python


      1. aamonster
        09.02.2019 22:40

        Э… "Чистый python" — в смысле, что исполняется нормальный питоновский код, и не надо въезжать в новые концепции (т.е. это с тем же успехом мог бы быть код на C или ещё каком языке общего назначения), или в смысле, что обошлись инфраструктурой питоновского интерпретатора, чтобы сделать DSL?


        1. Des333
          09.02.2019 23:11
          +1

          docs.myhdl.org/en/stable/manual/preface.html:

          Conversion to Verilog and VHDL
          Subject to some limitations, MyHDL designs can be converted to Verilog or VHDL. This provides a path into a traditional design flow, including synthesis and implementation. The convertible subset is restricted, but much wider than the standard synthesis subset. It includes features that can be used for high level modeling and test benches.

          The converter works on an instantiated design that has been fully elaborated. Consequently, the original design structure can be arbitrarily complex. Moreover, the conversion limitations apply only to code inside generators. Outside generators, Python’s full power can be used without compromising convertibility.

          Finally, the converter automates a number of tasks that are hard in Verilog or VHDL directly. A notable feature is the automated handling of signed arithmetic issues.


          1. aamonster
            09.02.2019 23:40
            +1

            Для меня звучит как "вы должны разобраться в программировании fpga, тогда сможете использовать связку HDL+Python".
            Т.е. нет упрощения входа, есть возможность помимо новых инструментов — оставить старый и привычный. Так?


            1. Arcpool
              10.02.2019 11:18
              +1

              Две подряд идущие строки в verilog/vhdl выполняется паралельно с вероятностью 99%, для последовательного выполнения нужно предпринять дополнительные меры. Все потому, что verilog/hdl по факту описывает как соединить отдельные элементы ПЛИС в цифровую схему. Так что да, лучше hdl/verilog изучать как отдельные концепции.


  1. uhf
    10.02.2019 14:21
    +1

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


    1. alter_fritz Автор
      10.02.2019 14:41

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


  1. lingvo
    10.02.2019 22:42

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