cancel
Showing results for 
Search instead for 
Did you mean: 

Jumping to Sysmem bootloader behaves differently across two STM32F405RGT7 batches

TWall.2
Associate II

The Question (since this is a little long): Is there any known interaction between the WWDG timer and the bootloader located in System Memory? Is there any known differences in the F405 revisions that could have an interaction between the WWDG and bootloader?

Hello, I'm working on a product that receives updates via the DFU bootloader. I have two batches of the same board received from the same manufacturer, and one batch is behaving strangely as I attempt to use this method to jump to the bootloader.

The code is based off of this tutorial, and works flawlessly on older batches of the board.

However, on a new batch, instead of jumping to system memory for DFU upload, the board will simply reset from the WWDG IRQ. The overall program feeds the WWDG every couple milliseconds, but by the time the program calls the SysMemBootJump() function, it should have disabled the RCC timers and the WWDG interrupt.

In order to demonstrate the issue, I'll attach a sample project I made in CubeIDE that produces the issue. The program will start with a GPIO set low, and after waiting for short period, will set it high, then jump to the bootloader. On the affected batch of boards (the bottom signal) the GPIO line will dip, indicating a hardware reset. The top signal is the a board operating normally, holding the GPIO high while it waits in system memory:

0693W00000WJSWaQAP.jpgThe only difference between these two boards is the batch number of the STM32F405RGT7 itself. The normal board has the following manufacturing information:

ID: STM32F405RGT7

Rev: y (0x100F)

Manufacturing information: GQ2CN VG CHN GQ 021 17

And the affected board:

ID: STM32F405RGT7

Rev: y (0x100F)

Manufacturing information: GQ2AD VG CHN GQ 213 18

It's pretty easy to dismiss this issue as a difference in the two batches of PCB, but I have been able to verify that the normal operations of this part are meeting expectations, it's just this WWDG/DFU situation.

Related Behaviours:

  • The affected board will boot to sysmem and allow DFU if the board is powered up and BOOT0 is pulled high
  • Explicitly disabling the WWDG peripheral clock (RCC->APB1RSTR &= ~RCC_APB1Periph_WWDG) before disabling interrupts via CPSID/__disable_irq() will allow the board to jump to sysmem

Other things I've checked:

  • Greymarket clones: chips sampled from the affected batch have unique chip ID values and internal serial numbers.
  • ARM revision: both affected and unaffected chips return 0x410fc241 from the SCB->CPUID register.
  • Wonky clocks/resonators: the boards are driven by a 25MHz external oscillator, and part of the program drives a PWM (on TIM1) signal at 533kHz. If there was a difference with the resonator the PWM signal should be affected, but it is not.
  • Running the same test code on an unrelated part with a similar processor (an STM32407VGTx): the F407 behaves the same as the "normal" F405

I'm truly at a loss here. This is a part that we've had in operation for several years with no revisions. But one batch of PCB's where the only visible change is a different batch of F405's is showing different behaviour that is completely reproducible across the entire batch. I really don't want to use the words "silicon error" but at this point I'm running out of ideas. I simply have no idea why the two chips are behaving differently. I couldn't find anything in the errata or AN2606 about WWDG and how it relates to the sysmem bootloader. My colleagues are similarly stumped.

PS, here's the main.c of the CubeMX project I mentioned. Running this on an F405RGT7 GQ2CN VG CHN GQ 021 17 will allow it to enter the bootloader, but running this same code on a GQ2AD VG CHN GQ 213 18 will result in the board rebooting.

#include "main.h"
 
WWDG_HandleTypeDef hwwdg;
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_WWDG_Init(void);
 
#define SYSMEM_ADDRESS (uint32_t)0x1FFF0000
void JumpToBootloader(void)
{
    void (*SysMemBootJump)(void);
    // Disable all interrupts
    __disable_irq();
 
    // Disable systick timer and reset it to default values
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
 
    // Disable RCC, set it to default (after reset) settings
    /* Set HSION bit */
     RCC->CR |= (uint32_t)0x00000001;
 
     /* Reset CFGR register */
     RCC->CFGR = 0x00000000;
 
     /* Reset HSEON, CSSON, PLLON, PLLI2S and PLLSAI(STM32F42/43xxx devices) bits */
     RCC->CR &= (uint32_t)0xEAF6FFFF;
 
     /* Reset PLLCFGR register */
     RCC->PLLCFGR = 0x24003010;
 
     /* Reset PLLI2SCFGR register */
     RCC->PLLI2SCFGR = 0x20003000;
 
     /* Reset HSEBYP bit */
     RCC->CR &= (uint32_t)0xFFFBFFFF;
 
     /* Disable all interrupts */
     RCC->CIR = 0x00000000;
 
    /**
     * Remap system memory to address 0x0000 0000 in address space
     * For STM32F4xx, MEMRMP register in SYSCFG is used (bits[1:0])
     */
    SYSCFG->MEMRMP = 0x01;
    // Set vector table offset to 0
    SCB->VTOR = 0;
 
    /**
     * Set jump memory location for system memory
     * Use address with 4 bytes offset which specifies jump location where program starts
     */
    SysMemBootJump = (void (*)(void)) (*((uint32_t *)(SYSMEM_ADDRESS + 4)));
 
    /**
     * Set main stack pointer.
     * This step must be done last otherwise local variables in this function
     * don't have proper value since stack pointer is located on different position
     *
     * Set direct address location which specifies stack pointer in SRAM location
     */
    __set_MSP(*(uint32_t *)(SYSMEM_ADDRESS));
 
    // Start system memory execution
    SysMemBootJump();
 
    while (1)
    {
    }
}
 
int main(void)
{
 
  HAL_Init();
 
  SystemClock_Config();
 
  MX_GPIO_Init();
  MX_WWDG_Init();
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
  int i = 0;
 
  while (1)
  {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
    HAL_WWDG_Refresh(&hwwdg);
    HAL_Delay(5);
    i++;
    if (i == 5) {
      JumpToBootloader();
    }
  }
}
 
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
 
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}
 
static void MX_WWDG_Init(void)
{
 
  hwwdg.Instance = WWDG;
  hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
  hwwdg.Init.Window = 127;
  hwwdg.Init.Counter = 127;
  hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;
  if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
  {
    Error_Handler();
  }
}
 
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
 
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
 
  GPIO_InitStruct.Pin = GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
 
void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}
 
#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */

1 ACCEPTED SOLUTION

Accepted Solutions

The processor doesn't normally start with the interrupts disabled/masked, and the code similarly doesn't assume that they are

SYSCFG clock is enabled via AHB2ENR, the memory won't remap without it.

This is the method I used on F2 and F4 boards a decade ago, resets and vectors into ROM, in reset conditions

;*****************************************************************************
 
startup.s
 
Reset_Handler   PROC
                EXPORT  Reset_Handler
                IMPORT  SystemInit
                IMPORT  __main
 
                LDR     R0, =0x2001FFFC ; TOP OF RAM
                LDR     R1, =0xDEADBEEF
                LDR     R2, [R0, #0]
                CMP     R1, R2
                BNE     NotDfuSe
                STR     R0, [R0, #0]    ; Invalidate
                LDR     R0, =0x40023844 ; RCC_APB2ENR
                LDR     R1, =0x00004000 ; ENABLE SYSCFG CLOCK
                STR     R1, [R0, #0]
                LDR     R0, =0x40013800 ; SYSCFG_MEMRMP
                LDR     R1, =0x00000001 ; MAP ROM AT ZERO
                STR     R1, [R0, #0]
                LDR     R0, =0x1FFF0000 ; ROM BASE
                LDR     SP,[R0, #0]
                LDR     R0,[R0, #4]
                BX      R0              ; BOOT ROM
NotDfuSe
 
                LDR     R0, =SystemInit
                BLX     R0
 
                LDR     R0, =__main
                BX      R0
 
                ENDP ; sourcer32@gmail.com
 
;*****************************************************************************
 
C Code
 
        printf("Entering Boot Loader..\r\n");
 
        *((unsigned long *)TOP_OF_RAM) = 0xDEADBEEF;
 
        __DSB();
 
        NVIC_SystemReset();
 
;*****************************************************************************

Perhaps the SPI and I2C pins being monitored now are moving in your design, or new USART were added.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

7 REPLIES 7
Uwe Bonnes
Principal III

I have no direct answer to your question. But I strongly suggest you set some marker in a memory that is not initilized by reset. Then you call NVIC_System reset and in the startup code you look up that marker and start the bootloader there.

Don't use __disable_irq(); there isn't a counter party to this in the loader to enable, and the task is to TURN OFF the interrupts YOU are using, at the peripheral level.

Check the version of the LOADER, I think STM32 Cube Programmer reports this now

SYSCFG has a clock it needs to be enabled to work

Watch what GNU/GCC generates with the stack frame, changing the stack pointer mid-function can be problematic depending on whether registers or stack has been used for auto/local variables.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TWall.2
Associate II

@Community member​  Considering the same code results in different behaviour across two batches of boards I'm hesitant to agree it's a GNU/GCC issue. I'll take a look at the loader, but I have a couple other questions:

  • "SYSCFG has a clock it needs to be enabled to work"
    • Is there a source I can find on this? Looking through the clock tree and SYSCFG chapters in the TRM, this statement leaves me a little perplexed. Is it derived from the SysClck?
  • "Don't use __disable_irq();"
    • Agreed, we should be disabling the interrupts we use directly.
    • (this is just a curiosity question) I'm not sure what you meant " there isn't a counter party to this in the loader to enable". That if __disable_irq() is used, then the bootloader doesn't re-enable any interrupts (even for use internally)?

@Uwe Bonnes​ That sounds like a more sustainable solution than what we're doing now. I can investigate that too.

TWall.2
Associate II

@Community member​ The boards use two different versions of the bootloader. The older F405 uses bootloader version 0x31 and the newer (non-cooperative) one uses 0x91. The only difference between the two according to AN2606 is that 0x91 supports SPI and I2C which we aren't using anyway, so it's moot. Are there any other differences that you could be aware of?

The processor doesn't normally start with the interrupts disabled/masked, and the code similarly doesn't assume that they are

SYSCFG clock is enabled via AHB2ENR, the memory won't remap without it.

This is the method I used on F2 and F4 boards a decade ago, resets and vectors into ROM, in reset conditions

;*****************************************************************************
 
startup.s
 
Reset_Handler   PROC
                EXPORT  Reset_Handler
                IMPORT  SystemInit
                IMPORT  __main
 
                LDR     R0, =0x2001FFFC ; TOP OF RAM
                LDR     R1, =0xDEADBEEF
                LDR     R2, [R0, #0]
                CMP     R1, R2
                BNE     NotDfuSe
                STR     R0, [R0, #0]    ; Invalidate
                LDR     R0, =0x40023844 ; RCC_APB2ENR
                LDR     R1, =0x00004000 ; ENABLE SYSCFG CLOCK
                STR     R1, [R0, #0]
                LDR     R0, =0x40013800 ; SYSCFG_MEMRMP
                LDR     R1, =0x00000001 ; MAP ROM AT ZERO
                STR     R1, [R0, #0]
                LDR     R0, =0x1FFF0000 ; ROM BASE
                LDR     SP,[R0, #0]
                LDR     R0,[R0, #4]
                BX      R0              ; BOOT ROM
NotDfuSe
 
                LDR     R0, =SystemInit
                BLX     R0
 
                LDR     R0, =__main
                BX      R0
 
                ENDP ; sourcer32@gmail.com
 
;*****************************************************************************
 
C Code
 
        printf("Entering Boot Loader..\r\n");
 
        *((unsigned long *)TOP_OF_RAM) = 0xDEADBEEF;
 
        __DSB();
 
        NVIC_SystemReset();
 
;*****************************************************************************

Perhaps the SPI and I2C pins being monitored now are moving in your design, or new USART were added.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TWall.2
Associate II

I suppose I should update this! I hate finding forum posts that don't end in a resolution or just "I fixed it myself" with no context.

I tried the methods outlined above on the newer batch of chips to no success. Which still strikes me as odd, but either way, on we go. I also swapped a newer F405 that exhibits this issue with an older F405 on the same PCB just to make sure it wasn't a quirk from a particular batch of PCB's. The issue was still manifesting with the newer chip, and was no longer present on the PCB that had the older chip swapped onto it. We're investigating the supply chain for these newer chips, but I'm 99% certain they aren't counterfeit to begin with. Either way, I do have the hacky solution where I manually disable the WWDG clock before proceeding with the rest of the shutdown, which is what we had to continue with due to timeline pressure.

Thanks for the pointers everyone. I'll be implementing the more correct bootloader jump sequence in future designs (and hope I never run into something like this ever again).

Again for future readers: this wasn't the actual solution, but it is better than the original method I was using (linked in the first post in this thread).