TISRW_SS + MSP430

19 views
Skip to first unread message

Valeriy V. Kozhevnikov

unread,
Feb 10, 2009, 6:09:04 PM2/10/09
to scmrt...@googlegroups.com
Добрый день!

Предположительно обнаружилась ошибка при использовании обертки TISRW_SS
Проявление - передача управления по несуществующим адресам
Обнаружено при вынужденной замене TISRW на TISRW_SS всвязи с нехваткой RAM

После поисков обнаружено, что в обработчике прерывания

#pragma vector = XXX_VECTOR
OS_INTERRUPT void XXX_ISR()
{
OS::scmRTOS_ISRW_TYPE ISRW;

//...
Flag.SignalISR();
}

при вызове
void OS::TEventFlag::SignalISR()
{
TCritSect cs;

//...
}

TCritSect cs создается на стеке по адресу 0x600 за пределами RAM
(0x200-0x599). Поэтому при выполнении ~TCritSect() в SR записывается
какято хрень в том числе разрешаются прерывания которые при входе были
запрещены... Дальше в проекте вызывается другой прерывание и отлаживать
становится невозможно...

В листинге видно что в указатель стека действительно записывается 0x600

Подправил строчку
INLINE inline void SetISRStackPointer()
{ __set_SP_register( reinterpret_cast<word>(__segment_end("CSTACK"))); }
на
INLINE inline void SetISRStackPointer()
{ __set_SP_register( reinterpret_cast<word>(__segment_end("CSTACK")) - 2); }

После этого проблема исчезла.

--
С уважением,
Валерий mailto:di...@inbox.ru

Harry Zhurov

unread,
Feb 11, 2009, 2:33:45 AM2/11/09
to scmrt...@googlegroups.com
Greeting Valeriy!
You wrote on Wed, 11 Feb 2009 02:09:04 +0300

VVK> Предположительно обнаружилась ошибка при использовании обертки TISRW_SS

[...]

Тут имеет место эффект от этих грязных хаков по переключению стека. Суть в том,
что компилятор резервирует в стеке память, которую потом может (планирует)
использовать. Резервирует он ее в самом начале функции (обработчика прерывания):

0F12 PUSH.W R15
0E12 PUSH.W R14
2183 SUB.W #0x2, SP <------ вот тут резервируется память в стеке
scmRTOS_ISRW_TYPE ISR;
5E42.... MOV.B &??Kernel + 10, R14
4F4E MOV.B R14, R15

и потом, когда указатель стека уже переключен (в обход компилятора) на стек
прерываний, он (компилятор) пытается юзать.

TCritSect cs;
??Slon_ISR_2:
81420000 MOV.W SR, 0(SP) <------- вот тут эта память используется
32C2 DINT
0343 NOP

При этом он "имеет в виду", что у него в стеке зарезервировано сколько-то
памяти - т.е. считает, что указатель стека указывает не на TOS (Top Of Stack), а
куда-то внутрь стека. А поскольку после переключения стека, выполненного с
помощью хака, указатель стека указывает не туда, куда полагает компилятор (в
этом-то и суть хака, и его граблегенерабельность), то тут и возникают траблы.
Т.е. в описанном случае компилятор считал, что размещает объект в стеке
(учитывая резервирование), а на деле - мимо стека.

После пропатчивания кода (добавления смещения в два байта), программа
заработала. Но если бы объект был толще, чем два байта, то опять были бы траблы.
Т.е. по хорошему надо знать, сколько компилятор резервирует и такое смещение и
вычитать при загрузке указателя стека значением адреса стека прерываний. Но мы
этого знать не можем - компилятор нам не сообщает, сколько и чего он там хочет
размещать и сколько памяти под это дело резервирует.

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

Для решения проблемы видятся два пути:

1. Включать смещение (с запасом либо вычислив его). Недостаток тут в том, что
все равно гарантий нет - мы не знаем, как компилятор работает с памятью в стеке.
И что еще хуже - всегда можно забыть изменить смещение, поменяв размер объектов
в стеке (например, вместо мелкого объекта на пару байт, завели массив на 16
байт). Т.е. за всем этим нужно следить руками. Это всегда будет место для
потенциальных граблей. Вот кабы был интринсик, который бы возвращал значение
зарезервированной памяти в стеке, то это было бы гарантированное и эффективное
решение. Но на это не рассчитывать не приходится. :)

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

Второй вариант мне представляется более грамотным, логичным и безопасным.
Во-первых, если мы производим переключение указателя стека на стек прерываний,
то это уже говорит о том, что прерывание достаточно "тяжелое", нужен стек, идем
на накладные расходы по переключению - вызов функции ничего не портит. Зато все
безопасно. А если ISR "легкий", то и переключаться на специальный стек нет
смысла - это тоже ведь приличный оверхед на фоне пары строк кода. Прямо тут все
и делаем. Но не забываем, что в этом случае ISR выполняется в стеке прерванного
процесса.

Что касается ISR системного таймера, то там ситуация как раз хорошо попадает под
п.2: внутри самого ISR непосредственно никаких объектов в стеке не создается, но
есть вызов функций, потребление стека которыми мы не знаем - например,
SystemTimerUserHook(), и вдобавок еще и возможно разрешение вложенных
прерываний - т.е. тут однозначно лучше переключиться на отдельный стек.

В общем, все всегда решает разработчик, исходя из задач, предпочтений, опыта. Но
чтобы у него не было проблем, он должен понимать, какие подводные камни тут
существуют. Наверное, все вышеописанное имеет смысл поместить в доку по порту -
типа, особенности применения переключения на отдельный стек прерываний.

Ну, и резюме не ново: если проц не поддерживает нативно фичу (аппаратное
переключение на отдельный стек прерываний в случае с нами MSP430), все пути
обхода, как правило, неполноценны и горбаты.


--
H.Z.

### Ну, выпил стакан, ну два, ну бутылку, две, но зачем же так нажираться?!


Reply all
Reply to author
Forward
0 new messages