STM32G030 NVIC_SystemReset from Application Freezes UART/GPIO on Bootloader Restart
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.
