|
|
  |
GCC: Аварийный выход из прерывания (функции), чем заменить оператор goto? |
|
|
|
May 20 2013, 19:05
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Цитата(_Артём_ @ May 20 2013, 22:56)  В случае использования локальных переменных компилятор может использовать одни и те же адреса в ОЗУ для разных переменных, а для глобальных так не получится. Это только в том случае, если они освобождаются вместе с окончанием той функции/блока, в котором были заведены. Но main() свои локальные переменные (те, что заводятся в ее начале) не освободит никогда - на то у нее и бесконечный цикл внутри сидит. В данном случае разговор зашел о том, что инциализация/сброс стека приведет к пропаже локальных переменных внутри main(). Так я и выразилась в том духе, что заводить внутри main() локальные переменные смысла не имеет. Т.е. речь шла не об экономии памяти, как вы это неверно поняли, а об устойчивости переменных к сбросу стека. P.S. Не стоит обольщаться тем, что войдя джампом из прерывания во внутрь какой-то функции (в том числе и main), вы обнаружите там локальные переменные на тех же местах. Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Поэтому как ни крути, но валидными в этом случае могут быть только глобальные переменные, но не локальные. Причем независимо от того, сбрасывали вы стек или нет.
|
|
|
|
|
May 20 2013, 22:31
|
Знающий
   
Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768

|
Цитата(Xenia @ May 20 2013, 22:05)  Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Поэтому как ни крути, но валидными в этом случае могут быть только глобальные переменные, но не локальные. Причем независимо от того, сбрасывали вы стек или нет. Тут поддержу Ксению, но какое это имеет отношение к вопросу топикстартера? Он же сказал, что состояние программы и стека ему не известно и не важно. Возможно, стек переполнился и залез на глобальные переменные. Надежнее не использовать вообще никакие заранее определенные переменные. Используйте константы или заведите новые переменные, порушив глобальные, переставьте указатель стека на начало и вперед! Только вперед и только хардкор
|
|
|
|
|
May 21 2013, 07:45
|

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

|
_Артем_ ответил по всем пунктам. Касаемо последних двух сообщений: компилятор не может знать вашей логики, заставить его "не использовать локальные переменные" нельзя. Решение с goto из одной функции в другую ущербное каким бы хаком оно бы ни было реализовано. longjump() еще куда ни шло - он поддержан компилятором. Было же грамотное решение - функция с атрибутом noreturn. И вызывать ее как функцию, без всяких указателей и т.д. Компилятору этого noreturn по идее должно быть достаточно, чтобы не сохранять лишние регистры в каждом прерывании. QUOTE (Xenia @ May 20 2013, 22:05)  Но main() свои локальные переменные (те, что заводятся в ее начале) не освободит никогда - на то у нее и бесконечный цикл внутри сидит. Во-первых не надо считать компиялтор настолько тупым. Во-вторых и внутри этого бесконечного цикла имеют право быть несколько блоков со своими локальными переменными. QUOTE (Xenia @ May 20 2013, 22:05)  Так я и выразилась в том духе, что заводить внутри main() локальные переменные смысла не имеет. Т.е. речь шла не об экономии памяти, как вы это неверно поняли, а об устойчивости переменных к сбросу стека. То есть еще одна распорка чтобы конструкция перевязанная предыдущей расчалкой не навернулась. QUOTE (Xenia @ May 20 2013, 22:05)  P.S. Не стоит обольщаться тем, что войдя джампом из прерывания во внутрь какой-то функции (в том числе и main), вы обнаружите там локальные переменные на тех же местах. Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Именно поэтому setjump() сохраняет состояние указателя стека. И после longjump() все локальные переменные находятся на своих местах совершенно нетронутые.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 21 2013, 18:08
|

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

|
Цитата(_Артём_ @ May 20 2013, 23:03)  goto заменилось на JMP my_exit без ненужного сохранения регистров. Надо же... так ведь это как бы и требовалось? Цитата(Сергей Борщ) Было же грамотное решение - функция с атрибутом noreturn. И вызывать ее как функцию, без всяких указателей и т.д. Компилятору этого noreturn по идее должно быть достаточно, чтобы не сохранять лишние регистры в каждом прерывании. такая функция скорее всего проинлайнится внутрь обработчика, и соответственно не даст никакого выигрыша по сравнению с нормальным указанием выполняемых операторов в этом обработчике. у меня была идея поместить эту функцию в секцию .fini1 и попадать туда стандартной функцией exit или abort, но компилятор оказался слишком глупым и тупо сохранял в стеке все регистры в обработчике прерываний...
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
May 21 2013, 19:20
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(ARV @ May 21 2013, 21:08)  так ведь это как бы и требовалось? Да, это оно самое и есть, чего хотел ТС. Цитата(ARV @ May 21 2013, 21:08)  такая функция скорее всего проинлайнится внутрь обработчика, и соответственно не даст никакого выигрыша по сравнению с нормальным указанием выполняемых операторов в этом обработчике. Не, не проинлайнится. noreturn роли не играет. Видимо регистров мало сохраняется из-за того что goto используется. Цитата(ARV @ May 21 2013, 21:08)  у меня была идея поместить эту функцию в секцию .fini1 и попадать туда стандартной функцией exit или abort, но компилятор оказался слишком глупым и тупо сохранял в стеке все регистры в обработчике прерываний... А если так: Код typedef void (*exit_handler_t) (); // ISR(SPM_RDY_vect) { if (fatal_error) { exit_handler_t exit_handler=my_exit; goto *exit_handler; } } то лишних сохранений не будет и функцию можно расположить по заданному адресу.
|
|
|
|
|
May 21 2013, 19:47
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(rudy_b @ May 21 2013, 22:32)  И что будет если из main дать return? Перейдёт на какую-нибудь функцию exit (или что там предусматривается на такой случай). Как-то так оно выглядит: Код CLI Global Interrupt Disable RJMP PC-0x0000 Relative jump Но можно и своим чем-то заменить. Цитата(rudy_b @ May 21 2013, 22:32)  Если нужно заблокировать работу программы находясь в прерывании - запретите все прерывания и зациклитесь. Возможно таких мест не одно. И там предполагается не только зацикливание, а ещё какие-то спасающие ситуацию действия.
|
|
|
|
|
May 21 2013, 20:17
|

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

|
Цитата(_Артём_ @ May 20 2013, 16:31)  Мне почему-то кажется что код идущий после метки будет выкинут оптимизатором. Или нет? Вы с какими настройками компилируете? -Os нет, код после метки отстается, возможно что там используются volatile-переменные Цитата(Xenia @ May 19 2013, 22:04)  Ветвление надо произвести только один раз - в самом начале main(), еще до вхождения в бесконечный цикл. Идея с семафором, ветвлением вначале main и оригинальная, но все же я не очень доверяю сохранности портов после сброса .. Цитата Функции setjmp / longjmp подойдут ? этот вариант не подходит т.к. по сути это тот же самый вызов функции с вытекающими последствиями Цитата(ARV @ May 20 2013, 20:12)  если очень хочется, то можно вот так: Код void __attribute__((noreturn)) my_exit(void){ PORTB = 0xFF; while(1); } ISR(TIMER1_OVF_vect){ if(PINB & 1){ PORTB = 0; } else { // проблема - обрабатываем лебединую песню!!! void *ptr = my_exit; goto *ptr; } } при желании my_exit() можно вызывать и в main, не нарушая общей структуры программы. хотя с моей точки зрения это кривой костыль, правильное по моему мнению решение я озвучивал ранее. Помоему самое оптимальное решение ! т.к. даже через снятие адреса метки: Код ptr=&&m1; .... goto *ptr; компилятор заменяет на ijmp ptr что задействует еще пару регистров, которые сохраняются в стек Только неясно почему опять кривой костыль ?
|
|
|
|
|
May 21 2013, 20:54
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(MaxiMuz @ May 21 2013, 23:17)  возможно что там используются volatile-переменные Может быть. Цитата(MaxiMuz @ May 21 2013, 23:17)  но все же я не очень доверяю сохранности портов после сброса .. О каких портах речь? Входы-выходы? После сброа они будут сброшены в дефолтное состояние. Цитата(MaxiMuz @ May 21 2013, 23:17)  Помоему самое оптимальное решение ! Есть, кстати ещё вариант: в прерывании, в котором обнаружена аварийная ситуация генерить вызов другого прерывания в котором и выполнять нужные действия (уход в sleep кажется?). Программное прерывание то есть. Код ISR(TIMER1_COMPA_vect) { if (fatal_error) { // вызов программного прерывания SPMCR=1<<SPMIE; } }
ISR(SPM_RDY_vect) {
SPMCR&=~(1<<SPMIE); // действия по аварии while (1); } На запуск програмного прерывания нужно всего пара команд, поэтому дополнительных регистров не потребуется. Только сработает оно через десятков-другой тактов, но программа при этом порушена не будет.
|
|
|
|
|
May 21 2013, 21:01
|

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

|
Цитата(_Артём_ @ May 21 2013, 23:54)  О каких портах речь? Входы-выходы? После сброа они будут сброшены в дефолтное состояние. тот вариант о котором говорила Xenia#17с вызовом прерывани из прерывания я тоже думал , как вариант , но всеже rjmp проще всего
|
|
|
|
|
May 22 2013, 07:05
|

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

|
Цитата(Палыч @ May 22 2013, 11:01)  В AVR программное прерывание "сгенерить" не так-то и просто. В АVR, обычно, запись единицы в флаг прерывания приводит к её сбросу. На худой конец можно задействовать и внешнее прерывание + дёрнуть уровень на ноге sbi или cbi или просто его разрешить на нужной ноге с нужным уровнем. Всё это уже лирика... Цитата • Bit 7 – SPMIE: SPM Interrupt Enable When the SPMIE bit is written to one, and the I-bit in the Status Register is set (one), the SPM ready interrupt will be enabled. The SPM ready Interrupt will be executed as long as the SPMEN bit in the SPMCR Register is cleared. Ведь не зря же его для нужд rtos частенько используют...
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
  |
3 чел. читают эту тему (гостей: 3, скрытых пользователей: 0)
Пользователей: 0
|
|
|