Перейти к содержанию
    

Blackfin перенести ассемблер на си

Есть рабочий проект для blackfin. Некоторые функции написаны на ассемблере. Хочу перенести код на ARM.

Blackfin у нас работает на частоте 60 Мгц, по предварительным наработкам получается что ARM на частоте 180 МГц дает примерно такую же производительность.

 Для начала хочу в blackfin убрать ассемблерные функции, чтобы там все работало на си.

В функции ниже в регистр R1 считывается элемент из входного массива. В регистр R5 считываются константы из таблицы синуса из 128 элементов.

Цикл выполняется 128 раз. С моими тестовыми данными после десятка итараций R6 достигает насыщения 0x7FFF FFFF и затем перестает меняться.

Даже если к A0 прибавляется отрицательный результат умножения R1*R5. Мне это кажется странным. Но это рабочий код.

start_sine_loop:
		r6 = (a0 += r1.l * r5.l) || i0 +=m0 || r5.l = w[i0];				
		
finish_sine_loop:	r1 =w[p0++] (z);

 

Код на си: Я рассчитываю получить в tmp_in тоже самое что в R6. Но результат совпадает только если отсчеты входного сигнала были с маленькой амплитудой.

Почему-то сишный код не обеспечивает насыщение. Как на си записать эквивалент ассемблерного кода?

for (j = 0, i = 0; i < 128; i++, j+= tone)
{
    tmp_im = L_mac(tmp_im, sig_buf[i], dft_sine[j % 128]);    	    	    	    
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

СИ сам по себе вроде бы ничего не знает про арифметику с насыщением, так что правильный ответ - ассемблерными вставками / интринсиками.

а ещё если и целевая платформа не умеет в операции с насыщением, ну тогда проверяйте руками переполнение и если да, то меняйте результат на 0x7fffffff.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

L_mac это встроенная в vdsp функция умножения с насыщением. Истинная проблема оказалась не в этом.

После разгона ядра до 180 МГц сишный код стал нормально работать в железке с реальным сигналом без насыщения.

Ассемблерный работает на частоте 60 МГц. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вы кодогенерацию посмотрите (и нам покажите) - что там компилятор рожает. Сразу будут понятны причины и следствия.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

#pragma optimize_for_speed
//------------------------------------------------------------------------------------------
int32_t	e1dft_l(int16_t sig_buf[], int16_t tone)
{	
[FFA0C704] LINK 0x0 ;
[FFA0C708] [ -- SP ] = ( R7:4 , P5:4 ) ;
[FFA0C70A] R3.L = 0 ;
[FFA0C716] P1 = R0 ;
[FFA0C718] R2 = R1 + R3 ;
[FFA0C71A] P2.L = 0x20cc ;
[FFA0C71E] P2.H = 0xff80 ;
    uint16_t i, j;
    
	fract32 tmp_im, tmp_re, dft_sq, tmp_mult;
	fract16 r_im, r_re;    
        
    tmp_im = 0;
	for (j = 0, i = 0; i < 128; i++, j+= tone)
	{
	    tmp_im = L_mac(tmp_im, (fract16)sig_buf[i], (fract16)dft_sine[j % 128]);    	    	    
[FFA0C70E] R5 = 127 ( X ) ;
[FFA0C712] R2 = R3 & R5 ;
[FFA0C714] P0 = R2 ;
[FFA0C722] R0 = R2 & R5 ;
[FFA0C724] P4 = P2 + ( P0 << 1 ) ;
[FFA0C726] P0 = R0 ;
[FFA0C728] I0 = P1 ;
[FFA0C72A] A0 = 0  || R0 = W [ P4 ] ( X )  || NOP ;
[FFA0C732] R2.L = R1.L + R2.L ( NS )  || R3 = W [ P1 ++ ] ( X )  || NOP ;
[FFA0C73A] P5 = 63 ;
[FFA0C744] LSETUP ( __BEGIN__.P59L64L , 40 /*0xFFA0C76C*/ ) LC0 = P5 ;
[FFA0C748] R3 = R2 & R5 ;
[FFA0C74A] P5 = R3 ;
[FFA0C74C] P0 = P2 + ( P0 << 1 ) ;
[FFA0C74E] R2.L = R1.L + R2.L ( NS )  || R3 = W [ P0 ] ( X )  || NOP ;
[FFA0C75E] R0 = R2 & R5 ;
[FFA0C760] P0 = R0 ;
[FFA0C762] P5 = P2 + ( P5 << 1 ) ;
[FFA0C764] R2.L = R1.L + R2.L ( NS )  || R0 = W [ P5 ] ( X )  || NOP ;
[FFA0C774] R3 = 32 ;
[FFA0C77A] P0 = P2 + ( P0 << 1 ) ;
[FFA0C77C] R6.L = R1.L + R3.L ( NS )  || R2 = W [ P0 ] ( X )  || R3.L = W [ I0 ++ ] ;
	}	

	r_im = round(tmp_im);

    tmp_re = 0;
	for (j = 32,i = 0; i < 128; i++, j+= tone)
	{	    
	    tmp_re = L_mac(tmp_re, (fract16)sig_buf[i], (fract16)dft_sine[j % 128]);    
[FFA0C776] R2 = R3 & R5 ;
[FFA0C778] P1 = R2 ;
[FFA0C784] R7 = R6 & R5 ;
[FFA0C78A] P1 = P2 + ( P1 << 1 ) ;
[FFA0C78C] P0 = R7 ;
[FFA0C790] A0 = 0  || R2 = W [ P1 ] ( X )  || NOP ;
[FFA0C798] P1 = 63 ;
[FFA0C79A] R4.L = R1.L + R6.L ( NS )  || R6.L = W [ I0 ++ ]  || NOP ;
[FFA0C7AA] LSETUP ( __BEGIN__.P59L60L , 40 /*0xFFA0C7D2*/ ) LC0 = P1 ;
[FFA0C7AE] R0 = R4 & R5 ;
[FFA0C7B0] P1 = R0 ;
[FFA0C7B2] P0 = P2 + ( P0 << 1 ) ;
[FFA0C7B4] R2.L = R1.L + R4.L ( NS )  || R0 = W [ P0 ] ( X )  || NOP ;
[FFA0C7C4] R0 = R2 & R5 ;
[FFA0C7C6] P0 = R0 ;
[FFA0C7C8] P1 = P2 + ( P1 << 1 ) ;
[FFA0C7CA] R4.L = R1.L + R2.L ( NS )  || R0 = W [ P1 ] ( X )  || NOP ;
[FFA0C7DA] P1 = P2 + ( P0 << 1 ) ;
[FFA0C7DC] R1 = R1.H * R1.H  || R0 = W [ P1 ] ( X )  || NOP ;
	    
	}	
	
	r_re = round(tmp_re);
	
	dft_sq = L_mult(r_im, r_im);
	dft_sq = L_mac(dft_sq, r_re, r_re);
	
	return (dft_sq);
[FFA0C7F2] ( R7:4 , P5:4 ) = [ SP ++ ] ;
[FFA0C7F8] UNLINK ;
}

Вот вся проблемная функция в режиме mixed

 

А ниже рукописный ассемблерный аналог

.section L1_code;
.global _e1dft_l;

.align 2;
.extern _dft_sine;
_e1dft_l:

	link 0;
	[--sp] = (r7:5, p5:5);
	
	[--sp] = lc0;
	[--sp] = lb0;
	[--sp] = lt0;		
	

					// r0 - addres inpute buffer
					// r1 - number tone
	i0.l	= lo(_dft_sine);
	i0.h	= hi(_dft_sine);	
	b0		= i0;
	p0 	= r0;
	p5	= 256;	// 256 = 128 * 2byte
	l0	= p5;
	p5	= 128;
	r1 <<= 1;
	m0 	= r1;	// m0 = r1 = STR_TON or STR_TON_PRM

#if defined(__WORKAROUND_BF532_ANOMALY_050000245)
        NOP;
        NOP;
#endif
	r1 = w[p0++] (z);	// R1 has value of first element in buffer
	a0 = 0;	
	nop;
	r5.l	= w[i0];	// R5 addres of dft_sine table
	i0 +=m0;
	
	lsetup (start_sine_loop, finish_sine_loop)	lc0 = p5;
	
start_sine_loop:
	r6 = (a0 += r1.l * r5.l) || i0 +=m0 ||	r5.l = w[i0];				
finish_sine_loop:	r1 =w[p0++] (z);	// read next item from buffer into R1

	
	
	p0 	= r0;	// P0 is pointer to first buffer element
	p5	= 128;
	i0	= b0;	// i0 is address of dft_sine table
	m1	= 64;
	i0 	+= m1;	//(32*2);	// ( 128 / 4 ) * 2
	r5.l= w[i0];	// r5.l load first elemnt of dft_sine table in r5.l
	i0 +=m0;
	a1 	= 0;	
	r1 	= w[p0++] (z);	// load first buffer elemnt in r1
	
	lsetup (start_cosine_loop, finish_cosine_loop)	lc0 = p5;
	
start_cosine_loop:
	r7 = (a1 += r1.l * r5.l) || i0 +=m0 || r5.l = w[i0];
finish_cosine_loop:	r1 =w[p0++] (z);


	r1.l= r7(rnd);	
	r5.l= r6 (rnd);
	a0 	= r1.l * r1.l;
	r0	= (a0 += r5.l * r5.l);	

	l0 = 0;
	
	lt0 = [sp++];					// modified		
	lb0 = [sp++];
	lc0 = [sp++];	
	
	(r7:5, p5:5) = [sp++];
	p0 = [fp + 4];
	unlink;	
	jump (p0);

._e1dft_l.end:

Мне уже кажется плохой идея перенести это на ARM

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 часа назад, uriy сказал:

Blackfin у нас работает на частоте 60 Мгц, по предварительным наработкам получается что ARM на частоте 180 МГц дает примерно такую же производительность.

 


start_sine_loop:
		r6 = (a0 += r1.l * r5.l) || i0 +=m0 || r5.l = w[i0];				
		
finish_sine_loop:	r1 =w[p0++] (z);

 

Я не знаком с системой команд blackfin, но насколько я понимаю указанный цикл должен тратить 2 такта на одну итерацию цикла?

Если так, то непонятно какие "наработки" позволили сделать заключение, что ARM-овский аналог этого цикла можно уложить в 6 тактов?  (180/60*2такта)

В этом цикле две выборки из памяти (по разным указателям, которые нельзя объединить в одно чтение одной командой ARM), а это уже 2*2 такта ARM;

также для ARM потребуется как минимум 1 такт на модификацию переменной цикла и 1такт + (0...x)тактов на prefetch при выполнении условного перехода в конце цикла. Итого - уже 6 тактов. А ведь нужно ещё собственно и саму MAC-операцию выполнить и нужно сделать i0+=m0 (если конечно m0 нельзя свести к константе), и операцию насыщения....

 

Хотя конечно если расположить оба чтения памяти подряд, то они возможно они будут pipelined и дадут не 4, а только 3 такта. И попробовать развернуть эти циклы (раз там всего 128 итераций) в линейный код, убрав счётчики цикла и команды перехода (код тогда придётся расположить в ОЗУ; или выбрать МК с широкой шиной (256 бит) к флеш-памяти). Может только так что-то и получится. Но о си тут уже никакой речи не идёт - Вам надо глубоко изучать систему команд ARM и писать на ассемблере.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Под наработками имелось ввиду другие проекты. Вокодер MELP2400 на блэкфине на 60 МГц и на STM32F446 на 180 МГц примерно одинаково нагружают процессор.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, uriy сказал:

Под наработками имелось ввиду другие проекты. Вокодер MELP2400 на блэкфине на 60 МГц и на STM32F446 на 180 МГц примерно одинаково нагружают процессор.

Это не значит, что они имеют одинаковый алгоритм. Там скорее всего код разный, выполняющий одинаковые функции. И ARM там может догонять за счёт бОльшей разрядности операций и изменённого алгоритма.

А Вы тут хотите переделать ассемблерный код один-в-один, не меняя алгоритма. Тогда придётся брать бОльший коэфф. запаса.

В Вашем случае можно попробовать подумать ещё над тем чтобы в таких циклах сделать пакетное чтение одной командой нескольких значений в группу регистров (например 4 регистра), а потом за одну итерацию выполнить действия 4 итераций DSP-кода. Так получится уже только 1+4+1+4 тактов на все операции чтения для такой счетверённой итерации и затраты на переходы в 4 раза меньше. Для этого возможно нужно перераспределить данные в памяти более удобно. Ну т.е. - без переработки алгоритма не обойтись.

Цитата

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

Не только. На DSP чтение/запись в память можно параллелить с операциями в АЛУ не тратя таким образом на них ни такта лишнего. А в ARM каждая отдельная команда обращения к памяти - 2 такта. Ну и другие плюшки тож.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...