Hello everyone!
I am writing here because I am having some issues developing my own bootloader. I am currently working with an ATMEL SAM R21 which has an ARM CORTEX M0+ in it.
Firstly I am going to summarize the more relevant points (or I think those are the most relevant ones):
1) Write appropriately in FLASH MEMORY. It is already done and working.
2) Relocate vector table. The Bootloader has been though to be at the first part of the memory, so immediately after the vector table and other. The reset vector is the starting point
I think, so I pretend to use in the code the VTOR (vector table offset register) in order to change it right before jumping to the application address.
As the application is going then to start at the 0x0000_2000 address, just before jump I set the VTOR like this:
SCB->VTOR = ((uint32_t)0x00002000 );
Edited 22-06-2016 10:18 a.m.:
I noticed that as I try to modify the VTOR as in the previous lines, the application leads me to the Hard_fault debugger, so maybe it can be modified like this. I tried to modify its value before and after initializing the board
3) BOOTPROT FUSE. I has been only able to modify this use using the ATMEL STUDIO, is there any other way? For example something like this:
uint32_t *bootprot = NULL;
*bootprot = *(uint32_t *)NVMCTRL_FUSES_BOOTPROT_ADDR;
*bootprot = *bootprot | NVMCTRL_FUSES_BOOTPROT(2);
I say this because in production maybe is a bit messy to use the ATMEL STUDIO in order to set the memory where the BOOTLOADER is going to be stored. Is there another way for
modifying this FUSE? In the datasheet there are continous references to the NVM, but I don't know if it would be practical to write it like this.
4)Jump to the application. I have seen many examples but with this function would it be enough???
void start_application( uint32_t app_link_location )
{
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
void jump_to_application( void )
typedef int(*fn_prt_t)(void);
fn_prt_t main_prog;
main_prog= (fn_prt_t)0x00002000;
main_prog();
In the star_application I don't know why is needed more directives than the last one. If I just access the function R0 has the for example 0x2000 value got as parameter, so
I should be able to jump there, shouldn't I?. I don't really know if I should store something or what, or if just jumping would be enough.
I should point out that as the BOOTLOADER is going to be approximately 8K size, I set the BOOTPROT FUSE to 0x2 value for this purpose, but I don't notice anything different after
setting it to such value.
5) Flash both bootloader and application. Well, I have modified the linker files in order to indicate that the bootloader should be in the following address range:
0x0000_0000 to 0x0000_1FFF.
The application:
0x0000_2000 to 0x0003_FFFF
If I use the JTAG and the IAR in order to load the application/firmware it seems that erases the memory before programing anything, so I only find 2 alternatives but I don't know
how can I carry them out:
Build a binary with the bootloader and the application in it using an external tool which merges both .hex in one .bin or something like that.
Does BOOTPROT protect the memory? So I am maybe able to program the bootloader, change the BOOTPROT FUSE from 0x7 to 0x2 (to protect 8K memory and set aside that amount of memory
for the bootloader) and maybe trying to expect that now as I program the application the bootloader will remain on memory. This is for sure just not working like this, isn't it?
I will very appreciate any advice you could provide me.
Thank you very much.
Iván.
Message was edited by: Iván
Hello again,
Maybe is a complicated question, but I keep contributing here.
2) I am still struggling with the VTOR. I think, because I haven't loaded the app in the right address yet, I should modify VTOR value in the low_level_init function within my low_level_init.c file, in which I define the vector table for example. So I should check there after a reset if I should run the app or the bootloader and depending on this load 0x0000_0000 value or 0x0000_0000 + OFFSET to the VTOR. I say OFFSET because as I have seen, the OFFSET should be the base address + 4, because in the relative zero should be the SP, and in the OFFSET + 4 (4 offset bytes between each interrupt vector) would be the reset). So, the code should be I guess after all the vector table, so base address + 4 * NUMBER_OF_INTERRUPTS or something like that. I am not sure If I am right or not.
3) The BOOTPROT FUSE. As I said I modified this using the ATMEL STUDIO, but I don't notice any change after changing it, so... I don't know if it is really working. I have still on queue to try to read and write its value from NVM avoiding the use of external apps ( ATMEL STUDIO). I really want to protect the BOOTLOADER.
4) I think the jump can be done using both methods shown before, but if I cannot modify the VTOR before jumping, I need to reset the device, modify the VTOR and now would be the right moment to perform such jump.
Those are the points that I felt I had some comments to add.
I will update this as I get any result.
Any help or comment would be appreciated .
Best regards,
Regarding to the point 2) I have been able to perform the jump to the application having interruptions in the firmware and in the application. The point was I was trying to use assembly inlined in order to perform the change of context and the jump, but some assembly directives seems to not work properly being inlined so what I did was to generate my own assembly file (.s) and declare the the function I pretend to use:
#define _PORT_ASM_ARM_SRC#define __ASSEMBLY__;/****************************************************************************;** **;** ASSEMBLY FUNCTIONS **;** **;****************************************************************************/ NAME start_app RSEG CODE:CODE(2) THUMB PUBLIC jump_to_app;/***************************************************************************/;/***************************************************************************/;/* jump_to_app(); * Jump to application function.; */jump_to_app: LDR R0,=0xE000ED08 ; Set R0 to VTOR address LDR R1,=0x00010000 ; User’s flash memory based address STR R1, [R0] ; Define beginning of user’s flash memory as vector table LDR R0,[R1] ; Load initial MSP value MOV SP, R0 ; Set SP value (assume MSP is selected) LDR R0,[R1, #4] ; Load reset vector BX R0 ; Branch to reset handler in user’s flash END
After doing this, the function prototipe should be included into a .h file of your project as a normal function, using something like this:
void jump_to_app(void);
Regarding to the fuses (BOOTPROT, EEPROM and others) which appears in the section 8.3 of the Atmel R21 datasheet, in order to write them you have to take account that there is needed another command to write in that memory space.
After saying this it doesn't seem to be very clear, but in the following lines I am going to try to explain what is going on.
When you want to write, erase or read to/from NVM you have to configure and perform some actions, but the one which is going to trigger the write, erase or read action will be a command. Something like this:
/* Set command */
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_WP | NVMCTRL_CTRLA_CMDEX_KEY;
Here what I am doing is to write into CTRLA that I want to execute something (a command) with the NVMCTRL_CTRLA_CMDEX_KEY, and the command I want to write concretely with NVMCTRL_CTRLA_CMD_WP, which is a write command. So If I have configured the rest appropriately before doing this I had written something to a concrete NVM address.
The point is that we should write within a range between 0x0000_0000 and 0x0004_0000, so 256KB, but... to configure the fuses the memory is out of this range starting at address 0x804000. So I removed the range check if I had coded for this purpose but... It still failed, and it was because some concrete write and delete commands are needed for doing this:
NVMCTRL_CTRLA_CMD_EAR /* ERASE NVM USER ROW */
NVMCTRL_CTRLA_CMD_WAP /* WRITE NVM USER ROW */
So, once you use your write and erase functions with those commands, you will be able to write to NVM USER ROW. For reading you just need to allow to your function for reading out of the 256KB memory addresses.
But BE CAREFUL!!!! If you erase the NVM USER ROW and don't program it with right values your device can stop running!!! In my case, I downloaded the NVM USER ROW data using ATMEL STUDIO desktop application in order to have a backup of NVM USER ROW.
I hope this helps!!
And now... I am going to struggle with the DFU USB FW download!!!
Iván
Hallo
I want to write a bootloader for samr21 am new to this and i would like to know how to write in flash memory
Rjoseph said: i would like to know how to write in flash memory
That has nothing to do with Keil or ARM.
That will be specified in the manufacturer's documentation:
https://www.microchip.com/wwwproducts/en/ATSAMR21G18A
More than likely, they will have examples on how to do this - browse the 'Documents' tab.
For specific questions about SAM chips, go to the SAM forums:
https://community.atmel.com/forums/atmel-smart-arm-based-mcus