реклама на сайте
подробности

 
 
4 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> BF533 отрисовка на дисплей, шина EBIU
__inline__
сообщение Sep 5 2017, 06:12
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 30
Регистрация: 5-09-17
Пользователь №: 99 126



Здравствуйте.

Использую 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, ничего не меняется. Как можно пере-инициализировать контроллер асинхронной шины, когда он уже работает?

Сообщение отредактировал __inline__ - Sep 5 2017, 06:14
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 06:33
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



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

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

PS: Вопрос про PIXEL снимается - не заметил сразу (кстати формат PIXEL у Вас неправильный).
Вам нужно посмотреть во что компилируется ваш PIXEL (асм). Ну и изучить ассемблер этого ядра.
Go to the top of the page
 
+Quote Post
__inline__
сообщение Sep 5 2017, 07:29
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 30
Регистрация: 5-09-17
Пользователь №: 99 126



Быстро, в смысле уменьшить время перекидывания буфера на экран. Перерисовка экрана полная.

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

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

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

Прикрепленный файл  asm.txt ( 35.19 килобайт ) Кол-во скачиваний: 10


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

Это два пиксела по 16 бит каждый.
Go to the top of the page
 
+Quote Post
_pv
сообщение Sep 5 2017, 09:09
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 193
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954



зачем на блэкфинах разворачивать циклы, они там и так аппаратные? в таких простых случаях компилятор вроде должен догадываться их использовать.
заведите буфер на строку или две во внутренней памяти, заполняйте её пикселями и потом отдавайте DMA, пусть перекладывает. а то и два канала дма, один на чтение из памяти - другой на запись на экран. причем не попиксельно, а кусками из sdrama чтение должно быть быстрее. я честно говоря не знаю что там контроллер sdrama делает когда ему ещё на EBUI вклиниваются с отбиранием шины через каждое чтение.
ну и этот Draw_Window обязательно из внешней памяти выполнять?
я так понимаю шина наружу там и без того узким местом становится, процессор из палитры цвет достанет быстрее чем прочитает из внешней памяти индекс цвета, а потом запишет обратно пиксель на дисплей. кэширование конечно как-то поможет, но зачем специально усложнять.
добейтесь сначала нормальной работы из внутренней памяти, а потом смотрите что будет при переносе во внешнюю.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 13:06
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(__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();
...
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 14:17
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(_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.
Прикрепленный файл  asm.txt ( 35.19 килобайт ) Кол-во скачиваний: 10

Я к сожалению не знаю асма Блэкфина, но на первый взгляд видно мало распараллеливания операций.
И в то же время много лишних пересылок между регистрами (выполняющихся не в параллель другим командам).
Если проанализировать операции для одного пикселя:
Код
  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) который я знаю, потребовалось бы гораздо меньше инструкций. И не надо разворачивать всю строку - так будет только медленнее.

Так что - надо учить ассемблер, чтобы написать быстрее.
Go to the top of the page
 
+Quote Post
_pv
сообщение Sep 5 2017, 14:25
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 2 193
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954



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

я так понимаю достать цвет из палитры процессору быстрее чем даже просто прочитать его индекс из внешней sdram, а его ещё потом в дисплей записать надо. причём не уверен что контроллеру динамической памяти понравится что у него шину постоянно забирают через каждый пиксель, вычитать кусок может оказаться куда быстрее. там же BURST какой-то должен быть тогда.
и получается что процессор будет только и делать что ждать внешнюю шину.
а так зарядили дма на чтение, в это время можно ещё чем-нибудь общественно полезным заняться, а как только прочиталось быстро перепахали строку и опять же дма отдали для записи в дисплей.
Go to the top of the page
 
+Quote Post
__inline__
сообщение Sep 5 2017, 14:49
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 30
Регистрация: 5-09-17
Пользователь №: 99 126



Цитата(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 МБайт.

Сообщение отредактировал __inline__ - Sep 5 2017, 14:50
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 15:13
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(_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 цветов в картинке мне в том проекте было достаточно.
Go to the top of the page
 
+Quote Post
_pv
сообщение Sep 5 2017, 15:26
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 2 193
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954



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

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

только вот шину у контроллера отбирают сразу же после каждого чтения, для записи в дисплей. и я что-то совсем не уверен что он в таком режиме будет хоть что-то читать, пусть даже и в кэш, бурстами. а это и получается самое узкое место.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 15:41
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



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

Когда автор изучит ассемблер своего DSP, он в начале итерации цикла напишет что-то типа:
LDM R0!, {R1-R8} ;сорри за ARM wink.gif
и получит burst-чтение SDRAM laughing.gif
Go to the top of the page
 
+Quote Post
_pv
сообщение Sep 5 2017, 15:51
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 193
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954



ну да, буфер можно и не на всю строку, а на несколько пикселей, но смысл тот же.
просто пока строка подгружается через ДМА процессор может что-нибудь ещё сделать, (переработать другую половину буфера), а тут он всё равно просто тупо будет ждать шину.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 5 2017, 23:04
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 3 643
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(_pv @ Sep 5 2017, 18:51) *
ну да, буфер можно и не на всю строку, а на несколько пикселей, но смысл тот же.
просто пока строка подгружается через ДМА процессор может что-нибудь ещё сделать, (переработать другую половину буфера), а тут он всё равно просто тупо будет ждать шину.

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

Да Вы сами посмотрите на выделенный мной выше фрагмент для пары пикселей - даже не зная архитектуры данного DSP мне он кажется очень неоптимальным - похоже займёт не менее 11 тактов(!) (это если там stall-ов нет). В то время как на аналогичном DSP с ядром C55xx (архитектуру которого я ещё более-менее помню) этот фрагмент займёт думаю примерно 3-4 такта (если написать его на асме оптимально).
Первые 4 команды наверняка можно выполнить за одну команду (возможно что ещё и с сохранением данных из предыдущего шага в параллель). Далее команда - абсолютно лишняя - на каждую пару пикселей заново перегружается адрес начала палитры. Зачем??? он же - константа. Далее - опять лишние команды программного вычисления адреса сложением - неужели в системе команд Блэкфина нет команд с косвенно-базовой адресацией? Это ещё минус 2 команды. Ну и плюс - наверняка тут возможно распараллеливание операций (операция OR думаю запараллелится с чтением или записью в память).
Go to the top of the page
 
+Quote Post
__inline__
сообщение Sep 5 2017, 23:23
Сообщение #14


Участник
*

Группа: Участник
Сообщений: 30
Регистрация: 5-09-17
Пользователь №: 99 126



По-разному пробовал:

Код
//#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 байт за раз переслать можно аналогично!
Go to the top of the page
 
+Quote Post
__inline__
сообщение Sep 6 2017, 05:42
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 30
Регистрация: 5-09-17
Пользователь №: 99 126



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

Смотрел внимательно тут:
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;"); \
Go to the top of the page
 
+Quote Post

4 страниц V   1 2 3 > » 
Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 24th September 2017 - 19:26
Рейтинг@Mail.ru


Страница сгенерированна за 0.01577 секунд с 7
ELECTRONIX ©2004-2016