|
|
  |
работа scmRTOS из RAM и несколько вопросов, GCC, STM32F10x |
|
|
|
Feb 26 2014, 11:35
|

Местный
  
Группа: Свой
Сообщений: 243
Регистрация: 5-10-06
Из: Зеленоград
Пользователь №: 21 007

|
Создал загрузчик для 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 по прямой ссылке.
|
|
|
|
|
Feb 26 2014, 12:17
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(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 по прямой ссылке. Может все прерывания запретить?
|
|
|
|
|
Feb 26 2014, 13:11
|

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

|
Цитата(Aaron @ Feb 26 2014, 17:35)  Вопрос 1: Что надо сделать, чтобы загрузчик мог обновлять помимо пользовательской прошивки в том числе и себя. Самое простое, имхо, поместить весь загрузчик в ОЗУ. (.text > RAM AT > FLASH) Цитата(Aaron @ Feb 26 2014, 17:35)  Вопрос 3: возможно ли как-нибудь остановить выполнение ОС и продолжить нормальное выполнение кода, находящегося после строчки OS::run(); ? Опять же, простейший вариант - выйти из цикла while() в наиболее приоритетном процессе и продолжить выполнение там. Если не вызывать функции ОС, то это будет работать практически как безосевой код.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 27 2014, 10:29
|

Местный
  
Группа: Свой
Сообщений: 243
Регистрация: 5-10-06
Из: Зеленоград
Пользователь №: 21 007

|
Спасибо за ответы  Да, видать .text > RAM AT > FLASH самое простое решение  А может и не стоит обновлять загрузчик - пока что такой функционал ни разу не понадобился в боевых устройствах. Так, просто пытаюсь пораскинуть мозгами над возможными областями применения. То же самое касается и выполнения кода после OS::run(). Скажем так, с эстетической точки зрения было бы приятнее, нагляднее и легче для понимания, если бы в main() было: init_functions(); OS::run(); terminate_and_backup_functions(); Если бы функционал 3й строчки затерялся где-то внутри процессов, программа уже была бы сложнее для понимания. Вот только из-за этого. Хочу писать код так, чтобы после меня другие разработчики могли в нём спокойно ориентироваться без стресса  Кстати, а вот ещё вопрос 4: по нулевому адресу находятся вектора прерываний. При обновлении загрузчика нулевую страницу с векторами тоже надо обновлять. Если секция .text находится в RAM - надо ещё какие-то телодвижения делать ведь для перебазирования таблицы векторов прерываний? NVIC_SetVectorTable(NVIC_VectTab_RAM) - оно?
|
|
|
|
|
Feb 27 2014, 11:02
|

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

|
Цитата(Aaron @ Feb 27 2014, 16:29)  OS::run(); terminate_and_backup_functions(); Если бы функционал 3й строчки затерялся где-то внутри процессов, программа уже была бы сложнее для понимания. Вот только из-за этого. Хочу писать код так, чтобы после меня другие разработчики могли в нём спокойно ориентироваться без стресса  Ну оставьте этот вызов terminate_and_backup_functions() после OS::run(), чтоб другие разработчики ориентировались. А заодно тихонечко вызовите terminate_and_backup_functions() из самого приоритетного процесса, чтоб она реально сработала  Цитата(Aaron @ Feb 27 2014, 16:29)  NVIC_SetVectorTable(NVIC_VectTab_RAM) - оно? Да, это оно.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Mar 25 2014, 04:44
|

Местный
  
Группа: Свой
Сообщений: 243
Регистрация: 5-10-06
Из: Зеленоград
Пользователь №: 21 007

|
Вернулся наконец-то к загрузчику, доделал. Начал тестировать и столкнулся неожиданно с проблемами. Пытаюсь вкурить, где косяк, но никак не могу докопаться. Суть в следующем: загрузчик передаёт управление пользовательской прошивке, и она сразу вываливается в 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) - то есть падение происходит на первой же строчке пользовательской прошивке? Я правильно понимаю? Посоветуйте плз, куда ещё копать, почему пользовательская прошивка не стартует нормально? Проблемы со стеком?
|
|
|
|
|
Mar 25 2014, 08:45
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Судя по содержимому PC, в него вместо адреса возврата из стека заносится код-заполнитель свободного пространства стека одной из задач ОС. OS::TKernel::register_process() вызывается из консруктпроцессор как раз перед заполением стека этим кодом. Похоже у вас основной стек налезает на область даннных, в которой и хранятся стеки процессов. Похоже, где-то вы пожалели стека для main(). И что интересно - в загрузчике вы корректно загружаете MSP, а в точке вылета почему-то используется PSP, хотя в scmRTOS переключение на него происходит при старте первого процесса. Возможно ваш загрузчик где-то переключается на PSP? Тогда надо бы перед передачей управления либо грузить его вместо MSP, либо, что более идеологически правильно, переключаться обратно на MSP после его загрузки.
Да, кстати - не факт, что используется PSP. Это я так подумал исходя из его упоминания в вашем анализе и вполне адекватного значения в нем. Чтобы убедиться надо бы посмотртеть содержимое первого бита регистра CONTROL.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 15 2014, 11:59
|

Местный
  
Группа: Свой
Сообщений: 243
Регистрация: 5-10-06
Из: Зеленоград
Пользователь №: 21 007

|
Сергей Борщ, ещё раз спасибо! Наконец-то время нашлось вновь заняться вопросом - решил за 5 минут! Твоя подсказка оказалась абсолютно верной  : Цитата Возможно ваш загрузчик где-то переключается на 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 уже сама по себе переключает управление на соответствующий стек )))
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|