Среда Jupyter Notebook упрощает управление конфигурированием ПЛИС, но, как уже говорилось ранее, оверлеи PYNQ приходится создавать с помощью инструментов разработки оборудования. Поэтому далее речь пойдет о том, как с нуля написать свой оверлей и затем интегрировать его с "железом" ПЛИС.
Воспользуемся для этого готовым проектом фрактального рендеринга, размещенным на Гитхабе в открытом доступе. Как отмечает автор проекта, рендеринг фракталов представляет собой вычислительную задачу, требующую большой производительности для достижения удовлетворительных результатов. Для аппаратного ускорения алгоритма путем его реализации на ПЛИС автор использует возможности PYNQ, чтобы взаимодействовать с загруженным в ПЛИС оверлеем. Вот что пишет автор в своем отчете.
"Множество Мандельброта определяется как набор комплексных чисел c, для которых функция (1) не расходится при итерации [начиная] от z=0... Для достижения лучших визуальных результатов при вычислении фрактала отдельным пикселям сгенерированного изображения присваивается цвет в зависимости от того, сколько итераций требуется, чтобы превысить фиксированное пороговое значение, например 4, что формирует цветовой градиент и создает интересные узоры. Пикселям, значения функции для которых не расходятся (не превышают порогового значения после вычисления определенного количества итераций), может быть присвоен специальный цвет, чтобы отличать их от остальных пикселей. Каждый пиксель может быть вычислен независимо, что позволяет аппаратно сгенерировать фрактал в виде потока независимых пикселей. Пример множества Мандельброта с цветным окружением в оттенках серого можно увидеть на рисунке..."
Итак, выбрав целью множество Мандельброта, в качестве отправной точки средствами высокоуровневого синтеза (High-Level Synthesys, HLS) автор скомпилировал блок интеллектуальной собственности (IP), способный генерировать фрактальные изображения на основе заданных входных данных.
Сначала проект состоял из простого IP-ядра Calc_pixel, вычисляющего значение итерации для одного пикселя. Функция верхнего уровня в этом случае называется calc_pixel. В целом же процесс вычислений описывается автором следующим образом.
"Модель состоит из единственного класса Mandelbrot с главной функцией calculator, которая вычисляет фрактальное изображение по заданной конфигурации и возвращает результат. Алгоритм выполняет итерацию по всем пикселям на экране. Для каждого пикселя по его координатам действительной оси x_0 и мнимой оси y_0, итеративно вычисляются уравнения (2-5), до тех пор, пока не будет достигнуто максимальное количество итераций или пока значение x^2 + y^2 не начнет отклоняться от порогового значения, равного 4 (условие x ^ 2 + y ^ 2 > 4 истинно)".
Дополненный директивами Vivado соответствующий код на языке С++ приведен ниже.
После успешного синтеза и тестирования был выполнен экспорт IP-ядра.
Затем непосредственно в среде Vivado IP Integrator был сформирован блочный дизайн всего оверлея, включающего в себя четыре библиотечных IP-блока и блок calc_pix_IP, синтезированный, как описано выше.
Сгенерированный Vivado файл битстрима с файлами сценария и описания оборудования затем были помещены в папку personal на microSD-карте PYNQ:
root@pynq:/home/xilinx/pynq/overlays/personal# ls -l SinglePixel/SinglePixel.*
-rw-rw-r-- 1 root xilinx 4045676 Oct 7 14:03 SinglePixel/SinglePixel.bit
-rw-r--r-- 1 root xilinx 276896 Oct 7 13:54 SinglePixel/SinglePixel.hwh
-rw-rw-r-- 1 root xilinx 2360 Oct 7 14:01 SinglePixel/SinglePixel.tcl
root@pynq:/home/xilinx/pynq/overlays/personal#
А теперь, товарищи, Smoke Test! Проверяем, видит ли Python наш первый оверлей.
В списке IP-блоков мы находим необходимую нам аппаратную функцию calc_pixel_0. Теперь дело двинулось. Напишем другой Notebook, точнее позаимствуем упрощенную версию из отчета автора исходного проекта, не забыв также про файлы модели, отображения и контроллера (соответственно Mandelbrot_HW.py, MatplotlibView.py и Controller.py). После копирования файлов на microSD жмем на Run All и по прошествии четверти с лишним часа (!) получаем долгожданную картинку, правда, в инвертированном виде.
Несмотря на то, что завершение рендеринга заняло аж 1025,3 994,13 секунды, полученный результат, указывает автор отчета, с одной стороны, может служить доказательством успеха реализации кода с аппаратным ускорением, правда, пока явно недостаточным. С другой стороны, эту начальную стадию необходимо пройти, чтобы лучше понимать концепции IP, оверлеев и аспектов проверки правильности их проектирования, прежде чем переходить к более сложному и "быстрому" решению.
Продолжение следует