![](https://habrastorage.org/getpro/habr/upload_files/dbf/3b1/c43/dbf3b1c43ecb7663addcbec4691820b5.jpg)
Вступление
Сжатие видео с камеры является важной задачей как при трансляции данных через сеть Интернет, так и при передаче данных через радиоканал. Это позволяет в разы сократить объем передаваемой информации при небольшой потере детализации в исконном видео. Сейчас самым популярным алгоритмом для сжатия видео является кодек h264. Хотя уже существует следующая реализация кодека – h265, но он имеет большую вычислительную сложность алгоритма и пока не нашел столь широкого применения, и его реализацию можно оставить на потом :)
Модельно-ориентированное проектирование - это что?
При разработке алгоритма для ПЛИС можно выделить два основных подхода: написать алгоритм сразу на языке описание аппаратуры VHDL/Verilog/SystemC или использовать более высокоуровневые средства проектирования. Во втором случае вначале строится эталонная модель алгоритма в MATLAB/Simulink, тестируется, верифицируется и постепенно подготавливается к автоматической генерации HDL кода из алгоритма.
![Концепция МОП Концепция МОП](https://habrastorage.org/getpro/habr/upload_files/1b4/eb1/e3d/1b4eb1e3d469382378a31e2ac412bc58.png)
При втором подходе становится проще сам процесс разработки и отладки алгоритма, но требует от разработчика дополнительных знаний в плане подготовки модели к генерации кода. Конечно, мы выбрали второй вариант, т. е. наш план – на основе MATLAB кода сделать эталонную модель алгоритма Simulink, а после – адаптировать ее к автоматической генерации кода и получить HDL для ПЛИС.
Как работает и из чего состоит кодек?
В качестве эталонный модели мы выбрали реализацию кодека h264 на MATLAB с File Exchange H.264 Baseline Codec v2. Глобально сам кодек состоит из двух основных частей: кодировщик опорного кадра (Intra-coded frames) и кодировщик разного кадра (Predicted frames). В начале кодировщик кодирует начальный кадр i-frame, а следующий кадр p-frame кодирует как разницу относительно предыдущего, третий кадр кодируется как разница между третьим и вторым. Через несколько кадров снова идет кодирование опорного кадра i-frame.
[СТАРТ] I-P-P-P-P-P-P-P-P-P-P- I-P-P-P-P-P-P-P-P- P- I-P-P-P-P-P-P ...
Само поле кадра кодируется независимыми макроблоками размером 16х16 пикселей, на выходе с каждого такого блока мы получаем сжатый битовый поток. Основная идея кодека строится на предположении, что на следующем кадре (макроблоке) присутствует часть предыдущей картинки, но возможно движение и смещение картинки за время съемки соседних кадров. Основная задача алгоритма – оценить движение и его направление для различных областей пикселей внутри макроблока, и дальше выполнить кодирование ошибки предсказания между реальным макроблоком из кадра видео и предсказанным.
Разработка эталонной модели энкодера i-frame и p-frame в Simulink
На основе эталонной реализации кодека в MATLAB было создано две Simulink модели: энкодер i-frame и p-frame кадра. Модель i-frame состоит из двух основных блоков: Intra_4 и Intra_16. Блоки Intra состоят из двух частей: блока предсказания макроблока и блока кодирования ошибки предсказания. В первом варианте предсказание выполняется сразу для блока 16х16 пикселей, во втором – последовательно для каждого блока 4х4 пикселей, после завершения кодирования из двух выбирается битовый поток с минимальной длиной, т. е. с лучшим сжатием. В битовом потоке сохраняется информация о выбранном алгоритме предсказания, и о том, какие значения ошибки нужно добавить при декодировании кадра.
![Модель p-frame Модель p-frame](https://habrastorage.org/getpro/habr/upload_files/057/190/e0f/057190e0fea7989885da802e2dc3fc2b.jpg)
![Модель i-frame Модель i-frame](https://habrastorage.org/getpro/habr/upload_files/dc5/f3a/d1e/dc5f3ad1ee3e5189342984da9edaefcc.jpg)
Само предсказание в i-frame больше похоже на экстраполяцию данных на основе данных двух соседних срок макроблоков. Тут есть как более простые варианты, когда к каждому пикселю приравнивается среднее значение всех пикселей двух строк (точнее столбца и строки), так и варианты предсказания по диагоналям:
![Возможные варианты предсказания макроблока для i-frame Возможные варианты предсказания макроблока для i-frame](https://habrastorage.org/getpro/habr/upload_files/96c/b89/711/96cb89711a1c30c7af2c9a86018ec22a.jpg)
Дальше ошибка предсказания блоками по 4х4 пикселя поступает на блок кодировщика CAVLC (контекстно-адаптивное кодирование с переменной длиной). В этом блоке входной сигнал вначале квантуется, к нему применяется «зиг-заг» преобразование для перестановки пикселей, после данных преобразований знающие элементы (не ноль) в векторе располагаются в самом его начале и дальше последовательно кодируются согласно алгоритму CAVLC.
![«Зиг-заг» преобразование для входного блока 4х4 пикселя «Зиг-заг» преобразование для входного блока 4х4 пикселя](https://habrastorage.org/getpro/habr/upload_files/bd2/601/ce3/bd2601ce3cdc3433d1f96a7ea6442c6f.png)
Глобально p-frame отличается от i-frame модулем предсказания движения (компенсации движения). Задача этого блока – найти такое положение макроблока, т. е. смещение, относительно предыдущего кадра, чтобы метрика SAD (сумма абсолютных разностей) между пикселями блоков была минимальной. Для поиска наилучшего совпадения окно макроблока делится на несколько областей 1-2-4-16 и для каждой области считается SAD и выбирается вариант с минимальным SAD. Движение пикселей в макроблоке может быть направлено в разные стороны для разных его областей, в этом случае разбиение макроблока на более маленькие части 8х8, 4х4 пикселей дает лучший результат предсказания движения.
![](https://habrastorage.org/getpro/habr/upload_files/371/4df/170/3714df1701231750680e9f27d3eb955b.jpg)
При разработке Simulink моделей активно использовались блоки MATLAB Function, в которых используя MATLAB код, можно просто описать логику блока с учетом аппаратной реализации и возможности генерации HDL кода из модели.
Для тестирования и верификации моделей использовалось несколько кадров видео с разрешение 48х48 пикселей – всего 9 макроблоков на каждый кадр. Из эталонной MATLAB реализации декодера i- и p-frame был получен эталонный битовый поток, который при тестировании в скрипт файле побитово сравнивался с данными симуляции моделей i- и p-frame в Simulink. После успешной верификации бит с эталонными данными к началу битстрима были добавлены биты заголовка последовательности, и битовый поток был успешно декодирован декодером h264 в MATLAB.
![Декодированный i- и p-frame Декодированный i- и p-frame](https://habrastorage.org/getpro/habr/upload_files/4aa/1e7/4c3/4aa1e74c3e27f78f671b45762feb3fc7.jpg)
Генерация HDL кода из модели и оптимизация модели по ресурсам на примере i-frame
При разработке модели Simulink учитывались требования к будущей аппаратной реализации модели на ПЛИС. Как правило, алгоритмы на ПЛИС работают с потоковыми данными, но для работы Intra_4 требуется блок 4х4 пикселя, а для Intra_16 16х16 пикселей данных было принято решение, что данные для энкодера будут записаны во внешнюю DDR память и оттуда вычитываются при необходимости блоками 4х4 по 16 пикселей, а при накоплении данных размером 16х16 будет запускаться алгоритм Intra_16. Как альтернативный вариант, для потоковой обработки без DDR на ПЛИС можно реализовать буфер на 16 строк и оттуда получать требуемый блок пикселей.
При оптимизации используемых ресурсов, занимаемых моделью, использовалась техника «совместного использования ресурсов» – Sharing Factor. В модуле кодировщике code_block_1 (CAVLC) изначально кодирование элементов вектора было распараллелено, и использовались 16 идентичных блоков для кодирования каждого элемента вектора, данная реализация занимала 101К LUT. Данную реализацию и вручную можно было переделать на последовательную, но это заняло бы некоторое время. Проще использовать опцию Sharing Factor в родительской подсистеме для совместного использования ресурсов в сгенерированном HDL коде, и при необходимости более быстро находить компромисс между используемыми ресурсами и скоростью. Данная оптимизация позволила в 2 раза сократить используемые ресурсы блоком Intra_4, и в 3 раза блоком code_block_1.
![](https://habrastorage.org/getpro/habr/upload_files/596/282/f45/596282f45e96cce2d864127a9f12ed01.jpg)
Подготовка модели p-frame к аппаратной реализации и тестирование на ПЛИС
Для тестирования использовалась отладочная плата Xilinx Zynq-7000 SoC ZC706 Evaluation Kit. В Simulink для этой платы есть пакет поддержки, включающий работу с DDR памятью. MATLAB через AXI4 шину позволяет читать и записывать данные в DDR память, а соответствующий Simulink блок DDR – общаться с памятью при генерации HDL кода из модели. За основу работы с DDR использовался пример «Performing Large Matrix Multiplication on FPGAs External DDR Memory Using Ethernet Based MATLAB as AXI Master». Этот пример содержит модель взаимодействия с DDR памятью, что позволило отладить чтение и запись в память на модели, а потом получить эквивалентный Vivado проект, содержащий интерфейсы к DDR памяти.
![](https://habrastorage.org/getpro/habr/upload_files/3cf/494/1e5/3cf4941e5c04ce4ccaab20b3c13f8643.jpg)
Для тестирования алгоритма на железе модель p-frame энкодера была еще доработана, добавлены блоки чтения и записи данных в DDR, а также были выполнены работы по оптимизации блока расчета интерполяции для блока поиска движения с субпиксельной точностью. В первой реализации интерполяция рассчитывалась параллельно для области 32х32 пикселей, но общее количество используемых ресурсов не укладывалось в выбранную ПЛИС. В ходе оптимизации на Simulink модели было проверено несколько вариантов реализации архитектур блока и выбран вариант с последовательным расчетом интерполяции для блоков размером 4х4 пикселя.
![](https://habrastorage.org/getpro/habr/upload_files/4cf/d9f/f9f/4cfd9ff9ff337e45f04301a8278e0758.jpg)
Для тестирования два соседних кадра изображения построчно записываются в DDR память после прошивки и инициализации платы. В самой модели добавлены блоки, контролирующие вычитку из DDR в нужной последовательности макроблоков, и запускающие алгоритм кодирования. Битовый поток c выхода энкодера вначале собирается в буфере и при накоплении данных больше 32 бит (ширина шины DDR) происходит запись данных в память. Интерфейс подключения p-frame блока в HDL проект показан на рисунке ниже. На 4-м шаге HDL Workflow Advisor автоматически создает готовый «под ключ» проект для синтеза в Vivado. После синтеза и разводки проекта в Vivado алгоритм тестировался на отладочной плате.
![](https://habrastorage.org/getpro/habr/upload_files/a38/952/13f/a3895213f8f69257e4c752d51b1bc438.jpg)
Для тестирования и верификации алгоритма на плате использовался MATLAB скрипт. MATLAB через JTAG интерфейс подключается к отладочной плате с возможностью писать и читать данные с DDR, как axi-мастер. При старте по заранее выделенным адресам происходит запись двух изображений, предварительно преобразованных в одномерный вектор, инициализируется и запускается IP-ядро кодировщика p-frame. Дальше по битам готовности происходит считывание битового потока из DDR в MATLAB и его верификация с битами из эталонной MATLAB модели алгоритма p-frame.
Заключение
Тестирование на железе показало работоспособность самого алгоритма на ПЛИС и битовую эквивалентность данных относительно эталонного MATLAB кода и Simulink моделей. Использование инструментов MATLAB/Simulink позволило выполнить аппаратную реализацию энкодера h264, не написав ни строчки кода на VHDL/Verilog как для самого энкодера, так и для тестов для него. Конечно, создание подобных моделей для генерации HDL кода требуют от разработчика представления о том, как подобные алгоритмы реализуются и работают на железе, но удобство более высокоуровневых инструментов позволяет сконцентрироваться непосредственно на самом алгоритме, быстро проверить различные его варианты и выбрать оптимальный, не тратя время на рутинное низкоуровневое описание интерфейсов и блоков.