Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Евклидово расстояние на Xilinx DSP48
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
count_enable
Вот такую задачку сейчас обдумываю. Надо считать евклидово расстояние, координаты точек передаются парами последовательно: А(0) В(0), А(1) В(1), А(2) В(2). Евклидово расстояние это sqrt(sum((А-B )^2)). Корень посчитаю кордиком, сумму аккумулятором, а вот как быть с квадратом разницы? Возможно ли это сделать на одном DSP48 блоке за вменяемое количество циклов? Подскажите где прочитать об этом. Все числа с фикс. точкой.
Maverick
Цитата(count_enable @ Feb 18 2016, 11:18) *
Вот такую задачку сейчас обдумываю. Надо считать евклидово расстояние, координаты точек передаются парами последовательно: А(0) В(0), А(1) В(1), А(2) В(2). Евклидово расстояние это sqrt(sum((А-B )^2)). Корень посчитаю кордиком, сумму аккумулятором, а вот как быть с квадратом разницы? Возможно ли это сделать на одном DSP48 блоке за вменяемое количество циклов? Подскажите где прочитать об этом. Все числа с фикс. точкой.

Для альтеры - примеры
С Xilinx давно не работал, попробуйте, должно аналогичным способом описываться....
blackfin
Цитата(count_enable @ Feb 18 2016, 13:18) *
Корень посчитаю кордиком, сумму аккумулятором, а вот как быть с квадратом разницы? Возможно ли это сделать на одном DSP48 блоке за вменяемое количество циклов?

Так готовый IP-Core комплексного умножителя должен подойти. Там и латентность выбрать можно.

Примерно так:

X_re = A(0)-B(0);
X_im = A(1)-B(1);

Y_re = A(0)-B(0);
Y_im = B(1)-A(1);

X*Y = [A(0)-B(0)]^2+[A(1)-B(1)]^2;

PS. Хотя, если все значения идут с задержкой в один такт, то можно, КМК, и один умножитель использовать ..
Наверное, это будет оптимальнее по ресурсам.
Maverick
нашел тему


count_enable
Цитата(blackfin @ Feb 18 2016, 13:37) *
Так готовый IP-Core комплексного умножителя должен подойти.
Там же вроде 2 пары только можно за маш. цикл умножать?

Maverick, не совсем понял как связана рекомендуемая тема с возведением в квадрат. Вы предлагаете закольцевать выход DSP на его же вход Carry, и поштучно возводить аргументы в квадрат, аккумулируя их? Звучит интересно. Так можно считать расстояния в N-мерном пространстве, и решение в принципе простое. Но наверно надо выбросить все внутренние регистры, потому что ждать 7 клоков задержки на один аргумент что-то неохота.
blackfin
Цитата(count_enable @ Feb 18 2016, 14:18) *
Там же вроде 2 пары только можно за маш. цикл умножать?

Ну, если у Вас в каждом такте приходит одна пара A и B, то можно умножитель с накоплением использовать:
Код
input signed [] A0,A1,A2;
input signed [] B0,B1,B2;

wire signed [] SA = (T == 0) ? A0 : (T == 1) ? A1 : (T == 2) ? A2 : 0;
wire signed [] SB = (T == 0) ? B0 : (T == 1) ? B1 : (T == 2) ? B2 : 0;

reg signed [] D;

always @(posedge clk)
begin
  D <= SA - SB;
end

wire [] P;

multadd multadd_m(.clk(clk),.A(D),.B(D),.C(P),.P(P));

reg [] R;

always @(posedge clk)
begin
  if (sclr)
    R <= 0;
  else
  if (valid)
    R <= P;
end

PS. И даже задержка не нужна.

Нужен сигнал valid совпадающий по времени с результатом суммирования: T = mult_latency + add_latency + 2;
count_enable
Цитата(blackfin @ Feb 18 2016, 14:35) *
Ну, если у Вас в каждом такте приходит одна пара A и B, то можно умножитель с накоплением использовать:

Извините, верилог знаю только со словарём. Где в этом коде возведение в степень (А-В )^2?
Maverick
Вам привели описание для этого случая...


count_enable
Последний multadd multadd_m(..,.A(D),.B(D),.C(P),.P(P)); ? И где Р это мой аккумулятор?
Maverick
Цитата(count_enable @ Feb 18 2016, 13:19) *

Код
library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;

entity half_dsp_block is  
  generic (WIDTH: integer := 18);
  port (
        clock       : in  std_logic;
        areset    : in  std_logic;
        clock_ena : in  std_logic;
        a0        : in  std_logic_vector(WIDTH-1 downto 0);
        b0        : in  std_logic_vector(WIDTH-1 downto 0);
        a1        : in  std_logic_vector(WIDTH-1 downto 0);
        b1        : in  std_logic_vector(WIDTH-1 downto 0);
        a2        : in  std_logic_vector(WIDTH-1 downto 0);
        b2        : in  std_logic_vector(WIDTH-1 downto 0);
        a3        : in  std_logic_vector(WIDTH-1 downto 0);
        b3        : in  std_logic_vector(WIDTH-1 downto 0);
        w         : out std_logic_vector(2*WIDTH+1 downto 0));
end half_dsp_block;

architecture rtl of half_dsp_block is

  signal a0_reg : signed(WIDTH-1 downto 0);
  signal b0_reg : signed(WIDTH-1 downto 0);
  signal a1_reg : signed(WIDTH-1 downto 0);
  signal b1_reg : signed(WIDTH-1 downto 0);
  signal a2_reg : signed(WIDTH-1 downto 0);
  signal b2_reg : signed(WIDTH-1 downto 0);
  signal a3_reg : signed(WIDTH-1 downto 0);
  signal b3_reg : signed(WIDTH-1 downto 0);
  signal p0     : signed(2*WIDTH downto 0);
  signal p1     : signed(2*WIDTH downto 0);
  signal w_sig  : signed(2*WIDTH+1 downto 0);

begin

  process (clock, areset)
  begin
      if (areset = '1') then          -- asynchronous reset
        a0_reg <= (others => '0');
          b0_reg <= (others => '0');
          a1_reg <= (others => '0');
          b1_reg <= (others => '0');
          a2_reg <= (others => '0');
          b2_reg <= (others => '0');
          a3_reg <= (others => '0');
          b3_reg <= (others => '0');
          p0     <= (others => '0');
          p1     <= (others => '0');
          w_sig  <= (others => '0');
    elsif clock'event and clock = '1' and clock_ena = '1'then  -- rising clock edge
        a0_reg <= signed(a0);
        b0_reg <= signed(b0);
        a1_reg <= signed(a0_reg);
        b1_reg <= signed(b1);
        a2_reg <= signed(a1_reg);
        b2_reg <= signed(b2);
        a3_reg <= signed(a2_reg);
        b3_reg <= signed(b3);
        --must follow the following order to be recognized to use shift register input
        p0        <= resize((a1_reg*b1_reg),p0'length) + resize((a0_reg*b0_reg),p0'length);
        p1        <= resize((a3_reg*b3_reg),p1'length) + resize((a2_reg*b2_reg),p1'length);
        w_sig  <= resize(p0,w_sig'length) + resize(p1,w_sig'length);
    end if;
  end process;

  w <= std_logic_vector(w_sig);

end rtl;

Вы можете аналогичным способом поступить - для альтеры работало...
blackfin
Цитата(count_enable @ Feb 18 2016, 15:13) *
Извините, верилог знаю только со словарём. Где в этом коде возведение в степень (А-В )^2?

Там три такта выполняется умножение: D*D.
Цитата(count_enable @ Feb 18 2016, 15:19) *
Последний multadd multadd_m(..,.A(D),.B(D),.C(P),.P(P)); ? И где Р это мой аккумулятор?

Результат суммирования - в регистре R.
count_enable
Спасибо большое. Уже "увидел" как оно будет работать. Сейчас пообедаю и буду смотреть что об этом синтезатор скажет.

Думаю тема будет полезна не только мне одному, задача широко распространена.
blackfin
Цитата(count_enable @ Feb 18 2016, 15:49) *
Спасибо большое. Уже "увидел" как оно будет работать. Сейчас пообедаю и буду смотреть что об этом синтезатор скажет.

Там только надо следить, чтобы при генерации IP параметр "Actual C Latency" был больше двух.

Для коротких чисел A,B,C,P это условие может не выполняться и тогда придется ставить задержку на один такт для входного порта .C().
Fat Robot
есть приближенная оценка модуля вектора для двумерного случая:
abs([x,y])=max(abs(x),abs(y)) + 0.375*min(abs(x),abs(y))

умножение на коэффициент делается, как shift and add

я думаю, что можно сделать аналогичное приближение для вашего трехмерного вектора:
отсортировать модули трех проекций, умножить на коэффициенты и сложить
blackfin
Цитата(count_enable @ Feb 18 2016, 14:49) *
Спасибо большое. Уже "увидел" как оно будет работать.

Это прекрасно! Тем более, что мне заставить xbip_multadd работать в режиме аккумулятора и без warnings так и не удалось. wink.gif

Для коротких векторов A и B синтезируется без ошибок xbip_dsp48_macro для единственной операции: (A+D)*B+P.

Если вектора A и B длиннее 18 бит, то придется, вероятно, использовать mult_gen_0 в режиме умножителя и суммировать результаты умножения во внешнем аккумуляторе.

Для xbip_dsp48_macro код получился вот такой:
Код
module acc
(
    clk,
    nrst,
    A0,A1,A2,B0,B1,B2,
    T,valid,R
);

input         clk;
input         nrst;

input signed [16:0] A0,A1,A2;
input signed [16:0] B0,B1,B2;
input         [1:0] T;
input               valid;
output       [36:0] R;

wire signed [16:0] SA = (T == 0) ? A0 : (T == 1) ? A1 : (T == 2) ? A2 : 0;
wire signed [16:0] SB = (T == 0) ? B0 : (T == 1) ? B1 : (T == 2) ? B2 : 0;

reg signed [17:0] D;

always @(posedge clk)
begin
  D <= SA - SB;
end

wire [36:0] P;

dsp48mac dsp48mac_m(.clk(clk),.a(D),.b(D),.d(0),.p(P));

reg  [36:0] R;

always @(posedge clk or negedge nrst)
begin
  if (nrst == 0)
  begin
    R <= 0;
  end
  else
  if (valid)
  begin
    R <= P;
  end
end

endmodule
Timmy
Цитата(blackfin @ Feb 22 2016, 13:02) *
Если вектора A и B длиннее 18 бит, то придется, вероятно, использовать mult_gen_0 в режиме умножителя и суммировать результаты умножения во внешнем аккумуляторе.

Можно также аккумулировать в DSP48 частичные произведения, а результаты потом складывать внешним сумматором.
анатолий
Цитата(count_enable @ Feb 18 2016, 11:18) *
Корень посчитаю кордиком, сумму аккумулятором, а вот как быть с квадратом разницы?

Если кордиком - так лучше и сам модуль вектора считать кордиком. Это больше десятка тактов.
А если считать на DSP48, то и корень лучше считать быстро.
Например, как в: http://kanyevsky.kpi.ua/GEN_MODUL/SQRT/index.php
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.