Skip to main content
Visitor II
July 8, 2025
Question

STM32G030 NVIC_SystemReset from Application Freezes UART/GPIO on Bootloader Restart

  • July 8, 2025
  • 2 replies
  • 489 views

Title:

Hi STM32 Community,

I’m working on a FOTA (Firmware Over-The-Air) project using the STM32G030C8T6. The setup consists of:

- A custom bootloader at 0x08000000 (32KB)
- An application at 0x08008000 (32KB)
- Communication via UART2 with an ESP32, which sends Intel HEX files

:white_heavy_check_mark:Working Bootloader Flow

- On any reset (power-on or manual), the bootloader always runs first.
- The bootloader listens for HEX data and writes the application to flash at 0x08008000.
- When it receives "JUMP_2_APP" over UART, it jumps to the application.
- This jump works correctly: the application runs, GPIO/LED blinks, UART works.

__attribute__((noreturn)) void Jump_To_Application(void)
{
uint32_t app_addr = 0x08008000;
uint32_t msp = *(__IO uint32_t *)app_addr;
uint32_t reset_addr = *(__IO uint32_t *)(app_addr + 4);
void (*app_entry)(void) = (void (*)(void))reset_addr;

if ((msp < 0x20000000) || (msp >= 0x20002000)) {
send_uart("[BOOT] Invalid MSP! Application not valid.\r\n");
while (1);
}

if ((reset_addr < 0x08008000) || (reset_addr > 0x0800FFFF)) {
send_uart("[BOOT] Invalid Reset Handler! Application not valid.\r\n");
while (1);
}

send_uart("[BOOT] Jumping to application...\r\n");
HAL_Delay(100);

HAL_UART_DeInit(&huart2);
HAL_RCC_DeInit();
HAL_DeInit();
__disable_irq();

SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;

for (IRQn_Type irq = 0; irq <= 31; irq++) {
NVIC_ClearPendingIRQ(irq);
NVIC_DisableIRQ(irq);
}

SCB->VTOR = app_addr;
__DSB();
__ISB();

__set_MSP(msp);
app_entry();

while (1);
}

:cross_mark:Problem: App to Bootloader via NVIC_SystemReset() Causes Freeze

In the application, I want to return to the bootloader when a "APP_2_BOOT" command is received over UART. I perform full deinitialization and issue a system reset.

void Clean_SystemReset_To_Bootloader(void) {
__disable_irq();

HAL_UART_DeInit(&huart2);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_All);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_All);
HAL_RCC_DeInit();
HAL_DeInit();

RCC->IOPRSTR |= 0xFFFFFFFF; RCC->IOPRSTR = 0;
RCC->APBRSTR1 |= 0xFFFFFFFF; RCC->APBRSTR1 = 0;
RCC->APBRSTR2 |= 0xFFFFFFFF; RCC->APBRSTR2 = 0;

SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;

for (IRQn_Type irq = 0; irq <= 31; irq++) {
NVIC_ClearPendingIRQ(irq);
NVIC_DisableIRQ(irq);
}

__set_MSP(*(volatile uint32_t*)0x08000000);
__DSB();
__ISB();

NVIC_SystemReset();
}

On receiving the command:

if (strcmp(cmd, "APP_2_BOOT") == 0) {
send_uart("[APP] CMD: APP_2_BOOT received\r\n");
HAL_Delay(100);
Clean_SystemReset_To_Bootloader();
}

🧪 Observed Behavior

- The application receives "APP_2_BOOT" correctly.
- Calls NVIC_SystemReset(), and the bootloader starts executing (verified via debug).
- But now: no UART output, no LED toggle — GPIO and UART2 are frozen.
- A manual reset or power-on reset works perfectly — bootloader UART prints and LED toggle are alive.

:white_heavy_check_mark:What Works

- Bootloader jump to application (Jump_To_Application) :white_heavy_check_mark:
- Power-on reset or manual reset into bootloader :white_heavy_check_mark:
- Application execution :white_heavy_check_mark:

:cross_mark:What Fails

- When jumping back to bootloader via NVIC_SystemReset() in the application, bootloader starts, but peripherals freeze (UART/GPIO dead).
- There is no hard fault — the bootloader main() is executing.

:magnifying_glass_tilted_left:What I Verified

- SCB->VTOR is set correctly in both:
- Bootloader: 0x08000000
- Application: 0x08008000
- Both use the same startup file (startup_stm32g030xx.s)
- No RAM flags used — pure command-based switch
- Reset_Handler runs in bootloader after reset from application
- Tried direct jump from app to bootloader (same issue)

🧠 What I Need Help With

Why does NVIC_SystemReset() from the application result in peripherals (especially UART2 and GPIO) being frozen when the bootloader restarts?

- Is there something still holding peripherals in an unknown state?
- Is calling __set_MSP() before NVIC_SystemReset() safe?
- Is the HSI or other clock configuration still preserved?
- Does RCC require extra handling before reset?

:hammer_and_wrench:System Summary

- MCU: STM32G030C8T6 (64KB Flash, 8KB RAM)
- IDE: Keil MDK-ARM
- Bootloader: @ 0x08000000, size 32KB
- Application: @ 0x08008000, size 32KB
- UART2 used for communication/logging
- No use of HAL jump helpers — all manual MSP + vector remap
- Only command-based jumps — no boot flags

Any help or suggestions would be deeply appreciated. I’ve spent days trying to understand why reset from the app results in frozen peripherals on bootloader restart.

Thanks in advance!


Edited to apply source code formatting - please see How to insert source code for future reference.

    This topic has been closed for replies.

    2 replies

    Visitor II
    July 8, 2025
    // Solution 1: Enhanced Clean Reset Function
    void Clean_SystemReset_To_Bootloader(void) {
    __disable_irq();
    
    // 1. Deinitialize all HAL peripherals first
    HAL_UART_DeInit(&huart2);
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_All); // Add all ports you're using
    
    // 2. Force complete RCC reset - this is crucial
    // Reset all peripheral clocks to default state
    RCC->AHBRSTR = 0xFFFFFFFF;
    RCC->AHBRSTR = 0;
    RCC->APBRSTR1 = 0xFFFFFFFF;
    RCC->APBRSTR1 = 0;
    RCC->APBRSTR2 = 0xFFFFFFFF;
    RCC->APBRSTR2 = 0;
    RCC->IOPRSTR = 0xFFFFFFFF;
    RCC->IOPRSTR = 0;
    
    // 3. Disable all peripheral clocks
    RCC->AHBENR = 0x00000100; // Keep SRAM enabled only
    RCC->APBENR1 = 0;
    RCC->APBENR2 = 0;
    RCC->IOPENR = 0;
    
    // 4. Reset system clock to default HSI state
    RCC->CR |= RCC_CR_HSION;
    while(!(RCC->CR & RCC_CR_HSIRDY));
    
    // Switch to HSI and disable PLL
    RCC->CFGR &= ~RCC_CFGR_SW;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
    
    RCC->CR &= ~RCC_CR_PLLON;
    RCC->CFGR = 0; // Reset CFGR to default
    
    // 5. Reset all GPIO to analog mode (lowest power, safest state)
    GPIOA->MODER = 0xFFFFFFFF;
    GPIOB->MODER = 0xFFFFFFFF;
    GPIOC->MODER = 0xFFFFFFFF;
    GPIOD->MODER = 0xFFFFFFFF;
    GPIOF->MODER = 0xFFFFFFFF;
    
    // 6. Clear all GPIO output data
    GPIOA->ODR = 0;
    GPIOB->ODR = 0;
    GPIOC->ODR = 0;
    GPIOD->ODR = 0;
    GPIOF->ODR = 0;
    
    // 7. Reset SysTick completely
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    
    // 8. Clear all NVIC interrupts
    for (IRQn_Type irq = 0; irq <= 31; irq++) {
    NVIC_ClearPendingIRQ(irq);
    NVIC_DisableIRQ(irq);
    }
    
    // 9. Reset NVIC priority grouping
    NVIC_SetPriorityGrouping(0);
    
    // 10. HAL cleanup
    HAL_RCC_DeInit();
    HAL_DeInit();
    
    // 11. Memory barriers
    __DSB();
    __ISB();
    
    // 12. Reset MSP to bootloader's initial stack
    __set_MSP(*(volatile uint32_t*)0x08000000);
    
    // 13. Final memory barriers before reset
    __DSB();
    __ISB();
    
    // 14. Perform system reset
    NVIC_SystemReset();
    
    // Should never reach here
    while(1);
    }
    
    // Solution 2: Alternative using Watchdog Reset (More reliable)
    void Watchdog_Reset_To_Bootloader(void) {
    __disable_irq();
    
    // Basic cleanup
    HAL_UART_DeInit(&huart2);
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_All);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_All);
    
    // Enable LSI for IWDG
    RCC->CSR |= RCC_CSR_LSION;
    while(!(RCC->CSR & RCC_CSR_LSIRDY));
    
    // Configure IWDG for immediate reset
    IWDG->KR = 0x5555; // Enable write access
    IWDG->PR = 0; // Prescaler /4 (fastest)
    IWDG->RLR = 1; // Reload value (minimum)
    IWDG->KR = 0xCCCC; // Start watchdog
    
    // Wait for watchdog reset
    while(1);
    }
    
    // Solution 3: Boot Flag Method (Most Reliable)
    // Add this to your main.c global variables
    volatile uint32_t __attribute__((section(".noinit"))) boot_flag;
    
    // In application, use this instead of direct reset
    void Request_Bootloader_Via_Flag(void) {
    boot_flag = 0xDEADBEEF; // Magic value
    __DSB();
    NVIC_SystemReset();
    }
    
    // In bootloader main(), check this flag early
    int main(void) {
    // Check boot flag first, before any HAL init
    if (boot_flag == 0xDEADBEEF) {
    boot_flag = 0; // Clear flag
    
    // Force complete hardware reset state
    RCC->AHBRSTR = 0xFFFFFFFF; RCC->AHBRSTR = 0;
    RCC->APBRSTR1 = 0xFFFFFFFF; RCC->APBRSTR1 = 0;
    RCC->APBRSTR2 = 0xFFFFFFFF; RCC->APBRSTR2 = 0;
    RCC->IOPRSTR = 0xFFFFFFFF; RCC->IOPRSTR = 0;
    
    // Reset all clocks to default
    RCC->AHBENR = 0x00000100;
    RCC->APBENR1 = 0;
    RCC->APBENR2 = 0;
    RCC->IOPENR = 0;
    }
    
    HAL_Init();
    SystemClock_Config();
    // ... rest of bootloader init
    }
    
    // Solution 4: Enhanced Bootloader Initialization
    // Add this to your bootloader's main() before HAL_Init()
    void Force_Clean_Hardware_State(void) {
    // This should be called in bootloader BEFORE HAL_Init()
    
    // 1. Reset all peripherals
    RCC->AHBRSTR = 0xFFFFFFFF;
    RCC->AHBRSTR = 0;
    RCC->APBRSTR1 = 0xFFFFFFFF;
    RCC->APBRSTR1 = 0;
    RCC->APBRSTR2 = 0xFFFFFFFF;
    RCC->APBRSTR2 = 0;
    RCC->IOPRSTR = 0xFFFFFFFF;
    RCC->IOPRSTR = 0;
    
    // 2. Disable all peripheral clocks
    RCC->AHBENR = 0x00000100; // Keep SRAM only
    RCC->APBENR1 = 0;
    RCC->APBENR2 = 0;
    RCC->IOPENR = 0;
    
    // 3. Reset system clock configuration
    RCC->CR |= RCC_CR_HSION;
    while(!(RCC->CR & RCC_CR_HSIRDY));
    
    RCC->CFGR &= ~RCC_CFGR_SW;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
    
    RCC->CR &= ~(RCC_CR_PLLON | RCC_CR_HSEON);
    RCC->CFGR = 0;
    
    // 4. Clear all NVIC state
    for (IRQn_Type irq = 0; irq <= 31; irq++) {
    NVIC_ClearPendingIRQ(irq);
    NVIC_DisableIRQ(irq);
    }
    NVIC_SetPriorityGrouping(0);
    
    // 5. Reset SysTick
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    
    // 6. Memory barriers
    __DSB();
    __ISB();
    }
    
    // Modified bootloader main()
    int main(void) {
    // CRITICAL: Call this BEFORE HAL_Init()
    Force_Clean_Hardware_State();
    
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART2_UART_Init();
    
    // Rest of your bootloader code...
    }

    Edited to apply source code formatting - please see How to insert source code for future reference.

    Super User
    July 9, 2025

    If your bootloader is at 0x08000000, resetting the chip will cause it to load. You're doing a whole bunch of stuff that is unnecessary. Calling NVIC_SystemReset brings NRST low which resets the chip and all volatile settings--including peripherals and clocks.

     

    If the program is frozen or stuck or whatever after calling NVIC_SystemReset, launch a debug session without downloading code or resetting the chip to understand why.

     

    MSP of 0x20002000 is valid and expected if you're using 8 kB of RAM.