|
IAR AVR (Cи). вопрос о указателе на глобальную структуру, как заставить компилятор использовать рег. Z как постоянный указатель |
|
|
|
Apr 1 2011, 12:12
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
Доброго времени суток уважаемые форумчане. На этом форуме давно, но отписываюсь не часто, практически по всем вопросам есть информация, за что всем посетителям и администрации поклон в ноги. При написании кода всегда обращаю внимание на генерируемый ассемблерный листинг, при этом не заострял внимания на указатели. Недавно обратил внимание на оч. интересный документ AVR035:Эффетивное программирование на Си для AVR (очень жалею, что давно его не прочел) В нем написан интересный способ уменьшения объема кода в случае использования множетсва глобальных переменных. Там предлагается объявить эти переменные как глобальную структуру и обращаться к каждой переменной как к элементу структуры, при этом указатель на структуру всегда используется один и тот же, а переменные считываются командой LDD r16,Z+30 (соответственно командой STD записываются обратно). плюс этой команды - использование считывания/записи со смещением, не изменяя при этом регистр Z. я попробовал повторить пример из этой Application Note, все получилось как надо: Цитата typedef struct { int hour; char min; int sec; }t; t global;
__C_task void main1(void) { t *time=&global; if(++time->sec==60) { time->sec=90; time->min=70; } } а это ассемблерный листинг, который генерируется в итоге: Цитата // 10 { // 11 t *time=&global; LDI R16, LOW(global) LDI R17, (global) >> 8 MOVW R31:R30, R17:R16 // 12 if(++time->sec==60) LDD R16, Z+2 INC R16 STD Z+2, R16 CPI R16, 60 BRNE ??main1_0 // 13 { // 14 time->sec=90; LDI R16, 90 STD Z+2, R16 // 15 time->min=70; LDI R16, 70 STD Z+1, R16 // 16 } // 17 } Обратите внимание, что указатель инициализируется сразу в регистры r30,r31 (Z-регистр) и далее вообще не изменяется, а только используется с командами LDD, STD. НО только я добавляю в цикл функцию delay, все кардинально меняется!!! Вот исходник Цитата typedef struct { int hour; char min; int sec; }t; t global; extern void delay(unsigned int);
__C_task void main1(void) { delay(107); t *time=&global; if(++time->sec==60) { time->sec=90; time->min=70; } } А вот ассемблерный листинг Цитата // 12 delay(107); FUNCALL main1, delay LOCFRAME CSTACK, 0, STACK LOCFRAME RSTACK, 2, STACK ARGFRAME RSTACK, 0, STACK LDI R16, 107 LDI R17, 0 RCALL delay // 13 t *time=&global; LDI R16, LOW(global) LDI R17, (global) >> 8 MOVW R27:R26, R17:R16 // 14 if(++time->sec==60) ADIW R27:R26, 2 LD R16, X SBIW R27:R26, 2 INC R16 ADIW R27:R26, 2 ST X, R16 SBIW R27:R26, 2 CPI R16, 60 BRNE ??main1_0 // 15 { // 16 time->sec=90; LDI R16, 90 ADIW R27:R26, 2 ST X, R16 SBIW R27:R26, 2 // 17 time->min=70; LDI R16, 70 ADIW R27:R26, 1 ST X, R16 SBIW R27:R26, 1 // 18 } // 19 } Обратите внимание, что теперь компилятору нафиг не сдался удобный регистр Z и команды LDD и STD вместе взятые. Вместо них он использует регистр X и каждый раз инкрементирует его на необходимый сдвиг, а считывает и записывает командами LD, ST, что естественно занимает больше времени чем команды LDD и STD. При оптимизации по скорости выполнения вообще заменяет указатель константой и обращается к каждому элементу структуры через команды STS LDS (занимающие в два раза больше программной памяти). Вот код функции delay: Цитата void delay(volatile unsigned int ticks) { while(ticks) ticks--; } volatile добавлен исключительно для того, чтобы компилятор не выбросил задержку из кода при оптимизации. Теперь вопрос: Объясните почему так поступает компилятор с командами LDD и STD, и как ему указать что так делать не нужно? Спасибо за внимание и понимание) p.s. Если разместил тему не в том разделе, прошу модераторов перенести ее туда, где будет актуальнее. уважаемые модераторы, изменил <codebox> на <quote> удобнее читать и есть возможность изменять цвет. в <code> нет возможности менять цвет текста.
Сообщение отредактировал ibiza11 - Apr 1 2011, 16:08
|
|
|
|
|
Apr 1 2011, 17:07
|
Группа: Новичок
Сообщений: 3
Регистрация: 1-04-11
Пользователь №: 64 064

|
Цитата(ibiza11 @ Apr 1 2011, 19:12)  НО только я добавляю в цикл функцию delay, все кардинально меняется!!!  Видимо, как и любую другую. Ибо: What registers are used by the C compiler? Data types: char is 8 bits, int is 16 bits, long is 32 bits, long long is 64 bits, float and double are 32 bits (this is the only supported floating point format), pointers are 16 bits (function pointers are word addresses, to allow addressing up to 128K program memory space). There is a -mint8 option (see Options for the C compiler avr-gcc) to make int 8 bits, but that is not supported by avr-libc and violates C standards (int must be at least 16 bits). It may be removed in a future release. Call-used registers (r18-r27, r30-r31): May be allocated by gcc for local data. You may use them freely in assembler subroutines. Calling C subroutines can clobber any of them - the caller is responsible for saving and restoring. Call-saved registers (r2-r17, r28-r29): May be allocated by gcc for local data. Calling C subroutines leaves them unchanged. Assembler subroutines are responsible for saving and restoring these registers, if changed. r29:r28 (Y pointer) is used as a frame pointer (points to local data on stack) if necessary. The requirement for the callee to save/preserve the contents of these registers even applies in situations where the compiler assigns them for argument passing. Fixed registers (r0, r1): Never allocated by gcc for local data, but often used for fixed purposes: это про gcc, но вроде у них похожие abi. можно попробовать объявить delay как static или inline. может поможет.
|
|
|
|
|
Apr 1 2011, 20:04
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
Спасибо за ответ!  единственный из всех, кто просмотрел) Теряюсь в догадках как внешнюю функцию объявить static или inline, только если в исходном файле, где находится тело функции, но ведь тогда она везде будет инлайновая/статическая. В общем не очень помогло  Да и довольно странно, что функция, после выхода из нее оставляет свои переменные в регистре Z. Даже если она и оставляет какие то статические переменные в памяти, то уж явно не в регистре, а запишет в ОЗУ. Думаю дело не в этом. В то же время не знаю в чем дело.... Помогите, кто чем может... или никто никогда об этом не задумывался? Блин, хоть на ассемблере пиши...
|
|
|
|
|
Apr 2 2011, 00:19
|
Группа: Новичок
Сообщений: 3
Регистрация: 1-04-11
Пользователь №: 64 064

|
Цитата(ibiza11 @ Apr 2 2011, 03:04)  Да и довольно странно, что функция, после выхода из нее оставляет свои переменные в регистре Z. Даже если она и оставляет какие то статические переменные в памяти, то уж явно не в регистре, а запишет в ОЗУ. Думаю дело не в этом. нет. не в этом. а в том, что регистр Z (r30:r31), не обязан быть сохранен вызываемой функцией (входит в Call-used registers). поэтому не имея дополнительных сведений о функции, компилятор не имеет права предпологать что она не испортит значение регистра. а значит должен будет его загрузить заново после каждого вызова. вот если-бы функция была локальной (static), то он мог-бы попытаться применить inter-procedural optimization. а для внешней функции он обязан предпологать худшее. Цитата(ibiza11) Блин, хоть на ассемблере пиши... ну дык. time critical части и положено на нем писать..
|
|
|
|
|
Apr 2 2011, 17:49
|
Группа: Новичок
Сообщений: 3
Регистрация: 1-04-11
Пользователь №: 64 064

|
Цитата(ibiza11 @ Apr 2 2011, 13:33)  Так ведь указатель инициализируется только после функции, пусть функция делает что хочет со своими регистрами, потом то функция не вызывается.... И почему заново инициализация не происходит именно в Z после каждого вызова? пардон. не углядел. положился на фразу: НО только я добавляю в цикл функцию delay, все кардинально меняется!!! ну чо. глюкавый оптимизтор у IAR. больше сказать нечего.
Сообщение отредактировал pwl - Apr 2 2011, 17:53
|
|
|
|
|
Apr 4 2011, 12:43
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
Спасибо за ответ. Отвечать буду тоже по пунктам (если есть что добавить или поправить) 1) Странно, но лично у меня IAR чаще использует LDS/STS (с оптимизацией по скорости выполнения), LD/ST (без оптимизации и глобальной структуры), а не LDD/STD 2) Программа была из Appnote AVR035, просто в качестве примера, на самом деле из-за часов я не начинал бы подобной дискуссии 3) Повлиял именно ввод вызова функции перед основным циклом. Листинг приведен полностью, можете попробовать у себя скомпилить, посмотреть что выдаст? И может сюда отпишете, если будет что-то отличаться? 4) Был приведен пример именно из Аппноута от производителя микроконтроллеров AVR. Именно они рекомендовали так структурировать переменные исходя из своей архитектуры. Все-таки порой эффективность стоит раньше в списке требований, нежели ясность.
|
|
|
|
|
Apr 4 2011, 13:56
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
2ibiza11 Как-то некрасиво себя IAR ведёт. В структуре вы описали sec как int sec, а IAR работает с ней, как с байтовой переменной: // 12 if(++time->sec==60) LDD R16, Z+2 INC R16 STD Z+2, R16
Что не есть гут. WINAVR более корректен, раз sec двухбайтовая переменная, то он так и работает с ней. if(++time->sec==60) be: 80 91 93 01 lds r24, 0x0193 c2: 90 91 94 01 lds r25, 0x0194 c6: 01 96 adiw r24, 0x01 ; 1 c8: 90 93 94 01 sts 0x0194, r25 cc: 80 93 93 01 sts 0x0193, r24 d0: cc 97 sbiw r24, 0x3c ; 60 d2: 49 f4 brne .+18 ; 0xe6 <main+0x5c>
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Apr 4 2011, 16:04
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
to =GM=, спасибо)  возможно, что я пробовал менять типы данных, чтобы посмотреть как ведет себя при этом IAR, при этом возможно, что я дизассемблер простого варианта скопипастил, а код уже нового варианта. В вашем примере от WinAVR применена какая-нибудь оптимизация или это дебаг вариант? WinAVR также не использовал косвенную адресацию, а именно прямую (LDS/STS), как и IAR при оптимизации кода по скорости выполнения.
|
|
|
|
|
Apr 5 2011, 06:56
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
Не понимаю, зачем Вам это, с таким упреком сказали "выложите соответствующий код". Тип переменной не повлияет на обращение с указателем на структуру. Вот не поленился: код с закоментированной функцией delay1
Цитата typedef struct { _int hour; _char min; _int sec; }t; t global; void delay1(unsigned int);//прототип функции, тело функции ниже
__C_task void main1(void) { _t *time=&global; _//delay1(107); _if(++time->sec==60) _{ ___time->sec=90; ___time->min=70; _} } Ассемблерный листинг:
Цитата // 13 t *time=&global; ........LDI R16, LOW(global) ........LDI R17, (global) >> 8 ........MOVW R31:R30, R17:R16 // 14 //delay1(107); // 15 if(++time->sec==60) ........LDD R24, Z+3 ........LDD R25, Z+4 ........ADIW R25:R24, 1 ........STD Z+3, R24 ........STD Z+4, R25 ........CPI R24, 60 ........LDI R16, 0 ........CPC R25, R16 ........BRNE ??main1_0 // 16 { // 17 time->sec=90; ........LDI R16, 90 ........LDI R17, 0 ........STD Z+3, R16 ........STD Z+4, R17 // 18 time->min=70; ........LDI R16, 70 ........STD Z+2, R16 // 19 } // 20 }
Ассемблерный листинг с функцией delay1:
Цитата // 13 t *time=&global; ........LDI R16, LOW(global) ........LDI R17, (global) >> 8 ........MOVW R27:R26, R17:R16 // 14 delay1(107); ........LDI R16, 107 ........LDI R17, 0 ........RCALL delay1 // 15 if(++time->sec==60) ........ADIW R27:R26, 3 ........LD R24, X+ ........LD R25, X ........SBIW R27:R26, 4 ........ADIW R25:R24, 1 ........ADIW R27:R26, 3 ........ST X+, R24 ........ST X, R25 ........SBIW R27:R26, 4 ........CPI R24, 60 ........LDI R16, 0 ........CPC R25, R16 ........BRNE ??main1_0 // 16 { // 17 time->sec=90; ........LDI R16, 90 ........LDI R17, 0 ........ADIW R27:R26, 3 ........ST X+, R16 ........ST X, R17 ........SBIW R27:R26, 4 // 18 time->min=70; ........LDI R16, 70 ........ADIW R27:R26, 2 ........ST X, R16 ........SBIW R27:R26, 2 // 19 } // 20 } как видите, симптомы те же)
Сообщение отредактировал ibiza11 - Apr 5 2011, 07:18
|
|
|
|
|
Apr 5 2011, 11:36
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(ibiza11 @ Apr 5 2011, 05:56)  Не понимаю, зачем Вам это, с таким упреком сказали "выложите соответствующий код" Да ну, какие упрёки. Если бы вы не сказали "возможно, что я дизассемблер простого варианта скопипастил, а код уже нового варианта", а просто проверили бы коды на соответствие и сообщили бы, то и моего поста не было бы. А так, я уж было подумал, что компилятор иар не совсем адекватный, решил посмотреть, что там унутре... Тем не менее, нет худа без добра. После оптимизации на данном фрагменте winavr дал 7 слов кода, а iar - 12 слов, несмотря на использование z-регистра. Конечно, это может ничего не значить, может быть в другом месте iar вырвется вперёд, но может и стоит задуматься. Тут соглашусь с СашейВитебским, не надо выкручивать руки си-компилятору и добиваться от него сиюминутной оптимизации, которая однако может сделать вашу программу зависимой не только от компилятора, но даже от его версии. Для себя я решил так, критичные куски выделяю в ассемблерную подпрограмму, остальное на си. Даже представить себе не можете, как упростилась жизнь эмбеддера :-) А раньше я пыхтел, вставляя инлайновые куски, кто только их придумал.. Ещё подумал, как горю помочь. Попробуйте переставить операторы _t *time=&global; _delay1(107); вот так _delay1(107); _t *time=&global; Компиляторы время от времени бывают туповаты, глядишь и сработает.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Apr 5 2011, 12:42
|

Участник

Группа: Участник
Сообщений: 54
Регистрация: 13-01-09
Пользователь №: 43 304

|
Простите за недопонимание Не хотел обидеть  Я тоже думаю писать все-таки на Ассемблере. Спасибо за ответ) IAR не туповат, это я не уследил просто. Изменение порядка объявления указателя и вызова функции ничего не дало В общем думаю вопрос исчерпан, надо смириться с величиной Си кода в угоду его удобности.
Цитата После оптимизации на данном фрагменте winavr дал 7 слов кода, а iar - 12 слов, несмотря на использование z-регистра. здесь не соглашусь. IAR получил 9 слов кода при использовании Z-регистра, а WinAVR получил 11 слов, поскольку инструкции LDS/STS занимают по два слова. IAR:
Цитата // 15 if(++time->sec==60) LDD R24, Z+3 LDD R25, Z+4 ADIW R25:R24, 1 STD Z+3, R24 STD Z+4, R25 CPI R24, 60 LDI R16, 0 CPC R25, R16 BRNE ??main1_0 WinAVR:
Цитата if(++time->sec==60) be: 80 91 93 01 lds r24, 0x0193 c2: 90 91 94 01 lds r25, 0x0194 c6: 01 96 adiw r24, 0x01 ; 1 c8: 90 93 94 01 sts 0x0194, r25 cc: 80 93 93 01 sts 0x0193, r24 d0: cc 97 sbiw r24, 0x3c ; 60 d2: 49 f4 brne .+18 ; 0xe6 <main+0x5c>
Сообщение отредактировал ibiza11 - Apr 5 2011, 13:04
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|