Как ускорить математические операции в VHDL?

В настоящее время у меня есть некоторые расчеты на переднем фронте тактовой частоты пикселей 75 МГц для вывода видео 720p на экран. Некоторые математические вычисления (например, некоторые по модулю) занимают слишком много времени (20+ нс, тогда как 75 МГц — это 13,3 нс), поэтому мои временные ограничения не выполняются. Я новичок в FPGA, но мне интересно, есть ли, например, способ запуска вычислений с более высокой скоростью, чем текущие часы пикселей, чтобы они были завершены к следующему такту часов 75 МГц. Кстати, я использую VHDL.


person bparker    schedule 08.02.2013    source источник
comment
Можете ли вы опубликовать полный код для ошибочных путей?   -  person    schedule 08.02.2013
comment
Существует множество способов оптимизации времени — если вы можете показать нам конкретную проблему, мы можем дать конкретный ответ (или хотя бы несколько)   -  person Martin Thompson    schedule 08.02.2013


Ответы (3)


Вот некоторые приемы:

  • Конвейерная обработка — разделите логику для работы в течение нескольких тактов.
  • многоцикловый путь - если вам не нужен ответ каждый цикл, вы можете указать инструментам, что это нормально, что это займет больше времени. Однако требуется осторожность, чтобы не сообщить инструментам неверную информацию!
  • Подумайте еще раз — например, действительно ли вам нужно делать x mod 3 на очень широком x, или вы могли бы использовать постоянно обновляемый счетчик по модулю 3?
  • Используйте лучшие инструменты — у меня были случаи, когда я мог уложиться в синхронизацию на пути глубокой логики с использованием дорогого синтезатора по сравнению с несоответствием синхронизации в том же коде с использованием синтезатора поставщика.

Более экстремальные решения включают замену кремния на более быстрое устройство, или более новое устройство, или более новое, более быстрое устройство.

person Martin Thompson    schedule 08.02.2013

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;
person user_1818839    schedule 08.02.2013
comment
Чип может работать намного быстрее (это спартанский6), но просто тактовая частота пикселей составляет 75 МГц, и я не знаю, как «запускать другие вещи быстрее». Теперь я понимаю, что могу просто считать. - person bparker; 08.02.2013
comment
Спасибо за объяснение «Как не конвейерить!». Я запомнил его, так как он помог мне понять конвейерную обработку, когда я только начинал. Теперь я вижу, что это очень глупый код. - person HeyYO; 09.02.2013

Обычно сложные математические операции в ПЛИС выполняются конвейерно. Конвейеризация означает, что вы разделяете свои операции на этапы. Допустим, у вас есть множитель, который занимает слишком много времени для вашей тактовой частоты. Вы делите свой множитель на 3 этапа. По сути, ваш множитель состоит из трех разных частей (у которых есть собственный тактовый вход), соединенных одна за другой. Эти три части будут меньше, чем одна часть, поэтому они будут иметь меньшую задержку, поэтому вы можете использовать для них более быстрые часы.

Недостатком этого будет «задержка». Ваша конвейерная система будет выдавать результат с задержкой. В приведенном выше примере с множителем, чтобы получить правильный результат, вам нужно подождать, пока ваш ввод не пройдет все 3 этапа. Но это обычно очень мало (конечно, в зависимости от вашего дизайна) и может быть проигнорировано.

Вот хороший (!) пост об этом: http://vhdlguru.blogspot.com/2011/01/what-is-pipelining-explanation-with.html РЕДАКТИРОВАТЬ: вместо этого см. сообщение Брайана.

Кроме того, поставщики обычно поставляют оптимизированные и конвейерные версии математических операций в качестве IP-ядер в своем программном обеспечении для проектирования. Смотреть на них.

person HeyYO    schedule 08.02.2013
comment
Если у меня есть операция, например, 1280 mod 3 или 720 mod 9, как ее разбить на более мелкие части? Я понимаю конвейерную обработку, просто не уверен, как будет конвейеризирован сам модуль. - person bparker; 08.02.2013
comment
Ну, вы должны попробовать реализовать свою собственную функцию по модулю. Затем вы работаете над конвейерной обработкой этого. Но это другая тема. - person HeyYO; 08.02.2013
comment
Боже мой, этот пример конвейера vhdlguru должен быть одним из самых глупых фрагментов VHDL, которые я видел за долгое время! - person user_1818839; 08.02.2013
comment
@BrianDrummond Возможно, мое мнение неверно, поскольку я новичок, но я нашел статью очень информативной по обучению тому, как работает конвейерная обработка и как ее можно реализовать. Если у вас есть статья получше, поделитесь ею с нами. - person bparker; 08.02.2013
comment
посмотрите на пример кода... цикл и оператор case ничего не делают! оставив только три задания. И это глючит, потому что ... Хорошо, я лучше отредактирую это в своем ответе. - person user_1818839; 09.02.2013