Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: scmRTOS + CortexM3 + printf
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
shreck
Столкнулся с такой проблемой.
Перестает работать printf (а также sprintf, vprintf и т.д.), если запущена ОСь. Проявляется это в том, что функция неправильно вынимает из стека, переданные ей параметры и, соответственно, неправильно их выводит.
Эффект наблюдается только на CortexM3 с компилятором IAR (конкретно 5.41.2). На ARM7, BlackFin, AVR, MSP430 - не наблюдается.
В чем может быть причина этого и как с это исправить?

Факты к размышлению.
1. У Cortexa есть два стека (в отличии от других упомянутых процев). Причем тот, который привычный CSTACK используется для прерываний. Для нужд приложения используется второй стек.
2. В связке CortexM3 + GCC printf сначала не работал, но после выравнивания стека задач TStackItem Stack[] на границу 8 байт, функция заработала. Выравнивание при использовании IAR не помогло.
AHTOXA
Цитата(shreck @ Feb 7 2011, 11:28) *
2. В связке CortexM3 + GCC printf сначала не работал, но после выравнивания стека задач TStackItem Stack[] на границу 8 байт, функция заработала. Выравнивание при использовании IAR не помогло.

Вот что я нарыл по этому поводу: 8 byte stack alignment is a requirement of the ARM Architecture Procedure Call Standard [AAPCS].
shreck
Цитата(AHTOXA @ Feb 7 2011, 14:34) *

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

Вопрос снят.
AHTOXA
Цитата(shreck @ Feb 7 2011, 13:25) *
Вопрос снят.

А как это выглядит в IAR-е? (Как задаётся выравнивание?)
sonycman
Цитата(shreck @ Feb 7 2011, 11:25) *
Да, проблема именно в выравнивании стека задачи.
Сейчас на свежую голову выполнил выравнивание и все заработало.

Хм, у меня тоже проект в ИАРе.
Никакого выравнивания не делал, всё работает.
Правда, printf() не пользуюсь, только vsnprintf().

А сама операционка разве не делает выравнивание стеков задач?
AHTOXA
Цитата(sonycman @ Feb 7 2011, 14:34) *
Никакого выравнивания не делал, всё работает.

Там как повезёт. Добавил переменную - работает, убавил - не работаетsm.gif
Цитата(sonycman @ Feb 7 2011, 14:34) *
А сама операционка разве не делает выравнивание стеков задач?

Пока не делает, починяемsm.gif
В качестве временной меры надо в OS_Kernel.h сделать так:
Код
     __attribute__ ((aligned (8))) TStackItem Stack[stack_size/sizeof(TStackItem)];

(не знаю как в IAR задаётся выравнивание)
shreck
Цитата(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;
};


sonycman
Цитата(AHTOXA @ Feb 7 2011, 12:40) *
Там как повезёт. Добавил переменную - работает, убавил - не работаетsm.gif

Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного.

Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт?
Ведь минимальная величина изменения указателя стека - 4 байта, не 8?
Тогда получается выравнивать надо по границе слова, а не двух слов?
shreck
Цитата(sonycman @ Feb 7 2011, 18:36) *
Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного.

Дело не в количестве вывода через printf. Вам везет, что стек выровнен как надо. Кстати какой проц. Не факт, что вам требуется выравнивание именно на 8 байт.


Цитата(sonycman @ Feb 7 2011, 18:36) *
Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт?

Требование конторы ARM, ссылка уже была. И у IARa в Development Guide для ARM Cores четко прописано 8 байтное выравнивание стека.
AHTOXA
Цитата(shreck @ Feb 7 2011, 14:41) *
Код
#pragma data_alignment=8
Some_task task_obj;

То есть, выравнивание стека задать таким образом нельзя, только выравнивание всего объекта? Это конечно не дело.

Цитата(sonycman @ Feb 7 2011, 15:36) *
Ну, тогда, наверное, мне сильно везёт, так как вывода через vsnprintf() много и самого разного.

Или IAR у вас старый, и ещё не научился пользоваться новыми фишкамиsm.gif

Цитата(sonycman @ Feb 7 2011, 15:36) *
Кстати, мне вот не особо понятно, как применительно к стеку может относится выравнивание в 8 байт?

Мне тоже не очень понятно. Но - стандарт, что поделать. Тем более, что имеем явный пример глюков, которые можно получить, не соблюдая этот стандарт.
sonycman
Цитата(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 у вас старый, и ещё не научился пользоваться новыми фишкамиsm.gif

EWARM v5.50.5.
Поновее, чем у автора.
Может, наоборот, старенький ИАР подглючивает? wink.gif
AHTOXA
Цитата(sonycman @ Feb 7 2011, 19:55) *
Как видно, стек третьего процесса не выровнен по границе 8 байт.
Однако же именно этот низкоприоритетный поток постоянно юзает печать через vsnprintf().

Возможно, проблема не гарантировано появляется, а лишь с какой-то вероятностью. Или стек расположен не в начале объекта TBaseProcess, и как раз наоборот, получается выровненным. Не знаю. Попробуйте ещё добавлять/убавлять переменные, чтобы выравнивание изменилось и возможно поймаете этот глюк.
Цитата
Вот и не верится мне что-то, что ваша проблема на 100% связана с выравниванием.

Если ничего не меняя, только выравнивание, удаётся решить проблему, то вероятнее всего, что проблема именно в выравнивании, не правда ли? sm.gif
Цитата
Может, наоборот, старенький ИАР подглючивает? wink.gif

Дело в том, что и на GCC та же проблема (см. первое сообщение топика, п. 2) (это как раз я проверял, с GCC).

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

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

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

Ведь даже если вершина стека является выровненной до 8 байт, разве не может на момент вызова любой функции текущий адрес стека быть равен его вершине-4, -12 и т.д. с кратностью 4?
AHTOXA
Цитата(sonycman @ Feb 7 2011, 21:20) *
Да, резервируется место, на 12 байт большее отведённого места под стек.
Для чего, интересно?

Как это для чего? Вот же:
Код
    class TBaseProcess
    {
...
    protected:
        TStackItem* StackPointer;
        TTimeout Timeout;
        TPriority Priority;
    };

Вот они, наши 12 байт. Итого, 0x10002414 + 12 = 0x10002420 (!) Вот потому и работает printf sm.gif

Цитата
Ведь даже если вершина стека является выровненной до 8 байт, разве не может на момент вызова любой функции текущий адрес стека быть равен его вершине-4, -12 и т.д. с кратностью 4?

Нет. Компилятор обеспечивает дискретность выделения стека, равную восьми байтам. Таким образом получается, что при условии изначального выравнивания стека на границу 8 байт, оно соблюдается (сохраняется) при любом уровне вложенности вызовов.
(ИМХО конечно)
sonycman
Цитата(AHTOXA @ Feb 7 2011, 19:47) *
Как это для чего? Вот же:

А, точно, вот они где sm.gif
Цитата
Итого, 0x10002414 + 12 = 0x10002420 (!) Вот потому и работает printf sm.gif

Э, нет, Антоха, так просто не получится - посмотри на другие два процесса - они тоже печатью занимаются sm.gif
Там выравнивание тогда будет нарушаться.
Цитата
Нет. Компилятор обеспечивает дискретность выделения стека, равную восьми байтам. Таким образом получается, что при условии изначального выравнивания стека на границу 8 байт, оно соблюдается (сохраняется) при любом уровне вложенности вызовов.
(ИМХО конечно)

Ну если компилер поддерживает выравнивание - тогда всё в порядке.
AHTOXA
Цитата(sonycman @ Feb 7 2011, 21:59) *
Э, нет, Антоха, так просто не получится - посмотри на другие два процесса - они тоже печатью занимаются sm.gif

Точно? С printf? С плавучкой (я про это не говорил, но это важно)?
sonycman
Цитата(AHTOXA @ Feb 7 2011, 20:11) *
Точно? С printf? С плавучкой (я про это не говорил, но это важно)?

Да, с vsnprintf(), без плавучки.

Считаешь, что именно плавучка вызывает сбой?
AHTOXA
Да, плавучка. Я не помню, где я про это читал, помню только, что именно плавучка особенно требовательна к выравниванию стека.
Собственно, благодаря этому смутному воспоминанию мы и нашли решениеsm.gif
ЗЫ. Попробуй, это ж несложноsm.gif
shreck
Цитата(sonycman @ Feb 8 2011, 00:17) *
Считаешь, что именно плавучка вызывает сбой?

Да, плавучка ибо double, который как раз-таки размером 8 байт (а возможно и long long).
VslavX
Я тоже наступил на эти шикарные грабли. biggrin.gif
Недавно добавил в свой printf поддержку 64-битных целых (signed/unsigned long long которые). Проверил, отладил - все работает. И даже пару месяцев нормально поработало. А потом - хрясь, и все, мусор вместо 64-битных лезть начал. И не сразу докопался я, что va_arg(list, long long) неверно из стека 64-битные вынимает. Добавил выравнивание стека в порт RTOS (TNKernel, не SCM, но это неважно) при инициализации задачи - и все снова работает. Да, грабли просто шикарные, и бьют больно.
cerebral
Принимаю эстафету в беге по граблям.

Не могу найти причину hardfault'а в ScmRTOS после вызова sprintf. В hardfault падает из ContextRestore, на инструкции bx lr c 0xfffffffd в lr.

ScmRTOS 3.10, arm-kgp-eabi-x86_32-20111129 (с yagarto аналогично).

Оч. расчитываю на вашу помощь.
Сергей Борщ
QUOTE (cerebral @ Feb 21 2012, 08:53) *
В hardfault падает из ContextRestore, на инструкции bx lr c 0xfffffffd в lr.
По симптомам очень похоже на порчу сохраненного на стеке контекста процесса. Хватает ли стека тому процессу, который вызывает sprintf? Хватает ли буфера, в который пишет sprintf?
cerebral
Сергей, спасибо. Проблема действительно была заключена в недостаточном объёме памяти, выделенной под стек.
haker_fox
РРРР!) Прошу прощения за поднятие старой темы... Может быть какой-нить дефайн добавим в OS_Kernel.h? Чтобы при компиляции оси под армы (под ARM7TDMI точно) стек задач выравнивался на 8. А то я уж чуть не поседел, когда несколько дней думал, почему vsprintf не печатает плавучку... Выравнивание на 8 рулит!!!

З.Ы. У меня 4-я версия, там еще ничего не выравнивается(
AHTOXA
Цитата(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, то поправьте по аналогии.
haker_fox
Я имел в виду под ARM7TDMI... Принтф у меня не работала, пока я не закомментировал то, что было, и не добавил то, что ниже комментария... Версия 4.00
CODE
        class process : public TBaseProcess
        {
        public:
            INLINE_PROCESS_CTOR process();

            OS_PROCESS static void exec();
            
        #if scmRTOS_PROCESS_RESTART_ENABLE == 1
            INLINE void terminate();
        #endif
            

        private:
            //stack_item_t Stack[stack_size/sizeof(stack_item_t)];
            __attribute__ ((aligned (8))) stack_item_t Stack[stack_size/sizeof(stack_item_t)];
AHTOXA
Цитата(haker_fox @ Jan 11 2013, 19:27) *
Я имел в виду под ARM7TDMI...

А тема про CortexM3 sm.gif
Тут такое дело. Дефайн не поможет в общем случае. Потому что выравнивать нужно не начальную вершину стека, а его вершину после сохранения начального контекста. У 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
...

Попробуйте, и, если всё нормально, то я внесу правку в репозиторий.
haker_fox
QUOTE (AHTOXA @ Jan 12 2013, 00:00) *
А тема про CortexM3 sm.gif
Попробуйте, и, если всё нормально, то я внесу правку в репозиторий.

Про кортекс не заметил, был увлечен ARM7 rolleyes.gif

Поправки внес. Все запустилось и работает прекрасно. Надеюсь, что если пошло, то вылетов на этой почве уже не будет.

Спасибо! rolleyes.gif
AHTOXA
Исправил в репозитории.

Попутно вскрылся интересный факт - я правил с старом репозитории, а в новом изменения пока не появились. Выходит, это два разных репозитория, и они синхронизируются. Надо тогда поаккуратнее быть...
haker_fox
QUOTE (AHTOXA @ Jan 12 2013, 01:54) *
Исправил

Что-то не так... Сейчас снова одни нули вместо плавучки выводятся. Причем если вернуть определение стека так, как я первоначально сделал - то работает. Как Вы предложили - нет crying.gif Неужели я в прошлый раз как-то компильнул проект не так maniac.gif Оперативы у меня 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
, которая инициализиует стэк. И уже там "готовится" указатель, выровненный пока не понятным для меня ( к сожалению опыта мало) образом. Т.е. что там, что тут - вроде без разницы...
AHTOXA
Цитата(haker_fox @ Jan 15 2013, 20:03) *
Что-то не так... Сейчас снова одни нули вместо плавучки выводятся. Причем если вернуть определение стека так, как я первоначально сделал - то работает. Как Вы предложили - нет crying.gif

Очень странно. Вообще-то эти варианты эквивалентны.
Только что увидел, что не до конца исправил:
Исправьте в конце 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 - один на все порты. Чтобы внести правку в него, нужно вносить изменения во все порты (какие-то макросы придумывать, дефайны, или что-то типа того). Ну и вообще, не дело решать проблемы одного порта правками в коде оси, если есть иное решение.
А если вы ведёте речь о локальной правке, у себя, то такую правку делать можноsm.gif (Только если вдруг, через полгода-год, давным-давно забыв об этой истории, обновите ось, и что-то перестанет работать, то не говорите, что я вас не предупреждалsm.gif )
haker_fox
Дико прошу прощения! Ввел в заблуждение себя и посеял сомнение вокруг((( Глаз замылился, и при правке кода допустил пару ошибок. Чтобы никого не смущать, приведу полный вариант правки, который у меня работает...
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
}


Извините, дамы и господа! sm.gif Работа по ночам - это плохо()

QUOTE (AHTOXA @ Jan 15 2013, 23:13) *
(Только если вдруг, через полгода-год, давным-давно забыв об этой истории, обновите ось, и что-то перестанет работать, то не говорите, что я вас не предупреждалsm.gif )

Ага, до меня дошло sm.gif)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.