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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> IAR AVR (Cи). вопрос о указателе на глобальную структуру, как заставить компилятор использовать рег. Z как постоянный указатель
ibiza11
сообщение Apr 1 2011, 12:12
Сообщение #1


Участник
*

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



Доброго времени суток уважаемые форумчане. На этом форуме давно, но отписываюсь не часто, практически по всем вопросам есть информация, за что всем посетителям и администрации поклон в ноги.
При написании кода всегда обращаю внимание на генерируемый ассемблерный листинг, при этом не заострял внимания на указатели.
Недавно обратил внимание на оч. интересный документ AVR035:Эффетивное программирование на Си для AVR (очень жалею, что давно его не прочел) sad.gif
В нем написан интересный способ уменьшения объема кода в случае использования множетсва глобальных переменных. Там предлагается объявить эти переменные как глобальную структуру и обращаться к каждой переменной
как к элементу структуры, при этом указатель на структуру всегда используется один и тот же, а переменные считываются командой 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, все кардинально меняется!!! blink.gif
Вот исходник
Цитата
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
Go to the top of the page
 
+Quote Post
pwl
сообщение Apr 1 2011, 17:07
Сообщение #2





Группа: Новичок
Сообщений: 3
Регистрация: 1-04-11
Пользователь №: 64 064



Цитата(ibiza11 @ Apr 1 2011, 19:12) *
НО только я добавляю в цикл функцию delay, все кардинально меняется!!! blink.gif

Видимо, как и любую другую. Ибо:

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. может поможет.
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 1 2011, 20:04
Сообщение #3


Участник
*

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



Спасибо за ответ! sm.gif единственный из всех, кто просмотрел) Теряюсь в догадках как
внешнюю функцию объявить static или inline, только если в исходном файле, где находится тело функции, но ведь тогда она везде будет инлайновая/статическая.
В общем не очень помогло sad.gif
Да и довольно странно, что функция, после выхода из нее оставляет свои переменные в регистре Z. Даже если она и оставляет какие то статические переменные в памяти, то уж явно не в регистре, а запишет в ОЗУ. Думаю дело не в этом.
В то же время не знаю в чем дело.... Помогите, кто чем может... или никто никогда об этом не задумывался?
Блин, хоть на ассемблере пиши... wacko.gif
Go to the top of the page
 
+Quote Post
pwl
сообщение Apr 2 2011, 00:19
Сообщение #4





Группа: Новичок
Сообщений: 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 части и положено на нем писать..
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 2 2011, 06:33
Сообщение #5


Участник
*

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



Так ведь указатель инициализируется только после функции, пусть функция делает что хочет со своими регистрами, потом то функция не вызывается....
И почему заново инициализация не происходит именно в Z после каждого вызова?
Go to the top of the page
 
+Quote Post
pwl
сообщение Apr 2 2011, 17:49
Сообщение #6





Группа: Новичок
Сообщений: 3
Регистрация: 1-04-11
Пользователь №: 64 064



Цитата(ibiza11 @ Apr 2 2011, 13:33) *
Так ведь указатель инициализируется только после функции, пусть функция делает что хочет со своими регистрами, потом то функция не вызывается....
И почему заново инициализация не происходит именно в Z после каждого вызова?
пардон. не углядел. положился на фразу:

НО только я добавляю в цикл функцию delay, все кардинально меняется!!!

ну чо. глюкавый оптимизтор у IAR. больше сказать нечего.


Сообщение отредактировал pwl - Apr 2 2011, 17:53
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 4 2011, 05:49
Сообщение #7


Участник
*

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



не знаю как бороться с этим, придется пока смириться. но вопрос остается открытым, так что если еще кому-нибудь есть что-нибудь сказать по теме, милости прошу)
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Apr 4 2011, 09:10
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Во-первых необязательно глобальные переменные объединять в структуру. Компилятор и так в большинстве случаев будет использовать LDD/STD.
Во-вторых работа с часами, ну никак не тянет на критически по времени важный участок программы. Притянуто за уши.
В-третьих. По какой-то причине компилятор посчитал, что эффективней работать именно так а не иначе. Причину такого решения трудно понять по нескольким строчкам. Я не помню чтобы компилятор у меня так поступал. Что-то повлияло, но вряд-ли простое применение п/п. У меня десятки п/п и при этом компилятор в большинстве случаев применяет LDD.
В-четвёртых. Да действительно можно повлиять на результат компиляции программы. В некоторых случаях даже введение промежуточной локальной переменной приводит к изменению компиляции. Применение указателей тоже порой ведёт к оверхеду а иногда наоборот. (Я имею ввиду что применение указателей либо массивов с индексами, в разных случаях дают разные эффекты.) В общем случае хорошее знание стуктуры процессора и его системы комманд, часто позволяют оценивать предварительно как именно надо писать программу.
Но ... самое главное ... не стоит на этом зацикливаться. Надо понимать что таким образом мы привязываем программу к определённой архитектуре. По-моему, лучше этого не делать. Лучше оценить программу на критическую по времени часть и некритическую. Минимизировать критическую. Обособить её. Оценить потребность в вылизывании её и только после этого приступать к её вылизыванию. А не делать огульные мероприятия. Основной критерий красоты программы - есть её ЯСНОСТЬ. пусть и в ущерб эффективности.

Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 4 2011, 12:43
Сообщение #9


Участник
*

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



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


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>


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 4 2011, 16:04
Сообщение #11


Участник
*

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



to =GM=, спасибо) blush.gif возможно, что я пробовал менять типы данных, чтобы посмотреть как ведет себя при этом IAR, при этом возможно, что я дизассемблер простого варианта скопипастил, а код уже нового варианта.
В вашем примере от WinAVR применена какая-нибудь оптимизация или это дебаг вариант? WinAVR также не использовал косвенную адресацию, а именно прямую (LDS/STS), как и IAR при оптимизации кода по скорости выполнения.
Go to the top of the page
 
+Quote Post
=GM=
сообщение Apr 4 2011, 22:19
Сообщение #12


Ambidexter
*****

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



Ну так, приведите соответствующий код. Оптимизация в WinAVR была максимальная -Os.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 5 2011, 06:56
Сообщение #13


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
=GM=
сообщение Apr 5 2011, 11:36
Сообщение #14


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;

Компиляторы время от времени бывают туповаты, глядишь и сработает.


--------------------
Делай сразу хорошо, плохо само получится
Go to the top of the page
 
+Quote Post
ibiza11
сообщение Apr 5 2011, 12:42
Сообщение #15


Участник
*

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



Простите за недопониманиеsm.gif Не хотел обидеть sm.gif
Я тоже думаю писать все-таки на Ассемблере. Спасибо за ответ) IAR не туповат, это я не уследил просто. Изменение порядка объявления указателя и вызова функции ничего не далоsm.gif В общем думаю вопрос исчерпан, надо смириться с величиной Си кода в угоду его удобности.
Цитата
После оптимизации на данном фрагменте 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
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 27th June 2025 - 18:21
Рейтинг@Mail.ru


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