|
Сохранение контекста в прерываниях, IAR |
|
|
|
May 14 2008, 11:51
|
Частый гость
 
Группа: Свой
Сообщений: 169
Регистрация: 10-11-05
Из: Воронеж
Пользователь №: 10 687

|
Да простят меня модераторы, если вопрос избитый, но всезнающий поиск вразумительного ответа мне не дал. А проблема, имхо, достаточно часто встречающаяся: Имеем некоторое прерывание, которое должно автономно обработать некоторый блок данных и по окончании работы сообщить об этом основному софту. Например, рассмотрим отправку пакета по УАРТ. Основную массу действий (а именно, выборку очередного байта из памяти и его отправку) прерывание делает своими силами, но, вот беда: когда оно закончит, оно должно вызвать некоторую функцию, которая должна просигнализировать основной программе о выполнении задания. Вариант с глобальной переменной отпадает сразу, т.к. ситуация гипотетическая и сигналом в данном случае может служить семафор или еще какие-либо средство РТОС. Естественно компилятор, видя в прерывании вызов внешней, неизвестной ему функции, далает сохранение кучи регистров в стек. Однако, если отправляемый пакет состоит из 1000 байтов, то 999 раз контекст сохранится зря и только последний раз, когда работа завершена, действительно необходимо слить регистры в стек и вызвать функцию - сигнал. Я хочу, чтобы контекст сохранялся только 1 раз из 1000, когда уже ясно, что необходим вызов внешней функции. Пока единственное решение, приходящее мне в голову, это писать код прерывания на асме и сохранять регистры вручную не на входе в прерывание, а в ветке проверки условия окончания задания. Но что-то мне подсказывает, что этот вопрос можно решить директивами/ключевыми словами/intrinsic функциями/другими расширениями языка, не прибегая к полному написанию кода прерывания на асме. Только вот пока такой вариант в голову не приходит  Подскажите, плз, кто и как избавляется от подобного оверхеда? PS: Интересует решение для компилятора ИАР.
|
|
|
|
|
May 14 2008, 12:03
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(gladov @ May 14 2008, 19:51)  Не понятно, для какой платформы вопрос. Цитата(gladov @ May 14 2008, 19:51)  Основную массу действий (а именно, выборку очередного байта из памяти и его отправку) прерывание делает своими силами, но, вот беда: когда оно закончит, оно должно вызвать некоторую функцию, которая должна просигнализировать основной программе о выполнении задания. Для AVR я делаю так: 1. Извлекаем из стека программ (CSTACK) адрес возврата. 2. Загружаем в CSTACK адрес вызываемой после прерывания функции. 3. Загружаем в CSTACK адрес возврата. Код CPU_IRQ_Epilog: /* Restore return address */ pop r16 pop r17
ldi r18, high(post_call>> 1) ldi r19, low(post_call >> 1)
push r19 push r18 push r17 push r16
ret; Данная функция вызывается в конце обработчика прерывания.
--------------------
|
|
|
|
|
May 14 2008, 12:15
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(gladov @ May 14 2008, 15:51)  Подскажите, плз, кто и как избавляется от подобного оверхеда? Для начала - я не вызываю никакой функции для приведенного примера. А действительно заканчиваю флагом/семафором. а в другой процедуре - анализирую флаг. В одном случае (не РТОС) необходимо было после N одних прерываний выполнить другое прерывание. Для этого задействовал неиспользуемый таймер. Он постоянно считает так что переполнение уже обязательно возникает к моменту вызова. Я просто разрешаю прерывание, а в этом прерывании запрещаю его. Для уменьшения оверхеда при вызове процедуры из прерывания (знающие люди пишут) необходимо чтобы вызываемая процедура находилась в том же файле где и обработчик прерывания. Хотя всётаки это и не рекомендуется делать. Ещё один момент использую в IAR для уменьшения оверхеда. Как обычно в прерывании используются volatile переменные. В связи с этим компилятор их обрабатывает без должной оптимизации. Поэтому иногда переприсваивание даст более грамотный код. Например: Код #pragma vector=USART_UDRE_vect // Прерывание на передачу по rs485 __interrupt static void Exeption(void) { uint8_t hBuf; hBuf=HeadOutBuf; if(hBuf!=EndOutBuf) { UDR0 = OutBuf[hBuf]; // Передаём символ ответа hBuf++; if(hBuf==LENGTH_OUT_BUF) hBuf=0; HeadOutBuf=hBuf; } else UCSR0B = 0x98; // Разрешить RXEN,TXEN,RXCIE чтобы закончить передачу }
|
|
|
|
|
May 14 2008, 12:39
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(gladov @ May 14 2008, 14:51)  Естественно компилятор, видя в прерывании вызов внешней, неизвестной ему функции, далает сохранение кучи регистров в стек. PS: Интересует решение для компилятора ИАР. Я использую компилятор ICC. Но, возможно, это справедливо и для ИАР... Если вызов функции из процедуры обработки прерывания делать ассемблерной вставкой, то сохранения кучи регистров - нет. Естественно, что сохранять регистры используемые вызываемой функцией (ту самую "кучу") и восстанавливать их нужно также ассемблерными вставками, соответственно до и после вызова функции.
|
|
|
|
|
May 14 2008, 12:56
|
Частый гость
 
Группа: Свой
Сообщений: 169
Регистрация: 10-11-05
Из: Воронеж
Пользователь №: 10 687

|
Цитата Не понятно, для какой платформы вопрос. Думал это очевидно, если задаю вопрос в ветке AVR. Цитата Для начала - я не вызываю никакой функции для приведенного примера. А действительно заканчиваю флагом/семафором. а в другой процедуре - анализирую флаг.
В одном случае (не РТОС) необходимо было после N одних прерываний выполнить другое прерывание. Для этого задействовал неиспользуемый таймер. Он постоянно считает так что переполнение уже обязательно возникает к моменту вызова. Я просто разрешаю прерывание, а в этом прерывании запрещаю его. По-моему, это больший геморрой, нежели сделать асмовую вставку. И не потребуется использование доп. периферии в виде таймера или еще чего-то. Цитата Для уменьшения оверхеда при вызове процедуры из прерывания (знающие люди пишут) необходимо чтобы вызываемая процедура находилась в том же файле где и обработчик прерывания. Хотя всётаки это и не рекомендуется делать. Это понятно, но в случае с использованием ОС разместить функцию возврата семафора в одном файле с собственным прерыванием практически нереально. Цитата Если вызов функции из процедуры обработки прерывания делать ассемблерной вставкой, то сохранения кучи регистров - нет. Естественно, что сохранять регистры используемые вызываемой функцией (ту самую "кучу") и восстанавливать их нужно также ассемблерными вставками, соответственно до и после вызова функции. Да, такой вариант пока кажется мне наиболее приемлемым...
|
|
|
|
|
May 14 2008, 13:28
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(gladov @ May 14 2008, 13:51)  Я хочу, чтобы контекст сохранялся только 1 раз из 1000, когда уже ясно, что необходим вызов внешней функции. Цитата(SasaVitebsk @ May 14 2008, 14:15)  Для уменьшения оверхеда при вызове процедуры из прерывания (знающие люди пишут) необходимо чтобы вызываемая процедура находилась в том же файле где и обработчик прерывания. Хотя всётаки это и не рекомендуется делать. рекомендуется, рекомендуется и #pragma inline=forced Цитата(gladov @ May 14 2008, 14:56)  Это понятно, но в случае с использованием ОС разместить функцию возврата семафора в одном файле с собственным прерыванием практически нереально. Не морочьте себе голову - лишние регистры НЕ сохраняются и многократно "семафор" не вызывается. Проблемы?
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
May 14 2008, 13:48
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(gladov @ May 14 2008, 19:51)  Например, рассмотрим отправку пакета по УАРТ. Основную массу действий (а именно, выборку очередного байта из памяти и его отправку) прерывание делает своими силами, но, вот беда: когда оно закончит, оно должно вызвать некоторую функцию, которая должна просигнализировать основной программе о выполнении задания. Чет сразу не обратил внимания  , что вопрос про USART. А что Вам мешает устроить FIFO и вызывать функцию сигнализации только по переполнению буфера? Как раз и получится, если FIFO = 1000 байт - 1 раз из 1000. И только в том случае, если буфер действительно переполнится. Похоже, дело просто в не ясном понимании задачи...
--------------------
|
|
|
|
|
May 14 2008, 18:10
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(zltigo @ May 14 2008, 19:52)  Естественно и в этом тоже - сначала неправильно глобально поставить задачу (1000 вызовов некой функции из одного прерывания!) а потом доблестно "c помощью ASM" и трюков ее решать  - к сожалению встречается очень часто  . имхо автор всё очень внятно объяснил: Цитата(gladov @ May 14 2008, 17:51)  компилятор, видя в прерывании вызов внешней, неизвестной ему функции, далает сохранение кучи регистров в стек. Однако, если отправляемый пакет состоит из 1000 байтов, то 999 раз контекст сохранится зря и только последний раз, когда работа завершена, действительно необходимо слить регистры в стек и вызвать функцию - сигнал. Вызов функции происходит не каждое прерывание, а сохранение контекста - каждое.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
May 14 2008, 19:56
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(AHTOXA @ May 14 2008, 20:10)  Вызов функции происходит не каждое прерывание, а сохранение контекста - каждое. Это уже проблемы конкретного компилятора/платформы/оптимизации. Радикальное решение уже приводил - inline. P.S. Посмотрел, что творится на тестике c вызовом подпрограммы из обработчика пркрывания - честно говоря ужас  . При этом для того-же ARM генерится много более разумный код. Явные проблемы у AVR с соглашениями о вызове функций - чрезмерное количество регистров отдается в безраздельное использование вызываемой функции. В результате реальные функции, в том числе и обработчик прерывания, их все и близко не использует и при вызове действительно жуткая картина. z.
Сообщение отредактировал zltigo - May 15 2008, 05:44
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
May 14 2008, 20:46
|

Местный
  
Группа: Участник*
Сообщений: 323
Регистрация: 11-02-08
Пользователь №: 34 947

|
Цитата(zltigo @ May 14 2008, 23:56)  Это уже проблемы конкретного компилятора/платформы/оптимизации. Радикальное решение уже приводил - inline. Проблема ещё в том, что нити бывают разные: с лёгким и тяжёлым контектстом... И сохранять весь контекст для облегчённой часто запусаемой нити крайне неэффективно. Тут надо подходить комплексно... И учитывать все аспекты проектируемой Вами RTOS
Сообщение отредактировал Дон Амброзио - May 14 2008, 20:49
--------------------
После устранения бага в программе она стала работать....хуже
|
|
|
|
|
May 15 2008, 05:05
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(defunct @ May 14 2008, 16:35)  У AVR есть возможность отделить прерывание непрерываной передачи байт от завершения передачи. Используйте прерывание Data Register Empty для отправки очередного байта и TX Complete для сигнализации о завершении передачи пакета. В примере речь идёт о приёме пакета, а в этом случае прерывание как раз одно. Если же подходить абстрактно, то можно поступить так, как это было реализовано в scmRTOS предыдущей версии, т. е. сделать прерывание без сохранения\восстановления контекста Код __raw __interrupt void my_interrupt_function() , а в том или ином случае применять разные обёртки. Но на мой взгляд это достаточно трудно автоматизируемо и потребует тщательного изучения листингов, а иначе неприятных глюков не избежать.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|