cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G030 NVIC_SystemReset from Application Freezes UART/GPIO on Bootloader Restart

rnd_inglo
Visitor

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.

2 REPLIES 2
MR_KT
Visitor
// 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.

TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".