|
как побороть WinAVR?, и заставить просто скомпиллить как написано |
|
|
|
Aug 7 2008, 17:01
|
Частый гость
 
Группа: Участник
Сообщений: 152
Регистрация: 18-03-06
Пользователь №: 15 366

|
написана функция:
volatile int32_t _corr[2 * 8];//result of correlations volatile uint8_t _corr_index;//index for sample table int8_t PROGMEM _dtmf_samples[] = {...};
int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = d * pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += mul; }
в результате генерится совершенно безобразный код. умножение производится в 32-х битном виде, хотя явно написано умножение 2-х байтов включена оптимизация O3
если массив объявить как int8_t, то умножение делается для 2-х байтов, но!!!! используется mul вместо muls (хотя написано, что величины знаковые!!!) и в результате он зануляет старший байт, даже если идет приведение результата операции к int16_t
как побороть WinAVR?
что хочется: сделать знаковое умножение 2-х байтов, получить 16-ти битное значение. затем расширить его до 32-х бит, сложить с 32-х битной величиной из массива и запомнить в массиве результат.
и еще: ткните, плиз, носом где понятно написано как делать ассемблерные вставки. собственно, непонятно, как из асма взять данные из Сишной переменной и в нужную Сишную переменную отдать
|
|
|
|
|
Aug 7 2008, 19:36
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(VDV @ Aug 7 2008, 21:01)  в результате генерится совершенно безобразный код. умножение производится в 32-х битном виде, хотя явно написано умножение 2-х байтов включена оптимизация O3 Скомпилировал Ваш код, у меня 16-битное умножение: Код mul r30,r28 ; 33 *mulhi3_enh [length = 7] movw r24,r0 mul r30,r29 add r25,r0 mul r31,r28 add r25,r0 clr r1 Какая версия AVR-GCC? Цитата(VDV @ Aug 7 2008, 21:01)  и еще: ткните, плиз, носом где понятно написано как делать ассемблерные вставки. собственно, непонятно, как из асма взять данные из Сишной переменной и в нужную Сишную переменную отдать avr-libc-user-manual + исходники avr-libc. Ну и если что в деталях не понятно - вопросы на форумах. Анатолий.
|
|
|
|
|
Aug 7 2008, 20:20
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(VDV @ Aug 7 2008, 21:01)  что хочется: сделать знаковое умножение 2-х байтов, получить 16-ти битное значение. В то время как pgm_read_byte возвращает uint8_t Цитата(VDV @ Aug 7 2008, 21:01)  затем расширить его до 32-х бит, сложить с 32-х битной величиной из массива и запомнить в массиве результат. Что gcc 4.1.2 отлично делает... Советую Вам _всегда_ использовать оптимизацию по размеру: OPT = s Цитата(VDV @ Aug 7 2008, 21:01)  ткните, плиз, носом где понятно написано как делать ассемблерные вставки. http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Aug 8 2008, 04:36
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(VDV @ Aug 7 2008, 21:01)  ткните, плиз, носом где понятно написано как делать ассемблерные вставки. Ассемблерные вставки нарушают работу оптимизатора. Поэтому лучше ассемблерную часть скомпоновать в отдельную функцию и вынеси в отдельнй файл. Цитата(VDV @ Aug 7 2008, 21:01)  собственно, непонятно, как из асма взять данные из Сишной переменной и в нужную Сишную переменную отдать Что непонятного? В ассемблере переменная называется так же, как и в Си. ЗЫ. А с оптимизацей -Os не пробовал? ЗЗЫ. Откуда у народа такая мания - создавать переменные с подчеркиванием в начале? Ну везде же написано, что это не рекомендуется, так как такие имена зарезервированы для системных целей.
Сообщение отредактировал 777777 - Aug 8 2008, 04:38
|
|
|
|
|
Aug 8 2008, 06:37
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(VDV @ Aug 7 2008, 21:01)  int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = d * pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += mul; } Макрос pgm_read_byte возвращает значение типа uint8_t (unsigned char), переменная d имеет int8_t то есть signed char. Не смотря на то что AVR имеет команду MULSU, avr-gcc ее не использует и вместо умножения (s)8 * (u)8 = 16, выполняет умножение 16*16=16. 8-битные умножения signed на signed и unsigned на unsigned выполняются как 8 * 8 = 16. Вы можете попробовать изменить тип d на uint8_t и для умножения компилятор сгенерирует код с использованием инструкции MUL: Код mul r30,r16; 30 umulqihi3 [length = 3] movw r24,r0 clr r1 Вот патч для GCC для выполнения умножения 8-битных signed на unsigned с помощью MULSU. Код Index: gcc/config/avr/avr.md =================================================================== --- gcc/config/avr/avr.md (revision 134646) +++ gcc/config/avr/avr.md (working copy) @@ -906,6 +920,17 @@ [(set_attr "length" "3") (set_attr "cc" "clobber")]) +(define_insn "usmulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "a")) + (sign_extend:HI (match_operand:QI 2 "register_operand" "a"))))] + "AVR_HAVE_MUL" + "mulsu %2,%1 + movw %0,r0 + clr r1" + [(set_attr "length" "3") + (set_attr "cc" "clobber")]) + (define_expand "mulhi3" [(set (match_operand:HI 0 "register_operand" "") (mult:HI (match_operand:HI 1 "register_operand" "") Можно на написать aыm вставку для 8-битного умножения signed на unsigned. Анатолий.
Сообщение отредактировал aesok - Aug 8 2008, 06:49
|
|
|
|
|
Aug 8 2008, 07:25
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(aesok @ Aug 8 2008, 10:37)  Макрос pgm_read_byte возвращает значение типа uint8_t (unsigned char), переменная d имеет int8_t то есть signed char. Не смотря на то что AVR имеет команду MULSU, avr-gcc ее не использует и вместо умножения (s)8 * (u)8 = 16, выполняет умножение 16*16=16. 8-битные умножения signed на signed и unsigned на unsigned выполняются как 8 * 8 = 16. Вы можете попробовать изменить тип d на uint8_t и для умножения компилятор сгенерирует код с использованием инструкции MUL:
Можно на написать asm вставку для 8-битного умножения signed на unsigned. У него массив в PROGMEM int8_t !!! Надо просто привести тип до умножения и будет счастье... Код (int8_t)pgm_read_byte(&_dtmf_samples[idx]) или написать макрос для чтения int8_t: Код #define pgm_read_sbyte(x) ((int8_t)pgm_read_byte(x))
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Aug 8 2008, 08:49
|
Частый гость
 
Группа: Участник
Сообщений: 152
Регистрация: 18-03-06
Пользователь №: 15 366

|
спасибо за ответы, я ошибся написав, что он делает 32 битное умножение, сорри, он делает 16-битное с 32-х битным результатом. при этом делает 3 умножения вместо 1! явное приведение типов при умножении к знаковым не помогает - перепробовал все, что пришло в голову.
Os оптимизация работает плохо. попробуйте, например, написать код: uint16_t d = ...; d >>= 1; d >>= 1; d >>= 1;
и посмотрите что он накомпиллит. с O3 тоже не сахар, но все получше код получается. //================================== в общем, к сожалению, в итоге я так и не понял, как сделать так, чтобы компиллятор сначала умножил два 8-ми битовых знаковых числа, затем получившееся 16-ти битное значение сложил 32-х битным. Использую WinAVR20080610 в паре с AVR studio 4.14
volatile int32_t _corr[2 * 8];//result of correlations volatile uint8_t _corr_index;//index for sample table int8_t PROGMEM _dtmf_samples[] = {...};
int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = (int8_t)d * (int8_t)pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += (int32_t)mul; }
такой код не приводит к желаемому результату
Сообщение отредактировал VDV - Aug 8 2008, 09:08
|
|
|
|
|
Aug 8 2008, 13:04
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(VDV @ Aug 8 2008, 12:49)  такой код не приводит к желаемому результату -O3: Код mul r22,r14 ; movw r24,r0 mul r22,r15 add r25,r0 mul r23,r14 add r25,r0 clr r1 -Os: Код muls r30,r16 ; movw r24,r0 clr r1 Анатолий.
|
|
|
|
|
Aug 8 2008, 13:13
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(VDV @ Aug 8 2008, 11:49)  Os оптимизация работает плохо. попробуйте, например, написать код: uint16_t d = ...; d >>= 1; d >>= 1; d >>= 1; и посмотрите что он накомпиллит. с O3 тоже не сахар, но все получше код получается. А что тут пробовать? Для трёх сдвигов при -Os будет цикл, так как он действительно займёт меньше места, пусть и всего на одну команду. Для -O3 будет линейный код. Для -O2 тоже. Нет смысла для AVR использовать -O3, он слишком много разворачивает в линию, размножает тела циклов и т.п. Разве что (я в это не очень верю, но не исключаю) для отдельных особых функций, вынесенных ради этого в отдельный файл. Цитата(VDV @ Aug 8 2008, 11:49)  Использую WinAVR20080610 в паре с AVR studio 4.14
volatile int32_t _corr[2 * 8];//result of correlations volatile uint8_t _corr_index;//index for sample table int8_t PROGMEM _dtmf_samples[] = {...};
int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = (int8_t)d * (int8_t)pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += (int32_t)mul; } такой код не приводит к желаемому результату WinAVR-20071221 -OsКод muls r30,r16 movw r24,r0 clr r1 clr r26 sbrc r25,7 com r26 mov r27,r26 add r24,r20 adc r25,r21 adc r26,r22 adc r27,r23 Я не стал цитировать чтение-запись массивов и организацию цикла, только умножение-суммирование. Да, для -O2 и -O3 почему-то делает 16-битное умножение. "ну не знаю", таки недоработка оптимизатора в этих режимах. Согласен, плохо. Повторю сказанное другими - в подавляющем большинстве случаев лучше использовать -Os. Изредка -O2. Иногда лучше фрагмент на асме написать, чем от -Os отказываться. -O3 явно не для AVR. Для Вашего примера разворачивать цикл на 16 проходов в одну линию, дублируя 16 раз тело цикла - нафиг, нафиг. Ускорение не стоит увеличения объёма. Собственно, есть всего четыре варианта. 1. Использовать avr-gcc и терпеть некоторые его недостатки. 2. Использовать avr-gcc и НЕ терпеть некоторые его недостатки, включиться в работу по его улучшению. 3. Использовать спёртый IAR. 4. Использовать купленный IAR.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Aug 8 2008, 13:39
|
Частый гость
 
Группа: Участник
Сообщений: 152
Регистрация: 18-03-06
Пользователь №: 15 366

|
мда.... ни с О3 ни с Os код нормально не генерит так чтоб нормально было. С Os он циклы строит, где ему явно прописано 3 раза сдвинуть (я уж не говорю, что он зачем-то перегрузку регистров в другую пару делает. В O3 тоже, кстати, зачем-то регистры туда-сюда гоняет) O3 дает 3 умножения вместо одного Os делает байтовое умножение, только регистры туда -сюда тусует зачем-то блин, а у меня это критичные места по времени. делать асмовские вставки в сишные код ну так не хочется. от асма изначально и отказался только потому, чтоб переносимость была. вместо программирования борьба с компилем. придется, видимо, отказываться от 8-бит контроллера и переходить на ARM  компиль все ресурсы убивает в никуда
Сообщение отредактировал VDV - Aug 8 2008, 13:50
|
|
|
|
|
Aug 8 2008, 14:04
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(VDV @ Aug 8 2008, 17:39)  блин, а у меня это критичные места по времени. НЕВЕРЮ!!!!!! Код int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = d * pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += mul; } Если б дейсвительно это было для Вас критично по времени, то первым делом Вы бы вынесли "int16_t mul = d * pgm_read_byte( &_dtmf_samples[_corr_index] );" из цикла. Анатолий.
Сообщение отредактировал aesok - Aug 8 2008, 14:11
|
|
|
|
|
Aug 8 2008, 14:14
|
Частый гость
 
Группа: Участник
Сообщений: 152
Регистрация: 18-03-06
Пользователь №: 15 366

|
Цитата(aesok @ Aug 8 2008, 18:04)  НЕВЕРЮ!!!!!! Код int8_t d = ADCH; for( uint8_t i = 16; i--; ) { int16_t mul = d * pgm_read_byte( &_dtmf_samples[_corr_index] ); _corr[i] += mul; } Если б дейсвительно это было для Вас критично по времени, то первым делом Вы бы вынесли "pgm_read_byte( &_dtmf_samples[_corr_index] )" из цикла. Анатолий. операция lpm r0, z выполняется быстро и умножение всего 2 такта. так что весь кусок поги занял бы очень мало времени гораздно быстрее, чем 3 умножения, которые туда лепит компиль или, при другой оптимизации, дважды туда-сюда гоняет данные из регистров. и раположение таблицы в памяти программ - это обычное дело. самый обычный корреляционный алгоритм. еще на 8051 делал с его 1 мипсом. и уж организация цикла для тройного сдвига явно медленнее, чем три раза подряд сдвинуть. + экономии памяти точно никакой - достаточно написать на асме код и убедиться в этом.
Сообщение отредактировал VDV - Aug 8 2008, 14:18
|
|
|
|
|
Aug 8 2008, 14:20
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(VDV @ Aug 8 2008, 18:14)  А вот ЭТО "&_dtmf_samples[_corr_index]" сколько выполняеться Вы не заметили? И вообще зачем в цикле выполнять код который не зависит от пременной цикла???? Во всех учебниках советуют такой код выносить из цикла. достаточно написать на асме код и убедиться в этом: Код ldi r18,3; 85 *lshrhi3_const/5 [length = 5] 1: lsr r25 ror r24 dec r18 brne 1b 5 инструкций. 3 последовательных сдвига на 1 это 6. Что короче? Поэтому на -Os цикл, на -O3 - 3 сдвига. Анатолий.
Сообщение отредактировал aesok - Aug 8 2008, 14:27
|
|
|
|
|
Aug 8 2008, 14:57
|
Частый гость
 
Группа: Участник
Сообщений: 152
Регистрация: 18-03-06
Пользователь №: 15 366

|
код приведен для пояснения смысла проблемы, а не объяснения работы программы. ес-но правильный цикл несколько другой. чем проще код в примере, тем проще понять суть проблемы.
по поводу сдвига, если на асме: если написать подряд, то получается 6 команд и 6 тактов если написать цикл, то получается 5 команд и 17 тактов
если скомпиллить O3, получаем 10 команд и 10 тактов если скомпиллить Os, получаем 9 команд и 21 такт лишние команды - это пересылки туда-сюда до сдвига и обратно после сдвига (совершенно не понимаю, зачем компиль их делает)
итого: проигрышь по объему и по скорости от 2-х до почти 4-х раз
Сообщение отредактировал VDV - Aug 8 2008, 14:59
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|