Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: GCC: Как избежать избыточного пользования стеком в прерываниях
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
MaxiMuz
К примеру имеется прерывание:
Код
ISR (INT0_vect)
   {    
       uint8_t temp;
       temp=PORTB;
       OnSound (temp);
   }

avr-gcc 4.7.1. кодит его как:
Код
ISR (INT0_vect)
   {    
    23e:    1f 92           push    r1
    240:    0f 92           push    r0
    242:    0f b6           in    r0, 0x3f ; 63
    244:    0f 92           push    r0
    246:    11 24           eor    r1, r1
    248:    3f 93           push    r19
    24a:    4f 93           push    r20
    24c:    5f 93           push    r21
    24e:    6f 93           push    r22
    250:    7f 93           push    r23
    252:    8f 93           push    r24
    254:    9f 93           push    r25
    256:    af 93           push    r26
    258:    bf 93           push    r27
    25a:    ef 93           push    r30
    25c:    ff 93           push    r31
       uint8_t temp;
       temp=PORTB;
    25e:    88 b3           in    r24, 0x18 ; 24
       OnSound (temp);
    260:    e4 df           rcall    .-56   ; 0x22a <OnSound>
   }
    262:    ff 91           pop    r31
    264:    ef 91           pop    r30
    266:    bf 91           pop    r27
    268:    af 91           pop    r26
    26a:    9f 91           pop    r25
    26c:    8f 91           pop    r24
    26e:    7f 91           pop    r23
    270:    6f 91           pop    r22
    272:    5f 91           pop    r21
    274:    4f 91           pop    r20
    276:    3f 91           pop    r19
    278:    0f 90           pop    r0
    27a:    0f be           out    0x3f, r0 ; 63
    27c:    0f 90           pop    r0
    27e:    1f 90           pop    r1
    280:    18 95           reti

собственно понятно, созранение всех задействованных регистров - перестраховка на случай если расстерянный программист в процедуре внутри прерывания изменит содержимое всех регистров. Если прерывание простое можно добавить аттрибут: "__attribute__((__naked__))" и iret и нет проблем.
А как сделать так чтобы компилятор сохранял только задействованные в конретном прерывании регистры ?
Kompot
Цитата(MaxiMuz @ Apr 25 2013, 15:36) *
А как сделать так чтобы компилятор сохранял только задействованные в конретном прерывании регистры ?


Ну как минимум не вызывать функций из прерывания.
OnSound (temp);

Или написать обработчик самому на ассемблере.
MrYuran
В прерываниях нужно делать только самые неотложные вещи.
В идеале - выставить флаг и неспешно парсить его в мэйне.
Локальные переменные, вызовы функций (особенно по указателю) вызывают дополнительные накладные расходы
neiver
Как вариант можно сделать вызываемую в перрывании функцию inline (естественно тело функции должно быть видимо компилятору в месе вызова). Тогда сохраняться будут только действительно используемые регистры.
MaxiMuz
Цитата(neiver @ Apr 25 2013, 17:30) *
Как вариант можно сделать вызываемую в перрывании функцию inline (естественно тело функции должно быть видимо компилятору в месе вызова). Тогда сохраняться будут только действительно используемые регистры.
неполучится! дело в том что в вызваемой ф-ии есть другая ф-ия которую убирать нельзя. вот такая засада. wacko.gif

чтобы обойти использование ф-ии в прерывании попробывал поставить команду goto m1; - переход в main на нужный участок (судьба сохраненных в стеке данных меня уже не беспокоит) , не фига выдает ошибку:
Цитата
error: label 'm1' used but not defined

что делать ?

интересно в IAR такиеже проблемы ?
neiver
А чтотак жалко несколько "лишних" регистров, сохраненных в стек?
MaxiMuz
Цитата(neiver @ Apr 25 2013, 18:05) *
А чтотак жалко несколько "лишних" регистров, сохраненных в стек?

нет , тактов не хватает
_Артём_
Цитата(MaxiMuz @ Apr 25 2013, 17:58) *
дело в том что в вызваемой ф-ии есть другая ф-ия которую убирать нельзя. вот такая засада. wacko.gif

Тогда нужно и вторую функцию проинлайнить.
Если это невозмодно - тады ой?

Цитата(MaxiMuz @ Apr 25 2013, 17:58) *
чтобы обойти использование ф-ии в прерывании попробывал поставить команду goto m1; - переход в main на нужный участок (судьба сохраненных в стеке данных меня уже не беспокоит) , не фига выдает ошибку:
что делать ?

Прерывание однократное что ли? Может задачу решать как-то подругому? Подробности не помешают...

Цитата(MaxiMuz @ Apr 25 2013, 17:58) *
интересно в IAR такиеже проблемы ?
А как же ж? Только регистры другие
_Pasha
Можно и просто static указывать для функций. Компилятор поймет.
MaxiMuz
Цитата(_Артём_ @ Apr 25 2013, 19:52) *
Прерывание однократное что ли? Может задачу решать как-то подругому? Подробности не помешают...
прерывание выполняется переодически с заданным периодом, а при наступлении опред.условий (уменьшение напряжения )выполнятеся ветка где как раз содержится ф-ия и по идее она выполняется и уходит в слип

я уже думал задачу можно решить только полностью переделав алгоритм щитай скилет программы

Цитата(_Pasha @ Apr 25 2013, 20:13) *
Можно и просто static указывать для функций. Компилятор поймет.
не совсем понял , как ? )
sigmaN
Объявлять как-то так
static int static_func( int param1 )
{
}

static для функции означает, что она видна только из этого .c файла. Это подсказывает компилятору, что её можно инлайнить агрессивнее.
На агрессивность инлайнов влияют настройки оптимизатора. Покурите доки GCC и найдете эти настройки.
MaxiMuz
Цитата(sigmaN @ Apr 26 2013, 03:12) *
Объявлять как-то так
static int static_func( int param1 )
{
}

static для функции означает, что она видна только из этого .c файла. Это подсказывает компилятору, что её можно инлайнить агрессивнее.
На агрессивность инлайнов влияют настройки оптимизатора. Покурите доки GCC и найдете эти настройки.

Да, функция обьявлена как : void OnSound (uint8_t );
поэтому наверное обьявлять как ee static былобы неверно.
И всетаки инлайнить - не лучший выход. Тоже надеюсь, что гдето в настройках оптимизатора есть такой ключик , который заставит компил.сохранять только используемые регистры. Вот если бы еще кто ткнул носом , ибо сам в английском буду долго искать.
Сергей Борщ
QUOTE (MaxiMuz @ Apr 26 2013, 09:00) *
Да, функция обьявлена как : void OnSound (uint8_t );
поэтому наверное обьявлять как ee static былобы неверно.
Объясните, почему?


QUOTE (MaxiMuz @ Apr 26 2013, 09:00) *
И всетаки инлайнить - не лучший выход.
Объясните, почему? Вам не хватает тактов, но вы готовы тратить их на call этой функции и ее ret?
QUOTE (MaxiMuz @ Apr 26 2013, 09:00) *
Тоже надеюсь, что где-то в настройках оптимизатора есть такой ключик , который заставит компил.сохранять только используемые регистры. Вот если бы еще кто ткнул носом , ибо сам в английском буду долго искать.
Там нет ключика "сделать мне все красиво". То, что вы хотите, является частью оптимизации на этапе связывания/линковки (link-time optimization, LTO). Без нее то что вы хотите невозможно в принципе. Не уверен, что для AVR она в gcc работает, а если работает - что она настолько эффективна. Встраивание (inline) же - 100% рабочий прием.
dxp
QUOTE (MaxiMuz @ Apr 26 2013, 13:00) *
И всетаки инлайнить - не лучший выход. Тоже надеюсь, что гдето в настройках оптимизатора есть такой ключик , который заставит компил.сохранять только используемые регистры. Вот если бы еще кто ткнул носом , ибо сам в английском буду долго искать.

То, что вы хотите сделать просто так ключиками, нереализуемо в принципе: при асинхронной передаче управления, что имеет место при вызове обработчика прерывания, компилятор обязан сохранить все используемые регистры. Если код обработчика прерывания не содержит вызова функций, тела которых компилятор не "видит" в момент генерации кода обработчика прерываний, то проблем нет - компилятор "видит" все используемые регистры и только их и сохраняет. Если же есть вызов функции, тела которой компилятор не "видит", то у него не остаётся никакого выбора - он должен сохранить все scratch регистры безусловно. Никакие ключи ему не дадут информации о том, какие регистры используются в вызываемой функции - он (компилятор) для этого должен видеть её код. Поэтому, если хотите тут эффективности - предоставьте компилятору код вызываемой функции (инлайн) или не вызывайте её из обработчика прерываний.
MaxiMuz
Цитата
поэтому наверное обьявлять как ee static былобы неверно.
- хотябы потому что она изначально возвращает ничего, а static д.относится к переменной (хотя могу запблуждаться)

Цитата(Сергей Борщ @ Apr 26 2013, 09:38) *
Объясните, почему? Вам не хватает тактов, но вы готовы тратить их на call этой функции и ее ret?
call выполняется только как исключение первый и последний раз ! , в рабочем цикле оно не задействовано.
А про инлайн , уже непомню писал или нет, нельзя включить т.к. в самой вызываемой ф-ии имеется еще одна ф. которую заинлайнить не получиться , т.к. она спец. вкл. чтобы компил. не выкидывал регистровую переменную,
уан момент:
Код
void EmptyCall (void)
{     uint8_t temp;
    temp=MaskEndLD;
    asm("nop");
    for (temp=0;temp<200;temp++)
    {asm("nop");
    }


Код
void OnDelay (uint16_t a)
{    
    CntLongDelay16l=a;
    bitclr(FlgsSt,EndLD);
    bitset(FlgsSt,OnLD);
    while (!(FlgsSt&MaskEndLD))
    {
        EmptyCall (); // вызов пустой функции
    }
}
neiver
Ого! EmptyCall ни разу не empty. Это таки задержка тактов этак на 800. А чтоб компилятор не выкидывал переменные, надо ему точнее объяснять свои намерения, например, volatile. там всякие...
И вообще что-то у Вас, сударь, там мутновато. Отсюда и желания странные.
_Артём_
Цитата(MaxiMuz @ Apr 26 2013, 10:47) *
- хотябы потому что она изначально возвращает ничего, а static д.относится к переменной (хотя могу запблуждаться)

Вы заблуждаетесь. static функция может возвращать результат, но не будет видна из других файлов.
Цитата(MaxiMuz @ Apr 26 2013, 10:47) *
А про инлайн , уже непомню писал или нет, нельзя включить т.к. в самой вызываемой ф-ии имеется еще одна ф. которую заинлайнить не получиться , т.к. она спец. вкл. чтобы компил. не выкидывал регистровую переменную,

Неубедительно всё это.
MaxiMuz
Цитата(_Артём_ @ Apr 26 2013, 11:13) *
Неубедительно всё это.

Есть регистровая переменная описанная в *.h
Код
#ifdef __ASSEMBLER__

#  define FlgsSt r18

#else  /* !ASSEMBLER */

register uint8_t FlgsSt asm("r18"); // рег. Флагов

часть ее меняется в другом прерывании, как в main организовать ожидание установки бита в рег.переменной ? просто while (!(FlgsSt&MaskEndS)) { ; } не прокатывает !

Цитата(neiver @ Apr 26 2013, 10:59) *
Ого! EmptyCall ни разу не empty. Это таки задержка тактов этак на 800. А чтоб компилятор не выкидывал переменные, надо ему точнее объяснять свои намерения, например, volatile. там всякие...
И вообще что-то у Вас, сударь, там мутновато. Отсюда и желания странные.

описал по новому:
Код
void EmptyCall (void) __attribute__((__noinline__));
void EmptyCall (void)
{     asm("nop");}

а причем тут volatile непонимаю ?
neiver
А. простите, что вам дает эта регистравая переменная? Экономия байта оперативки и двух тактов обращения к ней? Так EmptyCall в последней версии съест по крайней мере 7 тактов. Вот так экономия на спичках приводит к сами знаете чему.
ARV
Цитата(MaxiMuz @ Apr 26 2013, 12:33) *
а причем тут volatile непонимаю ?

при том, что переменные надо правильно описывать, чтобы было меньше проблем с оптимизицией. у вас, судя по всему, переменная FlgsSt должна быть о писана в *.h-файле как volatile uint8_t register FlgsSt asm("r18"); (в ветке #else вашего определения) тогда компилятор будет знать, что это за переменная, и где она хранится, а так же благодаря volatile он не выбросит ее при оптимизации. в принципе, вместо volatile должно пройти и extern - тогда тоже не выбросит...

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


_Pasha
Еще разок про static функции
Вот, предположим, Вам надо чтобы OnSound(uint8_t param) была видна всюду, и чтобы красиво вызывалась в прерывании. Есть типовый приемчик на сей случай:
Код
static void OnSound_prim(uint8_t param);
void OnSound(uint8_t param)
{
  OnSound_prim(param);
}
void OnSound_prim(uint8_t param)
{
// сюда помещаем то, что изначально находилось в OnSound()
}

и из прерывания вызываем OnSound_prim().
Профит: компилер уберет все лишние стековые операции и вообще соптимизирует все хорошо. И для вызова глобальной функции ничего эдакого лишнего не произойдет.
MaxiMuz
Цитата(_Pasha @ Apr 26 2013, 11:57) *
Еще разок про static функции
Вот, предположим, Вам надо чтобы OnSound(uint8_t param) была видна всюду, и чтобы красиво вызывалась в прерывании. Есть типовый приемчик на сей случай:
Код
static void OnSound_prim(uint8_t param);
void OnSound(uint8_t param)
{
   OnSound_prim(param);
}
void OnSound_prim(uint8_t param)
{
  // сюда помещаем то, что изначально находилось в OnSound()
}

и из прерывания вызываем OnSound_prim().
Профит: компилер уберет все лишние стековые операции и вообще соптимизирует все хорошо. И для вызова глобальной функции ничего эдакого лишнего не произойдет.
т.е. вызывать в прерывании из ф-ии1 ф-ию2, которую нужно обьявить static , т.е. какбы использовать подставную функцию ? - не работает !
psL
не использовать автоматических переменных в теле функции типа uint8_t temp;
не вызывать функции, передающие параметры через стек типа OnSound (temp);

т.е. примерно так:
Код
static uint8_t temp;
static void OnSound (void){
  temp;
  ...
}
ISR (INT0_vect)
{    
   temp=PORTB;
   OnSound ();
}

или использовать какую-нибудь очередь заданий, которая будет заполняться в прерывании, а обрабатываться в основной программе
MaxiMuz
Цитата(psL @ Apr 26 2013, 14:06) *
не использовать автоматических переменных в теле функции типа uint8_t temp;
не вызывать функции, передающие параметры через стек типа OnSound (temp);

т.е. примерно так:
Код
static uint8_t temp;
static void OnSound (void){
   temp;
   ...
}
ISR (INT0_vect)
{    
    temp=PORTB;
    OnSound ();
}

или использовать какую-нибудь очередь заданий, которая будет заполняться в прерывании, а обрабатываться в основной программе
компилятору пофигу какие там переменные в ф-ии используются и передаются параметры в ф-ии или нет , он либо делает ф-ию инлайн либо, либо сохранет все регистры , и фсе!
ЁЁ.. , столько времяни потратил на эти эксперементы !!!
psL
Цитата(MaxiMuz @ Apr 25 2013, 16:36) *
А как сделать так чтобы компилятор сохранял только задействованные в конретном прерывании регистры ?

По идее никак. Компилятор в принципе не может отследить контекст, в котором вызывается прерывание.
_Pasha
Слушайте, если у Вас так туго с времянкой, давным давно бы уже врукопашную на ассемблере написали... sad.gif
ar__systems
Цитата(psL @ Apr 26 2013, 07:12) *
По идее никак. Компилятор в принципе не может отследить контекст, в котором вызывается прерывание.

+1. На этапе компиляции компилятор не может знать, какие регистры используются во всех функциях, вызываемых в прерывании.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.