Jump to content

    

C674x и внешняя SDRAM, вопрос по размещению секций программы

Добрый день.

 

Использую C674x в связке с внешней SDRAM: организация памяти 32M x 16 (64 МБ), 4 банка памяти, одна страница памяти 1024 байт (10 бит).  Кеш L1D, L1P и L2 включены, регионы кеширования MAR для адресов 0xC0000000 ... 0xC3FFFFFF (все 64 МБ) включены в регистрах.

 

Программа выполняется из этой SDRAM, много  данных (на несколько МБ), кода - несколько сотен килобайт (от 500 кБ).  Активно используется выделения памяти из кучи (через memalign(128,x) ) - указатели выровнены на длину строки кеша L2 (максимальная длина) - 128 байт.

 

Вопрос собственно вот в чём:  есть ли смысл привязываться к банкам SDRAM для ускорения доступа к данным?   Например - стек - в одном банке,  куча - в другом,  Zero-Init, UnInit - в третьем,   код (.text) - в четвёртом?

Или это ничего не даст?

 

Прилагаю файл для линковщика - с расписанными секциями и регионами памяти, который сейчас используется.  Программа стартует с самого начала SDRAM 0xC0000000:

 

#define SDRAM_BASE 0xC0000000
#define SDRAM_SIZE 0x04000000 // 64  MB
#define RO_SIZE    0x00098000 // <512 kB

-c
-stack 0x01700000
-heap  0x02800000

MEMORY
{
 RO o = SDRAM_BASE         l = RO_SIZE
 RW o = SDRAM_BASE+RO_SIZE l = SDRAM_SIZE-RO_SIZE
}

SECTIONS
{
 .text:_c_int00* > SDRAM_BASE

 .text           > RO
 .const          > RO
 .switch         > RO
 .cinit          > RO
 .rodata         > RO
 .init_array     > RO //C++

 .sysmem         > RW
 .far            > RW
 .stack          > RW
 .bss            > RW
 .neardata       > RW
 .fardata        > RW
}

 

Share this post


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

Вопрос собственно вот в чём:  есть ли смысл привязываться к банкам SDRAM для ускорения доступа к данным?   Например - стек - в одном банке,  куча - в другом,  Zero-Init, UnInit - в третьем,   код (.text) - в четвёртом?

Имхо: не стек и пр. надо отвязывать, а входные данные и выходные. Т.е. - у вас в цикле обработки данных идёт чтение из одной области и запись в другую. Они должны идти как можно параллельнее. Вот их и нужно развязать. А стек тут не при чём. Он ведь в этих SPLOOP не используется?

И зачем тут динамическое выделение памяти вообще?

Share this post


Link to post
Share on other sites
1 hour ago, jcxz said:

Имхо: не стек и пр. надо отвязывать, а входные данные и выходные. Т.е. - у вас в цикле обработки данных идёт чтение из одной области и запись в другую.

 

Входные данные - SDRAM.   Выходные - L2 и дисплей. SDRAM - на EMIFB, LCD на EMIFA.  Вроде всё отвязано..

 

Конвеер видео-системы ниже на рисунке.  С SDRAM видео-данные записываются в L2 - не весь буфер, а только выборочные элементы - спрайты с SDRAM.  До 5 слоёв может быть.

 

 

1.thumb.png.27e3d021c9b6a80735c386579761ecb7.png

 

 

Quote

И зачем тут динамическое выделение памяти вообще?

 

Загрузка с файлов музыки, звуков, картинок, спрайтов....   Размер неизвестен.   С каждым новым уровнем - берутся новые данные:  старые данные более неактуальны,  идёт освобождение HEAP, занятие новыми объектами.   Динамический менеджмент памяти.

 

Однако же, помогло распределение памяти в скрипте ниже: модель памяти удалось сделать far_agregates, вместо far.  Near не канает - код сильно большой.   Выровнял указатели на 1024 байт (размер страницы SDRAM).    Все объекты кроме кучи - толкнул с первую банку SDRAM.

 

Итого: было 93 FPS, стало 110 FPS.   Имеется ввиду не чистый FPS видео (он равен 171), а грязный -   1 фрейм + весь остальной процессинг.

 

Нужно 60 FPS с VSYNC (что и обеспечивается), но запас не трёт ..... :dirol:

 

#define SDRAM_BASE 0xC0000000
#define SDRAM_SIZE 0x02000000 // 32  MB
#define RO_SIZE    0x00098000 //< 640 kB

-c
-stack 0x00100000
-heap  0x00FFFF00

MEMORY
{
 BANK0_RO o = 0xC0000000         l = RO_SIZE
 BANK0_RW o = 0xC0000000+RO_SIZE l = 0x00800000-RO_SIZE // BANK0_RO+BANK0_RW = 8MB
 BANK12   o = 0xC0800000         l = 0x01000000         // 16MB  

// BANK3    o = 0xC1800000         l = 0x00800000         // 8MB not used yet...
}

SECTIONS
{
 .text:_c_int00* > SDRAM_BASE

 .text           > BANK0_RO
 .const          > BANK0_RO
 .switch         > BANK0_RO
 .cinit          > BANK0_RO
 .rodata         > BANK0_RO
 .init_array     > BANK0_RO //C++

 .far            > BANK0_RW
 .bss            > BANK0_RW
 .neardata       > BANK0_RW
 .fardata        > BANK0_RW
 
 .stack          > BANK0_RW //Stack

 .sysmem         > BANK12 //Heap
}

 

Edited by __inline__

Share this post


Link to post
Share on other sites

Есть ли другие способы копирования из одной памяти в другую с помощью DSP (именно DSP, не DMA),  кроме как _mem8 и _amem8 ?

 

У меня отрисовка тайла 8x8 занимает 34 цикла:

 

void PPU16_Test(u16 SX,u32 SY,s16 DX,s16 DY)
{
 u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH8)+SX)<<1];
 u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH )+DX)<<1];

 _nassert((int)SROMOffset%16==0);
 _nassert((int)VRAMOffset%2==0);

 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*0)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*1)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*2)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*3)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*4)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*4)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*5)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*5)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*6)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*6)+0]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*7)+0])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*7)+0]);

 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*0)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*1)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*2)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*3)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*4)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*4)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*5)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*5)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*6)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*6)+1]);
 _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*7)+1])=_amem8(&SROMOffset[((SROM_WIDTH8>>2)*7)+1]);
}

ASM:

;******************************************************************************
;* FUNCTION NAME: PPU16_Test                                                  *
;*                                                                            *
;*   Regs Modified     : A3,A4,A5,A6,A7,A8,A9,A16,A17,A18,B4,B5,B6,B7,B8,B9,  *
;*                           B16,B17,B18,B19,B20,B21,B22,B23,B24,B25          *
;*   Regs Used         : A3,A4,A5,A6,A7,A8,A9,A16,A17,A18,B3,B4,B5,B6,B7,B8,  *
;*                           B9,DP,B16,B17,B18,B19,B20,B21,B22,B23,B24,B25    *
;*   Local Frame Size  : 0 Args + 0 Auto + 0 Save = 0 byte                    *
;******************************************************************************
PPU16_Test:
;** --------------------------------------------------------------------------*
;          EXCLUSIVE CPU CYCLES: 34
           LDW     .D2T1   *+DP(SROMBuffer),A5 ; [B_D64P] |8| 
           SHL     .S2     B4,8,B4           ; [B_Sb674] |8| 
           MVK     .S2     400,B5            ; [B_Sb674] |9| 
           ADD     .L1X    A4,B4,A3          ; [A_L674] |8| 
           LDW     .D2T2   *+DP(VRAMBuffer),B4 ; [B_D64P] |9| 
           ADDAH   .D1     A5,A3,A9          ; [A_D64P] |8| 

           LDDW    .D1T1   *A9(0),A5:A4      ; [A_D64P] |17| 
||         MPYSU   .M2     B6,B5,B5          ; [B_M674] |9| 

           MVK     .S1     512,A3            ; [A_S674] |18| 

           ADD     .L1     A3,A9,A8          ; [A_L674] |18| 
||         MVK     .S1     1024,A3           ; [A_S674] |19| 
||         ADD     .L2X    A6,B5,B5          ; [B_L674] |9| 
||         MVK     .S2     1536,B6           ; [B_Sb674] |20| 

           ADD     .L1     A3,A9,A18         ; [A_L674] |19| 
||         LDDW    .D1T1   *A8(0),A7:A6      ; [A_D64P] |18| 
||         ADDAH   .D2     B4,B5,B22         ; [B_D64P] |9| 
||         ADD     .L2X    B6,A9,B7          ; [B_L674] |20| 


           LDDW    .D1T1   *A18(0),A17:A16   ; [A_D64P] |19| 
||         MVK     .S2     2560,B4           ; [B_Sb674] |22| 
||         LDDW    .D2T2   *B7(0),B9:B8      ; [B_D64P] |20| 


           MVK     .S1     2048,A3           ; [A_S674] |21| 
||         ADD     .L2X    B4,A9,B6          ; [B_L674] |22| 
||         MVK     .S2     3072,B4           ; [B_Sb674] |23| 
||         STNDW   .D2T1   A5:A4,*B22(0)     ; [B_D64P] |17| 


           ADD     .L1     A3,A9,A3          ; [A_L674] |21| 
||         ADD     .L2X    B4,A9,B5          ; [B_L674] |23| 
||         LDDW    .D2T2   *B6(0),B21:B20    ; [B_D64P] |22| 
||         MVK     .S2     100,B16           ; [B_Sb674] |18| 


           LDDW    .D1T1   *A3(0),A5:A4      ; [A_D64P] |21| 
||         MVK     .S2     3584,B4           ; [B_Sb674] |24| 
||         LDDW    .D2T2   *B5(0),B25:B24    ; [B_D64P] |23| 


           ADD     .L2X    B4,A9,B4          ; [B_L674] |24| 
||         STNDW   .D2T1   A7:A6,*+B22[B16]  ; [B_D64P] |18| 


           MVK     .S2     200,B16           ; [B_Sb674] |19| 
||         LDDW    .D2T2   *B4(0),B19:B18    ; [B_D64P] |24| 
||         LDDW    .D1T1   *A18(8),A7:A6     ; [A_D64P] |28| 

           STNDW   .D2T1   A17:A16,*+B22[B16] ; [B_D64P] |19| 
||         MVK     .S2     300,B16           ; [B_Sb674] |20| 

           STNDW   .D2T2   B9:B8,*+B22[B16]  ; [B_D64P] |20| 

           LDDW    .D1T1   *A9(8),A17:A16    ; [A_D64P] |26| 
||         LDDW    .D2T2   *B7(8),B17:B16    ; [B_D64P] |29| 


           MVK     .S2     400,B7            ; [B_Sb674] |21| 
||         LDDW    .D1T1   *A8(8),A9:A8      ; [A_D64P] |27| 
||         LDDW    .D2T2   *B6(8),B9:B8      ; [B_D64P] |31| 

           STNDW   .D2T1   A5:A4,*+B22[B7]   ; [B_D64P] |21| 

           MVK     .S2     500,B23           ; [B_Sb674] |22| 
||         LDDW    .D1T1   *A3(8),A5:A4      ; [A_D64P] |30| 
||         LDDW    .D2T2   *B5(8),B7:B6      ; [B_D64P] |32| 

           STNDW   .D2T2   B21:B20,*+B22[B23] ; [B_D64P] |22| 
||         MVK     .S2     600,B20           ; [B_Sb674] |23| 


           STNDW   .D2T2   B25:B24,*+B22[B20] ; [B_D64P] |23| 
||         MVK     .S2     700,B20           ; [B_Sb674] |24| 

           STNDW   .D2T2   B19:B18,*+B22[B20] ; [B_D64P] |24| 

           STNDW   .D2T1   A17:A16,*B22(8)   ; [B_D64P] |26| 
||         MVK     .S2     101,B18           ; [B_Sb674] |27| 


           STNDW   .D2T1   A9:A8,*+B22[B18]  ; [B_D64P] |27| 
||         MVK     .S2     201,B18           ; [B_Sb674] |28| 


           STNDW   .D2T1   A7:A6,*+B22[B18]  ; [B_D64P] |28| 
||         MVK     .S2     301,B18           ; [B_Sb674] |29| 


           LDDW    .D2T2   *B4(8),B5:B4      ; [B_D64P] |33| 
||         RET     .S2     B3                ; [B_Sb674] |34| 


           STNDW   .D2T2   B17:B16,*+B22[B18] ; [B_D64P] |29| 
||         MVK     .S2     401,B16           ; [B_Sb674] |30| 


           STNDW   .D2T1   A5:A4,*+B22[B16]  ; [B_D64P] |30| 
||         ADD     .L2     1,B23,B16         ; [B_L674] 


           STNDW   .D2T2   B9:B8,*+B22[B16]  ; [B_D64P] |31| 
||         MVK     .S2     601,B8            ; [B_Sb674] |32| 


           STNDW   .D2T2   B7:B6,*+B22[B8]   ; [B_D64P] |32| 
||         ADD     .L2     1,B20,B6          ; [B_L674] 

           STNDW   .D2T2   B5:B4,*+B22[B6]   ; [B_D64P] |33| 
           ; BRANCH OCCURS {B3}              ; [] |34| 

 

Не всегда удаётся загрузить  все 8 юнитов, как бы не старался (особенно простаивают L и M, активно используются S и D).  В доке на ядро написано, что юниты не все операции могут, особенно M - это мультипликатор.

 

В ARM'ах помню, можно было мульти-пересылку сделать наподобие:

 

LDMCopy
      PUSH {r4-r10}
LDMloop
      LDMIA r1!, {r3 - r10}
      STMIA r0!, {r3 - r10}
      SUBS r2, r2, #32
      BGE LDMloop
      POP {r4-r10}

 

или через регистровый файл VFP.

Edited by __inline__

Share this post


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

Не всегда удаётся загрузить  все 8 юнитов, как бы не старался (особенно простаивают L и M, активно используются S и D).  В доке на ядро написано, что юниты не все операции могут, особенно M - это мультипликатор.

Так это быстрей всего из-за ограничений шин, а не ядра.

Ядро C55xx помню допускало максимум до 2-х 16-битных обращений к одному банку ОЗУ за такт ядра (там шина к памяти была 16-битная; ОЗУ разбито на 8 банков). Но всего ядро имело 5 шт. 16-битных шин. Так что в сумме ядро позволяло за такт до 5-ти 16-битных доступов за такт, если исполнительные адреса этих доступов раскидать по банкам ОЗУ не более чем по 2 доступа к банку. Если в какой-то команде осуществлялся 32-битный доступ, то он учитывался как два 16-битных. Из этих 5 шин к ОЗУ две использовались только для чтения команд в буфер предвыборки, так что для данных оставалось 3 шины. Правда ещё надо было учитывать доступы от DMA-контроллера и если с каким-то банком ОЗУ работал и DMA тоже, то количество непрерывно занятых ядром шин к этому банку ОЗУ приходилось ограничивать одной 16-битной шиной. Иначе DMA захлёбывался данными и выставлял ошибку.

Если же в каком такте DSP пытался осуществить более 3-х доступов к данным за такт (такого можно достичь только на спаренных командах), то на такую команду налагался штраф (penalty) на 1 такт.

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

Спойлер

#pragma CODE_SECTION(".textL2")
void CopyToXVectFFT(float const * restrict src0, float * restrict dst0,
  float const * restrict win, float addv, int n)
{
  _nassert((int)dst0 % 8 == 0);
  _nassert((int)src0 % 8 == 0);
  _nassert((int)win % 8 == 0);
  _nassert(n > 0 && n % 4 == 0);
  long long k, x0, x1;
  float const * restrict src1 = src0 + n - 2;
  float * restrict dst1 = dst0 + n * 2 - 4;
  do {
    k = _amem8((void *)win);
    x0 = _amem8((void *)src0);
    x1 = _amem8((void *)src1);
    _amem8((void *)dst0) =
      _itoll(_ftoi(0.0), _ftoi((_itof(_loll(x0)) + addv) * _itof(_loll(k))));
    _amem8((void *)(dst0 + 2)) =
      _itoll(_ftoi(0.0), _ftoi((_itof(_hill(x0)) + addv) * _itof(_hill(k))));
    _amem8((void *)dst1) =
      _itoll(_ftoi(0.0), _ftoi((_itof(_loll(x1)) + addv) * _itof(_hill(k))));
    _amem8((void *)(dst1 + 2)) =
      _itoll(_ftoi(0.0), _ftoi((_itof(_hill(x1)) + addv) * _itof(_loll(k))));
    win += 2;
    src0 += 2;
    dst0 += 4;
    src1 -= 2;
    dst1 -= 4;
  } while (n -= 4);
}

 

 

так как в моей задаче была работа с realtime-потоком, а не как у Вас - "чем быстрее тем лучше", то я, добившись того что "всё успевает" и + 5-кратного запаса по скорости (загрузка DSP-ядра <=20%) - на этом успокоился и дальше не копал.

Поэтому думаю: если хотите дальнейшего ускорения, то нужно погружаться в механизмы работы конвеера, организации шин и т.п.

 

Например (также в C55xx) был 5-ступенчатый (или даже 6-ти?) конвеер, в котором вычисление адреса доступа к памяти осуществлялось на 2-й ступени (и это делал только A-unit), а все логические-/арифметические-/сдвиги и прочие операции выполнялись на последнем или предпоследнем шаге (на 5-м шаге) D-unit-ом. A-unit - имел упрощённые возможности, он мог выполнять только сложения/вычитания и может ещё сдвиг на 1 влево (не помню точно), а также умел операцию кольцевой адресации (аппаратную закрутку адреса по границам кольца). Всё остальное - только на D-unit-е. Поэтому, если скажем в коде встречалось вычисление адреса с использованием не только операций сложения/вычитания, то такие операции выполнялись на D-unit-e на 5-й ступени конвеера. И, естественно, если сразу после такого вычисления стояла команда, использующая полученный адрес для доступа к памяти (адрес ей нужен на 2 ступени), то такая команда штрафовалась на время между 5-ступенью предыдущей команды и 2-ступенью её самой (т.е. - на 3 такта). Время этого штрафа можно было уменьшать, помещая между этими командами другие команды, никак не связанные по операндам и использованным регистрам с этой цепочкой вычислений. Уменьшение происходило на время выполнения этих команд. Именно поэтому я Вам и дал совет относительно индексной адресации.

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

Штрафы там были ещё по множеству других причин, уже всё и не вспомню за давностью лет.

В C674x внутренняя структура мне кажется должна быть во многом похожей на младшего собрата C55xx и оптимизатор си-кода за Вас должен её учитывать. На C55xx си-оптимизатор работал из рук вон плохо (бывало код получался даже в 10 раз более медленным чем написанный вручную на асме), так что там без асма было никуда. А в C674x мне кажется си-оптимизатор работает более-менее. Но как я уже сказал - глубоко это ядро я не копал.

 

Share this post


Link to post
Share on other sites

Достиг 140 FPS (отрисовка + процессинг). Сделал буфер поворота на весь кадр (ранее было на 4 строки) и в L2.  А видеобуфер расположил в SDRAM (номер банки SDRAM для буфера совпадает с номером банки где лежат исходные данные для отрисовки).   DSP делает поворот на 90 градусов - оперирует блоками 4x4 пикселя сразу:

 

void Video_Draw(void)
{
 for(int y=0;y<(SCREEN_HEIGHT<<1);y+=8) //копирование видеобуфера с SDRAM с поворотом на 90 градусов в буфер L2
 {
  u64 * __restrict vc=(u64*)(VIDEO_CACHE+y);
  u64 * __restrict vb0=(u64*)&VRAMBuffer[((SCREEN_WIDTH-4)<<1)+(SCREEN_WIDTH*y)];
  u64 * __restrict vb1=vb0+(SCREEN_WIDTH>>2);
  u64 * __restrict vb2=vb1+(SCREEN_WIDTH>>2);
  u64 * __restrict vb3=vb2+(SCREEN_WIDTH>>2); 

  for(int x=0;x<(SCREEN_WIDTH>>2);x++)
  {
   _amem8(&vc[(3*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16) _amem8(&vb0[-x])     ))|((u64)((u16) _amem8(&vb1[-x])     )<<16)|((u64)((u16) _amem8(&vb2[-x])     )<<32)|((u64)((u16) _amem8(&vb3[-x])     )<<48);
   _amem8(&vc[(2*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>16)))|((u64)((u16)(_amem8(&vb1[-x])>>16))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>16))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>16))<<48);
   _amem8(&vc[(1*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>32)))|((u64)((u16)(_amem8(&vb1[-x])>>32))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>32))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>32))<<48);
   _amem8(&vc[(0*(SCREEN_HEIGHT>>2))+(SCREEN_HEIGHT*x)])=((u64)((u16)(_amem8(&vb0[-x])>>48)))|((u64)((u16)(_amem8(&vb1[-x])>>48))<<16)|((u64)((u16)(_amem8(&vb2[-x])>>48))<<32)|((u64)((u16)(_amem8(&vb3[-x])>>48))<<48);
  }
 }
 while(PRUSS_VideoBuffer); //PRU закончил рисовать предыдущий кадр?
 PRUSS_VideoBuffer=VIDEO_CACHE; //даём команду PRU отрисовывать новый кадр из L2 в LCD
}

 

В итоге выходит такая же производительность, что и без поворота! И это радует.

 

Если же поворот переложить на PRU или дать ему буфер не в L2, то в SDRAM, то производительность падает в 3 раза  по понятным причинам (PRU не имеет кеша данных и доступ к SDRAM идёт медленно.  Также у PRU тактовая частота вдвое ниже частоты ядра DSP).

 

А вот рутинную пересылку PRU очень даже отлично переваривает -  можно бурстами делать (до 44 байт за 1 раз).  Драйвер  PRU для отрисовки всего LCD:

 

#define IO volatile

#define u32 unsigned int

#define LCD 0x60004000

#define Pixel16  memcpy((void*)LCD,(const void*)VideoBuffer,32);VideoBuffer+=32;

#define REP150(x) {x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x}

#pragma LOCATION(VideoBuffer,0x0000)
IO u32 VideoBuffer;

void main(void)
{
 VideoBuffer=0;

 Loop:

 if(!VideoBuffer)goto Loop;

 for(register int i=0;i<40;i++)REP150(Pixel16)

 VideoBuffer=0;

 goto Loop;
}

 

Ассемблер красиво это преобразует в бурсты по 32 байта:

 

;* --------------------------------------------------------------------------*
;*   BEGIN LOOP ||$C$L2||
;*
;*   Loop source line                : 22
;*   Loop closing brace source line  : 22
;*   Known Minimum Trip Count        : 40
;*   Known Maximum Trip Count        : 40
;*   Known Max Trip Count Factor     : 40
;* --------------------------------------------------------------------------*
||$C$L2||:    
        LDI32     r23, 0x60004000       ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 

          ...
          
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        LBBO      &r14.b0, r14, 0, 32   ; [] |22| 
        SBBO      &r14.b0, r23, 0, 32   ; [] |22| 
        LBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        ADD       r14, r14, 0x20        ; [] |22| 
        SBBO      &r14, r0, 0, 4        ; [] |22| $O$K2
        SUB       r22.b0, r22.b0, 0x01  ; [] |22| $O$L1,$O$L1
        JMPNE     ||$C$L2||, r22.b0, 0x00 ; [] |22| 

          ...

 

Думаю, на этих результатах можно остановиться:  достигнут чистый FPS видеосистемы: 172 FPS,  с процессингом : 140 FPS.  А нужно было не меньше 60 FPS  :dirol:

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
Sign in to follow this