Простое устройство для решения задачи Пифагора
Анатолий Сергиенко
Задачи на теорему Пифагора каждый решал еще в школе. В технике эту задачу тоже надо решать и, нередко, миллионы раз в секунду. При управлении подвижными объектами( роботами, станками ) с ее помощью необходимо в реальном времени вычислять координаты точек в пространстве, и в таком случае эту задачу решают очень точно. В цифровой обработке сигналов задачу Пифагора называют вычислением модуля комплексного числа. Уровень шума ошибки вычислений не должен превосходить определенной границы, обеспечивая должное отношение сигнал-шум.
При распознавании образов, реализации алгоритма Витерби и других алгоритмов необходимо быстро и многократно вычислять расстояние между двумя точками в пространстве — так называемое Эвклидово расстояние. Это тоже сводится к решению задачи Пифагора.
Решение задачи Пифагора получают с помощью формулы: С = SQRT(A*A+B*B). С достаточно высокой точностью и квадрат числа, и квадратный корень можно вычислить на устройствах, описанных в заметкe Вычисление квадрата . Но для этого потребуются, возможно, чрезмерные аппаратурные затраты и длинная задержка вычислений. Следует отметить, что квадратный корень необходимо брать из числа с удвоенной разрядностью, а это в 4 раза сложнее, чем с одинарной разрядностью. Так, для обработки 12- разрядных данных получится схема из более чем 2000 LUT, с задержкой до 500 нс при реализации на Xilinx XS4000.
Во многих практических случаях достаточно невысокой точности вычислений, порядка 1-5%. Например, для оценки уровня сигнала, его визуального отображения, при вычислении дистанции до ближайших точек в пространстве, что необходимо при распознавании образов и т.д.
При этом не так важна относительная погрешность результатов, как расчеты в большом динамическом диапазоне, т.е. для относительно большой разрядности входных чисел. В этом случае в выигрыше будут устройства, реализующие прямую аппроксимацию формулы Пифагора.
Французский математик-механик Понселе был первым, кто предложил наилучшее линейное приближение формулы Пифагора в 1828 году.
Если A > 0, B > 0, A > B , то С ≈ 0.9605*A + 0.3978*B ,
а если A > 2B , то С ≈ 0.9859*A + 0.2327*B и т.д.
В первом случае обеспечивается точность 4%, а во втором — 1.4% [1].
Коэффициенты можно приближенно представить двоичным кодом как
0.9605 ≈ 0.11110110 и 0.3978 ≈ 0.0110110,
и можно представить суммой весов соответствующих разрядов, т.е.
0.11110110 = 1 — 1/16 +1/64 +1/128 и 0.0110110 = 1/4 + 1/8 +1/64 +1/128.
Вычисление результата С можно проще всего реализовать, заменив умножение на эти константы рядом сложений сдвинутых вправо операндов |A | и |B |, где |A | — абсолютное значение числа А со знаком. Количество сложений зависит от точности представления этих констант. Для вычислений с почти максимальной точностью формула будет выглядеть так:
С ≈ |A|-1/16*|A| +1/64*|A| +1/128*|A| +1/4*|B| +1/8*|B| +1/64*|B| +1/128*|B|,
где, например, коэффициент 1/16 взят из разложения множителя и умножение на него реализуется сдвигом вправо на 4 разряда.
Функция вычисления модуля комплексного числа A+jB на языке VHDL выглядит следующим образом.
function MAGN_APPROX(A,B: INTEGER) return INTEGER is variable Re,Im,Tmp,Magn: INTEGER; begin Re:=ABS(A); Im:=ABS(B); if Re < Im then Tmp:=Re; Re:=Im;Im:=Tmp; end if; Magn:=Re - Re/16+Re/64+Re/128+ Im/4 +Im/8+Im/64+Im/128; return Magn; end function;
В ней вначале вычисляются абсолютные значения операндов и переставляются операнды так, чтобы комплексная точка находилась в нулевом октанте, т.е. чтобы |A|>|B|. Затем вычисляется форула Понселе.
Таким образом, вычисляя эту функцию, мы аппроксимируем радиус окружности радиусом некоторой кривой замкнутой линии, показанной на рис.1.

аппроксимираванная формулой Понселе (вишневая).
При реализации этого алгоритма в структуре можно использовать различное количество слагаемых в разложении коэффициентов. Естественно, это даст различные точность результатов и аппаратурные затраты полученных стрктур. Но среди этих структур можно выбрать оптимальную. Возможности VHDL обеспечивают несложную реализацию такой оптимизации структуры.
В таблице 1 приведены точностные параметры и некоторые результаты синтеза архитектуры MAGNITUDE синтезатором SYNPLIFY для ПЛИС серии XC4000XLA-0.7 для 12 - разрядных входных данных. Полученная модель устройства вычисления модуля комплексных чисел MAGNITUDE приведена в приложении 1.
Испытательный стенд для этого устройства приведен в приложении 2.
С помощью него и были получены параметры точности в таблице 1.
Таблица 1.
| № | Разложение коэффициентов функции Понселе | Макси- мальная ошибка Ммакс | Мат- ожи- дание М | Средне- квадра- тичное откло- нение q | Аппа- ратные затраты, LUT | Период вычис- лений, нс | 
| 1 | 1 или 1/4 | -232 | -13 | 85 | 71 | 26 | 
| 2 | 1 или 1/4 + 1/8 | 139 | 81 | 53 | 81 | 29 | 
| 3 | 1 -1/16 и 1/4 + 1/8 | -147 | -33 | 47 | 93 | 29 | 
| 4 | 1 -1/32 и 1/4 + 1/8 | -103 | 24 | 49 | 91 | 29 | 
| 5 | 1-1/16 +1/64 и 1/4+1/8+1/64 | -89 | 6 | 48 | 106 | 32 | 
| 6 | 1 -1/16 +1/64 +1/128 и 1/4 +1/8 +1/64 +1/128 | 82 | 25 | 48 | 122 | 33 | 
Изучая таблицу 1, можно сделать следующие выводы.
Во-первых, то, что максимальная ошибка Ммакс действительно довольно точно выражается через матожидание и среднеквадратическое отклонение Ммакс =M±SQRT(3)*q, как было упомянуто в заметке О пользе округления .
Во-вторых, повышение точности разложения коэффициентов с определенной грани не ведет к повышению точности результатов, так как нельзя получить точность выше методической точности формулы Понселе.
В-третьих, при сравнении вариантов 5 и 6 оказывается, что более грубое разложение коэффициентов иногда может дать более высокую точность.
В-четвертых, варьирование разрядов в битовом разложении коэффициентов может дать дополнительное повышение точности, как в вариантах 3 и 4.
Результирующий вывод тот, что оптимальным по точности и аппаратным затратам является 4-й вариант, который и реализован в окончательной модели устройства.
В таблице 2 показаны некоторые результаты синтеза архитектуры MAGNITUDE синтезатором SYNPLIFY для ПЛИС серии XC4000XLA-0.7 для различной разрядности входных данных.
Таблица 2.
| Разрядность, n | 8 | 12 | 16 | 20 | 24 | 28 | 31 | 
| Аппаратные затраты, LUT | 60 | 91 | 151 | 170 | 237 | 259 | 283 | 
| Период вычислений, нс | 27 | 29 | 34 | 33 | 35 | 38 | 42 | 
Синтезированная структура показана на рис. 2. На рисунке не показаны регистры, включенные на входе и выходе устройства для обеспечения высокой пропускной способности и повторяемости при реализации в ПЛИС. Устройство имеет 3 уровня логики.
Первый уровень составляют схемы получения абсолютного значения чисел А и В, второй уровень - схема перестановки с выбором максимального числа, третий уровень-четырехвходовый сумматор для вычисления формулы Понселе.

нарисованная с помощью Synplify.
Кое-что об испытательном стенде MAGN_TST
Он основан на сравнении результатов вычислений испытуемого устройства MAGNITUDE(MAGN_APPROX) с результатами эталонного устройства MAGNITUDEE(MAGN_EXACT).
Эталонное устройство, описанное в Приложении 2, вычисляет формулу Пифагора достаточно точно с плавающей запятой используя функцию SQRT пакета IEEE.math_real.
Исходные данные выдаются генератором синусов и косинусов, описанного в заметке Управляемый генератор синусоидальных сигналов . Таким образом, на входы двух устройств U2, U3 с одинаковыми интерфейсами, заданными компонентом MAGNITUDE, подается комплексный вектор М(cos+J*sin), конец которого описывает окружность радиусом М.
Процесс ERR вычисляет параметры ошибок вычислений равных разности результатов с выходов устройств U2, U3.
Варианты архитектур MAGNITUDE(MAGN_APPROX), MAGNITUDEE(MAGN_EXACT) назначаются экземлярам U2, U3 устройства MAGNITUDE с помощью конфигурации configuration MAGN_CONFIG of MAGN_TST.
Здесь показан пример того, как конфигурация позволяет привязать к экземплярам блоков с одинаковым названием их конкретные реализации в виде различных архитектур, даже если эти архитектуры уже странслированы в библиотеку проекта.
Устройство вычисляет функцию за 1 такт. Но это не всегда удобно. Часто реальная и мнимая части поступают по одной шине в четных и нечетных тактах. Когда-то автор получил авторское свидетельство на изобретение: "Устройство для вычисления функции √Y2 + X2 " А.С. СССР N 1541601, которое вычисляет формулу Понселе именно таким образом.
Так что в этом случае можно просто описать формулу этого изобретения на VHDL.
Как заключение, перечислим следующие выводы.
- Приведена вычислительная схема для вычисления модуля вектора или комплексного числа с минимальными аппаратурными затратами, высоким быстродействием и большим динамическим диапазоном.
- Минимальные затраты получены во-первых, благодаря линейной аппроксимации формулы Пифагора, во-вторых, путем замены умножения на коэффициенты операцией сложения сдвинутых слагаемых, в третьих, подбором оптимального числа сдвинутых слагаемых и количества разрядов сдвига.
- Ошибка вычислений составляет приблизительно 4%, но может быть уменьшена путем увеличения числа участков аппроксимации. Так, при разделении круга не на 8, а на 16 секторов, ошибку можно уменьшить до 1.5 %.
- Использование такого объекта VHDL, как конфигурация, позволяет привязать к экземплярам блоков с одинаковым названием их конкретные реализации в виде различных архитектур.
Литература
1. Люстерник Л.А., Червоненкис О.А., Янпольский А.Р. Справочная математическая библиотека. Математический анализ. Вычисление элементарных функций. -М.: Физматгиз. -1963. -247с.
Приложение 1.
--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--  FILE: MAGNITUDE.VHD
--  PROJECT: Unit for complex number magnitude calculating
--  AUTHOR:  Anatoli Sergyienko	
--  Created:	   09/02/00 
--  Email:   aser@comsys.kpi.ua
--
--  FUNCTION:  synthesable model for
--   calculating the complex number magnitude  
--  	from the positive integer data.
--  CONSTRAINTS: the integer input data A,B  is in range:
--  -2**n to 2**n-1, 
--     where n<31 is generic, complex number (A+jB).  
--    the output data is equal to  SQRT(A*A* + B*B).  
--   Mean error is +0.011,  sigma is +-0.024.
--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
library IEEE;
use IEEE.std_logic_1164.all;
entity MAGNITUDE is
   generic(n: integer:=11);
   port (
	CLK: in BIT;
	RESET: in BIT;
	A: in INTEGER range -2**n to 2**n-1;
	B: in INTEGER range  -2**n to 2**n-1;
	C: inout INTEGER range  0 to -1+2**n+ 2**n
	);
end MAGNITUDE;
architecture MAGN_APPROX of MAGNITUDE is
    signal X,Y : integer range -2**n to 2**n-1;
    function MAGN_APPROX(A,B: INTEGER) return INTEGER is
    variable Re,Im,Tmp,t1,t2:Natural range 0 to 2**n-1;
    variable Magn: NATURAL range 0 to 2**n+ 2**n-1;
    begin
    Re:=abs(A);
    Im:=abs(B);
	if Re < Im then 
		Tmp:=Re; Re:=Im; Im:=Tmp;--data swap
	end if;	
	Magn:=Re - Re/32+ Im/4 +Im/8;
	return Magn;
    end function;
	begin
	process(CLK,RESET)
		begin
		if RESET='1'  then
			X<=0;
			Y<=0;
			C<=0;
		elsif CLK='1' and CLK'event then
			X<=A;
			Y<=B;
			C<=MAGN_APPROX(X,Y);
		end if;
	end process;
end MAGN_APPROX;
Приложение 2.
--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--  FILE: MAGN_TST.VHD
--  PROJECT: Unit for complex number magnitude calculating
--  AUTHOR:       Anatoli Sergyienko	
--  Created: 		   09/02/00 
--  Email:        aser@comsys.kpi.ua
--
--  FUNCTION: - testbench for synthesable model for
--   calculating the complex number magnitude  
--  	from the positive integer data.
--  CONSTRAINTS: the integer input data A,B  is in range:
--   -2**n to 2**n-1, where n<31 is 
--	generic complex number (A+jB).
--    the output data is equal to  SQRT(A*A* + B*B).  
--   Mean error is +0.011,  sigma is +-0.024.
--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.math_real.all;
entity MAGNITUDEE is
	port (CLK: in BIT;
		RESET: in BIT;
		A: in INTEGER;
		B: in INTEGER;
		C: inout INTEGER);
end MAGNITUDEE;
architecture MAGN_EXACT of MAGNITUDEE is
	signal Xi,Yi : integer	;
	function MAGN_EXACT(A,B: INTEGER) return INTEGER is
		variable Re,Im: REAL;
		variable Magn: INTEGER;
		begin	
		Re:=REAL(A);
		Im:=REAL(B);
		Magn:=INTEGER(ROUND(SQRT(Re*Re+Im*Im)));
		return Magn;
	end;
	begin
	process(CLK,RESET)
		begin
		if RESET='1'  then
			Xi<=0;
			Yi<=0;
			C<=0;
		elsif CLK='1' and CLK'event then
			Xi <=A;
			Yi <=B;
			C<= MAGN_EXACT(Xi,Yi);
		end if;
       end process;
end MAGN_EXACT;			 
Library	IEEE;
use IEEE.math_real.all;
entity MAGN_TST is
end MAGN_TST;
architecture MAGN_TST of MAGN_TST is
	signal CLK,RESET:BIT;	
	signal mean,SUM,QSUM,M_exact,M_approx: INTEGER;
	signal error,SIN,COS,X,Y,FNUM,FDEN: INTEGER;
	signal  LMS:REAL ;
	signal i: INTEGER:=-3;	
	component  SINGEN is
		Port(CLK : in BIT;
			FNUM: in INTEGER;
			FDEN: in INTEGER;
			RESET: in BIT;
			SIN : inout INTEGER;
			COS : inout INTEGER);
	end component SINGEN;
	component MAGNITUDE is
		port (
			CLK: in BIT;
			RESET: in BIT;
			A: in INTEGER ;
			B: in INTEGER ;
			C: inout INTEGER
			);
	end component  MAGNITUDE;			  
	begin
	CLK_GEN:process	  begin
		CLK<=not CLK ;
		wait for 50 ns;  
	end process;
	INDEX_GEN:process	(CLK)	begin
		if CLK='1' and CLK'event then
			i<=i+1;
		end if;	
	end process;
	RESET<='1' after 0 ns, '0' after 50 ns;
	FNUM<=0 after 0 ns, 1 after 50 ns;
	FDEN<=0 after 0 ns, 1024 after 50 ns;	
	U1:SINGEN	port map(CLK =>CLK,
		FNUM=>FNUM,
		FDEN=>FDEN,
		RESET=>RESET,
		SIN =>SIN,
		COS =>COS);
	X<=COS/16;
	Y<=SIN/16;	   
	U2:	MAGNITUDE	port map (
		CLK=>CLK,
		RESET=>RESET,
		A=>X,
		B=>Y,
		C=>M_exact	);	 
	U3:	MAGNITUDE	port map (
		CLK=>CLK,
		RESET=>RESET,
		A=>X,
		B=>Y,
		C=>M_approx	);
	error<= M_approx - M_exact;
	err:process(CLK,i,RESET)
		begin
		if i=-1 or RESET ='1' then 
                  SUM<=0; QSUM<=0; LMS<=0.0;
		elsif CLK='1' and CLK'event
                  then SUM<=SUM+error; 
                  if i =1024 then	 
			Mean<=SUM/i;
			end if;
		if i >1024 then 
		  QSUM<=QSUM+(error-Mean)*(error-Mean);
		  LMS<= SQRT(REAL(QSUM/(i-1024)));
		end if;
           end if ;
     end process;
end MAGN_TST;
configuration MAGN_CONFIG of MAGN_TST is
	for MAGN_TST
		for U2 : MAGNITUDE 
			use entity  MAGNITUDEE(MAGN_EXACT);
		end for;
		for U3 : MAGNITUDE 
			use entity  MAGNITUDE(MAGN_APPROX);
	   	end for;
	end for;
end MAGN_CONFIG;