FPGA
26 November 2013

Генерация видео математической функцией на ПЛИС

From Sandbox
Здравствуйте!
В этой статье я хочу рассказать о своём опыте изучения разработки на ПЛИС и познакомить со своим проектом — генератором видео по формуле.



Цель поста


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

Цель работы


Направить вычислительную мощь ПЛИС на нужды генеративного искусства: порождать в реальном времени потоковое видео по сложной формуле в высоком разрешении, с высоким фрейм-рейтом. В качестве фронтэнда выбран IBNIZ, — язык описания формул для генерации демо, разработанный камрадом viznut в качестве виртуальной платформы для демосцены. Ранее я реализовывал на ПЛИС, на платформе "Марсоход", другую его находку.

Теория


Схемотехника и программирование

По моему мнению, схемотехника для программиста — интересная смежная область для освоения. Описание аппаратуры с одной стороны, во многом схоже с написанием программы что облегчает вхождение, а с другой определяет совершенно иной путь решения прикладной задачи, дает инженеру новые возможности и опыт. Обычно мы пишем программу, которая работает последовательно, строка за строкой. Поучительно попробовать написать программу, все части которой работают одновременно.

Итак, это интересно, но подумаем о целесообразности. Как выбрать задачу, решение которой на ПЛИС будет оправдано? Во-первых, в некоторых случаях схемное решение оказывается изящнее и надежнее программного. К примеру программировал я сумо-бот на микроконтроллере, сделал ему простейший голос — на один из выводов вешается динамик, сигнал на нем меняется со звуковой частотой. Всё просто — цикл, внутри переключение и задержка. Но робот на время гудка останавливался. Чтобы работало одновременно, нужна многозадачность, надо писать диспетчер, с ростом сложности превращающийся в ОС реального времени. В решении на ПЛИС же, не приходится делить ресурс центрального вычислителя, подсистема управления динамиком отъест некоторый объём ПЛИС-ы и далее никому мешать не будет.

Во-вторых, специализированная схема может иметь много большую производительность, чем программное решение. Для попиксельной обработки видеопотока 1280х1024@60Гц в реальном времени понадобится обрабатывать 80млн. точек в секунду, тут даже на мощном процессоре много не успеешь, схемное решение даст возможности куда богаче (при определенных ограничениях на алгоритм, — обработка должна быть стереотипной, ветвления нежелательны).

HDL

Для программирования ПЛИС можно использовать специальные языки, такие как Verilog и VHDL. Они на мой взгляд, значительно удобнее, чем схемный редактор, но человека привыкшего к высокоуровневому программированию разочаровывают. Мой опыт касается (System)Verilog-а, но насколько мне известно, VHDL отличается мало. Схема описывается на уровне регистровых передач (RTL), что вроде бы и естественно, но описание разделяется на две части: комбинационную схему и синхронную логику. Вот есть к примеру оператор цикла, описываю деление столбиком и обнаруживаю, что внутри тела цикла комбинационную часть описывать нельзя, только синхронную. Есть макросы, язык макросов верилога это С-шный препроцессор с точностью до замены '#' на '`'. Ну и у модуля могут быть числовые параметры (времени компиляции), вот пожалуй и все средства обобщенного программирования на нашем вооружении.

RTL, синхронные схемы

В подавляющем большинстве случаев разработчики электронных схем ограничиваются синхронными схемами. Это значит, что есть общий тактовый сигнал (clock, клок), и описание делится на множество регистров и комбинационную схему, определяющую, как значение регистра на следующем такте зависит от значения регистров на предыдущем. Комбинационная схема не обладает собственным состоянием. Минус — некоторое ограничение свободы, к примеру я встречал описание реализации генератора случайных чисел с помощью асинхронной схемы. Вообще, архитектура ПЛИС расчитана на работу только с синхронными схемами, а такие принципиально асинхронные модули как ФАПЧ(PLL) реализуются как отдельные аппаратные блоки.

ФАПЧ позволяет создавать несколько «клоковых доменов», например отделить вычислительный модуль от видеоадаптера, так чтобы у каждого была своя частота. К асинхронным схемам как я понял, схемотехники относятся примерно как программисты к самомодифицирующемуся коду.

Частота на которой сможет работать схема зависит, главным образом, от самой схемы, но и тип ПЛИС, её «класс скрости» играет роль. Смысл примерно такой — за один такт сигнал должен успеть пробежать по самому длинному пути вашей схемы и переключить целевой регистр. Когда я превышал частоту для описываемого решения, на экране появлялись характерные сопли.

Железо




Проект подготовлен для четырёх отладочных плат, Terasic DE0, Terasic DE2-115, Terasic DE0-nano с дисплеем LTM вместо VGA, а также Марсоход II. У Terasic-а отличное железо, с софтом не так радужно. Документация и примеры-образцы есть, но их не всегда достаточно. Например, уже научившись прошивать непосредственно ПЛИС, я убил день на то, чтобы загнать ту же прошивку в конфигурационную память(eeprom) (чтобы данная прошивка загружалась при включении платы). Еще проблема, что в России их трудно найти по разумной цене, а тайваньцы не отправляют обычной почтой. У Марсохода II функционал победнее, но гораздо лучше с описаниями и поддержкой, в статьях с их сайта я часто находил решение очередных своих трудностей. Моей первой отладочной платой был первый Марсоход, он слабее по возможностям но и проще в освоении выше названных, на попробовать самое то.

Инструментарий


Поскольку я использовал отладочные платы на базе ALTERA, то в качестве среды разработки выступал QUARTUS, бесплатный web edition. QUARTUS оставляет неоднозначное впечатление. Главная претензия — очень неинтуитивен и плохо документирован, туториалы никакие. В то же время, свои обязанности интегрированной среды разработки он выполняет. Это типичная ситуация при работе с ПЛИС — работать работает, но удобства (и в том числе лёгкость освоения) на последнем месте, энтузиастов тут как правило не ждут.

Реализация




Итак, генерируем видео по функции. ibniz, а точнее его «линейное» подмножество будет служить спецификацией. Польза в том, что во-первых можно позаимствовать несколько готовых демо, а во-вторых, можно сравнить результат работы с референсной софтовой реализацией что весьма ценно, поскольку отладка схемы куда сложнее чем отладка программы.

Работа в основном разделилась на две части, — инфраструктуру, подготавливающую данные и выводящую результат на дисплей и на ядро, реализующее отдельные элементарные функции ibniz.

Основной компонент инфраструктуры — VGA адаптер. Обычный подход — использовать буфер(ы) фрейма, вовлекает в проект внешнюю память (внутренней мало). Это усложнило бы проект и лишило возможности использовать память для чего-нибудь еще. Поэтому ядро работает синхронно с VGA, цвет пикселя вычисляется и тот сразу рисуется.

Недостатки:
Ядру навязана частота VGA
Пока VGA отрабатывает «невидимые куски экрана» ядро простаивает.
Жёсткое реальное время, на каждый такт надо отдать очередной пиксел, сэкономить там, чтобы потом потратить здесь не получится.
Команды ibniz прямой записи в буфер фрейма нереализуемы, также как и операторы перехода.

Преимущества:
Простота реализации
Можно использовать ширину канала, которую память бы не потянула.
Память сохранена для чего-нибудь еще (разделение ресурса тут вряд-ли было бы возможно).
Низкая латентность, тут от неё толку мало, а вот с сигналом от камеры выходит красиво, но это уже другой проект.
Тру 60Hz видео радует глаз.

Ядро



Ядро проекта составляют математические функции — sin, atan2, log, sqrt, а также деление; реализовал их сам, алгоритмами «цифра за цифрой» (CORDIC), благо был опыт программной реализации. Это итеративные алгоритмы, количество итераций пропорционально разрядности аргументов. Итерация у меня делается за такт, то есть от поступления аргументов на вход до выхода результата проходит количество тактов, равное разрядности чисел, для ibniz это 32. Как это примерить с необходимостью считать точку за такт? С помощью конвееризации, вычислительный блок состоит из 32-х (аппаратных) ступеней. Остаётся задержка, причём для последовательности блоков задержка накапливается. Бороться с ней несложно, но я для наглядности оставил, в результате изображение смещено вправо, а по левому краю видна артефактная полоса, ширина которой соответствует общей глубине конвеера конкретного примера.

Красивости




О генеративном искусстве на Хабре уже писали например тут, далее мои 5коп. Когда начинаешь генерировать компьютером картинки, по началу стремишься к необычному,- парадоксальным формам, ярким до кислотности краскам. Однако, такие «произведения» быстро надоедают. Интересная вещь — чтобы увеличить разнообразие приходится накладывать ограничения. «Случайная» картинка выглядит плоской, но можно добавить ощущение объёма, натянув её на некую форму. В демо «Юпитер» промежуточная картинка-текстура накладывается на сферу. Для этого просто используется преобразование координат от декартовых к сферическим также, как описано в этой статье. Ощущение объёма усиливается за счёт освещения, а также движения, см. демо с четырьмя кругами-сферами.

Итог



Итак, стоит ли программисту лезть в схемотехнику? Только при условии серьёзных намерений, надо хотя бы точно знать цель, поскольку дело это энергозатратное, что обусловлено работой на низком уровне, менее совершенными средствами разработки и менее очевидной логикой работы программы. На другой чаше весов — новые возможности и опыт. Я своим опытом вполне удовлетворён, кроме описанного проекта я ещё сделал генератор процедурной музыки, MIDI-синтезатор, фильтр сигнала от видеокамеры.
Сейчас у меня на стадии обдумывания и написания программной модели гораздо более серьёзный проект, — ненейманновский компьютер на базе комбинаторной логики. Главная идея, — на статическом большом регулярном графе, зашитом в железо живёт динамическое дерево функциональной программы, при этом ветви дерева эволюционируют по возможности одновременно. Узлы максимально простые, умеют только применять комбинаторы; целые числа только в виде нумералов Чёрча. Преимуществом должно стать массовое выполнение простых символьных операций в чистом функциональном стиле. Может быть, что-то вроде разбора регулярных выражений или логического вывода. Конечно, на подавляющем большинстве практических задач традиционную архитектуру не обогнать, но если вдруг найдётся хоть одна подходящая область, глядишь, и до кремния дойдём. Ну или по крайней мере мере появится архитектура с unlambda в качестве языка ассемблера:

```s``s``sii`ki
`k.*``s``s`ks
``s`k`s`ks``s``s`ks``s`k`s`kr``s`k`sikk
`k``s`ksk

+50
24.2k 97
Comments 27