Jump to content

    
Manvel

Максимальная частота тактирования

Recommended Posts

14 часов назад, Manvel сказал:

То что я делаю, по сути из себя представляет систему из двух диф уравнений 2 порядка (приближенно можно сказать, что 2 фильтра 2-го порядка ФНЧ и ФВЧ) с нелинейной обратной связью. Далее вместо того, чтобы решать методом Рунге Кутта эти уравнения я делаю билинейное преобразования и получаю цифровое представление этой системы. В приведенной выше статье все эти шаги достаточно подробно описаны. Я не использую Альтеровсике библиотеки цифровых фильтров, потому что они относительно медленные из-за использования блоков перемножения, в данном случае, чтобы максимально ускорить систему я даже перемножения не использую, а просто суммирование со смещением максимум по первым трем битам коэффициентов цифрового фильтра. Скоро я должен буду приготовить семинар по тому что я сделал, если вам интересно, скину презентацию.

То что вы предлагаете я ранее обдумывал, но у меня не вышло это никак реализовать, но я еще подумаю над этим, спасибо за совет!

Я бы рекомендовал использовать DSP модули. Манипуляции с заменой умножения на суммирование, - прошлый век. 

Оно, скорее всего, и подрезало частоту. 

Share this post


Link to post
Share on other sites
29.06.2021 в 01:54, yes сказал:

извините за любопытство, а где, в какой организации, вы этим занимаетесь? если не секрет

 

 

ИРЭ им. В.А. Котельникова РАН, вот ссылка на дореволюционный сайт, но там в принципе все есть http://cplire.ru/rus/InformChaosLab/index.htm

29.06.2021 в 09:13, vt313 сказал:

Я бы рекомендовал использовать DSP модули. Манипуляции с заменой умножения на суммирование, - прошлый век. 

Оно, скорее всего, и подрезало частоту. 

Можете порекомендовать какой-нибудь DSP борду для обучения, если такие существуют?

Share this post


Link to post
Share on other sites

под DSP модулями имеется ввиду - столбец выделен - жирный текст

image.thumb.png.da7513c4239b25944525ac8ba68f0e3c.png

DSP модули имеются в каждой современной плис

у хилых он имеет вид

image.thumb.png.126b080b8609be58ed30c266ed1c1b4c.png

у интел примерно такой же

Share this post


Link to post
Share on other sites
1 минуту назад, Maverick_ сказал:

под DSP модулями имеется ввиду - столбец выделен - жирный текст

image.thumb.png.da7513c4239b25944525ac8ba68f0e3c.png

DSP модули имеются в каждой современной плис

Ну, насколько я понял, когда я использовал умножение, квартус автоматом использовал эти DSP блоки при компиляции проекта, и скорость в таком случае был ниже, чем при замене умножения на суммирование.

Share this post


Link to post
Share on other sites

сделайте pipline обработку на каждом такте выполняется только  независимые операции, например

а  = b+c;  v=d*w ; r=t-n;

также можете попробовать сделать операции ввида

f= d+c*t

 

если вначале вы посчитали например а  = b+c; потом через несколько тактов вам понадобился результат то не забываем вставлять перезапись результата в регистры на каждом такте на эти несколько тактов

Share this post


Link to post
Share on other sites

например Вы хотите посчитать

image.png.d0c9ae94ee0f79ca5707c585edc38d5b.png

то pipeline описание будет примерно следующее

 library ieee;
 use ieee.std_logic_1164.all;
 use ieee.numeric_std.all;
 
 entity RGB_to_YCrCb is
 
 generic (
 	N : integer:= 8;
 	num_pixel_line  : integer:= 15);
 
 	port( 
 	clk         : in  std_logic;
 	rst         : in  std_logic;
	en			: in  std_logic;
 	
 	--RGB
 	pixel_R	: in std_logic_vector((N-1) downto 0); 
 	pixel_G	: in std_logic_vector((N-1) downto 0); 
 	pixel_B	: in std_logic_vector((N-1) downto 0); 
 	
 	--YCrCb 
	rdy : out std_logic;
 	Y  : out std_logic_vector((N) downto 0);
 	U : out std_logic_vector((N) downto 0);
 	V : out std_logic_vector((N) downto 0)
 		);
 end entity RGB_to_YCrCb;
 
 architecture rtl of RGB_to_YCrCb is
 
	signal reg_R : std_logic_vector((N-1) downto 0); 
	signal reg_G : std_logic_vector((N-1) downto 0); 
	signal reg_B : std_logic_vector((N-1) downto 0); 
	signal res_sum_half_RB  : std_logic_vector(N downto 0);
	signal res_sum	 : std_logic_vector(N downto 0);
	signal sum	 : std_logic_vector(N downto 0);
	signal reg_Y : std_logic_vector((N) downto 0); 
	--signal reg_R_shift3 : std_logic_vector((N-1) downto 0);
	--signal reg_G_shift3 : std_logic_vector((N-1) downto 0);
	--signal reg_B_shift3 : std_logic_vector((N-1) downto 0);
	signal reg_R_shift2 : std_logic_vector((N-1) downto 0);
	signal reg_G_shift2 : std_logic_vector((N-1) downto 0);
	signal reg_B_shift2 : std_logic_vector((N-1) downto 0);
	signal reg_R_shift1 : std_logic_vector((N-1) downto 0);
	signal reg_G_shift1 : std_logic_vector((N-1) downto 0);
	signal reg_B_shift1 : std_logic_vector((N-1) downto 0);
	signal reg_R_shift : std_logic_vector((N-1) downto 0);
	signal reg_G_shift : std_logic_vector((N-1) downto 0);
	signal reg_B_shift : std_logic_vector((N-1) downto 0);
	signal reg_half_R : std_logic_vector((N-1) downto 0);
	signal reg_half_B : std_logic_vector((N-1) downto 0);
	signal sum_half_RB : std_logic_vector((N) downto 0);
	signal reg_diff_YR : std_logic_vector((N) downto 0);
	signal reg_diff_YB : std_logic_vector((N) downto 0);
	signal res_diff0  : std_logic_vector(N downto 0);
	signal res_diff1  : std_logic_vector(N downto 0);
	signal reg_Y_shift : std_logic_vector((N) downto 0);
	signal reg_Y_shift1 : std_logic_vector((N) downto 0);
	signal reg_U : std_logic_vector(N downto 0);
	signal reg_V : std_logic_vector(N downto 0);
	signal reg_sum_U : std_logic_vector(N downto 0);
	signal reg_sum_V : std_logic_vector(N downto 0);
	signal reg_shift_ena : std_logic_vector(5 downto 0);
	
 begin
 
 process (all)
 begin
 if(rst = '1')  then
	reg_R <= (others=>'0');
	reg_G <= (others=>'0');
	reg_B <= (others=>'0');
	reg_half_R <= (others=>'0');
	reg_half_B <= (others=>'0');
 elsif rising_edge(clk) then
	if en = '1' then
		reg_half_R <= std_logic_vector(unsigned(pixel_R) / 2);
		reg_half_B <= std_logic_vector(unsigned(pixel_B) / 2);
		reg_R <= pixel_R;
		reg_G <= pixel_G;
		reg_B <= pixel_B;
	end if;
 end if;
 end process;	
 
 process (all)
 begin
 if(rst = '1')  then
	sum_half_RB <= (others=>'0');
	reg_R_shift <= (others=>'0');
	reg_G_shift <= (others=>'0');
	reg_B_shift <= (others=>'0');
 elsif rising_edge(clk) then
	if en = '1' then
		sum_half_RB <= res_sum_half_RB;
		
		reg_R_shift <= reg_R;
		reg_G_shift <= reg_G;
		reg_B_shift <= reg_B;
		
	end if;
 end if;
 end process;
 
 res_sum_half_RB <= STD_LOGIC_VECTOR(resize(unsigned(reg_half_R), res_sum_half_RB'length) + resize(unsigned(reg_half_B), res_sum_half_RB'length));

 process (all)
 begin
 if(rst = '1')  then
	sum <= (others=>'0');
	reg_R_shift1 <= (others=>'0');
	reg_G_shift1 <= (others=>'0');
	reg_B_shift1 <= (others=>'0');
 elsif rising_edge(clk) then
	if en = '1' then
		sum <= res_sum;
		reg_R_shift1 <= reg_R_shift;
		reg_G_shift1 <= reg_G_shift;
		reg_B_shift1 <= reg_B_shift;
		
	end if;
 end if;
 end process;
 
 res_sum <= STD_LOGIC_VECTOR(resize(unsigned(sum_half_RB), res_sum'length) + resize(unsigned(reg_G_shift), res_sum'length));
 
 process (all)
 begin
 if(rst = '1')  then
	reg_Y <= (others=>'0');
	reg_R_shift2 <= (others=>'0');
	reg_G_shift2 <= (others=>'0');
	reg_B_shift2 <= (others=>'0');
 elsif rising_edge(clk) then
	if en = '1' then
		reg_Y <=  std_logic_vector(unsigned(sum) / 2);
		reg_R_shift2 <= reg_R_shift1;
		reg_G_shift2 <= reg_G_shift1;
		reg_B_shift2 <= reg_B_shift1;
		
	end if;
 end if;
 end process;
 
 
 process (all)
 begin
 if(rst = '1')  then
	reg_diff_YR <= (others=>'0');
	reg_diff_YB <= (others=>'0');
	reg_Y_shift <= (others=>'0');
 elsif rising_edge(clk) then
	if en = '1' then
		
		reg_diff_YR <= res_diff0;
		reg_diff_YB <= res_diff1;
		reg_Y_shift <=  reg_Y;
		--reg_R_shift3 <= reg_R_shift2;
		--reg_G_shift3 <= reg_G_shift2;
		--reg_B_shift3 <= reg_B_shift2;
		
	end if;
 end if;
 end process;
 
 res_diff0 <= STD_LOGIC_VECTOR(resize(unsigned(reg_Y), res_diff0'length) - resize(unsigned(reg_R_shift2), res_diff0'length));
 res_diff1 <= STD_LOGIC_VECTOR(resize(unsigned(reg_Y), res_diff1'length) - resize(unsigned(reg_B_shift2), res_diff1'length));
 
 
 process (all)
 begin
 if(rst = '1')  then
	reg_U <= (others=>'0');
	reg_V <= (others=>'0');
	reg_Y_shift1 <= (others=>'0');

 elsif rising_edge(clk) then
	if en = '1' then
		reg_U <= reg_sum_U;
		reg_V <= reg_sum_V;
		reg_Y_shift1 <= reg_Y_shift;
	end if;	
 end if;
 end process;
 
 reg_sum_U <= STD_LOGIC_VECTOR(resize(signed(reg_diff_YR), reg_sum_U'length) + to_signed(128, reg_sum_U'length));
 reg_sum_V <= STD_LOGIC_VECTOR(resize(signed(reg_diff_YB), reg_sum_V'length) + to_signed(128, reg_sum_V'length));
 
 U <= reg_U;
 V <= reg_V;
 Y <= reg_Y_shift1;
 rdy <= reg_shift_ena(5);
 
 process (all)
 begin
 if(rst = '1')  then
	reg_shift_ena <= (others=>'0');
 elsif rising_edge(clk) then
	for i in 0 to 4 loop
		reg_shift_ena(i+1) <= reg_shift_ena(i);
	end loop;
	reg_shift_ena(0) <= en;
 end if;
 end process;
 
 
 end rtl;

image.thumb.png.500923fc1f37204af6fee6e95a609e4f.png

так же не забываем формировать сигнал разрешенния чтения результатов вычислений для следующего модуля - в моем описании последний процесс

 

Share this post


Link to post
Share on other sites
48 минут назад, Manvel сказал:

Ну, насколько я понял, когда я использовал умножение, квартус автоматом использовал эти DSP блоки при компиляции проекта, и скорость в таком случае был ниже, чем при замене умножения на суммирование.

Странно, они и созданы для скорости. 

Share this post


Link to post
Share on other sites
35 минут назад, Maverick_ сказал:

например Вы хотите посчитать

image.png.d0c9ae94ee0f79ca5707c585edc38d5b.png

то pipeline описание будет примерно следующее

 

так же не забываем формировать сигнал разрешенния чтения результатов вычислений для следующего модуля - в моем описании последний процесс

 

Спасибо за подробный ответ!

Share this post


Link to post
Share on other sites

Ещё момент если у вас сумматоры большой разрядности то можно увеличить тактовую частоту за счёт конвейера из четырех битных сумматоров. С умножением сложнее но тоже нужно обращать внимание на разрядности операндов и смотреть чтобы они не превышали разрядности встроенных умножителей в плис

22 minutes ago, Manvel said:

Спасибо за подробный ответ!

 

Share this post


Link to post
Share on other sites
18 минут назад, Maverick_ сказал:

Ещё момент если у вас сумматоры большой разрядности то можно увеличить тактовую частоту за счёт конвейера из четырех битных сумматоров. С умножением сложнее но тоже нужно обращать внимание на разрядности операндов и смотреть чтобы они не превышали разрядности встроенных умножителей в плис

 

О таком я читал, но пока до реализации не дошел

Share this post


Link to post
Share on other sites
6 hours ago, Manvel said:

ИРЭ им. В.А. Котельникова РАН, вот ссылка на дореволюционный сайт, но там в принципе все есть http://cplire.ru/rus/InformChaosLab/index.htm

Можете порекомендовать какой-нибудь DSP борду для обучения, если такие существуют?

знаком был со старшим составом когда-то...

---------------------------------

по поводу платы - практически, пользы от платы нет. все, что нужно для работы - это софт, прежде всего симулятор. 

как уже написали - если не работает в симуляторе, то на плате нет смысла смотреть. у альтеры (точнее уже у интела), кстати, бесплатный симулятор моделсим - лучший из бесплатных, то есть все там можно посмотреть.

опять же рекомендую продолжать использовать альтеру/интел, а не переходить на ксайлинс - по скорости вряд ли будет существенный выигрыш, а сложность работы с софтом вырастет (удобство упадет)

по поводу ДСП-блоков или каких-то схем улучшения операций сложения/умножения (carry-lookahead, carry-save, деревья Уоллеса, алгоритмы Бута и т.д. такого человечество придумало +100500 вариантов) можно не заморачиваться, инструмент (квартус) выберет оптимальное решение на ваш + или * в коде. это если не будет хватать 1-5% то есть, например, получилось 95МГц, а надо 100, то можно за это браться. и больший эффект тут даст не знание как улучшить описание сумматора, а знание внутренностей ПЛИС и расстановка элементов в кристалле по площади - то есть низкоуровневая возня с железом

я так понимаю, что вникать в детали устройства конкретной ПЛИС вам нет интереса, также нет каких-то ограничений по частоте, чтобы обеспечить тот или иной стандарт. поэтому описывайте на высоком уровне с использованием + - * алгоритм и оптимизируйте сам алгоритм (есть методы и для алгоритмов с обратными связями - как писали выше)

если для демонстрации, ну и вообще, для придания "солидности" нужна плата - посмотрите тут https://www.terasic.com.tw/en/

но еще раз - вначале сделайте код работающий на симуляторе, затем синтезируйте его и посмотрите сколько он занимает ресурсов ПЛИС и в какую ПЛИС влезет, а потом уже выбирайте плату

https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=167&No=921

https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=167&No=941&PartNo=1

во второй еще и процессор есть.

полагаю, что для демонстрации хватит.

возможно, что на алиэкспрессе или где-то еще в подобных местах, можно купить за те же деньги плату с ПЛИС с большими ресурсами. но отсутствие документации и возможные глюки, по-моему, того не стоят

 

 

Share this post


Link to post
Share on other sites
4 hours ago, yes said:

по поводу ДСП-блоков или каких-то схем улучшения операций сложения/умножения (carry-lookahead, carry-save, деревья Уоллеса, алгоритмы Бута и т.д. такого человечество придумало +100500 вариантов) можно не заморачиваться, инструмент (квартус) выберет оптимальное решение на ваш + или * в коде. это если не будет хватать 1-5% то есть, например, получилось 95МГц, а надо 100, то можно за это браться. и больший эффект тут даст не знание как улучшить описание сумматора, а знание внутренностей ПЛИС и расстановка элементов в кристалле по площади - то есть низкоуровневая возня с железом

спорно (выделил жирным) это особенно если брать обработку данных в 32 бита и выше... :) и делать описание схемы с помощью языка HDL. Стараться не использовать готовые IP core производителя для обеспечения переносимости код

Приведу пример на основе 64 битного счетчика

Так сказать "Новороченный" счетчик:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity PipelinedCounter is
    generic (
        width_g: natural := 64; -- Must be divisible by parts_g.
        parts_g: natural := 4
    );
    
    port (
        clk:            in std_logic;
        rst:          in std_logic;
        
        clear:          in std_logic;
        enable:         in std_logic;
        
        count:          out std_logic_vector(width_g - 1 downto 0);
        tick:           out std_logic
    );
end entity;

architecture rtl of PipelinedCounter is
    constant part_width_c:      natural := width_g / parts_g;
    signal almost_tick_r:       std_logic_vector(parts_g - 1 downto 0);
    signal count_r:             std_logic_vector(width_g - 1 downto 0);
begin
    count <= count_r;

    process (all)
        variable part_v:        unsigned(part_width_c - 1 downto 0);
        variable tick_v:        std_logic;
    begin
        if rst = '1' then
            count_r <= (others => '0');
            almost_tick_r <= (others => '0');
            tick <= '0';
        elsif rising_edge(clk) then
            
            tick_v := enable;
            for i in 0 to parts_g - 1 loop
                part_v := unsigned(count_r((i + 1) * part_width_c - 1 downto i * part_width_c));
                
                if tick_v = '1' then
                    -- Value is max - 1?
                    if part_v = to_unsigned(2**part_width_c - 2, part_width_c) then
                        almost_tick_r(i) <= '1';
                    else
                        almost_tick_r(i) <= '0';
                    end if;
                
                    part_v := part_v + 1;
                end if;
                
                count_r((i + 1) * part_width_c - 1 downto i * part_width_c) <=
                    std_logic_vector(part_v);
                
                tick_v := tick_v and almost_tick_r(i);
            end loop;
            tick <= tick_v;
            
            if clear = '1' then
                count_r <= (others => '0');
                almost_tick_r <= (others => '0');
                tick <= '0';
            end if;
        end if;
    end process;
end architecture;

и тот же счечик описанный стандартно:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity PipelinedCounter is
    Port ( clk : in std_logic;
           en : in std_logic;
           rst : in std_logic;
           count : out std_logic_vector(63  downto 0));
end PipelinedCounter;

architecture behavioral of PipelinedCounter is
signal cnt: std_logic_vector (63  downto 0):= (others => '0');
begin 
pr_d_e: process (clk, en, cnt, rst)
begin
if (rst = '0') then
cnt <= (others => '0');
elsif (clk'event and clk = '1') then
if (en = '1') then
cnt <= cnt + "0000000000000000000000000000000000000000000000000000000000000001";
end if;
end if;
count <= cnt;
end process pr_d_e;
end behavioral;

обычный счетчик дает

image.thumb.png.47fbdf022dc694896e9bd266e5191790.png

так сказать "Новороченный" счетчик

image.thumb.png.a7cf790fc5aedf1f4318b09999431057.png

 

PS Не для халивара. Это мое личное мнение/опыт. Все выше мною сказанное касается только если не использовать готовые IP core производителя, а стараться самому описать все вычислительные блоки на HDL, чтобы была переносимость.

 

 

 

Share this post


Link to post
Share on other sites

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

и конкретно в этой теме, как мне кажется, автору интересно показать "применимость метода", а не тратить время на оптимизацию (то есть выиграть ~20% по частоте и потерять время разбираясь в тонкостях плисоведения не имеет смысла)

и я тоже предпочитаю всегда описывать RTL и не брать IP от вендора, но наверняка, в конференции найдеся кто-то, кто из IP-шного сумматора сможет вытащить еще лучше (ну может 2-5%) чем из оптимального RTL. но это еще больше требует времени на освоение, и для человека, который не собирается становится плисоводом, путь тупиковый.

 

Share this post


Link to post
Share on other sites
20 minutes ago, yes said:

предпочитаю всегда описывать RTL и не брать IP от вендора

Если это hard IP, то описание на костылях RTL даёт ровным счётом ничего. А если строить на рассыпухе, тогда можно только потерять по всем параметрам.

Научитесь пользоваться макро описанием и конфигурировать его. Для Ксайлинкса вполне валидный код типа:

   // MACC_MACRO: Multiply Accumulate Function implemented in a DSP48E
   //             Artix-7
   // Xilinx HDL Language Template, version 2020.2

   MACC_MACRO #(
      .DEVICE("7SERIES"), // Target Device: "7SERIES" 
      .LATENCY(3),        // Desired clock cycle latency, 1-4
      .WIDTH_A(25),       // Multiplier A-input bus width, 1-25
      .WIDTH_B(18),       // Multiplier B-input bus width, 1-18
      .WIDTH_P(48)        // Accumulator output bus width, 1-48
   ) MACC_MACRO_inst (
      .P(P),     // MACC output bus, width determined by WIDTH_P parameter
      .A(A),     // MACC input A bus, width determined by WIDTH_A parameter
      .ADDSUB(ADDSUB), // 1-bit add/sub input, high selects add, low selects subtract
      .B(B),     // MACC input B bus, width determined by WIDTH_B parameter
      .CARRYIN(CARRYIN), // 1-bit carry-in input to accumulator
      .CE(CE),     // 1-bit active high input clock enable
      .CLK(CLK),   // 1-bit positive edge clock input
      .LOAD(LOAD), // 1-bit active high input load accumulator enable
      .LOAD_DATA(LOAD_DATA), // Load accumulator input data, width determined by WIDTH_P parameter
      .RST(RST)    // 1-bit input active high reset
   );

   // End of MACC_MACRO_inst instantiation

Тут и велосипед не засовывают, куда он не влазит, и поддержка производителя остаётся.

С более детальной конфигурацией IP можно ознакомиться в соответствующей документации вплоть до добавления своих параметров конфигурации.

p.s. Включите пайплайн в своём DSP и частота взлетит неимоверно. Обычно при dummy конфигурации DSP внутренние регистры не включаются от чего частота заваливается значительно. Включив внутри-IPшные регистры возможно придётся убрать что-то снаружи, но это очень поднимет общую рабочую частоту

Share this post


Link to post
Share on other sites

Приветствую!

12 minutes ago, Nick_K said:

Если это hard IP, то описание на костылях RTL даёт ровным счётом ничего. А если строить на рассыпухе, тогда можно только потерять по всем параметрам.

Научитесь пользоваться макро описанием и конфигурировать его.

Увы макро - это лишь  некоторая оболочка которая скрывает конкретную  реализацию за абстрактным  интерфейсом.
Поэтому  если хотите выжать  максимум  для конкретной FPGA то это  только инстанцирование конкретного примитива hart-IP в RTL с ручной конфигурацией.   

 

Удачи! Rob.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.