cancel
Showing results for 
Search instead for 
Did you mean: 

Jump to USB DFU Bootloader in startup code on STM32F042

ethanzonca
Associate
Posted on February 21, 2015 at 18:11

I'm running a STM32F04 and want to jump to the USB DFU bootloader from user code. As recommended in other posts, I set a magic bit in SRAM, issue a soft reset, check for the magic value and branch to the bootloader if the magic value exists in the startup code.

My problem is that I branch to the bootloader but the F042 doesn't show up as a USB DFU device, it just seems to hang. I'm confident I have the correct memory addresses and I can't find any issues with my approach. Any ideas? Thanks.

Reset_Handler:
/* Bootloader jumping */
ldr r0, =0x200017F0 
/* address of magic token */
ldr r1, =0xDEADBEEF 
/* magic token */
ldr r2, [r0, #0] 
/* load data from magic address */
str r0, [r0, #0] 
/* zero data at magic address so we don't get stuck here */
cmp r2, r1 
/* compare data at magic address to magic token */
beq Reboot_Loader 
/* jump to bootloader if token match */
/* End bootloader jumping */
ldr r0, =_estack
mov sp, r0 
/* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit

// On reset, SP=value at address 0x0
ldr r0, =0x00000000
ldr r0, [r0, #0]
mov sp, r0
ldr r0, =0x1FFFC800 
/* Address of bootloader on f042 from CD00167594 pg 15 table 3 */
// Branch to bootloader
ldr r0, [r0, #4]
bx r0

#bootloader #dfu #stm32f0 #stm32f042-bootloader-dfu
27 REPLIES 27
Posted on December 01, 2015 at 01:01

Sounds like a watchdog, not sure what can be done about that.

Does it otherwise enter at near reset conditions?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
atx
Associate II
Posted on December 01, 2015 at 13:09

This is all the code that gets run:

void
Reset_Handler()
{
int
*from = &_data_flash_start;
int
*to = &_data_start;
while
(to != &_data_end)
*(to++) = *(from++);
to = &_bss_start;
while
(to != &_bss_end)
*(to++) = 0;
main();
while
(1) {}
}
int
main()
{
 RCC->CSR |= RCC_CSR_RMVF;
enter_bootloader();
}
#define BOOTLOADER_START 0x1fffc400
static
uint32_t *bootloader_msp = (
void
*)BOOTLOADER_START;
static
void
*(**bootloader)() = (
void
*)BOOTLOADER_START + 4;
void
enter_bootloader()
{
RCC->APB2ENR = RCC_APB2ENR_SYSCFGCOMPEN;
SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE_0;
__set_MSP(*bootloader_msp);
(*bootloader)();
/* Should never happen */
while
(1) {}
}

I have also checked the RCC_CSR register and it has a value of0x00000000 (after it gets cleared in the first main() call). So it seems that the bootloader is deliberately jumping into my code. Anyway, looking at the appnote, it almost seems like it should be possible for me to jump to the address in the system memory at which DFU mode initialization starts. However, I have no idea how to determine where that would be without dumping and reverse engineering the bootloader. It would be nice if ST published the source code and binary with debugging symbols...
devanlai
Associate II
Posted on December 10, 2015 at 18:26

It would appear that Tony Arkles is correct - it does check if the BOOT pin is high during the bootloader. I did a trace of the execution after jumping to the bootloader, logging the registers after every instruction and I pulled out all of the memory accesses.

Here's the sequence of loads and stores to locations besides SRAM and the System memory for an aborted bootloader call that jumped to user code:

ldr 0x00000014 into r1 from MEM[0x40021014] [RCC]
str 0x00440014 from r1 into MEM[0x40021014] [RCC]
ldrh 0x00000000 into r1 from MEM[0x48000410] [GPIOB]
ldrh 0x00000000 into r1 from MEM[0x48001410] [GPIOF]
ldr 0x00440014 into r1 from MEM[0x40021014] [RCC]
str 0x00000014 from r1 into MEM[0x40021014] [RCC]
ldr 0xFFFFFF00 into r1 from MEM[0x4002201C] [FLASH interface]
ldr 0xFFFFFF00 into r0 from MEM[0x4002201C] [FLASH interface]
ldr 0x20001800 into r1 from MEM[0x08000000] [Main Flash memory]
ldr 0x00000001 into r1 from MEM[0x40021018] [RCC]
str 0x00000001 from r1 into MEM[0x40021018] [RCC]
str 0x00000000 from r1 into MEM[0x40010000] [SYSCFG/COMP]
ldr 0x00000001 into r1 from MEM[0x40021018] [RCC]
str 0x00000000 from r1 into MEM[0x40021018] [RCC]
ldr 0x08000891 into r1 from MEM[0x08000004] [Main Flash memory]
ldr 0x20001800 into r0 from MEM[0x08000000] [Main Flash memory]

After configuring the BOOT pin as an output and setting it high, I was able to jump to the bootloader and enter the USB DFU bootloader successfully. I think that if I didn't have an external pulldown, I could get away with just enabling the internal pullup on the BOOT pin first. However, I do also have a filter capacitor on my boot pin, so it's possible that the pin was forced low but stayed high long enough because of that. (Though looking at the memory writes, I don't think that's the case).
devanlai
Associate II
Posted on December 12, 2015 at 07:50

After tracing the bootloader execution with the boot pin driven high, I've determined that the bootloader respects the BOOT_SEL bit in the option bytes.

If you have no need for ever using the boot pin to enter DFU, you can clear the BOOT_SEL bit and the bootloader will happily enter DFU mode, even if the boot pin is low.

It might be possible to bypass the GPIO check and selectively call the DFU bootloader function (which I think starts at 0x1fffcad4), but you would have to initialize the contents of RAM yourself (or determine what function, if any, they call in order to initialize RAM with their variables)

However, since it doesn't look like the GPIO registers ever get reset, I think reconfiguring the boot pin to a high state (either by enabling the pullup or by configuring it as an output) before calling the bootloader is probably a simpler solution if you want to retain use of the boot pin while being able to jump to the bootloader at will.

wgt538
Associate
Posted on February 03, 2016 at 00:57

Just in case anyone hits this post and wonders if there was any solution. Here is my working code. This was built off the shoulders of giants, specifically the people above in this post. I cannot express enough thanks.

Some gotchas: 1) this works on the STM32F042

LQFP48 package only.

--- BOOT0 pin is pin PF11 for this package, check yours 2)BOOTLOADER_MAGIC_ADDR should work for any chip with at least 4k of sram 3) BOOTLOADER_START_ADDR will probably need to be changed for your specific chip Enjoy :) no need to waste space or time writing your own bootloader Will

#def
ine BOOTLOADER_MAGIC_ADDR ((uint
32
_t*) ((uint
32
_t) 
0
x
20001000
)) //
4
k into SRAM (out of 
6
k)
#def
ine BOOTLOADER_MAGIC_TOKEN 
0
xDEADBEEF // :D
//Value taken from CD
00167594
.pdf page 
35
, system memory start.
#def
ine BOOTLOADER_START_ADDR 
0
x
1
fffc
400
//for ST
32
F
042
void RebootToBootloader(){
//call this at any time to initiate a reboot into bootloader
*BOOTLOADER_MAGIC_ADDR = BOOTLOADER_MAGIC_TOKEN;
NVIC_SystemReset();
}
//This is the first thing the micro runs after startup_stm
32
f
0
xx.s
//Only the SRAM is initialized at this point
void bootloaderSwitcher(){
uint
32
_t jumpaddr,tmp;
tmp=*BOOTLOADER_MAGIC_ADDR;
//tmp=BOOTLOADER_MAGIC_TOKEN; //DEBUG
if (tmp == BOOTLOADER_MAGIC_TOKEN){
*BOOTLOADER_MAGIC_ADDR=
0
; //Zero it so we don't loop by accident
//For the LQFP
48
of STM
32
F
042
, the BOOT
0
pin is at PF
11
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
GPIO_InitTypeDef gp;
gp.GPIO_Pin = GPIO_Pin_
11
; //BOOT
0
pin
gp.GPIO_Mode = GPIO_Mode_OUT;
gp.GPIO_Speed = GPIO_Speed_
2
MHz;
gp.GPIO_OType = GPIO_OType_PP;
gp.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, &gp);
GPIO_WriteBit(GPIOF, GPIO_Pin_
11
, 
1
);
void (*bootloader)(void) = 
0
; //Zero the function 
pointer
.
jumpaddr = *(__IO uint
32
_t*)(BOOTLOADER_START_ADDR + 
4
);
bootloader = (void (*)(void)) jumpaddr; //Set the function 
pointer
to bootaddr +
4
__set_MSP(*(__IO uint
32
_t*) BOOTLOADER_START_ADDR); //load the stackpointer - bye bye program
bootloader(); //GO TO DFU MODE **** :D
//this should never be hit, trap for debugging
while(
1
){}
}
}

Posted on January 22, 2017 at 20:21

Thank you heaps for the note that the bootloader respects the boot pin when the boot_sel is configured appropriately

After understanding that, jumping to 0x1FFFC404 for stm32f042f4p6 worked ok for me

Posted on November 19, 2017 at 17:48

Anton,

Did you manage to make it work on an STM32L0...

I'm trying to do the same but the code that I found on the forum, doesn't seem to work I branch into the bootloader code but then the MCU resets again..

Hope you solved it.

Best

Massimo

Posted on May 02, 2018 at 02:47

Thank you for your solution!