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,
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!!!
Best regards,
Iván