I want to jump to system memory from user application in STM32L072KBUx microcontroller.
I think someone has already answered this question but unfortunately, I don't understand this code:
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Data_Address;
EraseInitStruct.NbPages = 1;
First_jump = *(__IO uint32_t *)(Data_Address);
if (First_jump == 0) {
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Data_Address, 0xAAAAAAAA);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = *(__IO uint32_t *)(0x1FF00004);
}
if (First_jump != 0) {
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = (0x1FF00369);
}
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t *)(0x1FF00000));
Jump_To_Application();
Is it possible to use Internal EEPROM instead of using flash to do jumping?
I would greatly appreciate it if someone can help me to use this code in my own application.
Thanks so much.
You don't need the FLASH calls at all. These are some checks to validate if a valid application code has been stored.
Just load the stack pointer and reset address from the bootloader vector table and jump to the reset address.
void (*JumpToSystemBootloader)(void);
// set vector table start address
// volatile uint32_t addr = 0x1FFF0000; // for STM32F4
volatile uint32_t addr = 0x1FF00000; // for STM32L07xxx
// load reset vector address #+0x4
JumpToSystemBootloader = (void (*)(void)) (*((uint32_t *)(addr + 4)));
// load stack pointer address #+0x0
__set_MSP(*(uint32_t *)addr);
// jump to address loaded previously from #+0x4
JumpToSystemBootloader();
You probably need to disable also all interrupts and restore several controller states before entering the system bootloader.
See also: https://stm32f4-discovery.net/2017/04/tutorial-jump-system-memory-software-stm32/
Edit2:
New bootloader versions of the device always jumps to the application code again as long as in either bank1 or bank2 valid stack pointer resides in the vector table.
This is also the case if jumping to the bootloader from the application code.
Edit3:
Your provided code is some hacky way to force entering the bootloader anyway.
Therefore a magic word is stored in some place of the flash rom.
And during reentering the application code, at an early stage,
this value is checked to reenter the bootloader.
To bypass the bank check a new jump address is supplied (0x1FF00369).
(Determined by reverse engineering?)
It is assumed that the controller and the RAM is previously already initialized correctly.
Because the vendor can change the bootloader code whenever he want
the use of this code snippet should be done with caution.
My suggestion is not to use this code at all.
Jump To Bootloader using magic value in SRAM [not for production]
Reference: https://stackoverflow.com/a/43072025/5388805
// vector table start address (STM32L07xxx)
volatile uint32_t u32_boot_vector_addr = 0x1FF00000;
// bootloader bypass offset address
volatile uint32_t u32_boot_vector_offset = 0x369; // substituted from https://stackoverflow.com/a/43072025/5388805
// bootloader check definitions
volatile uint32_t * pu32_boot_tag = (volatile uint32_t *)0x20001800; // AN2606 states bootloader uses up to 5kByte RAM, add some offset
const uint32_t u32_boot_tag_reenter = 0xCAFEFEED;
const uint32_t u32_boot_tag_clear = 0xFFFFFFFF;
// call this at an early stage during startup (preferably right after entering the reset routine)
void checkBootloader()
{
// if magic tag is set jump back to bootloader
if (*pu32_boot_tag == u32_boot_tag_reenter)
{
// erase magic tag
*pu32_boot_tag = u32_boot_tag_clear;
// load bypass address
void (*JumpToSystemBootloader)(void) = (void (*)(void)) (*((uint32_t *)(u32_boot_vector_addr + u32_boot_vector_offset)));
// load stack pointer address #+0x0
__set_MSP(*(uint32_t *)u32_boot_vector_addr);
// jump to bypass address
JumpToSystemBootloader();
}
}
// call this anywhere from your application code
void jumpToBootloader()
{
// set magic tag
*pu32_boot_tag = u32_boot_tag_reenter;
// load reset vector address #+0x4
void (*JumpToSystemBootloader)(void) = (void (*)(void)) (*((uint32_t *)(u32_boot_vector_addr + 4)));
// load stack pointer address #+0x0
__set_MSP(*(uint32_t *)u32_boot_vector_addr);
// jump to address loaded previously from #+0x4
JumpToSystemBootloader();
}
Related
I've got a question.
I'm developing an IAP (In-Application Programming) tool for my STM32F446RE board and I'm stuck.
I've developed all the necessary utilities in order to let to the microcontroller to receive a binary (.bin) compiled file from a GUI, write it on a specific flash memory sector and execute it.
My problem comes when, from the uploaded code, I want to jump again to the bootloader that is stored on the flash memory sector 0, I see that the code does not jump to the bootloader but, instead, it continues the execution of the user application code. I've debugged the code and I seen that all the addresses (the msp and the reset handler) of the bootloader code are correctly set and they are different compared to the ones of the uploaded code.
The flow that i want to achieve is the following:
1 --> Execute the bootloader code stored on sector 0 (starting at address 0x0800 0000, when an interrupt from the User button is received) and write the newly received code into the sector 2 (starting at address 0x0800 8000)
2 --> set the msp address (#0x0800 8000) and the reset handler address (0x0800 8004)
3 --> jump to the reset handler address of the new code (#0x0800 8004)
4 --> execute the new uploaded code.
5 --> during the user code execution, if an interrupt is received (from user push button) then set the bootloader msp address, the reset handler and jump to the bootloader
6 --> repeat again from step one.
This is the code used to jump from the bootloader to the user application:
IAP_loadProgram(&data);
//pointer to the user application reset handler address
void (*user_resetHandler)(void);
//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)APPLICATION_ADDRESS;
__set_MSP(msp_addr);
//Set now the addres of the reset handler
uint32_t resetAddr = *(volatile uint32_t *)(APPLICATION_ADDRESS + 4);
user_resetHandler = (void *)resetAddr;
//When there, the bootloader sector will be leaved and the user code execution starts
user_resetHandler();
Finally, the code used to jump from the user application code to the bootloader is:
if(toBootloader){
toBootloader = 0;
//pointer to the user application reset handler address
void (*bootLoader_resetHandler)(void);
//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)BOOTLOADER_ADDRESS;
__set_MSP(msp_addr);
//Set now the address of the reset handler
uint32_t bootLoaderResetAddr = *(volatile uint32_t *)(BOOTLOADER_ADDRESS + 4);
bootLoader_resetHandler = (void *)bootLoaderResetAddr;
//When there, the user code sector will be leaved and the bootloader code execution starts
bootLoader_resetHandler();
}
Where APPLICATION_ADDRESS is 0x0800 8000 and BOOTLOADER_ADDRESS is 0x0800 0000.
The content of the first two addresses of the bootloader code is:
0x08000000: 20020000
0x08000004: 080044DD
meanwhile the content of the first two addresses of the application code is:
0x08008000: 20020000
0x08008004: 0800A1F1
Last modify that i've done is on the user application linker (.ld) file, where i set the flash start to the address 0x0800 8000 (instead of the address 0x0800 0000).
All the interrupts are correctly working and, after that the code has been uploaded, if I do a hardware reset, the result is the same, the code execution starts from the user application code, not from the bootloader.
Any tips?
Your problem description is unclear but the procedure of invoking the app is far not sufficient. You need to make sure that the environment for the application is same as after the uC reset. You need to change the vector table address as well.
I wrote tens of bootloaders but I do not understrand your problem
Here you have an example how it should be done (app call from bootloader)
void startAPP(void)
{
static uint32_t *pAppPosition;
static voidFunc *appResetHandler;
static uint32_t newSP;
pAppPosition = (uint32_t *)(bankStartAddress[0] + (uint32_t)&_BOOTFlashSize);
appResetHandler = (voidFunc *)pAppPosition[1];
newSP = pAppPosition[0];
SPI_DeInit();
FLASH_DeInit();
I2C_DeInit();
IRQ_DeInit();
GPIO_DeInit();
__disable_irq();
__set_MSP(newSP);
__enable_irq();
SCB -> ICSR = 0x00000000; // reset value;
SCB -> SCR = 0;
SCB -> CCR = 0x00000200; // reset value
SCB -> SHP[0] = 0;
SCB -> SHCSR = 0;
SCB -> CFSR = (SCB_CFSR_DIVBYZERO_Msk | SCB_CFSR_UNALIGNED_Msk | SCB_CFSR_UNDEFINSTR_Msk | SCB_CFSR_NOCP_Msk | SCB_CFSR_INVPC_Msk | SCB_CFSR_INVSTATE_Msk);
SCB -> HFSR = (SCB_HFSR_DEBUGEVT_Msk | SCB_HFSR_FORCED_Msk | SCB_HFSR_VECTTBL_Msk);
SCB -> VTOR = bankStartAddress[0] + (uint32_t)&_BOOTFlashSize; // new vector table pos. I od not clear 8 LSB because APP start position is aligned to FLASH Sectors which are at least 2k aligned
// SysTick
SysTick -> CTRL = 0;
SysTick -> LOAD = 0;
SysTick -> VAL = 0;
appResetHandler();
__builtin_unreachable();
}
The simplest and safest method of running the bootloader from the application is simply to issue a soft reset using the CMSIS NVIC_SystemReset() function.
if( toBootloader )
{
NVIC_SystemReset() ;
}
Jumping to the bootloader by a direct call is unnecessary and ill-advised. While it can be done, just as you can jump from the bootloader to the application, you need to at least disable interrupts/exceptions and switch the vector table from that of the application to that of the bootloader. Neither your application code nor bootloader code appear to be doing that. See ARM: How to Write a Bootloader for example.
Issuing a reset has the advantage of setting the processor and all on-chip peripherals and I/O into their known reset state so you do not need to worry about de-initialising the NVIC, or any peripherals that might generate an interrupt while you are switching vector tables.
If you need to communicate information to the bootloader from the application the state of the on-chip SRAM will survive the reset process, so you can reserve space that the run-time start-up will not initialise to pass parameters to the bootloader if you need to.
My Environment
IDE : Keil
Processor : AT89C4051 (Simulation Mode)
I'm trying to work with P1 register (address 0x90 specified by datasheet) by setting its value to all 0 (RESET state) and setting some specific pin to be 1 (SET state). I try this code
int main() {
*((unsigned char*)0x90) = 0;
while(1) {
*((unsigned char*)0x90) = 0xE0;
}
}
But nothing change.
When I use this example every work flawlessly
sfr P1 = 0x90;
int main()
{
P1 = 0;
P1 = 0xE0;
while(1);
}
My question is, what make the different between these code since it's all pointing at address 0x90 using sfr and unsigned char pointer.
You can't access SFRs of the 8051 series microcontroller by indirect accesses.
On devices with more than 128 bytes internal RAM, the I/O addresses 0x80 to 0xFF are used for both the SFRs and the upper half of the internal RAM:
SFRs are accessed by direct addressing only.
The internal RAM is accessed by indirect addressing only.
EDIT:
Source: Right on the product summary page there is the 8051 instruction set that documents this on the very first page.
I am using ARM-GCC compiler and I found on Internet two versions for the startup_stm32f10x_cl.c file (startup code). The processor is: STM32F105RC (ARM Cortex M3).
Common part :
#define STACK_SIZE 0x100 /*!< The Stack size */
__attribute__ ((section(".co_stack")))
unsigned long pulStack[STACK_SIZE];
Then, the first version starts the vector table like this:
void (* const g_pfnVectors[])(void) =
{
(void *)&pulStack[STACK_SIZE], /*!< The initial stack pointer */
Reset_Handler, /*!< Reset Handler */
...
while the second version looks like this:
void (* const g_pfnVectors[])(void) =
{
(void *)&pulStack[STACK_SIZE - 1], /*!< The initial stack pointer */
Reset_Handler, /*!< Reset Handler */
...
So, my question is:
Which one is the correct stack pointer initialization?
From ARM documentation for M3 cores instruction-set:
PUSH uses the value in the SP register minus four as the highest
memory address
and
On completion, PUSH updates the SP register to point to the location
of the lowest stored value
So, my interpretation is that the starting point of SP must be +4 of the highest address, i.e. the address immediately after the Stack array bounds.
Hence
(void *)&pulStack[STACK_SIZE]
looks right, as that address (although not part of the array), will never be used.
Question: - how to locate application to non 0x0000.0000 address?
Processor: NXP LPC1768
Dev system: Keil ARM 4.73
Steps used:
1) scatter file below used to set load region and execution region to 0x0000.2000
2) copied vector table to 0x2000
3) udpated vtor register to 0x2000
Problem: Application does not run.
Scatter file used:
LR_IROM1 0x00002000 0x00000D000
{ ; load region size_region
ER_IROM1 0x00002000 0x0000D000
{ ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x10000000 0x00008000 { ; RW data
.ANY (+RW +ZI)
}
}
This follows instructions specified in NXP app note AN10744, something else I’m missing?
Vector Table Offset Register (VTOR) points to 0x00000000 at reset.
Thus, stack pointer must be at 0x00000000, and program start address (program counter) at 0x00000004.
If you change the location of the vector table in linker settings, you need to update VTOR to point to this new location. This can only happen at runtime.
This means that you need to have a small bootloader program which does the remapping, which means that first sector must be reserved for that purpose.
Bootloader needs to:
Make sure that interrupts are disabled, so you don't accidentally use VTOR.
Update VTOR register to address 0x2000.
Get stack pointer address from 0x2000 and update stack pointer register.
Get program start address from 0x2004 and update the program counter.
You might want to check out CMSIS library, it has functions like NVIC_SetVTOR and __set_MSP which make setting these registers a little easier.
To set the program counter, you can cast the address to function pointer and then call the function:
uint32_t * vtor = (uint32_t *)0x2000;
uint32_t startAddr = vtor[1];
( (void(*)(void))startAddr )(); // Cast and call
I've written a bootloader for my SAM4S that sits in sector 0 and loads an application in sector 1. The problem however is that when I attempt to jump to the new function it appears to generate an exception (debugger goes to Dummy_Handler()).
Bootloader contains the following entries in map:
.application 0x00410000 0x0
0x00410000 . = ALIGN (0x4)
0x00410000 _sappl = .
0x00410004 _sjump = (. + 0x4)
The application image map file has:
.vectors 0x00410000 0xd0 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x00410000 exception_table
…
.text.Reset_Handler
0x0041569c 0x100 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x0041569c Reset_Handler
Exception table is defined as follows:
const DeviceVectors exception_table = {
/* Configure Initial Stack Pointer, using linker-generated symbols */
.pvStack = (void*) (&_estack),
.pfnReset_Handler = (void*) Reset_Handler,
The bootloader declares the application jump point as:
extern void (*_sjump) ();
and then makes the following call:
_sjump();
The memory contents at 0x00410004 are 0x0041569d, and I notice that this is not word aligned. Is this because we are using Thumb instructions? Either way why is it not 0x0041569c? Or more importantly why is this going to an exception?
Thanks,
Devan
Update:
Found this but it does not appear to work for me:
void (*user_code_entry)(void);
unsigned *p;
p = (uint32_t)&_sappl + 4;
user_code_entry = (void (*)(void))(*p - 1);
if(applGood && tempGood) {
SCB->VTOR = &_sappl;
PrintHex(p);
PrintHex(*p);
PrintHex(user_code_entry);
user_code_entry();
}
The code prints:
00410004
0041569D
0041569C
Update Update:
The code that attempted to jump with a C function pointer produced the following Disassembly:
--- D:\Zebra\PSPT_SAM4S\PSPT_SAM4S\SAM4S_PSPT\BOOTLOADER\Debug/.././BOOTLOADER.c
user_code_entry();
004005BA ldr r3, [r7, #4]
004005BC blx r3
I was able to get this working with the following assembly:
"mov r1, r0 \n"
"ldr r0, [r1, #4] \n"
"ldr sp, [r1] \n"
"blx r0"
Based on this I wonder if the stack reset is required and, if so, is it possible to accomplish such in C?
I had the same problem with SAM4E. I cannot guess what your problem might be, but I can point out difficulties that I had and information I used.
My bootloader was not storing in the correct memory location a few parts of the firmware. This was leading to the dummy_handler exception. When I fixed the error in the address calculations the bootloader worked perfectly.
My suggestions:
Follow ATMEL's example: the Document and the Example Code should be enough. The main.c is enough to understand how the bootloader should work. It is not necessary to get into partitioning details at the beginning.
You may want to read how you can execute functions/ISRs from RAM
This webpage explains the Intel HEX format.
Finally, after the bootloader finishes with the upgrade, you can read the flash and send it back to the host computer. Then compare it with the original image (using a script). That is how I debugged my bootloader.
Other ideas that might help:
Do you erase each page before you write it?
Do you unlock each memory space before you erase/write it?
You could lock the Bootloader's Section to avoid overwriting it by mistake
You could lock the section(s) of the upgraded firmware.
The address you have to point to is 0x00410000 not 0x00410004. The Atmel's example code (see function binary_exec) in combination with the Intel Hex format (record type 05) should solve this question.
I hope this piece of information will be of some help!
I had the same problem on the SAM4S which brought me to this question. So if someone arrives here again, here is what I found.
As ChrisB mentions following the Atmel example code is a good start, however I found that the problem was the actual code requesting the jump which just didn’t work for the SAM4S.
What was missing was rebasing the stack pointer before the vector table and then loading the reset handler address.
Try something like this:
static void ExecuteApp(void)
{
uint32_t i;
// Pointer to the Application Section
void (*application_code_entry)(void);
// -- Disable interrupts and system timer
__disable_irq();
SysTick->CTRL = 0; // Disable System timer
// disable and clear pending IRQs
for (i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; // disable IRQ
NVIC->ICPR[i] = 0xFFFFFFFF; // clear pending IRQ
}
// Barriers
__DSB(); // data synchronization barrier
__ISB(); // instruction synchronization barrier
// Rebase the Stack Pointer
__set_MSP(*(uint32_t *) APPCODE_START_ADDR);
// Rebase the vector table base address
SCB->VTOR = ((uint32_t) APPCODE_START_ADDR & SCB_VTOR_TBLOFF_Msk);
// Load the Reset Handler address of the application
application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)
(APP_START_RESET_VEC_ADDRESS));
__DSB();
__ISB();
// -- Enable interrupts
__enable_irq();
// Jump to user Reset Handler in the application
application_code_entry();
}