Jump to content

    

__inline__

Участник
  • Content Count

    731
  • Joined

  • Last visited

Everything posted by __inline__


  1. "Потому что гладиолус! ..." (c) Да тут всё понятно. Сделать это можно, но через большую ... (так, а где тут смайлик с поворачивающейся жопой? :) раньше был такой!)
  2. Так карты давно уже отформатированы на размер кластера 32 кБ программой SDFormatter! Но FatFs от этого быстрее не заработала. Потому что внутри неё идёт чтение секторов архаичными порциями по 512 байт даже в мультисекторной командой: ждём токена, выкачиваем 512 байт, 2 байта CRC, и так по кругу. Да и вообще, FatFS не учитывает размер кластера. Только сектор - минимальный и максимальный (обычно они равны) и интерфейс SDIO позволяет читать за один запрос целым блоком по 4, 8, 16 и 32 КБайта. При этом резко повышается скорость чтения, т.к. "накладных расходов" гораздо меньше. Не видал такого! В STM-ном коде от кало-куба идёт общение карты через 4-битный SDIO, но чтение порциями в мультисекторных командах там по-прежнему - по 512 байт Нет такого там чтобы сделать чтение 4 кбайт через SPI, везде идут SPI-чтения по 512 байт. Тем не менее, мультисекторное чтение даёт и в этом случае выигрыш в разы по сравнению с чтением по 1 сектору. Но речь шла о том как заставить карту выдать к примеру 4 кБайт данных, с минимум опросом одного токена + 2 байта ЦРЦ
  3. Для того чтобы другие системы засинхронизировать от прерывания по опустошению звукового буфера с заданным интервалом. Нужен определённый размер буфера или кратный целому числу от минимального размера. )) Нужен буфер на 480 или 512 семплов.
  4. Ого! 32 кГц семплрейт, что надо! Спасибо! P.S. хотя, не пойдёт. Рано радовался: Надо чтобы размер семплов был фиксированным и задан. Так что пока вижу Opus без вариантов + ресемплирование 48->32
  5. Voice activity detection (VAD)

    В моих цифровых радейках работает Включается передача по голосу + небольшая задержка перед выключением. Монотонный шум в виде шума машин, гудения куллеров компа, звука работающей вытяжки на кухне - тоже убирает. Пользователи довольны. Ну и конечно же рассказывать как портировать Speex или его часть исходников на МК я тут не собираюсь ! Речь о вот этом устройстве: https://vrtp.ru/index.php?act=categories&CODE=article&article=3660
  6. Opus отлично жмёт музыку ))) А RLE оказался лучше Хаффмана на 16-битных картинках (используется модифицированный RLE, который понимает что сжимать, а что оставлять)
  7. Opus. Поддерживает регулируемую длину фрейма при условии если выбран флаг CBR. Мои задачи решает! Жаль что нет 32 кГц семплрейта, прийдётся пересемплировать ручками 48->32 кГц
  8. Voice activity detection (VAD)

    А это что? И причем тут fixed point? Я на floating point его применял, в моих рациях прекрасно работает как VAD, так и подавитель шума. Своим глазам и ушам я верю больше, чем неизвестным сомневающимся на форумах. speex_s=speex_preprocess_state_init(FRAME,8000); speex_i=1; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_DENOISE,&speex_i); speex_i=-48; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_NOISE_SUPPRESS,&speex_i); speex_i=0; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_AGC,&speex_i); speex_f=8000; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_AGC_LEVEL,&speex_f); speex_i=0; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_DEREVERB,&speex_i); speex_f=.0; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_DEREVERB_DECAY,&speex_f); speex_f=.0; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_DEREVERB_LEVEL,&speex_f); speex_i=1; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_VAD,&speex_i); speex_i=80; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_PROB_START,&speex_i); speex_i=65; speex_preprocess_ctl(speex_s,SPEEX_PREPROCESS_SET_PROB_CONTINUE,&speex_i);
  9. Нет. HighSpeed доступен и при 3,3 V. Есть ещё более быстрый режим, и он требует 1,8V. У меня карты без проблем переводятся в HighSpeed при питании 3,3V и через SPI. Во всяком случае, я вижу прирост скорости. И ответ после переключения карты получаю положительный. И на запрос переключится в HighSpeed получаю добро от карты. s8 SDCARD_HighSpeed(void) /* ??? */ { u8 CCCR[64]; u8 crc[2]; //Проверяем, умеет ли карта HS режим { SDCARD_Select(); SDCARD_WaitNotBusy(); //CMD6 command static const u8 cmd[]={0x40|0x06 /* CMD6 */,0x00,0xFF,0xFF,0xF1 /* ARG */,(0x7F<<1)|1 /* CRC7 + end bit*/ }; SPI_TX((u8*)cmd,sizeof(cmd)); if(SDCARD_ReadR1()!=0x00) { SDCARD_Unselect(); return -1; } if(SDCARD_WaitDataToken(DATA_TOKEN_CMD6)<0) { SDCARD_Unselect(); return -2; } SDCARD_ReadBytes(CCCR,sizeof(CCCR)); SDCARD_ReadBytes(crc,sizeof(crc)); SDCARD_Unselect(); //Проверка бита SHS регистра карты CCCR if((CCCR[63-(400/8)]&0x01)!=0x01) return -3; } //Просим карту переключиться в HS режим { SDCARD_Select(); SDCARD_WaitNotBusy(); //CMD6 command static const u8 cmd[]={0x40|0x06 /* CMD6 */,0x80,0xFF,0xFF,0xF1 /* ARG */,(0x7F<<1)|1 /* CRC7 + end bit*/ }; SPI_TX((u8*)cmd,sizeof(cmd)); if(SDCARD_ReadR1()!=0x00) { SDCARD_Unselect(); return -4; } if(SDCARD_WaitDataToken(DATA_TOKEN_CMD6)<0) { SDCARD_Unselect(); return -5; } SDCARD_ReadBytes(CCCR,sizeof(CCCR)); SDCARD_ReadBytes(crc,sizeof(crc)); SDCARD_Unselect(); } return 0; }
  10. У меня стоит убунта второй осью, надо будет попробовать ))) А вообще, я давным давно написал файловый эксплорер на FatFs под винду для SD-карточек ))) Можно делать размер виртуального сектора любым, я делал 8 кБ - скорость на порядок возрастала, да! Но проблема в том, что отформатировать карту проблема - приходится под виндой тереть нулевой сектор, перевтыкать карту и только потом форматировать своими побрякушками. Иначе винда определяет логический диск и не даёт отформатировать его. Это очень пичально.. (( Что же они SPI режимы невзлюбили? Не всегда удаётся SDIO задействовать, по причине банальной занятости ножек контроллера на другие интерфейсы ))) А мультиплексировать и делить во времени тоже не вариант. Хаффман плохо жмёт ADPCM, на 18 % выигрыш. RAR ещё хуже жмёт. Для звука скорее всего лучше будет только сжатие с потерями качества. А вот картинку в 16 бит Хаффман ужал в 5 раз почти ( RAR лучше, но он более ресурсоёмкий).
  11. Эксперимент выявил следующее: Чтение 2048 секторов (8 МБ) мультиблочной командой чтения занимает 3,6 секунды. Причем класс скорости не важен - 4 или 10. Всё читается одинаково, ибо этот класс скорости описывает операции записи, которые лично мне в данной задаче неинтересны. Выходит 2,2 МБайт/с Кеширование секторов в функции move_window() у Чана (FatFS) ни к чему не привело. Кеширование делал по принципу индексации секторов и проставлению лайков к ним если при чтении кеша они находятся. Заполнение кеша происходит от начала к концу и в первую очередь по принципу "слабого звена" - чем меньше лайков у индекса (редко используется) тем выше вероятность замены. Считал сектор - закешируй и поставь ему лайк..... Дальнейшее ускорение я вижу за счёт небольшого сжатия - добавить RLE или Хаффмана для исходных данных - картинки в формате 16 бит и звук -ADPCM. На счёт размера блоков карты памяти - там от 1 до 512, карт с бОльшим размером блока я не видел. HxD под виндами видит сектор 512 байт. На счёт установить буфер в FatFs больше 512 - это приведет к тому что надо будет чтение 1 сектора имитировать чтением сразу 4-х. Под виндой не закинешь на карту ничего, если самому FatFS под виндой не использовать и писать работу с диском через IOCTL (такую штуку я написал давно, но распостранения она не получила)
  12. Прироста в скорости переход с LFN на 8.3 не дал. Флажок в ffconf LFN выставлен в 0. Тут пишут, что мультиблочное чтение всёравно должно идти порциями по 512 байт и нужно ждать дата-токена и считывать 2 байта CRC. Разом не считаешь без дата-токена - нужно ждать. А это плохо. https://www.avrfreaks.net/forum/writeread-multiple-blocks-sd-card-0 Read multiple blocks is same as above except: Send READ_MULTIPLE_BLOCK command to card with address, number of blocks etc Repeat for number of blocks { Poll card for 0xFE (data token) Receive the 512 bytes. receive 2 CRC bytes } + READ END CMD
  13. Не могли бы вы осветить этот момент подробнее? Пробовал читать больше чем по 512 байт: по 1024 и по 4096 - файловая система работает некорректно с такими длинами чтений. Интересует SDCARD_ReadData(u8* buff) Там после каждых 512 байт надо делать опрос SDCARD_WaitDataToken(DATA_TOKEN_CMD18)<0) , иначе неправильно работает. Прорабатываю вариант без LFN, имена директорий и файлов в формате 8.3
  14. Да, так оно и есть. львиная доля времени уходит на получение ответа готовности карты выдать ОЧЕРЕДНУЮ порцию данных по 512 байт. Хоть и мультисекторная команда. Код: DRESULT MMC_disk_read(BYTE* buff,DWORD sector,UINT count) { SDCARD_ReadBegin(sector); for(u32 i=0;i<count;i++)SDCARD_ReadData(&buff[i<<9]); SDCARD_ReadEnd(); return RES_OK; //all OK } s8 SDCARD_ReadBegin(u32 blockNum) { SDCARD_Select(); SDCARD_WaitNotBusy(); /* CMD18 (READ_MULTIPLE_BLOCK) command */ u8 cmd[] = { 0x40 | 0x12 /* CMD18 */, (blockNum >> 24) & 0xFF, /* ARG */ (blockNum >> 16) & 0xFF, (blockNum >> 8) & 0xFF, blockNum & 0xFF, (0x7F << 1) | 1 /* CRC7 + end bit */ }; SPI_TX((u8*)cmd,sizeof(cmd)); if(SDCARD_ReadR1() != 0x00) { SDCARD_Unselect(); return -2; } SDCARD_Unselect(); return 0; } s8 SDCARD_ReadEnd(void) { SDCARD_Select(); /* CMD12 (STOP_TRANSMISSION) */ { static const u8 cmd[] = { 0x40 | 0x0C /* CMD12 */, 0x00, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 }; SPI_TX((u8*)cmd,sizeof(cmd)); } SPI(0xFF); if(SDCARD_ReadR1() != 0x00) { SDCARD_Unselect(); return -2; } SDCARD_Unselect(); return 0; } static inline s8 SDCARD_ReadData(u8* buff) { SDCARD_Select(); if(SDCARD_WaitDataToken(DATA_TOKEN_CMD18)<0) { SDCARD_Unselect(); return -1; } SDCARD_ReadBytes(buff,512); SPI(0xFF); SPI(0xFF); SDCARD_Unselect(); return 0; } static inline void SDCARD_ReadBytes(u8* buff, u32 buff_size) { while(buff_size>0) { *buff++=SPI(0xFF); buff_size--; } } Как видно, между полезными данными куча служебной лабуды... Интересную тему нашёл: https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=78105&page=1 То что описал уважаемый aarrrr - замечательно, в моём случае такой подход к кешированию поможет? Оперативной памяти под кеш могу до 8 МБ занять. Файлы читаю разом - крупным блоком сразу: открыл - считал - закрыл. Далее следующий файл... Файлов много (больше 10, меньше 100), есть папки с уровнем вложенности до 3, размер файлов 100 кБ - 20 МБ 3 мега-бита в секунду? Тогда 10 МБ должно открыться за 3,5 с, а не за 5,5 как в моём случае )) Есть ли способ быстро узнать стартовый сектор и размер таблицы FAT? Используется FAT32 с LFN
  15. Сравнивал время чтения файлов : в случае дефолтного режима (24 МГц) и High Speed (48 МГц) - соответственно 7 и 5,5 с. Поиск в интернете по "read ahead" толком ничего не находит. Ткните носом в существующие решения файлового кеша или дискового, изобретать велосипед как-то не хочется.
  16. Читаются большими блоками в память. Применительно к секторам карты ?
  17. Voice activity detection (VAD)

    Калина samsun, бред пишут. Наверное посты набивают ))) личка никак )))
  18. Добрый день. Использую 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 }
  19. Достиг 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
  20. Есть ли другие способы копирования из одной памяти в другую с помощью 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.
  21. Входные данные - SDRAM. Выходные - L2 и дисплей. SDRAM - на EMIFB, LCD на EMIFA. Вроде всё отвязано.. Конвеер видео-системы ниже на рисунке. С SDRAM видео-данные записываются в L2 - не весь буфер, а только выборочные элементы - спрайты с SDRAM. До 5 слоёв может быть. Загрузка с файлов музыки, звуков, картинок, спрайтов.... Размер неизвестен. С каждым новым уровнем - берутся новые данные: старые данные более неактуальны, идёт освобождение HEAP, занятие новыми объектами. Динамический менеджмент памяти. Однако же, помогло распределение памяти в скрипте ниже: модель памяти удалось сделать far_agregates, вместо far. Near не канает - код сильно большой. Выровнял указатели на 1024 байт (размер страницы SDRAM). Все объекты кроме кучи - толкнул с первую банку SDRAM. Итого: было 93 FPS, стало 110 FPS. Имеется ввиду не чистый FPS видео (он равен 171), а грязный - 1 фрейм + весь остальной процессинг. Нужно 60 FPS с VSYNC (что и обеспечивается), но запас не трёт ..... #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 }
  22. Внял рекомендациям jcxz (за что ему громное спасибо! ). Удалось увеличить производительность на 30...50% (в зависимости от случая). Оказывается, индексная адресация лучше ложится на конвеер, чем инкременты с указателями. И однотипные операции лучше дублировать 2 , 4 или 8 раз. Перестановка действий также может влиять. И ещё можно не присваивать переменным промежуточный результат - компилятор это видит и оптимизирует сам. Инкременторы и питчи задефайнены в константы: SROMBuffer - это закешированная SDRAM на частоте CPU_CLK/3 VRAMBuffer - это L2 на частоте CPU_CLK Вот пример простого отображения: //102 цикла: void PPU16_Tile16B_0(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<16;n++) { _mem8(&VRAMOffset[0])=_amem8(&SROMOffset[0]); _mem8(&VRAMOffset[2])=_amem8(&SROMOffset[2]); _mem8(&VRAMOffset[1])=_amem8(&SROMOffset[1]); _mem8(&VRAMOffset[3])=_amem8(&SROMOffset[3]); SROMOffset+=SROM_WIDTH>>2; VRAMOffset+=VRAM_WIDTH>>2; } } //96 циклов: ATT void PPU16_Tile16B_0(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<4;n++) { _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(0*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(0*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(1*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(1*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(2*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(2*(SROM_WIDTH>>2))+3]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+0])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+0]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+1])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+1]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+2])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+2]); _mem8(&VRAMOffset[(3*(VRAM_WIDTH>>2))+3])=_amem8(&SROMOffset[(3*(SROM_WIDTH>>2))+3]); SROMOffset+=SROM_WIDTH; VRAMOffset+=VRAM_WIDTH; } } Вариант с поворотом-отражением: //172 цикла //MIRROR_VERTICAL + ROTATE_90 ATT void PPU16_Tile16B_6(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset0=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict SROMOffset1=SROMOffset0+(SROM_WIDTH>>2); u32 * __restrict VRAMOffset =(u32*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset0%32==0); _nassert((int)SROMOffset1%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<8;n++) { _mem4(&VRAMOffset[0x0*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[0]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[0]) ); _mem4(&VRAMOffset[0x2*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>32)); _mem4(&VRAMOffset[0x4*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[1]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[1]) ); _mem4(&VRAMOffset[0x6*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>32)); _mem4(&VRAMOffset[0x8*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[2]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[2]) ); _mem4(&VRAMOffset[0xA*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>32)); _mem4(&VRAMOffset[0xC*(VRAM_WIDTH>>1)])=(((u32)((u16) _amem8(&SROMOffset1[3]) ))<<16)|(u32)((u16) _amem8(&SROMOffset0[3]) ); _mem4(&VRAMOffset[0xE*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>32)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>32)); _mem4(&VRAMOffset[0x1*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>16)); _mem4(&VRAMOffset[0x3*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[0])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[0])>>48)); _mem4(&VRAMOffset[0x5*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>16)); _mem4(&VRAMOffset[0x7*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[1])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[1])>>48)); _mem4(&VRAMOffset[0x9*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>16)); _mem4(&VRAMOffset[0xB*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[2])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[2])>>48)); _mem4(&VRAMOffset[0xD*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>16)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>16)); _mem4(&VRAMOffset[0xF*(VRAM_WIDTH>>1)])=(((u32)((u16)(_amem8(&SROMOffset1[3])>>48)))<<16)|(u32)((u16)(_amem8(&SROMOffset0[3])>>48)); SROMOffset0+=SROM_WIDTH>>1; SROMOffset1+=SROM_WIDTH>>1; VRAMOffset++; } } Горизонтальное отражение: //MIRROR_HORIZONTAL ATT void PPU16_Tile16B_1(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { /* //102 cycle u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<16;n++) { _mem8(&VRAMOffset[0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[3])),16),_rotl(_hill(_amem8(&SROMOffset[3])),16)); _mem8(&VRAMOffset[1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[2])),16),_rotl(_hill(_amem8(&SROMOffset[2])),16)); _mem8(&VRAMOffset[2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[1])),16),_rotl(_hill(_amem8(&SROMOffset[1])),16)); _mem8(&VRAMOffset[3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[0])),16),_rotl(_hill(_amem8(&SROMOffset[0])),16)); SROMOffset+=SROM_WIDTH>>2; VRAMOffset+=VRAM_WIDTH>>2; } */ //96 cycle u64 * __restrict SROMOffset=(u64*)&SROMBuffer[((SY*SROM_WIDTH)+SX)<<1]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*VRAM_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); _nassert((int)VRAMOffset%2==0); for(int n=0;n<4;n++) { _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+0])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+3])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+3])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+2])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+1])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+1])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*0)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*0)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*1)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*1)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*2)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*2)+0])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+1])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+2])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+2])),16)); _mem8(&VRAMOffset[((VRAM_WIDTH>>2)*3)+3])=_itoll(_rotl(_loll(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+0])),16),_rotl(_hill(_amem8(&SROMOffset[((SROM_WIDTH>>2)*3)+0])),16)); SROMOffset+=SROM_WIDTH; VRAMOffset+=VRAM_WIDTH; } } Кто смотрели асм-листинг выше, те заметили, что число циклов было на 30 ... 50% больше, чем в последних вариантах. На практике прирост скорости подтвердился также. Особо радует вот это (несколько инструкций впараллель): $C$L164: ; PIPED LOOP PROLOG ; EXCLUSIVE CPU CYCLES: 4 MVK .S1 8800,A5 ; [A_S674] || MVK .S2 800,B8 ; [B_Sb674] || ADD .L2X A18,B18,B24 ; [B_L674] || ADD .L1X A29,B18,A25 ; [A_L674] || LDDW .D1T1 *A20(0),A19:A18 ; [A_D64P] |363| (P) <0,0> || LDDW .D2T2 *B19(0),B7:B6 ; [B_D64P] |363| (P) <0,0> MVK .S2 9600,B17 ; [B_Sb674] || MVK .S1 1600,A17 ; [A_S674] || ADD .L2 B8,B18,B23 ; [B_L674] || ADD .L1X A5,B18,A26 ; [A_L674] || LDDW .D1T2 *A20(16),B9:B8 ; [A_D64P] |367| (P) <0,1> || LDDW .D2T1 *B19(8),A5:A4 ; [B_D64P] |365| (P) <0,1> MVK .S1 10400,A31 ; [A_S674] || MVKH .S2 0xffff0000,B27 ; [B_Sb674] || ADD .L2 B17,B18,B22 ; [B_L674] || ADD .L1X A17,B18,A21 ; [A_L674] || LDDW .D1T1 *A20(8),A17:A16 ; [A_D64P] |365| (P) <0,2> ADDK .S1 -2,A0 ; [A_S674] || ADD .L2 B6,B18,B20 ; [B_L674] || ADD .S2 B16,B18,B21 ; [B_Sb674] || ADD .D2 B29,B18,B25 ; [B_D64P] || ADD .L1X A31,B18,A27 ; [A_L674]
  23. Да, скачал DSPlib, там везде индексный доступ типа такого: float DSPF_sp_dotprod(const float * x, const float * y, const int nx) { int i; float sum = 0; _nassert(nx > 0); _nassert(nx % 8 == 0); _nassert((int)x % 8 == 0); _nassert((int)y % 8 == 0); for(i = 0; i < nx; i++) sum += x[i]*y[i]; return (sum); } Немного рвёт шаблон, так как я предполагал, что индексная адресация более тормозная, чем работа через указатели. Вот наподобие сделал: void PPU16_Tile16B(u16 SX,u32 SY,s16 DX,s16 DY,u8 M) { u64 * __restrict SROMOffset=(u64*)&SROMBuffer[(SY*SPRITE_PITCH)+(SX<<1)]; u64 * __restrict VRAMOffset=(u64*)&VRAMBuffer[((DY*SCREEN_WIDTH)+DX)<<1]; _nassert((int)SROMOffset%32==0); //адрес откуда забираются данные всегда кратен 32 байтам (шаг 16 пикселей) _nassert((int)VRAMOffset%2==0); //адрес куда писать в видеобуфер всегда кратен 2 - точность 1 пиксел for(u32 y=0;y<16;y++) { _mem8(&VRAMOffset[4*y ])=_amem8(&SROMOffset[4*y ]); _mem8(&VRAMOffset[4*y+1])=_amem8(&SROMOffset[4*y+1]); _mem8(&VRAMOffset[4*y+2])=_amem8(&SROMOffset[4*y+2]); _mem8(&VRAMOffset[4*y+3])=_amem8(&SROMOffset[4*y+3]); } } Оно уже 102 цикла делается, вместо 168 что были. Тут не угадаешь, только пробовать и смотреть листинг. На счёт знаний ядра DSP, как повлиять на генерацию кода компилятором?
  24. Voice activity detection (VAD)

    написал в личку