Hardware programming in python

Начинаем FPGA на Python

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

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

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

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

Читайте также:  Todo list react typescript

Создаём новый 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=="09fb", ATTR=="6001", MODE="0666" SUBSYSTEM=="usb", ATTR=="09fb", ATTR=="6002", MODE="0666" SUBSYSTEM=="usb", ATTR=="09fb", ATTR=="6003", MODE="0666" # USB-Blaster II SUBSYSTEM=="usb", ATTR=="09fb", ATTR=="6010", MODE="0666" SUBSYSTEM=="usb", ATTR=="09fb", ATTR=="6810", MODE="0666" 

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

sudo udevadm control --reload

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

ATTR=="09fb", ATTR=="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» конфигурируем пины на микросхеме:

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

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

Заключение

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

Источник

Оцените статью