Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: BF533 отрисовка на дисплей
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Сигнальные процессоры и их программирование - DSP
Страницы: 1, 2
__inline__
Здравствуйте.

Использую ADSP BF533 + SDRAM + OLED Display (подключен к EBIU 16 бит).
Программа загружена в SDRAM, кэширование включено: для данных и программ, политика write back.
Дисплей не прокеширован.

Периодически нужно отрисовывать кадр на дисплей, как можно быстрее.
Видеопамять дисплея - 16 бит на пиксел, адрес автоматически увеличивается на +1 пиксел после отрисовки точки.

Есть буфер в котором строистя изображение - источник. Кодировка цветов через палитру.
Приемником выступает сам дисплей.

Палитру засунул в L1_DATA_B, чтобы ускорить доступ к ней (попиксельно идет обращение).
Буфер источника находится в SDRAM (кеширована).

За один присест сразу рисую 2 пиксела по 16 бит, по сути формирую 32-битное обращение к памяти дисплея: #define OLED_Data_32 (*(volatile u32*) 0x20010000)
Хотя он подключен к 16-битной шине.

Фрагмент кода:

Код
//u8, u16, u32 - char, short и long соответственно 1 2 4 байта

UINT16* palette_16bit_lookup=(UINT16*)0xFF900000; //L1_DATA_B 16KB Тут палитра

#define SRC_PITCH  544 /* Ширина источника */
#define DST_HEIGHT 224 /* Высота приемника */

#define SCR_WIDTH  320 /* Ширина дисплея */
#define SCR_HEIGHT 240 /* Высота дисплея */

#define OLED_Data_32 (*(volatile u32*) 0x20010000) /* Это регистр данных дисплея с автоинкрементом адреса, подключен к EBIU Blackfin - разрядность 16 бит */

#define PIXEL OLED_Data_32=(palette_16bit_lookup[src[1]]<<16)|palette_16bit_lookup[src[0]];src+=2; /* выводим сразу 2 пиксела(по 16 bit) на дисплей, цвет берем из палитры */

#define PIXELLAST OLED_Data_32=(palette_16bit_lookup[src[1]]<<16)|palette_16bit_lookup[src[0]];src+=(SRC_PITCH-SCR_WIDTH+2); /* последние 2 пиксела */

void Draw_Window(struct BITMAP *bitmap) //Отправка буфера на дисплей
{
register u16* src=(u16*)(((u32)bitmap->base)+17536+64); //Стартовый адрес источника
register u32 y=DST_HEIGHT;
OLED_Rectangle(0,(SCR_HEIGHT-DST_HEIGHT)>>1,SCR_WIDTH-1,((SCR_HEIGHT+DST_HEIGHT)>>1)-1); //Задаёт прямоугольную область 320x224 по центру дисплея (сам дисплей 320x240)
while(y--) //цикл по Y, цикл по X развернут на 320 точек (160 слов)
{
  PIXEL /* 159 раз */
  PIXEL
  PIXEL
/* ... */
  PIXEL
  PIXEL
  PIXELLAST /* 160-й раз */
}
}


Цикл по X развернут макросами для ускорения.

Компилировал это дело в Visual DSP++ 5.1.2 - пробовал такую оптимизацию: Speed 100, Interprocedural optimization, Frame-pointer optimization.

Код работает как нужно , но подозреваю, что можно сделать быстрее!

Дает ли прирост скорости 32-битное обращение к регистру данных дисплея, когда он подключен к 16-битной шине?
Фактически это 2 операции подряд по 16 бит с увеличением адреса (не используются).

Как можно сделать ещё быстрее?
Рассмотрю любые способы: от изменения алгоритма, до системного управления процессором (кеширование, выравнивание).

Пробовал изменить тайминги шины EBIU, ничего не меняется. Как можно пере-инициализировать контроллер асинхронной шины, когда он уже работает?
jcxz
Цитата(__inline__ @ Sep 5 2017, 09:12) *
Как можно сделать ещё быстрее?
Рассмотрю любые способы: от изменения алгоритма, до системного управления процессором (кеширование, выравнивание).

Быстрее - в смысле уменьшить время выполнения функции Draw_Window() (уменьшить загрузку CPU)?
Или в смысле - увеличить максимальную частоту обновления дисплея?
Многое зависит от характера операций рисования: полная перерисовка экрана или множество мелких операций модификации экрана (множество отдельных операций прорисовки примитивов).
У Вас как я понял первый случай - только полная перерисовка экрана?
И что именно делается внутри PIXEL?
Самый очевидный способ ускорения в Вашем случае - переписать цикл на ассемблере. Для DSP при этом можно получить ускорение работы в несколько раз.

PS: Вопрос про PIXEL снимается - не заметил сразу (кстати формат PIXEL у Вас неправильный).
Вам нужно посмотреть во что компилируется ваш PIXEL (асм). Ну и изучить ассемблер этого ядра.
__inline__
Быстро, в смысле уменьшить время перекидывания буфера на экран. Перерисовка экрана полная.

Посмотрел листинг функции на ассемблере, мой опыт не позвояет сказать насколько оптимально делает VDSP++, листинг приложил.

Знающих в ассебмлере, прошу глянуть листинг - оцените насколько неоптимально или оптимально сделал компилятор свою работу?

Если же слабые места будут, то буду копать ассемблер BF.

Нажмите для просмотра прикрепленного файла

Цитата(jcxz @ Sep 5 2017, 07:33) *
Вопрос про PIXEL снимается - не заметил сразу (кстати формат PIXEL у Вас неправильный).

Это два пиксела по 16 бит каждый.
_pv
зачем на блэкфинах разворачивать циклы, они там и так аппаратные? в таких простых случаях компилятор вроде должен догадываться их использовать.
заведите буфер на строку или две во внутренней памяти, заполняйте её пикселями и потом отдавайте DMA, пусть перекладывает. а то и два канала дма, один на чтение из памяти - другой на запись на экран. причем не попиксельно, а кусками из sdrama чтение должно быть быстрее. я честно говоря не знаю что там контроллер sdrama делает когда ему ещё на EBUI вклиниваются с отбиранием шины через каждое чтение.
ну и этот Draw_Window обязательно из внешней памяти выполнять?
я так понимаю шина наружу там и без того узким местом становится, процессор из палитры цвет достанет быстрее чем прочитает из внешней памяти индекс цвета, а потом запишет обратно пиксель на дисплей. кэширование конечно как-то поможет, но зачем специально усложнять.
добейтесь сначала нормальной работы из внутренней памяти, а потом смотрите что будет при переносе во внешнюю.
jcxz
Цитата(__inline__ @ Sep 5 2017, 10:29) *
Это два пиксела по 16 бит каждый.

Неправильность в синтаксисе. Понятно, что это макрос. Но даже при использовании макросов, си-шный код должен выглядеть правильным с точки зрения синтаксиса языка.
Если человек со стороны глянет на ваши:
PIXEL
PIXEL
то совершенно не поймёт, что тут написано.
Вобщем корректно должно быть:
#define PIXEL() { OLED_Data_32=(palette_16bit_lookup[src[1]]<<16)|palette_16bit_lookup[src[0]];src+=2; } /* выводим сразу 2 пиксела(по 16 bit) на дисплей, цвет берем из палитры */
PIXEL();
PIXEL();
...
jcxz
Цитата(_pv @ Sep 5 2017, 12:09) *
зачем на блэкфинах разворачивать циклы, они там и так аппаратные? в таких простых случаях компилятор вроде должен догадываться их использовать.

Такое разворачивание может наоборот навредить: например у TI в C55 если размер цикла не более 64 байт, то он помещается целиком в prefetch буфер и выполняется оттуда, гораздо быстрее (и многие stall-ы убираются при этом если они были). Если же развернуть в огромный цикл, то он не влезет и будет уже гораздо медленнее.

Цитата(_pv @ Sep 5 2017, 12:09) *
заведите буфер на строку или две во внутренней памяти, заполняйте её пикселями и потом отдавайте DMA

Это будет медленнее. Так как автор переписывает сразу весь экран и цикл внутри простой, то быстрее будет писать сразу в видеопамять, на лету подменяя палитру (как сейчас), но надо оптимально написать это на асме.

Цитата(__inline__ @ Sep 5 2017, 10:29) *
Если же слабые места будут, то буду копать ассемблер BF.
Нажмите для просмотра прикрепленного файла

Я к сожалению не знаю асма Блэкфина, но на первый взгляд видно мало распараллеливания операций.
И в то же время много лишних пересылок между регистрами (выполняющихся не в параллель другим командам).
Если проанализировать операции для одного пикселя:
Код
  R0 = W[P5 + 8] (Z);
  P1 = R0;
  R1 = W[P5 + 6] (Z);
  P4 = R1;
  P0 = [P2];
  P1 = P0 + (P1<<1);
  R0.L = W[P1];
  P4 = P0 + (P4<<1);
  R0 = R0 << 16 || R1 = W[P4] (Z);
  R0 = R0 | R1;
  [I0] = R0;

то слишком много команд/тактов получается. На сравнимом DSP (TMS320VC5502) который я знаю, потребовалось бы гораздо меньше инструкций. И не надо разворачивать всю строку - так будет только медленнее.

Так что - надо учить ассемблер, чтобы написать быстрее.
_pv
Цитата(jcxz @ Sep 5 2017, 20:17) *
Это будет медленнее. Так как автор переписывает сразу весь экран и цикл внутри простой, то быстрее будет писать сразу в видеопамять, на лету подменяя палитру (как сейчас), но надо оптимально написать это на асме.

я так понимаю достать цвет из палитры процессору быстрее чем даже просто прочитать его индекс из внешней sdram, а его ещё потом в дисплей записать надо. причём не уверен что контроллеру динамической памяти понравится что у него шину постоянно забирают через каждый пиксель, вычитать кусок может оказаться куда быстрее. там же BURST какой-то должен быть тогда.
и получается что процессор будет только и делать что ждать внешнюю шину.
а так зарядили дма на чтение, в это время можно ещё чем-нибудь общественно полезным заняться, а как только прочиталось быстро перепахали строку и опять же дма отдали для записи в дисплей.
__inline__
Цитата(jcxz @ Sep 5 2017, 13:06) *
Вобщем корректно должно быть:
#define PIXEL() { OLED_Data_32=(palette_16bit_lookup[src[1]]<<16)|palette_16bit_lookup[src[0]];src+=2; } /* выводим сразу 2 пиксела(по 16 bit) на дисплей, цвет берем из палитры */

Согласен, иначе следующие PIXEL при if()else... не попадут под условие.

Цитата
Такое разворачивание может наоборот навредить: например у TI в C55 если размер цикла не более 64 байт, то он помещается целиком в prefetch буфер и выполняется оттуда, гораздо быстрее (и многие stall-ы убираются при этом если они были). Если же развернуть в огромный цикл, то он не влезет и будет уже гораздо медленнее.


Попробую вернуть внутренний цикл и посмотреть что сделает компилятор(асм- листинг).

Пробовал процедуру отрисовки скопировать в L1 Code SRAM (64K) из SDRAM.
Вот так:

Код
memcpy((void*)0xFFA00000, (const void*)Draw_Screen,0x8000); //скопировал 32 килобайта (с запасом, точный размер функции в map-файле)
// 0xFFA00000 - 0xFFA0FFFF  Code SRAM (64K)


При передаче управления туда - зависает.

Передаю управление так:

Код
#define CALL(Address) (*(void(*)(void)) Address)(); //Вызов функции по её адресу

CALL(0xFFA00000)


Что не так?

Как правильно грузить данный участок памяти кода L1 Code SRAM ? (от кеширования этот участок свободен).

Есть ли #pragma в Visual DSP позволяющая сделать отдельные фуккции позиционно-независимыми? (position independent) ?

Насколько оправдано размещать отдельно код процедуры отрисовки во внутренней памяти, если включено кеширование для SDRAM (I cache + D cache) ?

Приложение большое - около 2 МБайт.
jcxz
Цитата(_pv @ Sep 5 2017, 17:25) *
я так понимаю достать цвет из палитры процессору быстрее чем даже просто прочитать его индекс из внешней sdram, а его ещё потом в дисплей записать надо. причём не уверен что контроллеру динамической памяти понравится что у него шину постоянно забирают через каждый пиксель, вычитать кусок может оказаться куда быстрее. там же BURST какой-то должен быть тогда.

У автора в SDRAM находятся пиксели, т.е. - индексы в таблице палитры. И пиксели эти считываются последовательно. А вот палитра получается считывается произвольно.
Поэтому палитру нужно расположить во внутренней памяти, а пиксели можно во внешней, так как контроллер SDRAM как правило имеет кеширование и считывает данные из SDRAM в кеш сразу пакетами (burst-доступ).
Если кеширования SDRAM нет, то тогда да - такое ручное кеширование через промежуточный буфер в SRAM + DMA может дать прибавку скорости.

Цитата(__inline__ @ Sep 5 2017, 17:49) *
Пробовал процедуру отрисовки скопировать в L1 Code SRAM (64K) из SDRAM.

Вам надо найти как описываются RAM-функции в вашем компиляторе.
В IAR например: __ramfunc void Func();
Потому что там могут быть какие-то привязки по адресу да ещё много чего.
Изучайте ядро подробнее. По мануалу. Без этого дальнейшего ускорения вряд-ли получите.

PS: И кстати - насчёт организации картинки - я например в одном из проектов тоже делал картинку с пикселами, преобразуемыми через палитру. Но пикселы у меня были 4-битные и поэтому вся картинка влезала во внутреннее ОЗУ. А уж считать сразу пару пикселов одной байтовой операцией, разбить на две тетрады и преобразовать через таблицу палитры, а потом записать как единое 32-битное значение - получалось быстро.
16 цветов в картинке мне в том проекте было достаточно.
_pv
Цитата(jcxz @ Sep 5 2017, 21:13) *
У автора в SDRAM находятся пиксели, т.е. - индексы в таблице палитры. И пиксели эти считываются последовательно.
А вот палитра получается считывается произвольно.
Поэтому палитру нужно расположить во внутренней памяти, а пиксели можно во внешней,

спасибо, капитан очевидность sm.gif
Цитата(jcxz @ Sep 5 2017, 21:13) *
так как контроллер SDRAM как правило имеет кеширование и считывает данные из SDRAM в кеш сразу пакетами (burst-доступ).

только вот шину у контроллера отбирают сразу же после каждого чтения, для записи в дисплей. и я что-то совсем не уверен что он в таком режиме будет хоть что-то читать, пусть даже и в кэш, бурстами. а это и получается самое узкое место.
jcxz
Цитата(_pv @ Sep 5 2017, 18:26) *
только вот шину у контроллера отбирают сразу же после каждого чтения, для записи в дисплей. и я что-то совсем не уверен что он в таком режиме будет хоть что-то читать, пусть даже и в кэш, бурстами. а это и получается самое узкое место.

Когда автор изучит ассемблер своего DSP, он в начале итерации цикла напишет что-то типа:
LDM R0!, {R1-R8} ;сорри за ARM wink.gif
и получит burst-чтение SDRAM laughing.gif
_pv
ну да, буфер можно и не на всю строку, а на несколько пикселей, но смысл тот же.
просто пока строка подгружается через ДМА процессор может что-нибудь ещё сделать, (переработать другую половину буфера), а тут он всё равно просто тупо будет ждать шину.
jcxz
Цитата(_pv @ Sep 5 2017, 18:51) *
ну да, буфер можно и не на всю строку, а на несколько пикселей, но смысл тот же.
просто пока строка подгружается через ДМА процессор может что-нибудь ещё сделать, (переработать другую половину буфера), а тут он всё равно просто тупо будет ждать шину.

Если частота SDRAM близка к частоте ядра, а не отличается от неё в десятки раз, то с большой долей уверенности могу предположить, что даже в этом случае (с предварительным копированием через DMA во внутреннюю память) выигрыш в скорости будет несущественным.
Автор уже дошёл до потолка возможностей компилятора на этом цикле. Думаю, что дальнейшее существенное ускорение возможно только после досконального изучения работы ядра, системы команд, регистров, работы конвеера (это же CISC), взаимозависимостей разных фаз конвеера, количества шин доступа к памяти, аппаратных циклов и т.п.. И последующего переписывания этого цикла на асме.
На этот вывод наводит вид ассемблерного листинга, с совсем неплотным потоком команд.

Да Вы сами посмотрите на выделенный мной выше фрагмент для пары пикселей - даже не зная архитектуры данного DSP мне он кажется очень неоптимальным - похоже займёт не менее 11 тактов(!) (это если там stall-ов нет). В то время как на аналогичном DSP с ядром C55xx (архитектуру которого я ещё более-менее помню) этот фрагмент займёт думаю примерно 3-4 такта (если написать его на асме оптимально).
Первые 4 команды наверняка можно выполнить за одну команду (возможно что ещё и с сохранением данных из предыдущего шага в параллель). Далее команда - абсолютно лишняя - на каждую пару пикселей заново перегружается адрес начала палитры. Зачем??? он же - константа. Далее - опять лишние команды программного вычисления адреса сложением - неужели в системе команд Блэкфина нет команд с косвенно-базовой адресацией? Это ещё минус 2 команды. Ну и плюс - наверняка тут возможно распараллеливание операций (операция OR думаю запараллелится с чтением или записью в память).
__inline__
По-разному пробовал:

Код
//#define PIXEL {OLED_Data_32=(palette_16bit_lookup[src[1]]<<16)|palette_16bit_lookup[src[0]];src+=2;}

//#define PIXEL {OLED_Data_32=palette_16bit_lookup[*src++];OLED_Data_32=palette_16bit_lookup[*src++];}


Выходит так:
Код
1    LOOP_BEGIN .P38L6L;
2    R0 = W[P1 + 2] (Z);
3    P0 = R0;
4    R0 = W[P1 ++ P5] (Z);
5    P2 = R0;
6    P0 = P3 + (P0<<1);
7    R0.L = W[P0];
8    P0 = P3 + (P2<<1);
9    R1 = R0 << 16 || R0 = W[P0] (Z);
10    R0 = R1 | R0;
11    [P4] = R0;
12    LOOP_END .P38L6L;

1    LOOP_BEGIN .P38L6L;
2    R0 = W[P1++] (Z);
3    P2 = R0;
4    P2 = P4 + (P2<<1);
5    R0 = W[P2] (Z);
6    [P5] = R0;
7    R0 = W[P1++] (Z);
8    P2 = R0;
9    P2 = P4 + (P2<<1);
10    R0 = W[P2] (Z);
11    [P5] = R0;
12    LOOP_END .P38L6L;


Не особо вижу разницы, да и код на внешний вид действительно неплотный.

Вот это : R1 = R0 << 16 || R0 = W[P0] (Z); - параллельное выполнение команд?

Код
LDM R0!, {R1-R8};сорри за ARM wink.gif
и получит burst-чтение SDRAM


Это 8 порций по 32 бита подряд за одно чтение? Есть ли такая фишка в Блекфинах? rolleyes.gif rolleyes.gif rolleyes.gif

У Intel-ловских процессоров есть подобное с mmx- и xmm- регистрами: 8 байт за раз переслать можно аналогично!
__inline__
У Блекфина система команд такая, что особо не разбежишься.
Всё очень строго, и конвеер там допускает инструкции в строго определенной последовательности.
Строковых блочных команд нет вроде.

Смотрел внимательно тут:
http://microsin.net/programming/dsp/blackf...ence-part1.html
http://microsin.net/programming/dsp/blackf...ence-part2.html

Максимум получилось избавиться от 2 инструкций - вместо 11 строк кода имею 9:

P1 - адрес src, P3 - адрес палитры, P5 - видео-регистр дисплея

Код
__asm__ volatile ("R0=W[P1++] (Z);"); \
__asm__ volatile ("P0=R0;"); \
__asm__ volatile ("R0=W[P1++] (Z);"); \
__asm__ volatile ("P2=R0;"); \
__asm__ volatile ("P0=P3+(P0<<1);"); \
__asm__ volatile ("P2=P3+(P2<<1);"); \
__asm__ volatile ("R1.H=W[P0];"); \
__asm__ volatile ("R1.L=W[P2];"); \
__asm__ volatile ("[P5]=R1;"); \
_pv
ну вот есть у вас 5 инструкций на пиксель, можно ещё сократить, наверное.
P0=W[P1++]
P0=P3+(P0<<1)
да и в собирании двух пикселей в одно 32х разрядное слово смысла особого нет. EBUI всё равно 16 бит.
если бы был дополнительный буфер во внутренней памяти, куда дма данные само подтаскивает, наверное, можно было бы ещё какой-нибудь один такт сэкономить, запараллелив запись одного пикселя с чтением следующего и/или P0=P3+(P0<<1), P2=P3+(P2<<1)
но всё равно чтение из sdrama в лучшем случае 1SCLK = 3-4CCLK, (если не лень встаньте, осциллографом на шину памяти и посмотрите сколько тактов реально такие одиночные чтения из sdram занимают)
а запись в дисплей по асинхронной шине 2-3SCLK = 6-12CCLK.
что толку оптимизировать эти 4 инструкции, если обращение чтение из памяти и запись в дисплей длится в несколько раз дольше 9-16 тактов.
как уже не раз говорил, заставьте дма перекладывать данные, тогда обработка палитры процессором будет идти параллельно с перекладыванием данных.

ps. у вас 16ти разрядный цвет в палитре и 16ти разрядный индекс цвета, в чем смысл?
__inline__
Цитата(_pv @ Sep 6 2017, 09:14) *
что толку оптимизировать эти 4 инструкции, если обращение чтение из памяти и запись в дисплей длится в несколько раз дольше 9-16 тактов.
как уже не раз говорил, заставьте дма перекладывать данные, тогда обработка палитры процессором будет идти параллельно с перекладыванием данных.


Процессор разогнан до 550 МГц. Шина CCLK/3=183 МГц. SDRAM работает на 183 МГц с CAS latency =2. Кеш включен (для кода и данных).
Дисплей 7 тактов (setup + write + hold), в эквиваленте 183/7 = 26 МГц. Шина данных 16 бит.

Цитата(_pv @ Sep 6 2017, 09:14) *
да и в собирании двух пикселей в одно 32х разрядное слово смысла особого нет. EBUI всё равно 16 бит.

Обращение к 32 битам идет подряд как 2 раза по 16 бит, адрес+1.
Но та как адресов у контроллера дисплея нет (точнее есть и висит он на самом старшем адресном бите), инкремент адреса не мешает.
Это небольшой burst из двух коротких слов sm.gif

Сделал 14 строк ассемблера на 4 пикселя.
Раньше было 11 строк на 2 пикселя.
Ппришлось попарить мозг, пока вышло так (задействовал конвеер Блекфина).
Пока не проверял в работе, но компилируется.
Эффективность кода возросла на 36%


Код
#define PIXEL \
{ \
__asm__ volatile ("                 R0=W[P0++] (Z);"); \
__asm__ volatile ("R0=R0<<1      || R1=W[P0++] (Z);"); \
__asm__ volatile ("R1=R1<<1      || R2=W[P0++] (Z);"); \
__asm__ volatile ("R2=R2<<1      || R3=W[P0++] (Z);"); \
__asm__ volatile ("P3=R3                        ;"); \
__asm__ volatile ("P3=P5+(P3<<1)                ;"); \
__asm__ volatile ("R0=R0+R5 (NS) || R1.H=W[P3]  ;"); \
__asm__ volatile ("I0=R0                        ;"); \
__asm__ volatile ("R1=R1+R5 (NS) || R0.L=W[I0]  ;"); \
__asm__ volatile ("I1=R1                        ;"); \
__asm__ volatile ("R2=R2+R5 (NS) || R0.H=W[I1]  ;"); \
__asm__ volatile ("I2=R2                        ;"); \
__asm__ volatile ("R3=R3+R5 (NS) || R1.L=W[I2] || [P1]=R0 ;"); \
__asm__ volatile ("  [P1]=R1    ;"); \
\
} \


Цитата(_pv @ Sep 6 2017, 09:14) *
если бы был дополнительный буфер во внутренней памяти, куда дма данные само подтаскивает, наверное, можно было бы ещё какой-нибудь один такт сэкономить, запараллелив запись одного пикселя с чтением следующего


К сожалению объем графических данных не позволяет сделать буфер на строку во внутренней памяти + DMA. Потому что программу писал не я.

Цитата(_pv @ Sep 6 2017, 09:14) *
ps. у вас 16ти разрядный цвет в палитре и 16ти разрядный индекс цвета, в чем смысл?


Это эмуляция аркадных автоматов Capcom Play System 1/2, там палитра больше 256 цветов sm.gif

Вот это детище портировал на BF533:
https://osdn.net/projects/mamespi/releases/2046 / https://www.zophar.net/mame/caname.html
_pv
буфер не обязательно на всю строку делать, 8-16-32 пикселя возможно уже вполне хватит чтобы успеть и данные пережевать и дма перенастроить.
главное чтобы не процессор занимался перекладыванием данных из регистра/внутренней памяти наружу.

Цитата(__inline__ @ Sep 6 2017, 18:18) *
Это эмуляция аркадных автоматов Capcom Play System 1/2, там палитра больше 256 цветов sm.gif

раз это память никак не экономит, делайте конверсию в цвет по палитре при записи в буфер, а не при чтении, и выводите тогда просто направив дма из внешней памяти на экран вообще без участия процессора.
__inline__
Цитата(_pv @ Sep 6 2017, 11:44) *
раз это память никак не экономит, делайте конверсию в цвет по палитре при записи в буфер, а не при чтении, и выводите тогда просто направив дма из внешней памяти на экран вообще без участия процессора.


Там очень хитрый буфер. И он "грязный", потому что по-очереди отрисовываются несколько спрайтовых плоскостей (видеосистема Capcom PlaySystem 1/2). Если преобразовывать палитру там, то боюсь всё замедлится в число раз, равное числу плоскостей.

Цитата(_pv @ Sep 6 2017, 11:44) *
буфер не обязательно на всю строку делать, 8-16-32 пикселя возможно уже вполне хватит чтобы успеть и данные пережевать и дма перенастроить.
главное чтобы не процессор занимался перекладыванием данных из регистра/внутренней памяти наружу.


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

Проверил работу процедуры. На практике вышло чуть-лучше. Но ненамного.

Вот код всей процедуры - пока не вычесал, затронут только внутренний цикл.

Код
.section/DOUBLE32 program;

.extern _current_display;
.extern _palette_16bit_lookup

.global _dib_draw_window_asm;

_dib_draw_window_asm:
    [--SP] = (P5:3);
    P1.L = _current_display+4;
    P1.H = _current_display+4;
    P1 = [P1];
    P2 = 17600;
    P0 = 224;
    I0 = 0;
    I0.H = 8193;
    P1 = [P1 + 16];
    P5.L = _palette_16bit_lookup;
    P5.H = _palette_16bit_lookup;
    P4 = 4;
    P1 = P1 + P2;
    [SP + 12] = P1;
    LOOP CycleYL LC1 = P0;
CycleY:
    LOOP_BEGIN CycleYL;
    P1 = [SP + 12];
    P0 = 80;

    P3 = [P5];
        R5=P3;

    LOOP CycleXL LC0 = P0;
CycleX:
    LOOP_BEGIN CycleXL;
                     R0=W[P1 + 2] (Z)     ;
    R0=R0<<1      || R1=W[P1 ++ P4] (Z)   ;
    R1=R1<<1      || R2=W[P1 + 2] (Z)     ;
    R2=R2<<1      || R3=W[P1 ++ P4] (Z)   ;
    P0=R3                                 ;
    P0=P3+(P0<<1)                         ; //Preg read after write which requires 4 extra cycles
    R0=R0+R5 (NS) || R7.L=W[P0]           ;
    I1=R0                                 ;
    R1=R1+R5 (NS) || R6.H=W[I1]           ; //Dagreg read after write which requires 4 extra cycles
    I1=R1                                 ;
    R2=R2+R5 (NS) || R6.L=W[I1]           ; //Dagreg read after write which requires 4 extra cycles
    I1=R2                                 ;
    R3=R3+R5 (NS) || R7.H=W[I1] || [I0]=R6; //Dagreg read after write which requires 4 extra cycles
    [I0]=R7                               ;
    LOOP_END CycleXL;
.P35L13:
    P0 = [SP + 12];
    P1 = 1088;
    P1 = P0 + P1;                            //Preg read after write which requires 3 extra cycles
    [SP + 12] = P1;
    LOOP_END CycleYL;
.P35L14:
    (P5:3) = [SP++];
    RTS;


А вот это разочаровало:
//Preg read after write which requires 3 extra cycles
//Dagreg read after write which requires 4 extra cycles

Выходит вся оптимизация и конвеер лесом?? wacko.gif

Пока получилось 35 - 40 FPS, в идеале должно быть 60 FPS (частота смены кадров NTSC)
_pv
Цитата(__inline__ @ Sep 6 2017, 20:09) *
Там очень хитрый буфер. И он "грязный", потому что по-очереди отрисовываются несколько спрайтовых плоскостей (видеосистема Capcom PlaySystem 1/2). Если преобразовывать палитру там, то боюсь всё замедлится в число раз, равное числу плоскостей.

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

Цитата(__inline__ @ Sep 6 2017, 20:09) *
Процессор к сожалению будет конвертить палитру, а DMA так же занимать шину во время транзакций.
И можно будет авансом заранее сделать не более 1 цикла эмуляции, до того как экран будет полностью не отрисован, иначе пропуск кадра будет что плохо.
Пока получилось 35 - 40 FPS, в идеале должно быть 60 FPS (частота смены кадров NTSC)

ну у процессора-то не отберёт, там приоритет вроде как поставить можно кто главней процессор или дма если оба наружу хотят обратиться.
что такое цикл эмуляции?
вот есть у вас 15мс между кадрами при 60Гц, за это время надо успеть наэмулировать 15мс тактов приставки и отрисовать экран. какая разница в каком порядке.
320*240 / 26МГц = 3мс, это время в течении которого шина всё равно будет просто занята отрисовкой на экран, чтение из памяти гораздо быстрее можно не учитывать, так почему бы в эти 3мс процессору не заняться конвертированием цветов, ПАРАЛЛЕЛЬНО с выдачей, а не последовательно как сейчас. потому что ничего другого он всё равно без внешней шины делать не сможет.
__inline__
Цитата(_pv @ Sep 6 2017, 15:24) *
когда данные не во внешней памяти, а уже в регистре то это преобразование занимает два такта.
ну и если взять в среднем, пиксель всё таки читается каждый кадр, а вот пишется думаю далеко не каждый.
правда исходное разрешение наверное больше чем выходное 320х240, тогда пожалуй да, лучше конвертировать при выводе.


ну у процессора-то не отберёт, там приоритет вроде как поставить можно кто главней процессор или дма если оба наружу хотят обратиться.
что такое цикл эмуляции?
вот есть у вас 15мс между кадрами при 60Гц, за это время надо успеть наэмулировать 15мс тактов приставки и отрисовать экран. какая разница в каком порядке.
320*240 / 26МГц = 3мс, это время в течении которого шина всё равно будет просто занята отрисовкой на экран, чтение из памяти гораздо быстрее можно не учитывать, так почему бы в эти 3мс процессору не заняться конвертированием цветов, ПАРАЛЛЕЛЬНО с выдачей, а не последовательно как сейчас. потому что ничего другого он всё равно без внешней шины делать не сможет.


Весь цикл программы можно упрощенно представить так:

Код
while(!Quit)
{
эмулируем...
рисуем...
}


Если запустить DMA и отрисовывать построчно, то всеравно прийдется ждать окончания работы DMA чтобы подсунуть ему новую линию с декодированной палитрой при передаче.
В момент работы ДМА, процессор всеравно будет брать индексы из SDRAM, а это снова шина.
В итоге процессор и ДМА будут рвать шину по кускам.
_pv
Цитата(__inline__ @ Sep 6 2017, 21:37) *
Если запустить DMA и отрисовывать построчно, то всеравно прийдется ждать окончания работы DMA чтобы подсунуть ему новую линию с декодированной палитрой при передаче.
В момент работы ДМА, процессор всеравно будет брать индексы из SDRAM, а это снова шина.
В итоге процессор и ДМА будут рвать шину по кускам.

для этого и нужен небольшой буфер во внутренней памяти чтобы процессору было с чем работать пока шина занята отрисовкой.
и не обязательно буфер на всю строку.
0) завели два буфера по 64 байта.

1)настроили ДМА, чтобы забрать 32 точки из сдрама(ну или забрали руками, тут без разницы) в буфер №1
2)курим бамбук, так как без шины заняться нечем.
3)по получении данных отправили предыдущий буфер№2 из 32 точек на отрисовку через дма
4)пока дма перекладывает данные в дисплей, обрабатываем только что полученные данные в буфере№1.
5)как только старые данные ушли (обработка вроде бы должна закончиться гораздо быстрее, дисплею надо 21ССLK на пиксель, а процессор за 3-5 управиться должен из внутренней памяти), поменяли местами указатели на буферы
GOTO 1
__inline__
Цитата(_pv @ Sep 6 2017, 16:05) *
0) завели два буфера по 64 байта.
1)настроили ДМА, чтобы забрать 32 точки из сдрама(ну или забрали руками, тут без разницы) в буфер №1
2)курим бамбук, так как без шины заняться нечем.
3)по получении данных отправили предыдущий буфер№2 из 32 точек на отрисовку через дма
4)пока дма перекладывает данные в дисплей, обрабатываем только что полученные данные в буфере№1.
5)как только старые данные ушли (обработка вроде бы должна закончиться гораздо быстрее у дисплею надо 21ССLK на пиксель, а процессор за 3-5 управиться должен из внутренней памяти), поменяли местами указатели на буферы
GOTO 1


Всё сделал как написали, работает исправно, но прироста скорости всёравно нет. Пробовал убрать dma_wait, скорость повышается, но экран отрисовывается на 1/4 и весь трясётся (что и понятно почему).

Попробую не ждать, а в обработчик прерывания засунуть вывод на дисплей через DMA.

Рабочий код ниже:

Код
#ifndef __DMA_H__
#define __DMA_H__

//0xFF800000..0xFF803FFF //L1_DATA_A

#define NB 32

#define buf0 (0xFF803FC0-(NB<<1))
#define buf1 (0xFF803FE0- NB    )

#define DMA_INIT                         \
{                                        \
*pMDMA_S1_PERIPHERAL_MAP=0x0040;        \
*pMDMA_S1_X_COUNT=(NB>>1);              \
*pMDMA_S1_X_MODIFY=2;                   \
*pMDMA_S1_Y_COUNT=1;                    \
*pMDMA_S1_Y_MODIFY=0;                   \
*pMDMA_D1_PERIPHERAL_MAP=0x0040;        \
*pMDMA_D1_START_ADDR=(void*)&OLED_Data; \
*pMDMA_D1_X_COUNT=(NB>>1);              \
*pMDMA_D1_X_MODIFY=0;                   \
*pMDMA_D1_Y_COUNT=1;                    \
*pMDMA_D1_Y_MODIFY=0;                   \
}                                        \

#define DMA_SEND(buf)                  \
{                                      \
*pMDMA_S1_START_ADDR=(void*)buf;      \
*pMDMA_S1_CONFIG=WDSIZE_16|DMAEN;     \
*pMDMA_D1_CONFIG=WDSIZE_16|WNR|DMAEN; \
}                                      \

#define DMA_WAIT while(*pMDMA_D1_IRQ_STATUS&DMA_RUN);

#define MEMORY_COPY(dst,src)       \
{                                  \
register u16 i;                   \
register u32* d=(u32*)dst;        \
register u32* s=(u32*)src;        \
for(i=0;i<(NB>>2);i++) *d++=*s++; \
}                                  \

#define PALETTE_CONVERT(buf)                          \
{                                                     \
register u16 i;                                      \
register u16* c=(u16*)buf;                           \
for(i=0;i<(NB>>1);i++)*c++=palette_16bit_lookup[*c]; \
}                                                     \
#endif


Код
void dib_draw_window_DMA(void)
{
register u16* src=(u16*)(((u32)current_display.game_bitmap->base)+17536+64);
register u16 y=DST_HEIGHT;

while(y--)
{
  register u16 x=(u16)SCR_WIDTH/(u16)NB;

  MEMORY_COPY(buf1,src)
  PALETTE_CONVERT(buf1)
  src+=(NB>>1);

  while(x--)
  {
   MEMORY_COPY(buf0,src)
   DMA_SEND(buf1)
   PALETTE_CONVERT(buf0)
   src+=(NB>>1);
   DMA_WAIT

   MEMORY_COPY(buf1,src)
   DMA_SEND(buf0)
   PALETTE_CONVERT(buf1)
   src+=(NB>>1);
   DMA_WAIT
  }

  src+=(SRC_PITCH-SCR_WIDTH-(NB>>1));
}
}
_pv
Цитата(__inline__ @ Sep 7 2017, 16:04) *
Всё сделал как написали, работает исправно, но прироста скорости всёравно нет. Пробовал убрать dma_wait, скорость повышается, но экран отрисовывается на 1/4 и весь трясётся (что и понятно почему).

Core Timer в руки и смотрите сколько тактов что исполняется. тормозит где-то ещё.
вы же понимаете что тут быстрее уже вывод не сделать, так как в данном случае всё упирается в скорость перекладывания данных в дисплей.
ну разве что только на PPI или SPORT какой-нибудь его перевесить, чтобы внешнюю шину не занимал.

зы уже говорили про макросы.
inline void DMA_INIT(){
*pMDMA_S1_PERIPHERAL_MAP=0x0040;
...
}
ну вот абсолютно ничем не отличается, но выглядит не так безобразно.
jcxz
Цитата(__inline__ @ Sep 7 2017, 12:04) *
Всё сделал как написали, работает исправно, но прироста скорости всёравно нет.

И не будет. Я писал выше почему.

Цитата(_pv @ Sep 7 2017, 12:23) *
зы уже говорили про макросы.

Да. Похоже - не в коня корм... smile3046.gif

Цитата(__inline__ @ Sep 6 2017, 17:09) *
//Preg read after write which requires 3 extra cycles
//Dagreg read after write which requires 4 extra cycles
Выходит вся оптимизация и конвеер лесом?? wacko.gif

Чтобы что-то оптимизировать, нужно сначала досконально изучить процессор. А у Вас знания - поверхностные.
Я тут уже несколько раз писал по stall-ы в c55x и почему они возникают. И вот именно в этих же точках c55xx вставил бы stall-ы, и похоже, что конвеер Blackfin работает аналогично.
На ядре C55xx нельзя вычислить значение на основном АЛУ (D-unit АЛУ) и тут же использовать его для вычисления адреса, так как действия эти выполняются на разных фазах конвеера (причём: вычисление адреса - на более ранней фазе). Если так сделать, то ядро вставит необходимое число stall-ов (остановок конвеера) для синхронизции фаз, что у Вас похоже и наблюдается.
Кроме этой, есть есть куча причин для вставки stall-ов. Пока не изучите полностью - не напишите ничего путнего. DSP-ядра - это не ARM, они в разы сложнее ARM-а.
Да и операций у Вас куча лишних - какие-то пересылки - зачем они? Надо изучать систему команд и искать решения с меньшим их кол-вом.
Вобщем оптимизации там - кот наплакал. Никакой скорости выполнения от такого кода ждать не приходится.
_pv
Цитата(jcxz @ Sep 8 2017, 02:29) *
И не будет. Я писал выше почему.
Вобщем оптимизации там - кот наплакал. Никакой скорости выполнения от такого кода ждать не приходится.

дело не в оптимизации этого куска, его можно и как угодно криво написать,
на перекладывание точки в дисплей уходит 21 такт, который сейчас выполняется параллельно с вычислением палитры, а процессор потом почти всё время сидит сидит и ждёт while(*pMDMA_D1_IRQ_STATUS&DMA_RUN);
ну а то что fpsов мало получается - надо смотреть чем там ещё процессор занят.
переслать 320*240 при 26МГц это 3мс всего.
__inline__
Сделал DMA по прерываниям. Буфер - 320 пикселов (одна строка) 640 байт в L1, уже сконверченная палитрой(тоже в L1). Даём DMA команду на отрисовку строки, а сами делаем дальше остальные вещи. Прерывание по окончанию пересылки строки снова запускает DMA для новой строки. И так пока экран не отрисуется весь.

Код что ниже - рабочий, удаось поднять общую скорость (отрисовка экрана + вся программа) на 15 FPS !!! rolleyes.gif
Получается, что отрисовка экрана и работа процессора немного распараллелились.

Макросы использую, потому что 100% гарантия что с-инлайнится.

Код
#define SRC_PITCH  544
#define DST_HEIGHT 224

#define SCR_WIDTH  320
#define SCR_HEIGHT 240

//0xFF800000..0xFF803FFF //L1_DATA_A

#define NB (SCR_WIDTH<<1)

#define L1_MEMORY 0xFF804000

#define L1_BUF (L1_MEMORY-NB)

#define DMA_INIT                         \
{                                        \
*pMDMA_S1_PERIPHERAL_MAP=0x0040;        \
*pMDMA_S1_START_ADDR=(void*)L1_BUF;     \
*pMDMA_S1_X_COUNT=(NB>>1);              \
*pMDMA_S1_X_MODIFY=2;                   \
*pMDMA_S1_Y_COUNT=1;                    \
*pMDMA_S1_Y_MODIFY=0;                   \
*pMDMA_D1_PERIPHERAL_MAP=0x0040;        \
*pMDMA_D1_START_ADDR=(void*)&OLED_Data; \
*pMDMA_D1_X_COUNT=(NB>>1);              \
*pMDMA_D1_X_MODIFY=0;                   \
*pMDMA_D1_Y_COUNT=1;                    \
*pMDMA_D1_Y_MODIFY=0;                   \
}                                        \

#define DMA_START                            \
{                                            \
*pMDMA_S1_CONFIG=WDSIZE_16|DMAEN;           \
*pMDMA_D1_CONFIG=WDSIZE_16|WNR|DMAEN|DI_EN; \
}                                            \

#define DMA_STOP                       \
{                                      \
*pMDMA_S1_CONFIG=0;                   \
*pMDMA_D1_CONFIG=0;                   \
}                                      \


extern struct mame_display current_display;

u16* src;

EX_INTERRUPT_HANDLER(DMA_ISR)
{
static u16 y=DST_HEIGHT;
*pMDMA_D1_IRQ_STATUS=1;
y--;
if(y)
{
  register u16* buf=(u16*)L1_BUF;
  register u16 i;
  for(i=0;i<(NB>>1);i++)*buf++=palette_16bit_lookup[*src++];
  src+=(SRC_PITCH-SCR_WIDTH);
  DMA_START
}
else
{
  y=DST_HEIGHT;
  DMA_STOP
}
}

void DMA_Init_Interrupt(void)
{
*pSIC_IMASK&=0xFFBFFFFF;                       //Disable MDMA1 interrupt
*pSIC_IAR2=(*pSIC_IAR2&0xF0FFFFFF)|0x06000000;
register_handler(ik_ivg13,DMA_ISR);
*pSIC_IMASK|=0x00400000;                       //Enable MDMA1 interrupt
}

int win_init_window(void)
{
        // disable win_old_scanlines if a win_blit_effect is active
        if (win_blit_effect != 0)
                win_old_scanlines = 0;

DMA_Init_Interrupt();
DMA_INIT

OLED_Clear(0x0000);

return 0;
}

void dib_draw_window_DMA(void)
{
OLED_Rectangle(0,(SCR_HEIGHT-DST_HEIGHT)>>1,SCR_WIDTH-1,((SCR_HEIGHT+DST_HEIGHT)>>1)-1);

src=(u16*)(((u32)current_display.game_bitmap->base)+17536+64);

register u16* buf=(u16*)L1_BUF;
register u16 i;
for(i=0;i<(NB>>1);i++)*buf++=palette_16bit_lookup[*src++];
src+=(SRC_PITCH-SCR_WIDTH);
DMA_START
}
jcxz
Цитата(__inline__ @ Sep 8 2017, 22:49) *
Код что ниже - рабочий, удаось поднять общую скорость (отрисовка экрана + вся программа) на 15 FPS !!! rolleyes.gif
Получается, что отрисовка экрана и работа процессора немного распараллелились.

И сколько получилось? 40+15 FPS? И это по 16-битной шине?? Это всё равно очень медленно.
Для справки: у меня в одном проекте тоже используется 320*240 LCD в режиме 16 бит/пиксел.
Вот только подключен он по SPI на SCLK=45МГц. И частота CPU гораздо ниже - 180МГц.
Картинка у меня изначально нарисована в формате 4 бит/пиксел. А процедура отображения фрагментами преобразует содержимое в 16-битные пикселы (тоже с помощью палитры) в промежуточный буфер и передаёт через DMA-SPI. И исходная картинка и промежуточный буфер у меня находятся во внутренней памяти МК.
Так вот - преобразование следующего фрагмента (процессором) успевает закончится до завершения передачи (через DMA-SPI) предыдущего фрагмента. И ещё процессору остаётся время успеть заняться другими делами между преобразованиями фрагментов.
Т.е. - если бы я непрерывно только гнал кадры на LCD с высоким приоритетом, то у меня получилось бы: 45000000/(320*240*16) ~ 36FPS
И это по однобитному SPI!
И никаких ухищрений по оптимизации в цикле преобразования точек через таблицу палитры я не делалал - простой цикл на си, без каких-либо разворачиваний чего-либо.
А у Вас вроде 16-битная шина и процессор гораздо мощнее - у Вас скорость передачи картинки должна быть во много раз выше чем у меня. А почему-то получается сравнимая скорость с моей. Как будто по SPI картинку передаёте.
Да и вообще если просто посчитать: 550МГц/(320*240) - получается, что каждую секунду у процессора есть > 7100 тактов CPU на обработку одной точки экрана. Т.е. - даже при частоте FPS=60ГЦ будет около 120тактов CPU на точку! Это не просто много, это очень много - процессор должен просто почти всё время просто отдыхать. А у Вас он ещё и не успевает. wacko.gif
Какая-то у Вас там глобальная проблема в коде....

Цитата(_pv @ Sep 8 2017, 04:38) *
переслать 320*240 при 26МГц это 3мс всего.

Не понял - что за 26МГц?
_pv
Цитата(jcxz @ Sep 9 2017, 02:01) *
Не понял - что за 26МГц?

системная частота SCLK = частота ядра CCLK / 3 = 550/3 = 180МГц.
цикл асинхронной шины 7 тактов со всеми setup/holdами или 26МГц.
jcxz
Цитата(_pv @ Sep 9 2017, 02:17) *
системная частота SCLK = частота ядра CCLK / 3 = 550/3 = 180МГц.
цикл асинхронной шины 7 тактов со всеми setup/holdами или 26МГц.

А почему так много - 7 тактов/пересылка?
Если действительно такая тормозная шина, то тогда пожалуй Вы правы, что нужно преобразовывать через промежуточный буфер во внутреннем ОЗУ с последующей передачей его через DMA уже по этой тормозной шине.
Даже в этом случае у процессора автора должно быть просто море времени на эту работу и преобразование должно занимать меньше времени чем передача кадра за 3мсек.
__inline__
Цитата(jcxz @ Sep 8 2017, 19:01) *
И сколько получилось? 40+15 FPS? И это по 16-битной шине?? Это всё равно очень медленно.


Это FPS всего эмулятора, а не экранный FPS:

Код
while(!quit)
{
// эмуляция процессоров Z80, M68020
// эмуляция видео-подсистемы (4 графических слоя)
// эмуляция звуковой системы (FM синтезатор + ADPCM + Wav sound*4 канала)
// эмуляция системы ввода
// эмуляция памяти
//эмуляция ROM-set-а

// отрисовка на дисплей
// вывод звука
// считывание клавиш
}


Вот это всё ВМЕСТЕ - 55 FPS ! biggrin.gif
_pv
Цитата(__inline__ @ Sep 9 2017, 05:24) *
Это FPS всего эмулятора, а не экранный FPS:
Код
// эмуляция процессоров Z80, M68020}

да как-то всё равно не очень-то быстро.
Z80 на АВРах даже как-то эмулируется, 68к с его 5мипсами не особо сложнее должно быть.
а тут целый блэкфин на 550МГц.
jcxz
Цитата(_pv @ Sep 9 2017, 06:13) *
да как-то всё равно не очень-то быстро.
Z80 на АВРах даже как-то эмулируется, 68к с его 5мипсами не особо сложнее должно быть.
а тут целый блэкфин на 550МГц.

Не просто "не быстро", а ОЧЕНЬ медленно.
Лет уже почти 20 назад писал подобный эмулятор только для КР580ВМ80 3МГц-ового и на компе с CPU = i486 работающем на 100МГц (или 120?).
FPS у меня получался (если без задержек и без эмуляции реалтайма) в несколько раз выше чем на эмулируемой системе.
Так, что я даже сделал потом эмуляцию реалтайма: для каждой эмулируемой команды суммировал её время выполнения и периодически делал задержку для компенсации лишнего времени. И графика у меня тоже эмулировалась (2 режима: 256*256*4 бита или 512*256*2 бита с регистрами палитры) и звуковой синтезатор (3 + 1 канала) и прочая периферия.
И (если не вносить задержки) всё летало в несколько раз быстрее чем на эмулируемой системе.
Да и как иначе - процессор эмулятора уже был на порядки быстрее эмулируемого.
А у автора ещё в разы более мощная система.
__inline__
Если критические участки в программе переписывать на Ассемблере (эмуляция процессоров, эмуляция графики / звука), то производительность должна вырасти.
Это выходит за рамки моей задачи, так как было нужно портировать эмулятор (автором которого я НЕ являюсь) на устройство с ADSP BF533.

В целом результатом доволен, быстродействие вышло таким:

45 - 55 FPS при эмуляции CPS1
30 - 40 FPS при эмуляции CPS2.

При этом показывается каждый кадр, звуковой поток не рвётся (темп плавно понижается).

Система CPS слишком наворочена, так как графика/звук у неё заточены под игры(быстрые).
И не надо сравнивать какой-то допотопный КР580ВМ80 в связке с убогой графикой (Спектрум небось? biggrin.gif )

Ну и вдогонку: телефон с процессором ARM9 800 МГц воспроизводит данный эмулятор хуже (не мой порт), и КПК на 300 МГц ещё хуже!

Замерял количество полных отрисовок экрана за 1 секунду (Буфер 320x224x2 из SDRAM + конвертация 16-битной палитры + вывод на дисплей) - 188 раз.

Мерял этим:

Код
static __inline__ enable_cycles(void)
{
__asm__ __volatile__ ("R2=SYSCFG;");
__asm__ __volatile__ ("BITSET(R2,1);");
__asm__ __volatile__ ("SYSCFG=R2;");
}

static __inline__ disable_cycles(void)
{
__asm__ __volatile__ ("R2=SYSCFG;");
__asm__ __volatile__ ("BITCLR(R2,1);");
__asm__ __volatile__ ("SYSCFG=R2;");
}

static __inline__ start_cycles(void)
{
__asm__ __volatile__ ("R2=0;");
__asm__ __volatile__ ("CYCLES=R2;");
__asm__ __volatile__ ("CYCLES2=R2;");
}

static __inline__ u64 get_cycles(void)
{
u32 t0,t1;
__asm__ __volatile__ ("%0=cycles;%1=cycles2;":"=d"(t0),"=d"(t1));
return t0|((u64)t1<<32);
}



Цитата(_pv @ Sep 8 2017, 23:13) *
да как-то всё равно не очень-то быстро.
Z80 на АВРах даже как-то эмулируется, 68к с его 5мипсами не особо сложнее должно быть.
а тут целый блэкфин на 550МГц.


Это типа троллинг? santa2.gif
Полный перечень того что делается:

Цитата
// эмуляция процессоров Z80, M68020
// эмуляция видео-подсистемы (4 графических слоя)
// эмуляция звуковой системы (FM синтезатор + ADPCM + Wav sound*4 канала)
// эмуляция системы ввода
// эмуляция памяти
//эмуляция ROM-set-а

// отрисовка на дисплей
// вывод звука
// считывание клавиш

_pv
Цитата(__inline__ @ Sep 9 2017, 13:10) *
Это типа троллинг?

вовсе нет, просто на первый взгляд ресурсов там более чем достаточно для этой задачи. и то что просто использование ДМА для вывода в дисплей на треть подняло fps просто намекает на не очень оптимальное этих ресурсов использование.
но тут да, надо допиливать код под блэкфин.


> Полный перечень того что делается:
// эмуляция процессоров Z80, M68020
Z80 вполне эмулируется на avr, 68к не особо сложнее, арифметика только по разрядности не для 8ми битного авр.

// эмуляция видео-подсистемы (4 графических слоя)
тут не скажу, но отрисовка 2д спрайтов не должна быть сильно уж затратнее чем эмуляция основного процессора.

// эмуляция звуковой системы (FM синтезатор + ADPCM + Wav sound*4 канала)
с этим в оригинале вроде как Z80 и справлялся.

// эмуляция памяти return mem[addr]
//эмуляция ROM-set-а return rom[addr]
// отрисовка на дисплей DMA
// вывод звука DMA

// эмуляция системы ввода
// считывание клавиш
jcxz
Цитата(__inline__ @ Sep 9 2017, 14:10) *
И не надо сравнивать какой-то допотопный КР580ВМ80 в связке с убогой графикой (Спектрум небось? biggrin.gif )

В чём убогость-то? У Вас вроде 320*224? И во сколько раз это отличается от 256*256? А во сколько раз отличается частота вашего CPU от древнего i486?
И какие частоты у эмулируемых ваших CPU? Во сколько раз они отличаются от ВМ80?
Эмулируемые системы вполне сравнимы по скорости.
И это при том, что система команд у DSP гораздо более эффективна для обработки потоковых данных (конвертации экранной картинки).
И ресурсов (и по частоте CPU и по скорости работы системы памяти) у вас в разы больше чем было у меня на i486.
Да и тут уже несколько раз Вам показали с цифрами, что вывод картинки у Вас должен быть в разы быстрее.

Цитата(__inline__ @ Sep 9 2017, 14:10) *
Это типа троллинг? santa2.gif

Нет. Просто кому-то для частотного управления двигателем достаточно слабого 8-битника, а кому-то гигагерцового ARM-а не хватает для мигания лампочками.... laughing.gif

Цитата(__inline__ @ Sep 9 2017, 14:10) *
Полный перечень того что делается:

И что?
И сколько тактов тратится на "эмуляция памяти"? Или на "эмуляция ROM-set-а"? biggrin.gif
В чём сложность-то? Самое тяжёлое там: эмуляция графики. Но, как уже заметил ув. _pv, даже с самыми тормозными характеристиками вашей шины достаточно 3мсек на вывод всей картинки. Остальное - вообще крохи.

Цитата(_pv @ Sep 9 2017, 15:03) *
// эмуляция звуковой системы (FM синтезатор + ADPCM + Wav sound*4 канала)
с этим в оригинале вроде как Z80 и справлялся.

Не знаю, что там у автора понимается под этим синтезатором, но думаю скорее всего - три канала генерации тонального сигнала с устаналиваемыми частотами, с последующим смешиванием этих каналов в один.
Для DSP (тем более такого довольно высокого класса) это пара процентов загрузки CPU максимум.
Когда-то писал на TMS320VC5502 на 220МГц. И у меня ЧМ-модулятор генерил поток 96kS/s на внешний кодек, параллельно принимая с него-же поток сэмплов. При этом нагружая этот самый DSP всего на несколько единиц процентов.

PS: Сорри - нашёл и заглянул сейчас в тот свой код ЧМ-модулятора, пересчитал - он занимал процессорного времени менее 0.5%. И это ещё не самый быстрый алгоритм (считаю синус полиномом), можно гораздо быстрее.
Непонятно какие могут быть трудности с каким-то "FM-синтезатором" звуковой частоты wacko.gif
На CPU автора (даже если предположить, что он генерит 3 синусоидальных сигнала и смешивает их потом, а не тупо - 3 меандра biggrin.gif ) даже в случае 3 синусоид и частоты квантования 48кГц, загрузка CPU этим синтезатором должна быть менее 1%.
__inline__
Цитата
заглянул сейчас в тот свой код ЧМ-модулятора, пересчитал - он занимал процессорного времени менее 0.5%. И это ещё не самый быстрый алгоритм (считаю синус полиномом), можно гораздо быстрее. Непонятно какие могут быть трудности с каким-то "FM-синтезатором" звуковой частоты


Цитата
На CPU автора (даже если предположить, что он генерит 3 синусоидальных сигнала и смешивает их потом, а не тупо - 3 меандра biggrin.gif ) даже в случае 3 синусоид и частоты квантования 48кГц, загрузка CPU этим синтезатором должна быть менее 1%.


Так и хочется натянуть свой опыт, да? biggrin.gif
Не выйдёт.
Не мешайте говно с мёдом.......

Для начала советую поразмыслить над тем, как эмуляция вашего ЧМ-модулятора относится к:

1) Capcom System QSound™
2) Yamaha YM2151
3) OKI6295

Вы хотя бы один из этих чипов эмулировали ?

Более на дебаты теоретиков не реагирую.

------------------

А теперь вопросы:

1) Насколько тормозной тип double для BlackFin ? Имеет ли смысл его заменить float ?

2) Каким образом можно изменить параметры растактовки шины EBIU для асинхронного банка, когда он был инициализирован ранее?
Тоесть переинициализировать. После того как EBIU запущен, изменить параметры перезаписью в региcтры: EBIU_AMBCTL0 EBIU_AMBCTL1 EBIU_AMGCTL не получается!


jcxz
Цитата(__inline__ @ Sep 9 2017, 16:23) *
Для начала советую поразмыслить над тем, как эмуляция вашего ЧМ-модулятора относится к:

Относится так, что любой источник звука - это генератор синусоиды (в самом тяжёлом случае) или их суммы. ЧМ-модулятор - тоже генерит синусоиду.
А более простые звуковые синтезаторы вообще меандры генерят. laughing.gif
__inline__
Попробовал поставить частоту дискретизации 22050 Гц вместо 44100. Скорость всей эмуляции возросла до 75 FPS.
Из чего можно сделать вывод, что звуковая система занимает приличный ресурс времени.
jcxz
Цитата(__inline__ @ Sep 9 2017, 16:56) *
Из чего можно сделать вывод, что звуковая система занимает приличный ресурс времени.

Из чего можно сделать вывод что её тоже надо оптимизировать biggrin.gif
PS: Насчёт "параметров растактовки шины EBIU" - это правильный ход мыслей, но тут я не советчик.

Цитата(__inline__ @ Sep 9 2017, 16:23) *
1) Насколько тормозной тип double для BlackFin ? Имеет ли смысл его заменить float ?

Если ваш Blackfin не имеет аппаратной поддержки double (смотреть надо мануал на ядро), то очень тормозной - на порядки медленнее аппаратного. И если можно заменить - следует менять.
А если он не имеет аппаратной поддержки и float тоже, то лучше стараться вообще не использовать плавучку. Тем более, что она редко когда реально нужна.
_pv
Цитата(__inline__ @ Sep 9 2017, 15:23) *
А теперь вопросы:

1) https://ez.analog.com/thread/43898
2) что-то не припомню никаких граблей при одновременном использовании на EBUI sdram и асинхронной шины.

Цитата(jcxz @ Sep 9 2017, 17:07) *
Из чего можно сделать вывод что её тоже надо оптимизировать biggrin.gif

так точно.
генераторы синуса и проигрование wavов, по нормальному реализованные, не могут занимать столько же времени как та же обработка палитры для всей картинки, объёмы данных не сравнимы.
__inline__
Цитата(_pv @ Sep 9 2017, 10:19) *
2) что-то не припомню никаких граблей при одновременном использовании на EBUI sdram и асинхронной шины.

Не про то вопрос был.
Когда я меняю параметры асинхронного банка (на котором висит дисплей) ПОВТОРНО - новые параметры не вступают в силу!
В мануале написано, что не следует менять параметры контроллера шины EBIU именно AMB во время его работы (тоесть когда уже проинициализирован) - мой случай.
Тогда как их менять, если по зарез надо?
При нажатии на reset ведь как-то работает. Может есть какой-нибудь способ перезагрузить параметры шины?

Цитата
Если ваш Blackfin не имеет аппаратной поддержки double (смотреть надо мануал на ядро), то очень тормозной - на порядки медленнее аппаратного. И если можно заменить - следует менять.
А если он не имеет аппаратной поддержки и float тоже, то лучше стараться вообще не использовать плавучку. Тем более, что она редко когда реально нужна.

Это плохо!
Весь код звуковой системы на плавучке. Эмулятор был написан для x86 на языке Cи, при перекладке кода на Blackfin, будет эмуляция FPU.
_pv
Цитата(__inline__ @ Sep 9 2017, 17:33) *
Когда я меняю параметры асинхронного банка (на котором висит дисплей) ПОВТОРНО - новые параметры не вступают в силу!
В мануале написано, что не следует менять параметры контроллера шины EBIU именно AMB во время его работы (тоесть когда уже проинициализирован) - мой случай.

то есть в регистр EBIU_AMBCTL0 ничего не пишется??
а если сначала перед этим в EBIU_AMGCTL ноль записать, а потом поменять EBIU_AMBCTL0 и активировать банк обратно?
__inline__
Цитата(_pv @ Sep 9 2017, 10:19) *

Чуть-помогло (+5 FPS), сделал -fast-fp , бинарник чуть-вырос, что свидетельствует о замене либы, что радует.

Цитата
то есть в регистр EBIU_AMBCTL0 ничего не пишется??
а если сначала перед этим в EBIU_AMGCTL ноль записать, а потом поменять EBIU_AMBCTL0 и активировать банк обратно?

Принял решение временно поиграться с загрузчиком - в нем менял времянки, увы - те что стоят оптимальны по устойчивости и быстродействию.
Попытка укоротить setup, hold, write - приводит к плачевным результатам: кривая картинка на дисплее или её отсутствие.
Кстати, дисплей буферизован - он подключен к шине через микросхему буфера, чтобы избежать ёмкостного шунтирования SDRAM, которая тоже висит на шине.

Если дисплей привешать к PPI, то к нему можно будет обращаться как к массиву точек? Или PPI генерит свою развёртку ?
Дисплей PPT9999-A003-06-Q со встроенным контроллером S6E63D6.

Вот драйвер для него (тоже сам писал):

Код
/*
Display PPT9999-A003-06-Q
S6E63D6 Display Controller Driver
*/

#define OLED_Command (*(volatile u16*) 0x20000000)
#define OLED_Data    (*(volatile u16*) 0x20010000)

const u8 Font8x8[2048]=
{
//тут моноширный шрифт 8x8 пикселей кодировка DOS :)
}

u16 OLED_Key;  //Цвет прозрачности
u16 OLED_Back; //Цвет фона

void OLED_Register(u8 c,u16 d)
{
OLED_Command=c;
OLED_Data=d;
}

void OLED_Prepare(void)
{
OLED_Command=0x23;          //Select 18-/16-bit Data Bus Interface
OLED_Register(0x03,0x0111); //16-bit Mode
OLED_Register(0x10,0x0000); //IC Standby Off
OLED_Register(0x05,0x0000); //Display Off
OLED_Register(0x18,0x003D); //Frame Rate > 80 Hz
OLED_Register(0xF8,0x000F); //VGH = +5V
OLED_Register(0xF9,0x000F); //VGL = -5V
OLED_Register(0x70,0x2B80); //Gamma Top/Bottom R
OLED_Register(0x71,0x3600); //Gamma Top/Bottom G
OLED_Register(0x72,0x3E00); //Gamma Top/Bottom B
OLED_Register(0x73,0x1F19); //Gamma Top Bottom R1,2
OLED_Register(0x74,0x2214); //Gamma Top Bottom R3,4
OLED_Register(0x75,0x221B); //Gamma Top Bottom G1,2
OLED_Register(0x76,0x1E16); //Gamma Top Bottom G3,4
OLED_Register(0x77,0x241E); //Gamma Top Bottom B1,2
OLED_Register(0x78,0x2617); //Gamma Top Bottom B3,4
SimpleDelay(1000000);
OLED_Register(0x05,0x0001); //Display On
}

void OLED_Rectangle(u16 xs,u8 ys,u16 xe,u8 ye)
{
OLED_Register(0x35,319-xe);
OLED_Register(0x36,319-xs);
OLED_Register(0x37,(ys<<8)|ye);
OLED_Register(0x20,ys);
OLED_Register(0x21,319-xs);
OLED_Command=0x22;
}

void OLED_Clear(u16 Color)
{
register u32 i;
OLED_Rectangle(0,0,319,239);
for(i=0;i<320*240;i++) OLED_Data=Color;
}

void OLED_OutChar(u16 x,u8 y,u8 c,u16 k)
{
register u32 i;
OLED_Rectangle(x<<3,y<<3,(x<<3)+7,(y<<3)+7);
for(i=0;i<64;i++)
{
  if(((Font8x8[(c<<3)+(i>>3)]>>(7-(i&7)))&1)) OLED_Data=k;
  else OLED_Data=OLED_Back;
}
}

void OLED_OutString(u16 x,u8 y,u8* s,u16 k)
{
register u32 i=0;
while(s[i])
{
  OLED_OutChar(x+i,y,s[i],k);
  i++;
}
}

void OLED_OutNumber(u16 x,u8 y,u16 n,u16 k)
{
OLED_OutChar(x  ,y,(n/100)%10+'0',k);
OLED_OutChar(x+1,y,(n/ 10)%10+'0',k);
OLED_OutChar(x+2,y, n     %10+'0',k);
}

void OLED_OutSprite(u16 x,u8 y,u16 w,u8 h,const u8* s)
{
register u32 i;
register u16 c;
register u16 *S=(u16*)s;
OLED_Rectangle(x,y,x+w-1,y+h-1);
for(i=0;i<(w*h);i++)
{
  if((c=S[i])!=OLED_Key) OLED_Data=c;
  else OLED_Data=OLED_Back;
}
}
_pv
Цитата(__inline__ @ Sep 11 2017, 11:10) *
Если дисплей привешать к PPI, то к нему можно будет обращаться как к массиву точек? Или PPI генерит свою развёртку ?
Дисплей PPT9999-A003-06-Q со встроенным контроллером S6E63D6.

у этого контроллера даже есть тупо 16ти битный RGB интерфейс, который на ppi вешается без какой-либо дополнительной логики вообще, просто напрямую.
__inline__
Цитата(_pv @ Sep 11 2017, 07:44) *
у этого контроллера даже есть тупо 16ти битный RGB интерфейс, который на ppi вешается без какой-либо дополнительной логики вообще, просто напрямую.

В своё время я поостерёгся использовать PPI + RGB-интерфейс дисплея. Показалось, что постоянная отрисовка на дисплей(формирование развертки видео-сигнала) отрицательно скажется на всей работе системы в целом. Не помню чем оттолкнула эта идея, то-ли от того что надо будет выжидать освобождения памяти или ещё чего-то...
Давно это было.

Вопрос про PPI был в другом: можно ли его настроить на управление контроллером S6E63D6 или тупо видео-сигнал?
Хочется чтобы регенерацией экрана занимался именно S6E63D6 , а не ДМА Блекфина через PPI.
И шину разгрузить хотелось бы sm.gif

Речь идет об этом девайсике:

Нажмите для просмотра прикрепленного файла

Решил "тряхнуть стариной" и портировать ещё несколько эмуляторов на него
_pv
Цитата(__inline__ @ Sep 11 2017, 14:30) *
В своё время я поостерёгся использовать PPI + RGB-интерфейс дисплея. Показалось, что постоянная отрисовка на дисплей(формирование развертки видео-сигнала) отрицательно скажется на всей работе системы в целом.

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

Цитата(__inline__ @ Sep 11 2017, 14:30) *
Вопрос про PPI был в другом: можно ли его настроить на управление контроллером S6E63D6 или тупо видео-сигнал?
Хочется чтобы регенерацией экрана занимался именно S6E63D6 , а не ДМА Блекфина через PPI.
И шину разгрузить хотелось бы sm.gif


писать в регистры дисплея, в том числе и для того чтобы включить этот самый RGB (который, насколько я понял, можно использовать только для записи в видеопамять контроллера), можно и через spi.

ну и sdram в несколько раз (в 7) быстрее чем асинхронная шина дисплея, поэтому если даже завести отдельный буфер на весь экран во внешней памяти, куда будет складываться результат обработки цвета из палитры, а потом из этого же буфера DMA будет перекладывать всю картинку на экран, то даже эти дополнительные копирования туда-сюда будут быстрее чем сейчас. ну а если обрабатывать картинку построчно и сразу же выдавать через ppi, так тем более.

ну и ещё как вариант есть sport, c 133мбитами пропускной способности каждый, которых хватит на сотню fps 320*240*16бит, а параллельная 16ти разрядная шина делается из него двумя сдвиговыми регистрами.
__inline__
На самом деле при включенном кешировании арбитраж шины не слишком сильно душит производительность всей системы в целом.

Иначе, почему память видеокарт в ПК отображена на общее адресное пространство процессора?
В неё тоже можно писать и читать из нее процессором и ПК не тормозит из-за арбитража шины.

Понимаю, что видео-карта сама может ворочать в своей памяти и будет быстрее, но доступ со стороны CPU сохранен.

Ну и финальный вопрос: вы можете примерно предположить, во сколько раз должна подняться производительность системы, если дисплей перевешать на PPI?
_pv
Цитата(__inline__ @ Sep 11 2017, 15:32) *
Ну и финальный вопрос: вы можете примерно предположить, во сколько раз должна подняться производительность системы, если дисплей перевешать на PPI?

ну чисто теоретически, сейчас дисплей занимает шину на 3мс из 16 при 60фпс. вот эти 20% и сэкономите,
но из них ещё поди примерно четверть-треть времени процессор всё равно занят преобразованием цвета по палитре.

если избавитесь от плавающей запятой при работе со звуком, разница, думаю, заметнее будет.
переход на fast-fp(который раза в 3-4 быстрее обычного) дал прирост 10%fps, то есть грубо говоря 2мс процессорного времени, то есть получается что почти половина времени вашего эмулятора тратится на звук с плавучкой - 6мс, которые можно больше чем порядок сократить с целочисленной арифметикой.
__inline__
Ещё обнаружил интересную ситуацию: Visual DSP 5.1.2 даёт рабочий код, а если собрать в VDSP 5.0, то эмулятор зависает в момент когда должен появиться звук.
Если не использовать -fp-fast, то обе среды дают рабочий код.

Подозреваю, что в 5.0 -fp-fast выполнен через ОПУ и не гарантирует корректной работы с эмуляцией плавучки sm.gif))
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.