Jump to content

    

CLPRU -TI PRUSS C Compiler v.2

Начал писать код для ПРУССов (PRUSS: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem) - хорошие славные малые целочисленные RISC-процессоры.

Использую C Compiler от TI для PRUSS: clpru v.2.

Ставлю самую сильную оптимизацию  -O4 -Speed=5.

Проблема:  на выходе АСМ-листинг крайне низкого качества.

 

Особо убило, что делаю умножение  - с помощью сдвигов и сложений - так:

register unsigned int P;
register unsigned int L=(P<<4)+(P<<1)+P;

Компилятор распознаёт умножение и делает CALL на RTL-овскую функцию умножения - что НЕ ДОПУСТИМО  в  попиксельной обработке данных:

JAL ___abi_MUL19 r, r, r

Причем выключить это никак не представляется возможным, но я обошёл это так:

register unsigned int L=(P<<4);
L+=(P<<1);
L+=P;

Это глюк номер 1.

 

Глюк номер 2:  неоптимальное ассемблирование.

Например - клампинг по 0 и 255 делаю тернарными операторами:

P=(P<0)?0:((P>255)?255:P);  //суть этого: if(P<0)P=0; else if(P>255)P=255;

Ассемблирование проходит с большим треском:  вместо того чтобы использовать инструкции MIN и MAX, компилятор делает условия и переходы на метки, что снижает производительность.

Причем результат зависит от фазы Луны - достаточно поменять порядок вычислений или алгоритм - частично начинает применять MIN, MAX.

 

 

Глюк номер 3:

инлайнит функции без указания inline,  инит регистров задвигает в конец - в результате ничего не работает.

если сделать цикл не метками,  а while(1),  то неоптимально делает переходы.

 

Резюмируя,  качеством компилятора я не доволен. 

 

На PRUSS крутится такой алгоритм:   конверсия кадра из YUV в RGB с посылкой в LCD:

Spoiler

#include "Type.h"

#define LCD8  (*(IO  u8*)0x60004000)
#define LCD16 (*(IO u16*)0x60004000)

#define W2 200
#define H2 120

#define src_ystride  496
#define src_uvstride 248

/*
Параметры функции
*/
#pragma LOCATION(y,0x0000)
#pragma LOCATION(u,0x0004)
#pragma LOCATION(v,0x0008)
#pragma LOCATION(e,0x000C)
IO u16 *y;
IO s8  *u;
IO s8  *v;
IO u32 e=0;

#define Y0 ((s8) Y    )
#define Y1 ((s8)(Y>>8))

#define R ((u8)(V+G))
#define B ((u8)(U+G))

void YUV2RGB(void)
{
 register u16 *yy=(u16*)y;
 register s8 *uu=(s8*)u;
 register s8 *vv=(s8*)v;

 for(register u32 j=0;j<H2;j++)
 {
  for(register u32 k=0;k<2;k++)
  {
   for(register u32 i=0;i<W2;i++)
   {
    register u16 Y=*yy++;

    register s8 U=(*uu++)^128;
    register s8 V=(*vv++)^128;

    register s8 UV=(U+V)>>2;

    register u8 G=(u8)(Y0-UV);

/*
    LCD8=(R&0xF8)|(G>>5);
    LCD8=((G>>2)<<5)|(B>>3);
*/
    LCD16=((((G>>2)<<5)|(B>>3))<<8)|(R&0xF8)|(G>>5);

    G=(u8)(Y1-UV);

/*
    LCD8=(R&0xF8)|(G>>5);
    LCD8=((G>>2)<<5)|(B>>3);
*/
    LCD16=((((G>>2)<<5)|(B>>3))<<8)|(R&0xF8)|(G>>5);

   }
   yy+=(src_ystride/2)-W2;
   uu-=W2;
   vv-=W2;
  }
  uu+=src_uvstride;
  vv+=src_uvstride;
 }
}

void main(void)
{
 Loop:

 if(!e)goto Loop;

 YUV2RGB();

 e=0;

 goto Loop;
}

 

 

Видео работы декодера H264 + MP3 : https://www.youtube.com/watch?v=hmlP_sZ4cPY

 

Есть ли способ этот алгоритм ещё ускорить?

С PRUSS декод MP4 идёт на 38 FPS (это со считыванием с SD карты по SPI,  400x240 разрешение).   Без PRUSS всего 29 FPS.  Функция отрисовки та же.

Share this post


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

Есть ли способ этот алгоритм ещё ускорить?

Так не используйте его (си). Пишите на ассемблере. Иначе все ваши ухищрения будут - пляски с бубном.

Я даже на ARM-е такие места пишу на ассемблере. Даже продвинутые IAR-ы и подобные им не создадут кода лучше, чем человек, знающий алгоритм.

36 минут назад, __inline__ сказал:

С PRUSS декод MP4 идёт на 38 FPS (это со считыванием с SD карты по SPI,  400x240 разрешение).   Без PRUSS всего 29 FPS.  Функция отрисовки та же.

Прикольно  :wink2:

Вижу что проект - хоббийный, для своего удовольствия.  :wink2:

Share this post


Link to post
Share on other sites
50 minutes ago, __inline__ said:

Начал писать код для ПРУССо

Может еще и Risc V попробуем? ;)

Вообще у Техаса эта подсистема ровно для того же, что и C64 в их DM386 соках - для того же, для чего и собаке пятая нога. То ли со скуки занять место на кристалле решили не убирать. Мало того что док нету, так и передать и забрать все параметры будет по времени дольше, чем на основном проце посчитать. А все это становится еще более странным у процев со встроенным приличным видеоускорителем, который на этих задачах порвал бы все, да только вот описания к нему вы не найдете, строжайшее NDA. Так и продают процы с кучей наворотов, из которых руководствуясь их даташитам как ни крути снова распберри пи выходит (точно такое же уродство по закрытости документов)

20 minutes ago, jcxz said:

им не создадут кода лучше, чем человек, знающий алгоритм.

держите в голове буфера предсказаний условным ветвлений, все политики сбросов кешей, спекулятивные исполнения, да? Класс. Я так не умею. Я лучше компилятору это поручу. FPGA в блокнотике карандашиком не пробовали разводить?

Share this post


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

держите в голове буфера предсказаний условным ветвлений, все политики сбросов кешей, спекулятивные исполнения, да?

Вы о чём вообще? Тема про PRUSS, а не про x86. И не про FPGA тем более...

Share this post


Link to post
Share on other sites
4 minutes ago, jcxz said:

Вы о чём вообще? Тема про PRUSS, а не про x86. И не про FPGA тем более...

да так

Share this post


Link to post
Share on other sites

Удалось поднять производительность отрисовки PRUSS на 22,6 %.

 Увеличение получено с помощью копирования компонент Y,U,V каждой строки из дальней памяти во внутреннюю память PRUSS'а.

Копирую блоками по 8 байт, хотя LBB0,SBB0 позволяют до 124 байт скопировать - но выше 8 байт бурст делать нет смысла - на производительность уже не влияет.

Ниже усовершенствованный код:

Spoiler

#include "Type.h"

#define LCD (*(IO u8*)0x60004000)

#define W2 200
#define H2 120

#define YS  496
#define UVS 248

#define Y0 ((s8) Y    )
#define Y1 ((s8)(Y>>8))

#define R0 ((u8)(V+G0))
#define R1 ((u8)(V+G1))

#define B0 ((u8)(U+G0))
#define B1 ((u8)(U+G1))

#pragma LOCATION(y,0x0000)
#pragma LOCATION(u,0x0004)
#pragma LOCATION(v,0x0008)
#pragma LOCATION(e,0x000C)

IO u16 *y;
IO s8  *u;
IO s8  *v;
IO u32 e=0;

#pragma DATA_SECTION(YM,".ram1")
#pragma DATA_ALIGN(YM,8)
#pragma DATA_ALIGN(UM,8)
#pragma DATA_ALIGN(VM,8)

u16 YM[W2];
s8  UM[W2];
s8  VM[W2];

void YUV2RGB(void)
{
 register u16 *yy=(u16*)y;
 register s8  *uu=(s8*)u;
 register s8  *vv=(s8*)v;

 for(register u32 j=0;j<H2;j++)
 {

  for(register u32 i=0;i<W2;i+=8)
  {
   *(u64*)(UM+i)=(*(u64*)(uu+i))^0x8080808080808080ULL;
   *(u64*)(VM+i)=(*(u64*)(vv+i))^0x8080808080808080ULL;
  }

  for(register u32 k=0;k<2;k++)
  {

   for(register u32 i=0;i<W2;i+=4)*(u64*)(YM+i)=(*(u64*)(yy+i));

   for(register u32 i=0;i<W2;i++)
   {
    register u16 Y=YM[i];
    register s8  U=UM[i];
    register s8  V=VM[i];

    register s8 UV=(U+V)>>2;

    register u8 G0=(u8)(Y0-UV);
    register u8 G1=(u8)(Y1-UV);

    LCD=(R0&0xF8)|(G0>>5);
    LCD=((G0>>2)<<5)|(B0>>3);

    LCD=(R1&0xF8)|(G1>>5);
    LCD=((G1>>2)<<5)|(B1>>3);
   }

   yy+=YS>>1;

  }

  uu+=UVS;
  vv+=UVS;

 }
}

void main(void)
{
 Loop:

 if(!e)goto Loop;

 YUV2RGB();

 e=0;

 goto Loop;
}

 

 

Edited by __inline__

Share this post


Link to post
Share on other sites
21 hours ago, jcxz said:

Я даже на ARM-е такие места пишу на ассемблере. Даже продвинутые IAR-ы и подобные им не создадут кода лучше, чем человек, знающий алгоритм

Я не настолько хорошо знаю ассемблер PRUSS'ов,  проще наверное получить асм-листинг и потом с ним работать. И всё-же моя мечта чтобы компилятор C жмяхал код настолько сильно , насколько это возможно.  Код должен быть буквально "лысым" - ничего лишнего, только по делу. Часть регистров отвести под константы разные, чтоб не перезагружать данные.

 

Ещё один прикол с CLPRU: вызов функции memcpy() компилятор инлайнит в пару бурстов LBBO/SBBO, что радует

Edited by __inline__

Share this post


Link to post
Share on other sites
21 hours ago, DASM said:

Вообще у Техаса эта подсистема ровно для того же, что и C64 в их DM386 соках - для того же, для чего и собаке пятая нога. То ли со скуки занять место на кристалле решили не убирать

У C6745 с PRUSS'ами всё более плачевно - нет мэппинга на GPO,GPI - а это значит реалтаймовский ногодрыг на нём не сделаешь.

Во всяком случае в мануале на C6754 нет в PINMUX'ах упоминания что можно использовать GPIO для  PRUSS'ов. У 6748-го уже есть.

Но есть возможность сделать ногодрыг через Global Register Area, но это будет медленно.

 

Ну и 4 кБ памяти кода (всего 1024 команды) и 1 кБ памяти данных (ближняя, быстрая) - сильно с-ужают круг допустимых задач.

 

Тем не менее, как графический ускоритель - конвертер-фильтр  он годится!

Вырисовываются такие задачи для PRUSS:

 

1) Конвертер YUV в RGB => отрисовка на дисплей

2) Конвертер палитровых 256 цветов в 16 бит => отрисовка

3) Смарт-фильтры для 2D-графики (Eagle, Sai2x, HQ2x, LQ2x,.... в ранних темах писал и выкладывал)  => отрисовка

4) Up-Скейлер, Down-скейлер,  отрисовка с поворотом.

 

Почти как DMA, но ещё и с попиксельным процессингом! Аля пиксельный шейдер! )))

 

Quote

 Мало того что док нету, так и передать и забрать все параметры будет по времени дольше, чем на основном проце посчитать. А все это становится еще более странным у процев со встроенным приличным видеоускорителем, который на этих задачах порвал бы все, да только вот описания к нему вы не найдете, строжайшее NDA

А что за там ускоритель? Если 2D, то фтопку, DMA же есть, на нём делается Bit-Blt.   Если 3D, то может посмотреть в сторону Фуджитсы с открытым 3D-ускорителем ? (Fujitsu Crimson, Scarlet, некоторые имеют встроенную память).  Текстурированные перспективно-корректные треугольники со смуффингом там поддерживаются.

 

Quote

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

Как же уже достало меня это говно! Его только под линуксом с пыхтоном в паре пускать... Баре-металом там даже и не пахнет. И под линуксом если что - драйвера на видео-часть в виде блобов, свободой там даже и не пахнет.

 

Quote

FPGA в блокнотике карандашиком не пробовали разводить?

Не пробовал )) Хотя возможность рисования и компиляции схем в Квартусе есть.   Писал на VHDL'e  дизайн видеокарты 11 лет назад.  Успешно, понравилось.  Сейчас уже ничего не помню! Хотя при желании за 3 дня восстановлю навыки.

Edited by __inline__

Share this post


Link to post
Share on other sites
2 часа назад, __inline__ сказал:

И всё-же моя мечта чтобы компилятор C жмяхал код настолько сильно , насколько это возможно.

Сильно долго ждать придётся.  Если за 7 лет прошедших с начала моего знакомства с PRUSS только появился хоть так работающий си-компилятор, то сколько ещё потребуется лет?  :lazy:

2 часа назад, __inline__ сказал:

Ещё один прикол с CLPRU: вызов функции memcpy() компилятор инлайнит в пару бурстов LBBO/SBBO, что радует

Я такое и с ARM-компилятором видел. Вот только не помню с каким именно. Возможно что как раз с CCS.

Share this post


Link to post
Share on other sites
23 часа назад, DASM сказал:

Вообще у Техаса эта подсистема ровно для того же, что и C64 в их DM386 соках - для того же, для чего и собаке пятая нога. То ли со скуки занять место на кристалле решили не убирать. Мало того что док нету, так и передать и забрать все параметры будет по времени дольше, чем на основном проце посчитать.

Вполне себе нормальная система. О каком времени на "забрать/принять" речь вообще речь? PRUSS имеет доступ к почти всей памяти системы (по крайней мере в OMAP-L137). Не надо ему ниоткуда ничего забирать специально.

И доки на неё есть. А как бы иначе без док я смог написать код на неё?

И какая-нить хитрая или недостающая периферия на нём вполне себе нормально эмулируется, не мешая работе основных ядер (а это - одно из назначений PRUSS).

 

2 часа назад, __inline__ сказал:

Ну и 4 кБ памяти кода (всего 1024 команды) и 1 кБ памяти данных (ближняя, быстрая) - сильно с-ужают круг допустимых задач.

Зато, насколько смутно помнится, эти команды там могут оперировать сразу с несколькими операндами и выполнять несколько действий.

И почему только 1кБ памяти? 1кБ - это вроде только внутренняя дата-память каждого PRU-ядра. Но PRU-ядра также имеют доступ почти ко всей памяти системы (по-крайней мере в L137). Там собственно с помощью него как раз ARM-ядро запускается, потому что после RESET-а ARM-ядро выключено, работает только DSP. DSP может включить ARM-ядро, но не может прописать ему таблицу векторов, так как не имеет доступа к региону системной памяти ARM. И эту таблицу туда можно записать при помощи PRUSS (что я и делал).

 

PS: И почему Вы всё время говорите о PRU в единственном числе? В C6745 всего одно PRU-ядро что-ли?

Share this post


Link to post
Share on other sites
Quote

PRUSS имеет доступ к почти всей памяти системы (по крайней мере в OMAP-L137). Не надо ему ниоткуда ничего забирать специально

Доступ ко всей памяти, но БЕЗ кеширования внешней! Проверил, с SDRAM данные тащит медленно.

Quote

И какая-нить хитрая или недостающая периферия на нём вполне себе нормально эмулируется, не мешая работе основных ядер (а это - одно из назначений PRUSS).

С этим беда у C6745 - в регистрах R30 и R31 нет мультиплексирования на GPIO. Писал об этом выше.

Quote

И почему только 1кБ памяти?

На выполнение PRUSS-кода имелось в виду! PRUSS не могут выполнять код за пределами IRAM0, IRAM1

Quote

Но PRU-ядра также имеют доступ почти ко всей памяти системы (по-крайней мере в L137).

С внешней памятью PRUSS работают крайне медленно, так как не используют кеширования. Весь разбор  полётов надо делать с внутренней памяти, предварительно забуферизовав строку с внешней во внутреннюю память (писал выше - прирост в скорости 22 %).

 

Quote

И почему Вы всё время говорите о PRU в единственном числе? В C6745 всего одно PRU-ядро что-ли?

Потому что я использую PRU0.   PRU1 пока отключено и не используется.

Share this post


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

С внешней памятью PRUSS работают крайне медленно, так как не используют кеширования. Весь разбор  полётов надо делать с внутренней памяти, предварительно забуферизовав строку с внешней во внутреннюю память (писал выше - прирост в скорости 22 %).

Я все быстрые данные располагал исключительно во внутренней ОЗУ. Мне 256+128 кБ вполне хватало.

Не знаю сколько её в C6745, но если достаточно, то можно попробовать копировать туда через EDMA3 например. Или вроде у DSP ядра были какое-то своё DMA (в доке что-то говорилось про IDMA, но я не разбирался - мне хватало остального) - может через него попробовать?

Share this post


Link to post
Share on other sites
39 minutes ago, jcxz said:

Я все быстрые данные располагал исключительно во внутренней ОЗУ. Мне 256+128 кБ вполне хватало.

Не знаю сколько её в C6745, но если достаточно, то можно попробовать копировать туда через EDMA3 например. Или вроде у DSP ядра были какое-то своё DMA (в доке что-то говорилось про IDMA, но я не разбирался - мне хватало остального) - может через него попробовать?

У меня была цель сконвертить фрейм в формате YUV 8:8:8 в RGB 5:6:5 размером 400x240.  EDMA3 тут не идёт, потому что он только копирует данные без преобразования.  А PRUSS могут параллельно конвертить YUV в RGB, но работают быстро только с внутренней памятью.  У меня она отдана L1D, L1I и L2  - с включенным кешированием декод MP4 должен ещё хорошо работать.  Если же отключить L1, L2 и отдать её PRUSS'ам,  то в целом производительность упадёт.   Уже кот который  раз замечаю, что если часть внутреннего ОЗУ дать кому-нибудь другому,   при этом подрезать L2 кеш, то в целом будет только хуже.

 

Но в целом я решение нашёл - построчно копировать в ближнюю память, затем работать с ней PRUSS'ами. Выходит быстрее всего в целом

Edited by __inline__

Share this post


Link to post
Share on other sites

а этот 6745 он для минимальной системы чего требует? У 21489 нутряной озу 600 кБайт, и он при загрузке и еепром заливает в нее прогу. У меня и внешняя сдрам висит, но пока что до нее даже не доходил. Заливаете джитагом? Я на покупной чего то жадничаю Ice, если припрет - посмотрю на имеющемся EZKIT  , но пока что не требовалось, светодиода и самописного spi-uart для отладки хватает. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now