Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: работа scmRTOS из RAM и несколько вопросов
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
Aaron
Создал загрузчик для STM32F10x, работающий в двух процессах ОС.
Все функции загрузчика объявлены в классе, размещаются в .ramfunc секции:
Код
void erasePage(const uint32_t * const page_addr) __attribute__ ((section(".ramfunc")));
void writePage(const uint32_t * const page_addr, const uint8_t * const pagebuf) __attribute__ ((section(".ramfunc")));
uint8_t read(const uint8_t * const addr) __attribute__ ((section(".ramfunc")));

Соответственно, секция в линковщике определяется перед .data:
Код
    .text.align :
    {
        . = ALIGN(8);
        _etext = .;
        _sidata = _etext;        /* start of initialized data label */
    } > FLASH

    /* RAM functions section */
    .ramfunc :                    
    {
        . = ALIGN(8);
        _sdata = .;                    /* start of .data label */
        KEEP( *(.ramfunc) )
        KEEP( *(.ramfunc.*) )
    } > RAM AT > FLASH

    .data :                        /* AT makes the LMA follow on in the binary image */
    {
        . = ALIGN(4);
        KEEP( *(.data) )
        KEEP( *(.data.*) )
        . = ALIGN(4);
        _edata = .;                /* end of .data label */
    } > RAM AT > FLASH
...

Вопрос 1: Что надо сделать, чтобы загрузчик мог обновлять помимо пользовательской прошивки в том числе и себя.
Предположение: для этого необходимо все функции ОС выделить в отдельную секцию, например .scmRTOS, и секцию поместить в RAM по аналогии с .ramfunc.
Насколько это легко осуществимо? Или мне надо оставить всё в секции .text и её целиком в RAM пихать, т.к. есть ещё глобальные объекты со своими функциями? Ни разу так не пробовал ещё...

Вопрос 2: возможно ли использовать какую-то #pragma, чтобы пихать содержимое всего файла или части файла в отдельную секцию? Сейчас каждый раз перед телом функции пишу атрибут:
Код
__attribute__ ((section(".ramfunc"))) void flashdrv_t::erase (void) { ... }
Предположение: я неверно понимаю использование атрибута section и его достаточно 1 раз написать в начале файла.

Вопрос 3: возможно ли как-нибудь остановить выполнение ОС и продолжить нормальное выполнение кода, находящегося после строчки OS::run(); ?
Предположение: запретить pensv, systick, и осуществить переход goto label по прямой ссылке.
_Артём_
Цитата(Aaron @ Feb 26 2014, 15:35) *
Вопрос 1: Что надо сделать, чтобы загрузчик мог обновлять помимо пользовательской прошивки в том числе и себя.

Задаче не типичная - может проще написать загрузчик, который не надо обновлять?
Возможный вариант: иметь две копии загрузчика, если в процессе его обновления произойдёт сбой, то запускать нужно рабочую копию. Запускать копию может первичный загрузчик, в функции которого входит проверка валидности копий основного загрузчика и запуск рабочей копии. Это часть обновлять не нужно.

Цитата(Aaron @ Feb 26 2014, 15:35) *
Вопрос 3: возможно ли как-нибудь остановить выполнение ОС и продолжить нормальное выполнение кода, находящегося после строчки OS::run(); ?
[i]Предположение: запретить pensv, systick, и осуществить переход goto label по прямой ссылке.
Что у вас там за код? Зачем он? Может проще сброс сделать и там выполнять код, который вы хотите за OS::run поместить?


Цитата(Aaron @ Feb 26 2014, 15:35) *
Предположение: запретить pensv, systick, и осуществить переход goto label по прямой ссылке.
Может все прерывания запретить?
AHTOXA
Цитата(Aaron @ Feb 26 2014, 17:35) *
Вопрос 1: Что надо сделать, чтобы загрузчик мог обновлять помимо пользовательской прошивки в том числе и себя.

Самое простое, имхо, поместить весь загрузчик в ОЗУ. (.text > RAM AT > FLASH)
Цитата(Aaron @ Feb 26 2014, 17:35) *
Вопрос 3: возможно ли как-нибудь остановить выполнение ОС и продолжить нормальное выполнение кода, находящегося после строчки OS::run(); ?

Опять же, простейший вариант - выйти из цикла while() в наиболее приоритетном процессе и продолжить выполнение там. Если не вызывать функции ОС, то это будет работать практически как безосевой код.
Aaron
Спасибо за ответы sm.gif Да, видать .text > RAM AT > FLASH самое простое решение sm.gif А может и не стоит обновлять загрузчик - пока что такой функционал ни разу не понадобился в боевых устройствах. Так, просто пытаюсь пораскинуть мозгами над возможными областями применения.
То же самое касается и выполнения кода после OS::run(). Скажем так, с эстетической точки зрения было бы приятнее, нагляднее и легче для понимания, если бы в main() было:
init_functions();
OS::run();
terminate_and_backup_functions();
Если бы функционал 3й строчки затерялся где-то внутри процессов, программа уже была бы сложнее для понимания. Вот только из-за этого. Хочу писать код так, чтобы после меня другие разработчики могли в нём спокойно ориентироваться без стресса wink.gif

Кстати, а вот ещё вопрос 4: по нулевому адресу находятся вектора прерываний. При обновлении загрузчика нулевую страницу с векторами тоже надо обновлять. Если секция .text находится в RAM - надо ещё какие-то телодвижения делать ведь для перебазирования таблицы векторов прерываний? NVIC_SetVectorTable(NVIC_VectTab_RAM) - оно?
AHTOXA
Цитата(Aaron @ Feb 27 2014, 16:29) *
OS::run();
terminate_and_backup_functions();
Если бы функционал 3й строчки затерялся где-то внутри процессов, программа уже была бы сложнее для понимания. Вот только из-за этого. Хочу писать код так, чтобы после меня другие разработчики могли в нём спокойно ориентироваться без стресса wink.gif

Ну оставьте этот вызов terminate_and_backup_functions() после OS::run(), чтоб другие разработчики ориентировались. А заодно тихонечко вызовите terminate_and_backup_functions() из самого приоритетного процесса, чтоб она реально сработалаsm.gif
Цитата(Aaron @ Feb 27 2014, 16:29) *
NVIC_SetVectorTable(NVIC_VectTab_RAM) - оно?

Да, это оно.
Aaron
Вернулся наконец-то к загрузчику, доделал. Начал тестировать и столкнулся неожиданно с проблемами. Пытаюсь вкурить, где косяк, но никак не могу докопаться.
Суть в следующем: загрузчик передаёт управление пользовательской прошивке, и она сразу вываливается в HardFault_Handler.
Передача управления из загрузчика стандартная на основе AN2557 для stm32f1:
Код
static void JumpToApplication(uint32_t addr)
{
    typedef  void (*pFunction)(void);
    pFunction Jump_To_Application;
    uint32_t JumpAddress;
    if(addr < ADDR_USER_START) {
        printf("Try to start User App from BL code area!\r\n");
        return;
    }
    /* Test if user code is programmed starting from our address */
    if (((*(__IO uint32_t*)addr) & 0x2FFE0000) == SRAM_BASE) {
        __disable_irq();
        NVIC_SetVectorTable(NVIC_VectTab_FLASH, FLASH_BL_SIZE);
        JumpAddress = *(__IO uint32_t*) (addr + 4);
        Jump_To_Application = (pFunction) JumpAddress;
        /* Initialize user application's Stack Pointer */
        __set_MSP(*(__IO uint32_t*) addr);
        Jump_To_Application();
    } else {
        printf("User App detect failed!\r\n");
    };
}

Соответственно, пользовательский код линкуется в stm32f107 со смещением (прошивка с дефолтным нулевым смещением работает нормально, проверено), никаких дополнительных манипуляций в коде не делается:
Код
MEMORY
{
    RAM (xrw)    : ORIGIN = 0x20000000, LENGTH =  64K
    FLASH (rx)    : ORIGIN = 0x0800A000, LENGTH = 216K    /* bootloader version. FLASH_BL_SIZE = 0xA000 */
}

Подробности анализа пользовательской прошивки при падении:
Цитата
---- SP registers ------
R0 : 0x20003094 адрес Process_ADC object
R1 : 0x20003168 адрес внутри Process_ADC object
R2 : 0x0000ABBA scmRTOS_STACK_PATTERN
R3 : 0x20003128 адрес внутри Process_ADC object
R12: 0x888344B4
LR : 0x0800B2DB ссылка на конец функции void OS::TKernel::register_process(OS::TBaseProcess* const p) :
800b2da: mov r0, r4
800b2dc: pop {r4, r5, r6, pc}
PC : 0x0000ABBA scmRTOS_STACK_PATTERN
PSR: 0x60000000

Stack at : 0x20003098 reg_PSP (Process Stack Pointer)

----
PSR: 0x60000000, Program Status Register, expanding:
Z = 1 Zero condition
C = 1 Carry or borrow condition
ICI/IT = 0 (saved IT state): ICI
reg# = 0 (used)
T = 0 ARM mode
ISR Vector = 0: &_estack (program entry point)
Vector addr: 0x2000FFF8 - соответствует ld-файлу: PROVIDE (_estack, ALIGN (((ORIGIN (RAM) + 0x10000) - 0x8), 0x8))

---- Fault Status ------
SCB->CFSR: 0x00020000 Configurable Fault Status Register, expanding:
INVSTATE: 1 Invalid combination of EPSR and instruction

SCB->VTOR: 0x0800A000 Vector table offset register

Остальные регистры не содержат интересной информации.
Это нормально, что в PC содержится паннерн для анализа стека?
Я так понимаю, это ещё не важно, т.к. ISR Vector = 0: &_estack (program entry point) - то есть падение происходит на первой же строчке пользовательской прошивке? Я правильно понимаю?
Посоветуйте плз, куда ещё копать, почему пользовательская прошивка не стартует нормально? Проблемы со стеком? help.gif
Сергей Борщ
Судя по содержимому PC, в него вместо адреса возврата из стека заносится код-заполнитель свободного пространства стека одной из задач ОС. OS::TKernel::register_process() вызывается из консруктпроцессор как раз перед заполением стека этим кодом. Похоже у вас основной стек налезает на область даннных, в которой и хранятся стеки процессов. Похоже, где-то вы пожалели стека для main(). И что интересно - в загрузчике вы корректно загружаете MSP, а в точке вылета почему-то используется PSP, хотя в scmRTOS переключение на него происходит при старте первого процесса. Возможно ваш загрузчик где-то переключается на PSP? Тогда надо бы перед передачей управления либо грузить его вместо MSP, либо, что более идеологически правильно, переключаться обратно на MSP после его загрузки.

Да, кстати - не факт, что используется PSP. Это я так подумал исходя из его упоминания в вашем анализе и вполне адекватного значения в нем. Чтобы убедиться надо бы посмотртеть содержимое первого бита регистра CONTROL.
Aaron
Месяц целый тяжело болел, в больничке провалялся, всё забыл sad.gif Только сейчас на работу вышел.
Сергей Борщ, спасибо за подсказки, буду разбираться дальше!
Aaron
Сергей Борщ, ещё раз спасибо! Наконец-то время нашлось вновь заняться вопросом - решил за 5 минут! Твоя подсказка оказалась абсолютно верной sm.gif :
Цитата
Возможно ваш загрузчик где-то переключается на PSP? Тогда надо бы перед передачей управления либо грузить его вместо MSP, либо, что более идеологически правильно, переключаться обратно на MSP после его загрузки.

Ответ нашёл гуглением "cortex-m3 switch to MSP" по ссылке fault-stack MSP setup
В общем, оказалось достаточно добавить ещё одну строчку в код:
__set_MSP(*(__IO uint32_t*) addr);
__set_CONTROL(0x00000000); // switch to "main" stack pointer PSP

Я-то по неопытности до этого думал, что __set_MSP уже сама по себе переключает управление на соответствующий стек )))
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.