|
scmRTOS + CortexM3 + printf, некорректная работа printf |
|
|
|
Feb 7 2011, 06:28
|

Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 24-06-06
Из: Томск
Пользователь №: 18 328

|
Столкнулся с такой проблемой. Перестает работать printf (а также sprintf, vprintf и т.д.), если запущена ОСь. Проявляется это в том, что функция неправильно вынимает из стека, переданные ей параметры и, соответственно, неправильно их выводит. Эффект наблюдается только на CortexM3 с компилятором IAR (конкретно 5.41.2). На ARM7, BlackFin, AVR, MSP430 - не наблюдается. В чем может быть причина этого и как с это исправить?
Факты к размышлению. 1. У Cortexa есть два стека (в отличии от других упомянутых процев). Причем тот, который привычный CSTACK используется для прерываний. Для нужд приложения используется второй стек. 2. В связке CortexM3 + GCC printf сначала не работал, но после выравнивания стека задач TStackItem Stack[] на границу 8 байт, функция заработала. Выравнивание при использовании IAR не помогло.
|
|
|
|
|
Feb 7 2011, 08:25
|

Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 24-06-06
Из: Томск
Пользователь №: 18 328

|
Цитата(AHTOXA @ Feb 7 2011, 14:34)  Да, проблема именно в выравнивании стека задачи. Сейчас на свежую голову выполнил выравнивание и все заработало. В прошлый раз по запарке не тем средством для выравнивания воспользовался. Вопрос снят.
|
|
|
|
|
Feb 7 2011, 09:34
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(shreck @ Feb 7 2011, 11:25)  Да, проблема именно в выравнивании стека задачи. Сейчас на свежую голову выполнил выравнивание и все заработало. Хм, у меня тоже проект в ИАРе. Никакого выравнивания не делал, всё работает. Правда, printf() не пользуюсь, только vsnprintf(). А сама операционка разве не делает выравнивание стеков задач?
|
|
|
|
|
Feb 7 2011, 09:40
|

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

|
Цитата(sonycman @ Feb 7 2011, 14:34)  Никакого выравнивания не делал, всё работает. Там как повезёт. Добавил переменную - работает, убавил - не работает  Цитата(sonycman @ Feb 7 2011, 14:34)  А сама операционка разве не делает выравнивание стеков задач? Пока не делает, починяем  В качестве временной меры надо в OS_Kernel.h сделать так: Код __attribute__ ((aligned (8))) TStackItem Stack[stack_size/sizeof(TStackItem)]; (не знаю как в IAR задаётся выравнивание)
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 7 2011, 09:41
|

Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 24-06-06
Из: Томск
Пользователь №: 18 328

|
Цитата(AHTOXA @ Feb 7 2011, 16:05)  А как это выглядит в IAR-е? (Как задаётся выравнивание?) Сейчас временно сделал так. При определении объекта задачи: Код #pragma data_alignment=8 Some_task task_obj; Но это не совсем правильно. В scmRTOS, как мне думается, выравнивание надо делать как-то вроде этого (не помню как это называется): Код class process : public TBaseProcess { ... private: union Align_buff { long long dummy; TStackItem Stack[stack_size/sizeof(TStackItem)]; }; Align_buff buff; };
|
|
|
|
|
Feb 7 2011, 10:36
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Feb 7 2011, 12:40)  Там как повезёт. Добавил переменную - работает, убавил - не работает  Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного. Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт? Ведь минимальная величина изменения указателя стека - 4 байта, не 8? Тогда получается выравнивать надо по границе слова, а не двух слов?
|
|
|
|
|
Feb 7 2011, 10:46
|

Местный
  
Группа: Свой
Сообщений: 327
Регистрация: 24-06-06
Из: Томск
Пользователь №: 18 328

|
Цитата(sonycman @ Feb 7 2011, 18:36)  Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного. Дело не в количестве вывода через printf. Вам везет, что стек выровнен как надо. Кстати какой проц. Не факт, что вам требуется выравнивание именно на 8 байт. Цитата(sonycman @ Feb 7 2011, 18:36)  Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт? Требование конторы ARM, ссылка уже была. И у IARa в Development Guide для ARM Cores четко прописано 8 байтное выравнивание стека.
|
|
|
|
|
Feb 7 2011, 10:51
|

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

|
Цитата(shreck @ Feb 7 2011, 14:41)  Код #pragma data_alignment=8 Some_task task_obj; То есть, выравнивание стека задать таким образом нельзя, только выравнивание всего объекта? Это конечно не дело. Цитата(sonycman @ Feb 7 2011, 15:36)  Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного. Или IAR у вас старый, и ещё не научился пользоваться новыми фишками  Цитата(sonycman @ Feb 7 2011, 15:36)  Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт? Мне тоже не очень понятно. Но - стандарт, что поделать. Тем более, что имеем явный пример глюков, которые можно получить, не соблюдая этот стандарт.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 7 2011, 14:55
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(shreck @ Feb 7 2011, 13:46)  Дело не в количестве вывода через printf. Вам везет, что стек выровнен как надо. Кстати какой проц. Не факт, что вам требуется выравнивание именно на 8 байт. У меня LPC1768. В теме, по моему, ясно написано Cortex-M3. Неужели двум чипам на одном и том же ядре требуется разное выравнивание? У себя использую три процесса с такими стеками: Код typedef OS::process<OS::pr1, 500> TProc1; typedef OS::process<OS::pr2, 800> TProc2; typedef OS::process<OS::pr3, 500> TProc3; Посмотрел в map файле линкера их расположение: Код Proc1 0x10001ee8 0x200 Data Gb main.o [1] Proc2 0x100020e8 0x32c Data Gb main.o [1] Proc3 0x10002414 0x200 Data Gb main.o [1] Как видно, стек третьего процесса не выровнен по границе 8 байт. Однако же именно этот низкоприоритетный поток постоянно юзает печать через vsnprintf(). Вот и не верится мне что-то, что ваша проблема на 100% связана с выравниванием. Цитата(AHTOXA @ Feb 7 2011, 13:51)  Или IAR у вас старый, и ещё не научился пользоваться новыми фишками  EWARM v5.50.5. Поновее, чем у автора. Может, наоборот, старенький ИАР подглючивает?
|
|
|
|
|
Feb 7 2011, 15:38
|

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

|
Цитата(sonycman @ Feb 7 2011, 19:55)  Как видно, стек третьего процесса не выровнен по границе 8 байт. Однако же именно этот низкоприоритетный поток постоянно юзает печать через vsnprintf(). Возможно, проблема не гарантировано появляется, а лишь с какой-то вероятностью. Или стек расположен не в начале объекта TBaseProcess, и как раз наоборот, получается выровненным. Не знаю. Попробуйте ещё добавлять/убавлять переменные, чтобы выравнивание изменилось и возможно поймаете этот глюк. Цитата Вот и не верится мне что-то, что ваша проблема на 100% связана с выравниванием. Если ничего не меняя, только выравнивание, удаётся решить проблему, то вероятнее всего, что проблема именно в выравнивании, не правда ли?  Цитата Может, наоборот, старенький ИАР подглючивает?  Дело в том, что и на GCC та же проблема (см. первое сообщение топика, п. 2) (это как раз я проверял, с GCC). ЗЫ. Дополню картину. В проекте вызываю тестовую функцию дважды - один раз до вызова OS:Run(), второй - после, уже из процесса. В первом случае результат корректный, во втором - нет. Запрет прерываний во втором случае не спасает. Зато выравнивание стека процесса - устраняет глюк. Какие тут можно придумать варианты, кроме стека?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 7 2011, 16:20
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Feb 7 2011, 18:38)  Возможно, проблема не гарантировано появляется, а лишь с какой-то вероятностью. Или стек расположен не в начале объекта TBaseProcess, и как раз наоборот, получается выровненным. Не знаю. Да, резервируется место, на 12 байт большее отведённого места под стек. Для чего, интересно? Цитата(AHTOXA @ Feb 7 2011, 18:38)  ЗЫ. Дополню картину. В проекте вызываю тестовую функцию дважды - один раз до вызова OS:Run(), второй - после, уже из процесса. В первом случае результат корректный, во втором - нет. Запрет прерываний во втором случае не спасает. Зато выравнивание стека процесса - устраняет глюк. Какие тут можно придумать варианты, кроме стека? Было бы неплохо для наглядности продебажить на самом низком уровне и увидеть, в каком именно месте затык. Ведь даже если вершина стека является выровненной до 8 байт, разве не может на момент вызова любой функции текущий адрес стека быть равен его вершине-4, -12 и т.д. с кратностью 4?
|
|
|
|
|
Feb 7 2011, 16:47
|

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

|
Цитата(sonycman @ Feb 7 2011, 21:20)  Да, резервируется место, на 12 байт большее отведённого места под стек. Для чего, интересно? Как это для чего? Вот же: Код class TBaseProcess { ... protected: TStackItem* StackPointer; TTimeout Timeout; TPriority Priority; }; Вот они, наши 12 байт. Итого, 0x10002414 + 12 = 0x10002420 (!) Вот потому и работает printf  Цитата Ведь даже если вершина стека является выровненной до 8 байт, разве не может на момент вызова любой функции текущий адрес стека быть равен его вершине-4, -12 и т.д. с кратностью 4? Нет. Компилятор обеспечивает дискретность выделения стека, равную восьми байтам. Таким образом получается, что при условии изначального выравнивания стека на границу 8 байт, оно соблюдается (сохраняется) при любом уровне вложенности вызовов. (ИМХО конечно)
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 7 2011, 16:59
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Feb 7 2011, 19:47)  Как это для чего? Вот же: А, точно, вот они где  Цитата Итого, 0x10002414 + 12 = 0x10002420 (!) Вот потому и работает printf  Э, нет, Антоха, так просто не получится - посмотри на другие два процесса - они тоже печатью занимаются  Там выравнивание тогда будет нарушаться. Цитата Нет. Компилятор обеспечивает дискретность выделения стека, равную восьми байтам. Таким образом получается, что при условии изначального выравнивания стека на границу 8 байт, оно соблюдается (сохраняется) при любом уровне вложенности вызовов. (ИМХО конечно) Ну если компилер поддерживает выравнивание - тогда всё в порядке.
|
|
|
|
|
Jan 30 2012, 00:01
|

embarrassed systems engineer
    
Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038

|
Я тоже наступил на эти шикарные грабли. Недавно добавил в свой printf поддержку 64-битных целых (signed/unsigned long long которые). Проверил, отладил - все работает. И даже пару месяцев нормально поработало. А потом - хрясь, и все, мусор вместо 64-битных лезть начал. И не сразу докопался я, что va_arg(list, long long) неверно из стека 64-битные вынимает. Добавил выравнивание стека в порт RTOS (TNKernel, не SCM, но это неважно) при инициализации задачи - и все снова работает. Да, грабли просто шикарные, и бьют больно.
|
|
|
|
|
Jan 11 2013, 13:18
|

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

|
Цитата(haker_fox @ Jan 11 2013, 18:32)  З.Ы. У меня 4-я версия, там еще ничего не выравнивается( Как это не выравнивается? Может быть, вы имели в виду 3-ю версию? Так и там исправлено (по крайней мере в 3.11/GCC - точно). Вот же ( tags/3.11/CortexM3/GCC/scmRTOS/CortexM3/OS_Target_cpp.cpp): TBaseProcess::TBaseProcess(TStackItem* Stack, TPriority pr, void (*exec)()) : StackPointer((TStackItem*)((unsigned int)Stack & 0xFFFFFFF8))
Если у вас IAR, то поправьте по аналогии.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 11 2013, 15:00
|

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

|
Цитата(haker_fox @ Jan 11 2013, 19:27)  Я имел в виду под ARM7TDMI... А тема про CortexM3  Тут такое дело. Дефайн не поможет в общем случае. Потому что выравнивать нужно не начальную вершину стека, а его вершину после сохранения начального контекста. У ARM7 и у Cortex-M3 контекст 16 слов, поэтому можно выравнивать и так и так. А вот с Cortex-M4F такой фокус уже не прокатит, там 17 слов. Поэтому выравнивать надо не централизованно, а в порте. Лучше - так, как я сделал в порте для Cortex-M4F. Цитата(haker_fox @ Jan 11 2013, 19:27)  Принтф у меня не работала, пока я не закомментировал то, что было, и не добавил то, что ниже комментария... Не, так точно не надо. Надо как-то вот так (в OS_Target_cpp.cpp): Код void TBaseProcess::init_stack_frame( stack_item_t * Stack , void (*exec)() #if scmRTOS_DEBUG_ENABLE == 1 , stack_item_t * StackBegin #endif ) { StackPointer = (stack_item_t*)((uintptr_t)Stack & 0xFFFFFFF8); *(--StackPointer) = (stack_item_t)exec; // return from interrupt address StackPointer -= 14; // emulate "push R0-R12", "push LR" if((uintptr_t)exec & (1 << 0)) // if exec is THUMB-mode code *(--StackPointer) = 0x003F; // SR value: system mode, FIQ & IRQ enabled, THUMB mode else *(--StackPointer) = 0x001F; // SR value: system mode, FIQ & IRQ enabled, ARM mode
#if scmRTOS_DEBUG_ENABLE == 1 ... Попробуйте, и, если всё нормально, то я внесу правку в репозиторий.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 15 2013, 14:10
|

Познающий...
     
Группа: Свой
Сообщений: 2 963
Регистрация: 1-09-05
Из: г. Иркутск
Пользователь №: 8 125

|
QUOTE (AHTOXA @ Jan 12 2013, 01:54)  Исправил Что-то не так... Сейчас снова одни нули вместо плавучки выводятся. Причем если вернуть определение стека так, как я первоначально сделал - то работает. Как Вы предложили - нет  Неужели я в прошлый раз как-то компильнул проект не так  Оперативы у меня 32 метра, дефицита не наблюдается. malloc() столь необходимая (иногда?) printf - работает. Пока думаю, куда копать... Для очистки совести сделал CODE make clean && make all для двух вариантов... Вывод 1 (плохой) CODE #ai6di2 ADC0: 0x81 129 0.00 ADC1: 0x80 128 0.00 ADC2: 0x80 128 0.00 ADC3: 0x80 128 0.00 ADC4: 0x7f 127 0.00 ADC5: 0x7b 123 0.00 Вывод 2 (хороший) CODE #ai6di2 ADC0: 0x81 129 2.52 ADC1: 0x80 128 2.50 ADC2: 0x80 128 2.50 ADC3: 0x80 128 2.50 ADC4: 0x7f 127 2.48 ADC5: 0x7b 123 2.40 Сама функция работает (текст, hex, dec) AHTOXA, а почему не стоит делать правку в кернел.h? Ведь это логично, там мы выравниваем стек именно процессов? Более того, Вы сами предложили этот вариант в первых постах) Нэ понимает моя))) З.Ы. Блин, в map-файле от GCC без поллитра не разберешься... Пошел мой разбор полетов: 1. По-сути при создании процесса мы попадаем в функцию CODE init_stack_frame , которая инициализиует стэк. И уже там "готовится" указатель, выровненный пока не понятным для меня ( к сожалению опыта мало) образом. Т.е. что там, что тут - вроде без разницы...
--------------------
Выбор.
|
|
|
|
|
Jan 15 2013, 14:13
|

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

|
Цитата(haker_fox @ Jan 15 2013, 20:03)  Что-то не так... Сейчас снова одни нули вместо плавучки выводятся. Причем если вернуть определение стека так, как я первоначально сделал - то работает. Как Вы предложили - нет  Очень странно. Вообще-то эти варианты эквивалентны. Только что увидел, что не до конца исправил: Исправьте в конце TBaseProcess::init_stack_frame(): Код #if scmRTOS_DEBUG_ENABLE == 1 for (stack_item_t* pDst = StackBegin; pDst < StackPointer; pDst++) *pDst = STACK_DEFAULT_PATTERN; #endif // scmRTOS_DEBUG_ENABLE Цитата(haker_fox @ Jan 15 2013, 20:03)  AHTOXA, а почему не стоит делать правку в кернел.h? Потому что kernel.h - один на все порты. Чтобы внести правку в него, нужно вносить изменения во все порты (какие-то макросы придумывать, дефайны, или что-то типа того). Ну и вообще, не дело решать проблемы одного порта правками в коде оси, если есть иное решение. А если вы ведёте речь о локальной правке, у себя, то такую правку делать можно  (Только если вдруг, через полгода-год, давным-давно забыв об этой истории, обновите ось, и что-то перестанет работать, то не говорите, что я вас не предупреждал  )
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jan 15 2013, 14:26
|

Познающий...
     
Группа: Свой
Сообщений: 2 963
Регистрация: 1-09-05
Из: г. Иркутск
Пользователь №: 8 125

|
Дико прошу прощения! Ввел в заблуждение себя и посеял сомнение вокруг((( Глаз замылился, и при правке кода допустил пару ошибок. Чтобы никого не смущать, приведу полный вариант правки, который у меня работает... CODE void TBaseProcess::init_stack_frame( stack_item_t * Stack , void (*exec)() #if scmRTOS_DEBUG_ENABLE == 1 , stack_item_t * StackBegin #endif ) { //--------------------------------------------------------------- // // Prepare Process Stack Frame // StackPointer = (stack_item_t*)((uintptr_t)Stack & 0xFFFFFFF8); *(--StackPointer) = (stack_item_t)exec; // return from interrupt address
StackPointer -= 14; // emulate "push R0-R12", "push LR"
if((uintptr_t)exec & (1 << 0)) // if exec is THUMB-mode code *(--StackPointer) = 0x003F; // SR value: system mode, FIQ & IRQ enabled, THUMB mode else *(--StackPointer) = 0x001F; // SR value: system mode, FIQ & IRQ enabled, ARM mode
// StackPointer = Stack; // pointer to stored context
#if scmRTOS_DEBUG_ENABLE == 1 for (stack_item_t* pDst = StackBegin; pDst < StackPointer; pDst++) *pDst = STACK_DEFAULT_PATTERN; #endif // scmRTOS_DEBUG_ENABLE } Извините, дамы и господа!  Работа по ночам - это плохо() QUOTE (AHTOXA @ Jan 15 2013, 23:13)  (Только если вдруг, через полгода-год, давным-давно забыв об этой истории, обновите ось, и что-то перестанет работать, то не говорите, что я вас не предупреждал  ) Ага, до меня дошло  )
--------------------
Выбор.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|