Atmel SAM4E Bootloader/VTOR executing new application

I've written a boot loader for the Atmel SAM4E device which is working to a point, it can receive new application code via SPI, erase flash and write to flash, but I don't understand how to execute the new application. I've learned the VTOR can be relocated, but I'm not sure how. (my application sits at address 0x00420000).
I'm very new to writing code for ARM, so I'd appreciate any help.

Many thanks

Paul

  • binary_exec((void*) 0x00420000);

    int binary_exec(void * vStart){
    int i;

    // -- Check parameters
    // Should be at least 32 words aligned
    if ((uint32_t)vStart & 0x7F) return 1;

    wdt_disable(WDT);

    Disable_global_interrupt();

    // Disable IRQs
    for (i = 0; i < 8; i ++) NVIC->ICER[i] = 0xFFFFFFFF;
    // Clear pending IRQs
    for (i = 0; i < 8; i ++) NVIC->ICPR[i] = 0xFFFFFFFF;

    // -- Modify vector table location
    // Barriars
    __DSB();
    __ISB();

    __set_MSP(vStart);

    // Change the vector table
    SCB->VTOR = ((uint32_t)vStart & SCB_VTOR_TBLOFF_Msk);

    // Barriars
    __DSB();
    __ISB();


    Enable_global_interrupt();


    // -- Load Stack & PC
    _binExec(vStart);
    return 0;
    }


    void _binExec (void * l_code_addr){
    __asm__ ("mov r1, r0 \n"
    "ldr r0, [r1, #4] \n" //I also tryed #5 but that doesn't work, too
    "ldr sp, [r1] \n"
    "blx r0"
    );
    }

  • Hello everyone!

    I am also trying to write my own bootloader, so I am facing similar problems dealt in this post.

    One good question would be, when am I able to modify the VTOR? I say this because I init the bootloader as a normal application with its Interrupt Vector Table, and being the reset vector in 0x0000_0000, I pretend to do all the bootloader stuff and after finishing, modify VTOR register and jump to 0x0000_2000 (the bootloader has been configured with BOOTPROT 2, so 8K memory has been "protected" for this purpose). Is this possible? It is not working right now. I tried to modify VTOR before and after initializing the board (ATMEL R21 with a ARM CORTEX M0+).

    In order to modify the VTOR I do it like this:

    SCB->VTOR = ((uint32_t)0x00002000);

    But being the VTOR just from 32:7 won't it be better to do it like this?

    SCB->VTOR = ((uint32_t)0x00002000 << (SCB_VTOR_TBLOFF_Pos - 1)); // shift 7 to the left ARM Information Center

    After doing this (that is nor working), I pretend to jump to the application so... which considerations should I bear in mind? Would be enough just with a jump to the 0x0000_2000 address?

    void start_application( uint32_t app_link_location )

    {

       asm(" blx r0");

    }

    Because I have seen in other posts code like this inside the function:

    asm(" ldr r1, [r0,#0]"); // get the stack pointer value from the program's reset vector

    asm(" mov sp, r1");      // copy the value to the stack pointer asm(" ldr r0, [r0,#4]"); // get the program counter value from the program's reset vector

    asm(" mov r0, #0");

    asm(" blx r0");          // jump to the start address

    or even something like this avoiding the assembly:

    void jump_to_application( void )

    {

      typedef int(*fn_prt_t)(void);

      fn_prt_t main_prog;

      main_prog= (fn_prt_t)0x00002000;

      main_prog();

    }

    I would really appreciate any advice.

    Best regards,

    Iván

  • Hi, Paul!!!

    You can shared you application?
    I try to do somthing like that in my bootloader.

    Best regards

  • Hello Paul,

    I am afraid to change a single position of a flash would be impossible as the flash should be block erased before writing a new code.

    If you want to do so, you can copy the whole data of the flash block to SRAM, changing a value on the SRAM and write back to the SRAM contents to the flash.

    Best regards,

    Yasuhiko Koumoto.

  • I have found the problem

    It would seem that on boot a number of things are 'assumed' as being in a certain state by the compiler.

    If I disable the global interrupts, cpu_irq_disable() , before jumping, and then re-enable them, cpu_irq_enable() in the application, it works.

    My next challenge is reading and writing a single value to a specific location in flash (say 0x0048FFFF) so I can detect if I should be self writing or jumping to the application.

  • Hello again Paul,


    it is the Intel Hex, isn't it?
    I understand and it would be reasonable.
    However, it is strange "((start_fn *)  *((uint32_t *) 0x00420004))()" causes a stack overflow.
    Did you set the stack pointer before execution the asm statement?
    Otherwise, you should add the "__attribute__((naked))" attribute to the bootloader function in order not to use stack.

    Best regards,
    Yasuhiko Koumoto.

  • Hello Paul,

    I don't know the meaning of ":10000000" but it seems that the SP and PC initial values are respectively 0x200045A0 and 0x004220f9 from the code.
    What is the meaning of "when the next line - ((start_fn *)  *((uint32_t *) 0x00420004))();"?
    I think the next step of the line would be 0x004220f9.
    Can you show us the dis-assembly code around 0x0040660A?

    Best regards,
    Yasuhiko Koumoto.

  • I just checked the code in flash;

    :020000040042B8

    :10000000A0450020F92042006920420069204200FA

    Pulling out the first 32bit word in the VTOR gives me the SP; A0450020

    Putting that the correct way round gives; 200045A0,

    Stepping through the code, after this line has run - asm volatile ("ldr sp,[%0,#0]": "=r" (v) : "0" (v));

    it does indeed show SP as 200045A0

    However when the next line - ((start_fn *)  *((uint32_t *) 0x00420004))();

    Causes the stack overflow but the PC is showing as 0x0040660A, so is in the boot loader code rather than the application code.


    Paul

  • Hi,

    I tried this code ;

    1.   uint32_t v=0x00420000
    2.   asm volatile ("ldr sp,[%0,#0]": "=r" (v) : "0" (v)); 
    3.   ((start_fn *)  *((uint32_t *) 0x00420004))(); 

    Here's the output of my boot loader

    -- Boot Loader --

    VTOR:0x00400000                                                     <- I have a printf to show me the current VTOR value

    BootLoader Starting....

    BootVTORValue = 0xffffffff                                         <- This is the value read from flash location 0x00420004, before receiving the code

    waiting..... for 165 bytes                                             <- this is my code waiting for data

    PageNumber:511                                                       <- This is the code showing it has recived 512 pages (each being 256 bytes, so 64K)

    65536 Bytes Page Written to 00420000                    <- This is showing

    Wait For Flash Ready                                                <- Pause whilst flash writes

    NewBootVTORValue = 0x004220f9                          <- This is the value read from flash location 0x00420004, after receiving the code

    reboot                                                                         <- This is sent by the bootloader just before jumping to the new code.

    stack overflow 20015b68 Monitor                              <- this is what I got after trying your code.

    At this point, the PC is wrong, it's showing - 0x0040660A

    The SP is showing - 0x20014AC0

  • Hello Yusuhiko, thank for your reply.

    • aren't your boot program located on SRAM?

    no, my boot program is located at the lowest point of flash (0x00400000).

    • By the way, what is the stack pointer value when you execute  "((start_fn *)  *((uint32_t *) 0x00420004))()"?
      Was it reasonable?

    Yes, my stack point after I pause the debugging is showing as - 0x200148FC

    • When you located your app codes from 0x00400000, to what address did the boot loader jump?

    it jumped to 0x004220f9

    • Was it the address in 0x00420004?

    the same as the one it jumped to - 0x004220f9

    The disassembly show that I'm stuck in an infinte loop;

    0042206C   b #-4 

    I assume this is an error trap to stop the processor just running off and doing crazy things.

    I will try your suggested restart code.

  • Hello Paul,


    aren't your boot program located on SRAM?

    If it is correct, the direct brabch to 0x004220f9 would be impossible.

    I think the reason is it why you used the function pointer.

    By the way, what is the stack pointer value when you execute  "((start_fn *)  *((uint32_t *) 0x00420004))()"?
    Was it reasonable?

    When you located your app codes from 0x00400000, to what address did the boot loader jump?

    Was it the address in 0x00420004?


    How about trying the below codes?

      uint32_t v=0x00420000;
      asm volatile ("ldr sp,[%0,#0]": "=r" (v) : "0" (v));
      ((start_fn *)  *((uint32_t *) 0x00420004))();
    
    

    These would be the same as the followings.

    uint32_t v=0x00420000;
       asm volatile ("ldr sp,[%0,#0]\n\r"
                    "ldr pc,[%0,#4]": "=r" (v) : "0" (v));
    
    

    Best regards,
    Yasuhiko Koumoto.

  • Yasuhiko Koumoto

    Originally I was trying to jump directly to the start of the code. Since then I've found that the actual address is the second entry in the VTOR.

    So I have a function that reads the boot address (i.e. "main()" ) from the VTOR and display it on screen, first I define a pointer;

         static uint32_t *MyAddress = 0x00420004;

    Then when I want to view the content of the boot address in the VTOR I do this


         printf("NewBootVTORValue = 0x%08x\r\n", *MyAddress );

    which for my latest application (one that just flashes an LED) gives me this on the debug port;

         NewBootVTORValue = 0x004220f9

    I've also tried this;

         //Typedef the entrypoint

         typedef void start_fn(void);

    And then when I want to boot to the new application;

         //Will branch and execute at the entry point at 0x00450004 which is the reset vector in the VTOR in the new application code

         ((start_fn *)  *((uint32_t *) 0x00420004))();

    Again, the same thing, it certainly jumps to the right location in memory, but I get a Stack Overflow every time.

  • Hello Paul,

    what is the address of _JumpBootLoader?

    Is it possible to jump from the _JumpBootLoader to 0x00423c3c?

    Best regards,

    Yasuhiko Koumoto.


  • Thanks, I tried that but sadly it gives the same result.

    If I compile my application to start at 0x00400000 it works fine, so I'm fairly confident it's not a problem with the initialisation of the stack pointer in the app.

  • Hi,

    This is probably an issue with your inline assembly not properly exposing its intentions to the compiler. You should tell the compiler that it is using app_link_location like this:

    extern void start_application(unsigned long app_link_location)

    {

        asm volatile( "LDR SP, [%0, #0] \n"

                      "LDR PC, [%0, #4]"

                      : /* No outputs */

                      : "r" (app_link_location)

                      : /* No clobbers */ );

    }

    Quick explanation:

    • The volatile keyword prevents the compiler from optimising this block of code
    • %0 refers to the first entry in the output/input list (in this case, app_link_location)
    • app_link_location is in the input list with the "r" designation, which leaves it up to the compiler which register to actually use

    You can find more information regarding inline assembly syntax here.


    Hope that helps.