В настоящее время у меня есть некоторые расчеты на переднем фронте тактовой частоты пикселей 75 МГц для вывода видео 720p на экран. Некоторые математические вычисления (например, некоторые по модулю) занимают слишком много времени (20+ нс, тогда как 75 МГц — это 13,3 нс), поэтому мои временные ограничения не выполняются. Я новичок в FPGA, но мне интересно, есть ли, например, способ запуска вычислений с более высокой скоростью, чем текущие часы пикселей, чтобы они были завершены к следующему такту часов 75 МГц. Кстати, я использую VHDL.
Как ускорить математические операции в VHDL?
Ответы (3)
Вот некоторые приемы:
- Конвейерная обработка — разделите логику для работы в течение нескольких тактов.
- многоцикловый путь - если вам не нужен ответ каждый цикл, вы можете указать инструментам, что это нормально, что это займет больше времени. Однако требуется осторожность, чтобы не сообщить инструментам неверную информацию!
- Подумайте еще раз — например, действительно ли вам нужно делать
x mod 3
на очень широкомx
, или вы могли бы использовать постоянно обновляемый счетчик по модулю 3? - Используйте лучшие инструменты — у меня были случаи, когда я мог уложиться в синхронизацию на пути глубокой логики с использованием дорогого синтезатора по сравнению с несоответствием синхронизации в том же коде с использованием синтезатора поставщика.
Более экстремальные решения включают замену кремния на более быстрое устройство, или более новое устройство, или более новое, более быстрое устройство.
75 МГц уже довольно медленно по сегодняшним стандартам FPGA.
Проблема заключается в операции по модулю, которая фактически включает в себя деление; и деление медленное.
Тщательно подумайте о необходимых операциях и о том, есть ли способ реорганизовать вычисления. Если вы синхронизируете пиксели, это не значит, что вам нужно иметь дело с 32-битными целыми числами; с ограниченными значениями легче иметь дело.
Мартин намекнул на один вариант: снижение прочности. Если у вас 1280 пикселей на строку и вам нужно работать с каждой третьей, вам не нужно вычислять 1280 по модулю 3! Вместо этого посчитайте 0,1,2,0,....
Другой, если вам нужно по модулю 3 8-битного (или 12-битного) числа, хранить все возможные значения в таблице поиска, что будет достаточно быстро.
Или иногда вы можете умножить на 1/3 (X "5555") вместо деления на 3, затем умножить на 3 (что является одним сложением) и вычесть, чтобы получить модуль. Этот конвейер действительно хорошо работает, но поскольку X "5555" является лишь приближением к 1/3, вам необходимо убедиться в моделировании, что он обеспечивает правильный вывод для каждого входа. (для 16-битных входных данных это небольшая симуляция!) Расширение по модулю 9 выполняется легко.
РЕДАКТИРОВАТЬ:
Два момента из ваших комментариев: Другой вариант, который у вас есть, - это создать часы X2 (150 МГц), используя генераторы часов Spartan, что дает вам 2 цикла на пиксель. Хорошо конвейерный код должен работать на частоте 150 МГц без особых проблем.
PROCESS(Clk)
BEGIN
if(rising_edge(Clk)) then
for i in 0 to 2 loop
case i is
when 0 => temp1 <= a*data;
when 1 => temp2 <= temp1*b;
when 2 => result <= temp2*c;
when others => null;
end case;
end loop;
end if;
END PROCESS;
Первое, что нужно понять, это то, что оператор цикла и оператор case нейтрализуют друг друга, так что это упрощается до
PROCESS(Clk)
BEGIN
if rising_edge(Clk) then
temp1 <= a*data;
temp2 <= temp1*b;
result <= temp2*c;
end if;
END PROCESS;
что глючит! Тестбенч тоже глючит, что скрывает проблему.
В цикле 1 представляются данные, a, b, c, и вычисляется temp1 = Data*a.
В цикле 2 temp1 умножается на НОВОЕ значение b вместо правильного!
То же самое снова в 3 цикле!
Поскольку тестовый стенд устанавливает входные данные и оставляет их постоянными, он не обнаружит проблему!
PROCESS(Clk)
BEGIN
if rising_edge(Clk) then
-- cycle 1
temp1 <= a*data;
b_copy <= b;
c_copy1 <= c;
-- cycle 2
temp2 <= temp1*b_copy;
c_copy2 <= c_copy1;
-- cycle 3
result <= temp2*c_copy2;
end if;
END PROCESS;
Мне нравится комментировать каждый цикл; каждый термин, который я использую в цикле, должен исходить из непосредственно предшествующего цикла либо путем вычисления, либо из копии.
По крайней мере, это работает, но его можно уменьшить до 2 циклов глубины и меньшего количества регистров копирования, потому что в этом примере четыре входа независимы (и я предполагаю, что не требуется никаких мер, чтобы избежать переполнения). Так:
PROCESS(Clk)
BEGIN
if rising_edge(Clk) then
-- cycle 1
temp1 <= a * data;
temp2 <= b * c;
-- cycle 2
result <= temp1 * temp2;
end if;
END PROCESS;
Обычно сложные математические операции в ПЛИС выполняются конвейерно. Конвейеризация означает, что вы разделяете свои операции на этапы. Допустим, у вас есть множитель, который занимает слишком много времени для вашей тактовой частоты. Вы делите свой множитель на 3 этапа. По сути, ваш множитель состоит из трех разных частей (у которых есть собственный тактовый вход), соединенных одна за другой. Эти три части будут меньше, чем одна часть, поэтому они будут иметь меньшую задержку, поэтому вы можете использовать для них более быстрые часы.
Недостатком этого будет «задержка». Ваша конвейерная система будет выдавать результат с задержкой. В приведенном выше примере с множителем, чтобы получить правильный результат, вам нужно подождать, пока ваш ввод не пройдет все 3 этапа. Но это обычно очень мало (конечно, в зависимости от вашего дизайна) и может быть проигнорировано.
Вот хороший (!) пост об этом: http://vhdlguru.blogspot.com/2011/01/what-is-pipelining-explanation-with.html РЕДАКТИРОВАТЬ: вместо этого см. сообщение Брайана.
Кроме того, поставщики обычно поставляют оптимизированные и конвейерные версии математических операций в качестве IP-ядер в своем программном обеспечении для проектирования. Смотреть на них.