This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Keil RTX based bootloader launching Keil RTX based FW

Hello. I inherited a project that is running on a TI TM4C1230H6PM microcontroller and I've run into a bit of a snag.

We have a bootloader running Keil RTX that starts at address 0x0000 which ultimately launches FW running Keil RTX starting at address 0x8000. After adding lots of new functionality to the FW (including several new tasks) I found that we were encountering random stack overflow issues.

After some heavy debugging, I discovered that the Main Stack Pointer (MSP) was overrunning the stack of one of my new tasks and causing the error. Essentially, what I see happening is that when the code jumps over from the bootloader to the FW the MSP is loaded with the initial stack pointer from the bootloader (0x20003EB8) loaded from address 0x0000. This, apparently, never caused an issue in the past, but I now have a task whose stack ends at 0x20003E38. So, not only is the main stack not where it should be, but it's dangerously close to the top of a process stack.

The original code to jump from the bootloader to the firmware looked as follows

jump(*(U32 *)code_start, *(U32 *)(code_start + 4));

; jump to application code
        AREA    asm_func, CODE, READONLY
        EXPORT  jump
jump
        MOV     R13, R0
        BX      R1
      END

The problem I found here is that this executes in User context and so R13 refers to the PSP rather than the MSP.

I tried several different ways of resolving this issue by loading the contents (0x20007280) of address 0x8000 into the MSP register prior to branching, but no matter what I do I get a hardware fault almost immediately after the FW begins to run.

Can anyone shed some light on what's going on here?

Any assistance in solving this would be greatly appreciated.

  • The code I used to jump to application FW from bootloader is listed below:

    typedef    void (*pFunction)(void);
    uint32_t   timingdelay;
    pFunction  Jump_To_Application;
    uint32_t   JumpAddress;
    
    
    // function which never returns,
    // if conditions not met, return with 0
    
    BOOL JumpToFirmware(void)
    {
       int USER_FLASH_FIRST_PAGE_ADDRESS;
    
       USER_FLASH_FIRST_PAGE_ADDRESS = 0x08020200;
    
       // Check if valid stack address (RAM address) then jump to user application
       if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000) {
          // Jump to user application
          JumpAddress = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
          Jump_To_Application = (pFunction) JumpAddress;
    
          // Initialize user application's Stack Pointer
          __set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS);
          Jump_To_Application();
       }
    
       return 0;
    }
    

    Georg

  • Thanks for the response Georg. I tried this and got the same results as with my assembly code. It successfully changes the MSP and then jumps to the correct code, but almost immediately hits a HW fault. I believe the fault is occurring during the __main RTX initialization function which I do not have source code for.

    Does anyone know if RTX is doing some sort of check that PSP and MSP aren't equal/within some range or any other check on the pointer that may have been configured by the bootloader?

    I ask because I just arbitrarily subtracted 256 from the FW's MSP value and got the code to run w/o causing an immediate fault. Obviously, this isn't really a solution though since I just reduced the main stack by 256 bytes.

  • So after observing the state of the processor when initially booting into the bootloader I came up with the following that seems to work...

    ; jump to application coode
            AREA    asm_func, CODE, READONLY
            EXPORT  jump
    jump
        MOV     R4, #0x0
        MSR     PSP, R4      ; set PSP to zero
        MSR     MSP, R0      ; set MSP to 1st argument
        MRS     R4, CONTROL  ; read control into R4 (don't care to preserve R4)
        MOV     R5, #0x2     ; 0x2 into R5 (don't care to preserve R5)
        MVN     R5, R5       ; NOT R5
        AND     R4, R4, R5   ; set the ASP bit low (MSP) and preserve any other bits that were set in CONTROL
        MSR     CONTROL, R4  ; write the control regsister
        ISB                  ; Instruction Synchronisation Barrier
        BX      R1
      END
    

    Here I set the PSP to 0x0000 and the MSP to the value stored at the beginning of the FW binary. I then switch the processor into handler mode before jumping to the reset handler in the FW image.

    Is this the correct way to handle this? Are there any potential perils I'm not seeing here?