Hello All,
my goal is to use the OpenBLT Bootloader on a STM32F103C6 to flash and later update a Keil RTX project.
The bootloader part (OpenBLT with UART Interface) is using the space between 0x08000000 to 0x08001800 on ROM and 0x20000000 to 0x20000C00 on RAM.
The RTX Application is unsing a scatter-file to create the file for flashing with the OpenBLT:
; ************************************************************* ; *** Scatter-Loading Description File for OpenBLT *** ; ************************************************************* LR_IROM1 0x08001800 0x00005800 { ; load region size_region ER_IROM1 0x08001800 0x00005800 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000C00 0x00001C00 { ; RW data .ANY (+RW +ZI) } }
As described in the OpenBLT guide, the "Checksum placeholder" is placed at the end of the vector table of the RTX Project (startup_stm32f10x_ld.s):
... DCD 0 ; Reserved DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend DCD 0x55AA11EE ; OpenBLT Checksum Reserved place __Vectors_End
The BOOT_FLASH_VECTOR_TABLE_CS_OFFSET is set to 0x130 in the flash.c file.
When I try to flash an RTX-like Project, the application is calling immediately the _sys_exit() function. (I'm using the
#pragma import(__use_no_semihosting)
and have implemented the function to get over the BKPT 0xAB).
When I try to flash an non-RTX Project, the application is running fine.
I think, that the problem is somewhere in the initializing code of the RTX kernel, maybe some Stack or Heap related part, but I really don't know where to start, because debugging is only possible when running the OpenBLT application and then following the program in the disassembly window...
Unfortunately I havent found a OpenBLT example who includes an Keil RTX program to run, only non-RTX examples.
Thanks
Alain
Hi Robert,
thanks for your answer.
I've increased the heap in the Bootloader application and also in the RTX application from 0x200 to 0x400 but no success. Should I increase the heap on the Bootloader or RTX application or both?
I can now debug the "flashed" firmware by downloading it by debugger (wrong OpenBLT checksum) and re-flashing it with the OpenBLT flasher (= creates correct checksum). So I can see, where the problem occours.
Right at the jump to the __main symbol the application goes into hard fault. So maybe the location of the __main (library init) is wrong?
; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R1, =__initial_sp ; Added for OpenBLT support MOV SP, R1 LDR R0, =SystemInit BLX R0 LDR R0, =__main ; -> loading jump address BX R0 ; -> Hard fault when jumping ENDP
The __main jump address is 0x080018F0, loaded in R0 is the value 0x080018F1 but I think, this is correct.
Assuming that _sys_exit() was called from the application then the heap of the application should matter.
Are you sure that the hard fault occurs right at the jump to _main or maybe inside _main? The _main address and R0 seem ok, therefore it is more likely that the hard fault occurred within _main. Try to debug and figure out why the hard fault occurs and who triggered it.
Take a look at the following application note which also explains how to "Debug faults with uVision" (page 15): http://www.keil.com/appnotes/files/apnt209.pdf
thanks for your answer. By stepping through the disassembly window, the fault is inside the __main function.
Somehow the function _platform_post_lib_init (in RTX_CM_lib.h) gets called who contains the following:
__asm void _platform_post_lib_init (void) { IMPORT os_thread_def_main IMPORT osKernelInitialize IMPORT osKernelStart IMPORT osThreadCreate IMPORT exit ADD SP,#0x10 BL osKernelInitialize LDR R0,=os_thread_def_main MOVS R1,#0 BL osThreadCreate BL osKernelStart BL exit ALIGN }
Inside of this function, the hard fault occures on the osKernelStart function, or better said, at the end of it.
The caller code (as mentioned in the app note), leads to the function sysTimerTick(). But I think the problem here is the initial call to the _platform_post_lib_init, because this looks like an "empty" rtx start with no threads, so the exit function is called and therefore the hard fault.
So why is this function called?
You probably need to make sure you have remapped the vector table before you call the app main.
Function _platform_post_lib_init is a hook called from __main by the C library.
osKernelStart should never return when there are no errors and execution should switch to main function (via os_thread_def_main thread).
It seems that something goes wrong inside osKernelStart (or already in osKernelInitialize).
Is the bootloader changing some system properties and not restoring them to reset values?
And as pointed out by Mr. McNamara: is the vector table remapped?
Hello Together,
alright, so the jump from the OpenBLT to the RTX application is made here (applies only to the OpenBLT application, not the RTX application):
/************************************************************************************//** ** \brief Starts the user program, if one is present. In this case this function ** does not return. ** \return none. ** ****************************************************************************************/ void CpuStartUserProgram(void) { void (*pProgResetHandler)(void); #ifndef BLT_IGNORE_CHECKSUM /* check if a user program is present by verifying the checksum */ if (NvmVerifyChecksum() == BLT_FALSE) { /* not a valid user program so it cannot be started */ return; } #endif #if (BOOT_CPU_USER_PROGRAM_START_HOOK > 0) /* invoke callback */ if (CpuUserProgramStartHook() == BLT_FALSE) { /* callback requests the user program to not be started */ return; } #endif #if (BOOT_COM_ENABLE > 0) /* release the communication interface */ ComFree(); #endif CpuIrqDisable(); /* reset the timer */ TimerReset(); /* remap user program's vector table */ SCB_VTOR = CPU_USER_PROGRAM_VECTABLE_OFFSET & (blt_int32u)0x1FFFFF80; /* set the address where the bootloader needs to jump to. this is the address of * the 2nd entry in the user program's vector table. this address points to the * user program's reset handler. */ pProgResetHandler = (void(*)(void))(*((blt_addr *)CPU_USER_PROGRAM_STARTADDR_PTR)); /* The Cortex-M3 core has interrupts enabled out of reset. the bootloader * explicitly disables these for security reasons. Enable them here again, so it does * not have to be done by the user program. */ CpuIrqEnable(); /* start the user program by activating its reset interrupt service routine */ pProgResetHandler(); } /*** end of CpuStartUserProgram ***/
- The BLT_IGNORE_CHECKSUM is a own define who I made to skip the OpenBLT checksum verification, this is currently not defined. - The BOOT_CPU_USER_PROGRAM_START_HOOK is also zero, because I don't need this - The BOOT_COM_ENABLE is set to 1, because I need the UART for the bootloader
Following the called functions:
/************************************************************************************//** ** \brief Releases the communication module. ** \return none ** ****************************************************************************************/ void ComFree(void) { #if (BOOT_COM_USB_ENABLE > 0) /* disconnect the usb device from the usb host */ UsbFree(); #endif } /*** end of ComFree ***/
/************************************************************************************//** ** \brief Disable global interrupts. ** \return none. ** ****************************************************************************************/ void CpuIrqDisable(void) { __asm volatile ("cpsid i"); } /*** end of CpuIrqDisable ***/
/************************************************************************************//** ** \brief Reset the timer by placing the timer back into it's default reset ** configuration. ** \return none. ** ****************************************************************************************/ void TimerReset(void) { /* set the systick's status and control register back into the default reset value */ SYSTICK->CTRL = 0; } /* end of TimerReset */
/************************************************************************************//** ** \brief Enable global interrupts. ** \return none. ** ****************************************************************************************/ void CpuIrqEnable(void) { __asm volatile ("cpsie i"); } /*** end of CpuIrqEnable ***/
Then the define CPU_USER_PROGRAM_VECTABLE_OFFSET is set to 0x08001800 and CPU_USER_PROGRAM_STARTADDR_PTR is set to 0x08001804.
On the other hand, at the SystemInit function of the RTX application, the Vector Offset register is not touched:
/** * @brief Setup the microcontroller system * Initialize the Embedded Flash Interface, the PLL and update the * SystemCoreClock variable. * @note This function should be used only after reset. * @param None * @retval None */ void SystemInit (void) { /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ /* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001; /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ #ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */ /* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ RCC->CFGR &= (uint32_t)0xFF80FFFF; #ifdef STM32F10X_CL /* Reset PLL2ON and PLL3ON bits */ RCC->CR &= (uint32_t)0xEBFFFFFF; /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x00FF0000; /* Reset CFGR2 register */ RCC->CFGR2 = 0x00000000; #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x009F0000; /* Reset CFGR2 register */ RCC->CFGR2 = 0x00000000; #else /* Disable all interrupts and clear pending bits */ RCC->CIR = 0x009F0000; #endif /* STM32F10X_CL */ #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) #ifdef DATA_IN_ExtSRAM SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */ #endif /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ /* Configure the Flash Latency cycles and enable prefetch buffer */ SetSysClock(); //#ifdef VECT_TAB_SRAM // SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ //#else // SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ //#endif }
The application is started via pProgResetHandler read out form the application vector table (CPU_USER_PROGRAM_VECTABLE_OFFSET + 4).
However the stack pointer is not configured based on the application (first entry of the application vector table) but rather inherited from the bootloader. Maybe this is causing issues.
Also is the core left in handler mode and using MSP (as default out of reset)? Otherwise RTX initialization and startup might fail.
thanks for your feedback.
The stack pointer should be set to the applications stack pointer on the SystemInit at the RTX application:
; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R1, =__initial_sp ; Load position of SP MOV SP, R1 ; set SP to position LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
While stepping through, the SP is located at the following addresses: 0x20000D68 on reset 0x20000D50 when entering this Reset_Handler and 0x200024D8 after moving R1 into SP
About the core modes: I'm not so sure about that, but as far that I know, the core is started in thread mode with privileged access.
Have you tried the RTX application without the bootloader?
If yes, you could check with the debugger what behaves differently.
so I've debugged the last days to check various things.
What I've found out (and will be most obvious to you I think) is, that the _platform_post_lib_init() function is used to create a main thread to call the user code main() function.
After creating the thread, the osKernelStart is called and should execute the main thread and thus should never "return" to the _platform_post_lib_init() function where the next instruction is the exit = hard fault, if semihosting is enabled...
Anyhow, the problem seems to be in the creation of the task. So the function svcThreadCreate() could not create the main task and so no task is running when the kernel starts.
// Thread Service Calls /// Create a thread and add it to Active Threads and set it to state READY osThreadId svcThreadCreate (const osThreadDef_t *thread_def, void *argument) { P_TCB ptcb; OS_TID tsk; void *stk; if ((thread_def == NULL) || (thread_def->pthread == NULL) || (thread_def->tpriority < osPriorityIdle) || (thread_def->tpriority > osPriorityRealtime)) { sysThreadError(osErrorParameter); return NULL; } if (thread_def->stacksize != 0U) { // Custom stack size stk = rt_alloc_mem( // Allocate stack os_stack_mem, thread_def->stacksize ); if (stk == NULL) { sysThreadError(osErrorNoMemory); // Out of memory return NULL; } } else { // Default stack size stk = NULL; } tsk = rt_tsk_create( // Create task (FUNCP)thread_def->pthread, // Task function pointer (uint32_t) (thread_def->tpriority-osPriorityIdle+1) | // Task priority (thread_def->stacksize << 8), // Task stack size in bytes stk, // Pointer to task's stack argument // Argument to the task ); if (tsk == 0U) { // Invalid task ID if (stk != NULL) { rt_free_mem(os_stack_mem, stk); // Free allocated stack } sysThreadError(osErrorNoMemory); // Create task failed (Out of memory) return NULL; } ptcb = (P_TCB)os_active_TCB[tsk - 1U]; // TCB pointer *((uint32_t *)ptcb->tsk_stack + 13) = (uint32_t)osThreadExit; return ptcb; }
Where exactly does the svcThreadCreate() function fail?
I think it's a problem inside the rt_tsk_create() function where a memory box should be allocated:
OS_TID rt_tsk_create (FUNCP task, U32 prio_stksz, void *stk, void *argv) { /* Start a new task declared with "task". */ P_TCB task_context; U32 i; /* Priority 0 is reserved for idle task! */ if ((prio_stksz & 0xFFU) == 0U) { prio_stksz += 1U; } task_context = rt_alloc_box (mp_tcb); if (task_context == NULL) { return (0U); } /* If "size != 0" use a private user provided stack. */ task_context->stack = stk; task_context->priv_stack = (U16)(prio_stksz >> 8); /* Pass parameter 'argv' to 'rt_init_context' */ task_context->msg = argv; /* For 'size == 0' system allocates the user stack from the memory pool. */ rt_init_context (task_context, (U8)(prio_stksz & 0xFFU), task); /* Find a free entry in 'os_active_TCB' table. */ i = rt_get_TID (); if (i == 0U) { return (0U); } os_active_TCB[i-1U] = task_context; task_context->task_id = (U8)i; DBG_TASK_NOTIFY(task_context, __TRUE); rt_dispatch (task_context); return ((OS_TID)i); }
on the line where rt_alloc_box() gets called, the task_conext is Null because the function call returns after this line. It's a bit tricky because this code is only as assembler available and some of it can't get accessed with the debugger. For example I can't get the content of the tast_context but jumping to the return statement let's me guess, that there is a problem with this.
Here is the called function where the __USE_EXCLUSIVE_ACCESS is defined (so the #else part is running):
void *rt_alloc_box (void *box_mem) { /* Allocate a memory block and return start address. */ void **free; #ifndef __USE_EXCLUSIVE_ACCESS U32 irq_mask; irq_mask = (U32)__disable_irq (); free = ((P_BM) box_mem)->free; if (free) { ((P_BM) box_mem)->free = *free; } if (irq_mask == 0U) { __enable_irq (); } #else do { if ((free = (void **)__ldrex(&((P_BM) box_mem)->free)) == 0U) { __clrex(); break; } } while (__strex((U32)*free, &((P_BM) box_mem)->free)); #endif return (free); }
It looks as if the memory for task control block could not be allocated. This should normally not occur since the memory is provided statically by the kernel.
Are you sure that rt_tsk_create() fails due to rt_alloc_box() returning NULL?
Even if the main thread could not be created, the function osKernelStart() should not return but switch to the Idle task which is created implicitly during osKernelInitialize().
I suggest you debug further and check first osKernelInitialize() and then osKernelStart().
Or you could try out a simple RTX based example without using the bootloader and examine what is different. Maybe you have a tool or configuration issue.
You could take a look at existing examples (Blinky) found in the Device Family Pack for STM32F1 (http://www.keil.com/dd2/pack/).
It should be also very easy to create a test project with empty main that uses RTX and verify that is starts properly. It takes less than a minute (a few clicks) to create an RTX project with MDK and using Run-Time Environment. It can be also configured for the simulator.
You could also consider the newer RTX5 (CMSIS RTOS2) with many new features (available as source code also - so debugging should be eaiser).
Try these in conjunction with what Robert #1 suggested.
1) See that your RTX_Config.c file is configured properly. Make sure you are looking at the EXACT one that is being used.
2) Look in the map file and see how much space is allocated for mp_tcb, mp_stk, os_stack_mem and make sure it matches what your RTX_Config.c files thinks they should be.
3) For a test, build a scatter file for the APP that loads at 0x08000000, rebuild and overwrite the bootloader and see if this runs properly or not.
An item of interest is that the Bootloader seems to set the stack to the highest RAM address as opposed to something within the range of 0x20000000 and 0x20000C00. Probably not your issue, but if you are really intending to keep these 2 separate, they are not.
Yes it is not something that occurs on its own, but it is only "semi" statically allocated. It can be set to 0 (i.e. it is possible that this is "empty" or "has no items" in it, but the compiler will not tell you that (so not quite statically allocated). As it seems that the rt_alloc_box for the STACKS may also be failing, maybe it is also zero or something else is wrong but related what makes the rt_alloc fail for both the TCB and Stack area's.
Yes and I am not seeing any reason to believe that it not being scheduled and dispatched. It would seem that it is probably the only thread the system thinks it has. The TCB for the idle thread is statically allocated (it is REALLY statically allocated such that if it compiles space for it will exist). My guess is the rt_alloc for the stack for the Idle thread is failing, and this causes it to be set to 0. When the initial stack frame is generated and the stack adjusted, it will not actually be pointing to or set to anything valid. I believe the hard fault comes when the system "returns" to the idle thread.
And remember (or note) you can add the source code to the os to your application and it will allow you to do source level debugging of your app including the os. (just make sure that after you add the files, you select do not include in build)
Yes, the blinky examples are always great.
thanks for your answer. Unfortunately changing/upgrading the rtos seems to much for me, due to licensing and affected products. But I've created a simple blinky project (who matches my hardware) and the problem occures too. So I've debugged a lot and found out, that the variables os_initialized and os_running are not set to zero when I start the application and thus the memory box (who is used to allocate memory for everything in the rtx) is not created. If I add the following code into the startup reset handler:
; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit IMPORT ClearSpecial LDR R1, =__initial_sp ; Added for OpenBLT support MOV SP, R1 LDR R0, =ClearSpecial BLX R0 LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
and then in my c-code:
void ClearSpecial(void) { os_initialized = 0; os_running = 0; }
the RTX code works (!), but only on the "simple" blinky project.
When I went further to use this on my application, who I want to use with the OpenBLT, the kernel starts and the threads were initialized, but after the threads are running I get again a hardfault.
This time the hardfault is fired "somewhere". By somewhere I mean that while stepping through the code, that I think not the shown code in the debugger is causing the issue, but more some IRQ related part...
As I've looked into the *.map file I found, that the both variables are data (for me = not zero initialized). So they must be initialized somewhere in the startup / arm library, because the RTX is running when I don't use the bootloader part.
So I've set an access-breakpoint on the os_initialized (write) and "found" the function __scatterload. So now I'm observing this (but I wanted to inform you about it), because I think my problems are caused by some not-initialized variables...
Hello together,
sorry for you Robert #2, I haven't seen your reply but finally I got things running. But I'm a bit confused, if I've found the "best practise" solution for it. So to summarize what I've done (and as a possible ToDo-List for future projects):
- Prepare the OpenBLT depending on your needs (in my case only UART is needed)
- Specify on your own, which memory addresses are used for the bootloader and the rtx application you would like to run, depending on used microcontroller in my case a STM32F103C6: + OpenBLT (only with UART), ROM from 0x08000000 - 0x080017FF, RAM from 0x2000000 - 0x20000BFF + RTX App, ROM from 0x080001800 - 0x08008000, RAM from 0x20000C00 - 0x20002800
- Apply the OpenBLT specific settings (vector table offset, checksum placeholder) on the bootloader application
- To run the RTX application with the OpenBLT, the semihosting must be disabled with the following pragma in the c-code, in my case placed in the main.c file:
- After compiling, implement the needed semihosting functions (like in retarget.c, the compiler should tell you the missing functions) like the _sys_exit:
void _sys_exit(int code) { // in my case: just reset to re-run the application, replace with more specific solution NVIC_SystemReset(); }
- Implement an "RTX Reset" function, who cleares the os_initialized and os_running variables, call it at the startup of the RTX application, in my case the function is named "ClearSpecial" in startup_stm32f10x_ld.s:
; Reset handler routine Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit IMPORT ClearSpecial LDR R1, =__initial_sp ; Added for OpenBLT support to get the correct stack pointer location MOV SP, R1 LDR R0, =ClearSpecial ; Added for RTX support BLX R0 LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
- Use a scatter file for relocating the RTX application:
- Edit the core_cm3.h file definitions (line 250 and 256 in original) to place constant variables in ROM and not in RAM. To do this, explore to the file and remove the write-protection and edit the following:
/* IO definitions (access restrictions to peripheral registers) */ /** \defgroup CMSIS_glob_defs CMSIS Global Defines <strong>IO Type Qualifiers</strong> are used \li to specify the access to peripheral variables. \li for automatic generation of peripheral register debug information. */ #ifdef __cplusplus #define __I volatile /*!< Defines 'read only' permissions */ #else //#define __I volatile const /*!< Defines 'read only' permissions */ #define __I const /*!< Defines 'read only' permissions, special version for compiler V5.05, update 2, build 169 */ #endif #define __O volatile /*!< Defines 'write only' permissions */ #define __IO volatile /*!< Defines 'read / write' permissions */ /* following defines should be used for structure members */ //#define __IM volatile const /*! Defines 'read only' structure member permissions */ #define __IM const /*! Defines 'read only' structure member permissions, special version for compiler V5.05, update 2, build 169 */ #define __OM volatile /*! Defines 'write only' structure member permissions */ #define __IOM volatile /*! Defines 'read / write' structure member permissions */ /*@} end of group Cortex_M3 */
- Add the after build command in the RTX application to generate a *.srec file, used by the OpenBLT MicroBoot application (Options for Target->User->Run #1), and don't forget to check the checkbox!
fromelf --m32 --output=OutputDirectory\RTX_App.srec OutputDirectory\RTX_App.axf --cpu Cortex-M3
To debug the RTX application, do the following: + Compile the OpenBLT application and flash the microcontroller + Compile the RTX application and flash the microcontroller (start a debug-session) + Flash the controller again with the MicroBoot application (microcontroller isn't leaving the bootloader, because the checksum is incorrect until the RTX application is flashed within the bootloader)
- Be happy!