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.
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.
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?
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.
Hi,
I tried this code ;
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.
no, my boot program is located at the lowest point of flash (0x00400000).
Yes, my stack point after I pause the debugging is showing as - 0x200148FC
it jumped to 0x004220f9
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.
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));
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.
what is the address of _JumpBootLoader?
Is it possible to jump from the _JumpBootLoader to 0x00423c3c?
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.
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 */ ); }
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:
You can find more information regarding inline assembly syntax here.
Hope that helps.