[Из песочницы] Начинаем FPGA на Python

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

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

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

pip3 install myhdl


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

-llkc88kifaz0umesq_pctqoim4.jpeg

Создаём новый 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.

frqflvkzfgc2s_7s2ct4bsr6lem.png

В итоге сгенерировалась случайная последовательность входных сигналов 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.

Скачать 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/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» конфигурируем пины на микросхеме:

j1tq2xs2vmum49hubukjqbamibs.png

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

9iget2ugm70gdnp1rt1w5egwbii.png

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

7cz59jrtezjl8jku1jzol5sjfhm.gif

Заключение


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

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

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

© Habrahabr.ru