|
GCC: Как избежать избыточного пользования стеком в прерываниях |
|
|
|
Apr 25 2013, 12:36
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
К примеру имеется прерывание: Код 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 и нет проблем. А как сделать так чтобы компилятор сохранял только задействованные в конретном прерывании регистры ?
Сообщение отредактировал MaxiMuz - Apr 25 2013, 12:54
|
|
|
|
|
Apr 25 2013, 12:41
|
Местный
  
Группа: Участник
Сообщений: 242
Регистрация: 10-06-08
Из: Хочу в пампасы...
Пользователь №: 38 192

|
Цитата(MaxiMuz @ Apr 25 2013, 15:36)  А как сделать так чтобы компилятор сохранял только задействованные в конретном прерывании регистры ? Ну как минимум не вызывать функций из прерывания. OnSound (temp); Или написать обработчик самому на ассемблере.
|
|
|
|
|
Apr 25 2013, 14:58
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(neiver @ Apr 25 2013, 17:30)  Как вариант можно сделать вызываемую в перрывании функцию inline (естественно тело функции должно быть видимо компилятору в месе вызова). Тогда сохраняться будут только действительно используемые регистры. неполучится! дело в том что в вызваемой ф-ии есть другая ф-ия которую убирать нельзя. вот такая засада.  чтобы обойти использование ф-ии в прерывании попробывал поставить команду goto m1; - переход в main на нужный участок (судьба сохраненных в стеке данных меня уже не беспокоит) , не фига выдает ошибку: Цитата error: label 'm1' used but not defined что делать ? интересно в IAR такиеже проблемы ?
Сообщение отредактировал MaxiMuz - Apr 25 2013, 14:51
|
|
|
|
|
Apr 25 2013, 16:52
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(MaxiMuz @ Apr 25 2013, 17:58)  дело в том что в вызваемой ф-ии есть другая ф-ия которую убирать нельзя. вот такая засада.  Тогда нужно и вторую функцию проинлайнить. Если это невозмодно - тады ой? Цитата(MaxiMuz @ Apr 25 2013, 17:58)  чтобы обойти использование ф-ии в прерывании попробывал поставить команду goto m1; - переход в main на нужный участок (судьба сохраненных в стеке данных меня уже не беспокоит) , не фига выдает ошибку: что делать ? Прерывание однократное что ли? Может задачу решать как-то подругому? Подробности не помешают... Цитата(MaxiMuz @ Apr 25 2013, 17:58)  интересно в IAR такиеже проблемы ? А как же ж? Только регистры другие
|
|
|
|
|
Apr 25 2013, 17:39
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(_Артём_ @ Apr 25 2013, 19:52)  Прерывание однократное что ли? Может задачу решать как-то подругому? Подробности не помешают... прерывание выполняется переодически с заданным периодом, а при наступлении опред.условий (уменьшение напряжения )выполнятеся ветка где как раз содержится ф-ия и по идее она выполняется и уходит в слип я уже думал задачу можно решить только полностью переделав алгоритм щитай скилет программы Цитата(_Pasha @ Apr 25 2013, 20:13)  Можно и просто static указывать для функций. Компилятор поймет. не совсем понял , как ? )
|
|
|
|
|
Apr 26 2013, 06:00
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(sigmaN @ Apr 26 2013, 03:12)  Объявлять как-то так static int static_func( int param1 ) { }
static для функции означает, что она видна только из этого .c файла. Это подсказывает компилятору, что её можно инлайнить агрессивнее. На агрессивность инлайнов влияют настройки оптимизатора. Покурите доки GCC и найдете эти настройки. Да, функция обьявлена как : void OnSound (uint8_t ); поэтому наверное обьявлять как ee static былобы неверно. И всетаки инлайнить - не лучший выход. Тоже надеюсь, что гдето в настройках оптимизатора есть такой ключик , который заставит компил.сохранять только используемые регистры. Вот если бы еще кто ткнул носом , ибо сам в английском буду долго искать.
|
|
|
|
|
Apr 26 2013, 06:38
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
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% рабочий прием.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 26 2013, 06:51
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (MaxiMuz @ Apr 26 2013, 13:00)  И всетаки инлайнить - не лучший выход. Тоже надеюсь, что гдето в настройках оптимизатора есть такой ключик , который заставит компил.сохранять только используемые регистры. Вот если бы еще кто ткнул носом , ибо сам в английском буду долго искать. То, что вы хотите сделать просто так ключиками, нереализуемо в принципе: при асинхронной передаче управления, что имеет место при вызове обработчика прерывания, компилятор обязан сохранить все используемые регистры. Если код обработчика прерывания не содержит вызова функций, тела которых компилятор не "видит" в момент генерации кода обработчика прерываний, то проблем нет - компилятор "видит" все используемые регистры и только их и сохраняет. Если же есть вызов функции, тела которой компилятор не "видит", то у него не остаётся никакого выбора - он должен сохранить все scratch регистры безусловно. Никакие ключи ему не дадут информации о том, какие регистры используются в вызываемой функции - он (компилятор) для этого должен видеть её код. Поэтому, если хотите тут эффективности - предоставьте компилятору код вызываемой функции (инлайн) или не вызывайте её из обработчика прерываний.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Apr 26 2013, 07:47
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата поэтому наверное обьявлять как 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 (); // вызов пустой функции } }
|
|
|
|
|
Apr 26 2013, 08:13
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

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

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(_Артём_ @ 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 непонимаю ?
|
|
|
|
|
Apr 26 2013, 08:50
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
Цитата(MaxiMuz @ Apr 26 2013, 12:33)  а причем тут volatile непонимаю ? при том, что переменные надо правильно описывать, чтобы было меньше проблем с оптимизицией. у вас, судя по всему, переменная FlgsSt должна быть о писана в *.h-файле как volatile uint8_t register FlgsSt asm("r18"); (в ветке #else вашего определения) тогда компилятор будет знать, что это за переменная, и где она хранится, а так же благодаря volatile он не выбросит ее при оптимизации. в принципе, вместо volatile должно пройти и extern - тогда тоже не выбросит... но вообще говоря, вызывать из обработчика прерываний не-static функции, да еще с вложенными функциями, да еще и с задержками - это уж какой-то совсем плоховатый стиль... при таком стиле в одном месте вы выровняете - в другом разладится...
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Apr 26 2013, 08:57
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

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

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(_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 , т.е. какбы использовать подставную функцию ? - не работает !
Сообщение отредактировал MaxiMuz - Apr 26 2013, 11:13
|
|
|
|
|
Apr 26 2013, 11:06
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 5-08-05
Пользователь №: 7 390

|
не использовать автоматических переменных в теле функции типа uint8_t temp; не вызывать функции, передающие параметры через стек типа OnSound (temp); т.е. примерно так: Код static uint8_t temp; static void OnSound (void){ temp; ... } ISR (INT0_vect) { temp=PORTB; OnSound (); } или использовать какую-нибудь очередь заданий, которая будет заполняться в прерывании, а обрабатываться в основной программе
|
|
|
|
|
Apr 26 2013, 11:48
|

Местный
  
Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658

|
Цитата(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 (); } или использовать какую-нибудь очередь заданий, которая будет заполняться в прерывании, а обрабатываться в основной программе компилятору пофигу какие там переменные в ф-ии используются и передаются параметры в ф-ии или нет , он либо делает ф-ию инлайн либо, либо сохранет все регистры , и фсе! ЁЁ.. , столько времяни потратил на эти эксперементы !!!
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|