Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Скорее всего кто-то из читателей уже слышал о генерации HDL кода из моделей Simulink, а также есть немало людей кто задается вопросом насколько это эффективно.
Чтобы ответить на этот вопрос, в этой статье я покажу процесс разработки в целом, который помимо генерации HDL кода, включает разработку через моделирование с оптимизацией на уровне алгоритмов и непрерывное тестирование.
Сделаю это на примере разработки LDPC декодера стандарта DVB-T2, который удалось разработать меньше чем за два месяца. Итак, начнем с описания процесса разработки, который мы использовали.
МОП широкими мазками
Применение модельно ориентированного проектирования (МОП) позволяет сократить время разработки за счет нахождения ошибок на ранних стадиях проекта. Это достигается с помощью построения эталонов и непрерывного тестирования разрабатываемых алгоритмов относительно этих эталонов. Быстро построить эталон помогает обширная библиотека параметризированных блоков и функций, и при разработке LDPC декодера сначала была построена эталонная модель, содержащая источник данных, LDPC кодер, BPSK модулятор, AWGN канал связи, BPSK демодулятор с мягкими решениями, LDPC декодер и блок, считающий количество ошибок относительно исходных данных (рисунок 1).
С эталонной моделью у нас появился источник тестовых воздействий на LDPC декодер и ожидаемый результат от его работы.
Далее, чтобы понять как устроен LDPC декодер внутри, был разработан прототип на MATLAB содержащий только базовые математические операции. Прототип кроме понимания, дает возможность оценить, насколько упрощения, требуемые для реализации на аппаратуре, в LDPC декодере это, например, замена суммы логарифмов на сумму минимальных значений, влияют на исправляющую способность декодера.
Потом была разработана модель LDPC декодера со структурой для реализации на аппаратуре. Потенциально мы могли бы сгенерировать HDL код из прототипа на MATLAB, но он получился бы не эффективным. Модель для реализации на аппаратуре отличается от прототипа на MATLAB тем, что как правило данные на вход модели для аппаратуры приходят последовательно, а не целым кадром, циклы заменены счетчиками и для хранения больших объемов данных используются блочная память. Обычно процесс получения модели для реализации на аппаратуре начинается с поиска соответствующей статьи в интернете. В статье есть структурные схемы, которые переносятся на диаграмму Simulink.
После полной отладки структуры для аппаратуры был сгенерирован HDL код, который с точностью до бита и такта соответствует модели. То, что код полностью соответствует модели, позволяет выполнять всю отладку алгоритма на модели, что значительно экономит время на его верификацию – сравнение скорости симуляции модели и HDL кода я привел ниже в статье. После генерации HDL кода мы получаем оценку по ресурсам и производительности. Если она не удовлетворительная, то модифицируем модель, а не код, например если проблемы с производительностью добавляем регистры конвейеризации в критичный путь, и снова генерируем HDL код.
Далее детальнее остановимся на этих этапах.
Структура LDPC декодера для аппаратуры
В этой статье я опишу общую схему декодера, которая включает в себя устройство быстрого сдвига (barrel shifter), вычислитель вероятностей (node processor) и память для хранения вероятностей.
Упрощенно процесс декодирования можно описать следующим алгоритмом:
вероятности информационного бита считываются из памяти по адресам заданными поверочной матрицей;
к считанным вероятностям применяется операция быстрого сдвига со значением заданной поверочной матрицей;
результат сдвига попадает на вычислитель вероятностей;
вычисленные вероятности после обратного сдвига записывается в память;
Далее на примере устройства быстрого сдвига рассмотрим процесс получения HDL кода. Почему из всех компонентов LDPC рассматриваем именно устройство быстрого сдвига? Во-первых, можно сравнить качество сгенерированного кода, поскольку частный случая этого алгоритма реализуется одним оператором VHDL. Во-вторых, при прямой реализации устройство быстрого сдвига с требуемыми параметрами занимает неприемлемо много ресурсов, поэтому мы рассмотрим, как сделать более оптимальную реализацию.
Проработка вариантов реализации устройства быстрого сдвига Barrel Shifter
Устройство быстрого сдвига делает циклический сдвиг входного вектора на заданную величину. Например если входной вектор равен 1 2 3 4 5 6 7 8, после применения сдвига на 3, результатом будет вектор 4 5 6 7 8 1 2 3.
Реализовав устройство быстрого сдвига на языке MATLAB и встроив этот код в Simulink через блок MATLAB Function можно выполнить симуляцию и убедиться что устройство работает корректно.
Быстрый сдвиг для битового вектора (например [1 0 1 0 1 1 0 0]) можно реализовать с помощью оператора rol в VHDL (циклический сдвиг влево). VHDL код для вектора длинной 8 будет выглядеть следующим образом:
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY BarrelShifter IS
PORT( u : IN std_logic_vector(7 DOWNTO 0);
shift : IN std_logic_vector(2 DOWNTO 0);
y : OUT std_logic_vector(7 DOWNTO 0)
);
END BarrelShifter;
ARCHITECTURE rtl OF BarrelShifter IS
SIGNAL u_unsigned : unsigned(7 DOWNTO 0);
SIGNAL shift_unsigned : unsigned(2 DOWNTO 0);
SIGNAL y_tmp : unsigned(7 DOWNTO 0);
SIGNAL k : unsigned(7 DOWNTO 0);
BEGIN
u_unsigned <= unsigned(u);
shift_unsigned <= unsigned(shift);
k <= resize(shift_unsigned, 8);
y_tmp <= u_unsigned rol to_integer(k);
y <= std_logic_vector(y_tmp);
END rtl;
Циклический сдвиг влево для восьмибитного вектора после синтеза на ПЛИС Xilinx Zynq7000 занимает 16 LUT.
Далее мы опишем устройство быстрого сдвига алгоритмически на языке MATLAB, сгенерируем VHDL код из него и сравним с ресурсами, полученными при реализации с помощью оператора rol.
Алгоритмически устройство быстрого сдвига можно представить следующим кодом на языке MATLAB, где вычисляются индексы выходного вектора
function y = fcn(u, shift)
N = 8;
ind = zeros(N,1, 'int16');
for i = 1:N
ind(i) = i+int16(shift);
if ind(i) > N
ind(i) = ind(i) - N;
end
end
y = u(ind);
Cгенерированный VHDL код выглядит следующим образом:
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE work.Subsystem_pkg.ALL;
ENTITY MATLAB_Function IS
PORT( u : IN std_logic_vector(0 TO 7); -- boolean [8]
shift : IN std_logic_vector(7 DOWNTO 0); -- uint8
y : OUT std_logic_vector(0 TO 7) -- boolean [8]
);
END MATLAB_Function;
ARCHITECTURE rtl OF MATLAB_Function IS
-- Signals
SIGNAL shift_unsigned : unsigned(7 DOWNTO 0); -- uint8
BEGIN
shift_unsigned <= unsigned(shift);
MATLAB_Function_1_output : PROCESS (shift_unsigned, u)
VARIABLE ind : vector_of_signed16(0 TO 7);
VARIABLE add_cast : vector_of_signed32(0 TO 7);
VARIABLE add_temp : vector_of_signed32(0 TO 7);
VARIABLE sub_temp : vector_of_signed17(0 TO 7);
BEGIN
FOR i IN 0 TO 7 LOOP
add_cast(i) := signed(resize(shift_unsigned, 32));
add_temp(i) := (to_signed(i, 32) + add_cast(i)) + 1;
ind(i) := add_temp(i)(15 DOWNTO 0);
IF ind(i) > to_signed(16#00000008#, 16) THEN
sub_temp(i) := resize(ind(i), 17) - to_signed(16#00008#, 17);
IF (sub_temp(i)(16) = '0') AND (sub_temp(i)(15) /= '0') THEN
ind(i) := X"7FFF";
ELSIF (sub_temp(i)(16) = '1') AND (sub_temp(i)(15) /= '1') THEN
ind(i) := X"8000";
ELSE
ind(i) := sub_temp(i)(15 DOWNTO 0);
END IF;
END IF;
y(i) <= u(to_integer(resize(ind(i), 32) - 1));
END LOOP;
END PROCESS MATLAB_Function_1_output;
END rtl;
Видно, что сгенерированный код с алгоритмического описания устройства быстрого сдвига через работу с индексами выглядит совершенно по-другому чем через оператор rol, хотя реализуется таже самая функция. К тому же код содержит некоторую избыточность – выполняется проверка на выход за границы диапазона int16, после операции разности, производимой с индексами. Однако после синтеза этого кода на ПЛИС Xilinx Zynq7000 мы получаем те же 16 LUT.
Алгоритмическое описание даст нам гибкость с типами данных на входе, для сравнения ресурсов мы на вход подавали битовый вектор, а теперь для использования в LDPC вектор размером 360 из чисел разрядностью 8.
После синтеза на ПЛИС Xilinx Zynq7000 мы получаем 45574 LUT.
На реализацию одного устройства быстрого сдвига с параметрами нужными для LDPC декодера у нас уйдет 45574 LUT, а устройств быстрого сдвига будет два в декодере, получается, что это самый ресурсоемкий элемент в декодере, который далее мы оптимизируем.
LDPC декодер стандарта DVB-T2 может параллельно обрабатывать 360 вероятностей обеспечивая максимальную пропускную способность, но при этом используется большое количество ресурсов. На практике используют меньшую параллельность, один из вариантов рассчитывать 30 вероятностей в параллели.
Если параллельность будет меньше 360, очевидно, что можно сократить количество вычислителей вероятностей, и один из вариантов использовать полностью параллельное устройство быстрого сдвига поставив ключ, выбирающий сдвинутые данные, для которых необходимо рассчитать вероятность. Однако можно также оптимизировать и устройство быстрого сдвига, так чтобы за раз оно работало только с вектором размера выбранной параллельности.
В оптимизированном устройстве быстрого сдвига при обработке с параллельностью 30 входной вектор из 360 запишем в матрицу размером 12x30 как показано на рисунке (полученное число 12, как 360/30, будет дальше использоваться при расчетах адресов и сдвигов).
Например при сдвиге 4, в полностью параллельной версии мы должны получить вектор [5 6 7 8 9 10 11 12 13 14 15 16 17 18 … 3 4]. В версии с параллельностью 30, сначала считается строка по адресу 4 (вычисляется как остаток от деления сдвига 4 на 12), вычитывается вектор [5 17 29 41 53 … 341 353], затем по адресу 5, вычитывается вектор [6 18 30 42 54 … 342 354] и т.д. Так мы можем последовательно за 12 итераций выдать сдвинутые на 4 данные с прореживанием на 12. Поскольку данные при сдвиге на 4 находятся в первом столбце, то не надо делать дополнительный сдвиг вычитанной строки. Однако если необходимо сдвинуть, например на 22, то надо считать строку по адресу 10 (вычисляется как остаток от деления сдвига 22 на 12), получаем вектор [11 23 35 47 59 ... 347 359], который сдвигаем на 1 (вычисляется как целая часть от деления сдвига 22 на 12), в результате получаем вектор [23 35 47 59 ... 347 359 11].
Хотя алгоритмически устройство быстрого сдвига усложнилось, оно теперь работает только с вектором размером 30. Если выполнить синтез этой реализации на ПЛИС Xilinx Zynq7000 мы получаем 1045 LUT.
Таким образом за счет оптимизации на уровне алгоритма удалось сократить количество ресурсов на реализацию одного устройства быстрого сдвига с 45574 LUT до 1045 LUT.
Проработка вариантов реализации устройства быстрого сдвига Barrel Shifter
Отладка HDL кода сложных систем обычно весьма длительный процесс, обусловленный как сложностью разработки тестов, так и скоростью симуляции HDL кода. В этой части статьи мы посмотрим, как повторно использовать тесты и сравним скорость выполнения LDPC декодера на модели со скоростью симуляции эквивалентного HDL кода.
Имея модель с тестами, мы можем сгенерировать не только HDL код из алгоритма, но и тестбенч, который будет получен на основе логированных данных со входа и выхода алгоритма.
Загрузив полученный тестбенч в HDL симлуятор, например Vivado, можно его выполнить и убедиться что сгенерированный HDL код выполняется также как выполняется модель.
Тажке можно сравнить время симуляции декодирования одного блока на модели, которое равно 1.6 минуты с временем симуляции HDL кода в Vivado – примерно 5 минут. Получилось, что скорость моделирования приерно в три раза выше скорости симуляции в HDL симуляторе.
Выводы
Разработка LDPC декодера с помощью модельно ориентированного проектирования длилась меньше двух месяцев после чего он был внедрен в проект DVB-T2 демодулятора, где совместное моделирование помогло ускорить процесс интеграции. Во время разработки был построен эталон, данные с которого использовались при отладке детализированной модели для реализации на аппаратуре. Оптимизация за счет изменения алгоритмов позволила значительно сократить использование ресурсов ПЛИС, а отладка алгортмов на модели сократило время разработки, как за счет повторного использования тестов, так и за счет скорости симуляции, что было особенно актуально при построении BER кривых для декодера. Вообще с помощью МОП мы разработали различные IP, среди который IP по ЦОС, системам связи, компьютерному зрению и нейронным сетям, полный список с описанием доступны по ссылке https://exponenta.ru/news/ip-yadra-dlya-plis