skip to content
Tang's Blog

STM32 SystemClock Modified When Debugging with OpenOCD

/ 4 min read

Updated:
Table of Contents

Phenomenon

The issue was reproduced on an STM32F4 target. Register addresses, maximum clock rates, and OpenOCD reset scripts vary across STM32 families, so the exact behavior should not be generalized to every STM32 device.

After enabling the PLL in STM32CubeMX, execution appeared to become stuck in SystemClock_Config() regardless of the configured SYSCLK or HCLK frequency. image.png

More specifically, HAL_RCC_OscConfig() failed while validating the existing RCC and PLL configuration:

//Line 149 in main.c
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
//Line 550 in stm32f4xx_hal_rcc.c
if (((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_OFF) ||
(READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) ||
(READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != (RCC_OscInitStruct->PLL.PLLM) << RCC_PLLCFGR_PLLM_Pos) ||
(READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != (RCC_OscInitStruct->PLL.PLLN) << RCC_PLLCFGR_PLLN_Pos) ||
(READ_BIT(pll_config, RCC_PLLCFGR_PLLP) != (((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U)) << RCC_PLLCFGR_PLLP_Pos) ||
(READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != (RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)))

Initial hypotheses

The configured system clock is too high

The maximum clock depends on the exact STM32F4 part number. On this board, reducing the configured clock did not fix the problem:

  • Decreasing the clock to 72 MHz did not help, which ruled out a simple overclocking error.
  • If the system clock came directly from HSI or HSE, the problem disappeared. - The system ran correctly in this configuration, but the clock was too slow for the application. - Reproducing the PLL problem with HSI as its source ruled out an HSE oscillator or layout problem.
  • If the PLL is participated, no matter how low the clock speed is, or whether the clock source comes from HSI or HSE, the problem is always encountered

After that, I tried to use the examples provided by manufacturer, which is also weird:

  • The source code of RCC/PLL is pretty much similar to generated before, while none of them could work
  • The binary program provided could work perfectly! Which excludes the problems from hardware.

After some tries, the problem is narrowed down to the RCC/PLL configuration

STM32CubeMX or HAL bug

Though I have generated code thousands times directly from CubeMX and it ran perfectly before on STM32F103, it could be possible a vital bug made by ST official. And it could be true. Some reported a bug of mis-configuring a register in HAL few years ago and ST promised to solve the problem by updating a firmware pack. https://community.st.com/t5/stm32-mcus-embedded-software/bugs-encountered-in-f4-rcc-hal-code-are-there-any-workarounds/td-p/209967 The installed firmware package already contained the reported fix, and testing the previous package did not solve the problem.

Debugger behavior

The firmware worked when flashed and started without a debug session. This narrowed the difference to the ST-Link/OpenOCD reset sequence rather than the generated application code.

Reasons

I was inspired by https://www.eevblog.com/forum/microcontrollers/stm32-clock-gets-modified-when-debugger-is-connected/

The OpenOCD target script modifies registers during reset init.

OpenOCD provides several reset commands: image.png In the OpenOCD version used for this test, reset init ran the following handler from stm32f4x.cfg:

Terminal window
$_TARGETNAME configure -event reset-init {
# Configure PLL to boost clock to HSI x 4 (64 MHz)
mww 0x40023804 0x08012008 ;# RCC_PLLCFGR 16 Mhz /8 (M) * 128 (N) /4(P)
mww 0x40023C00 0x00000102 ;# FLASH_ACR = PRFTBE | 2(Latency)
mmw 0x40023800 0x01000000 0 ;# RCC_CR |= PLLON
sleep 10 ;# Wait for PLL to lock
mmw 0x40023808 0x00001000 0 ;# RCC_CFGR |= RCC_CFGR_PPRE1_DIV2
mmw 0x40023808 0x00000002 0 ;# RCC_CFGR |= RCC_CFGR_SW_PLL
# Boost JTAG frequency
adapter speed 8000
}

The handler configures the PLL for 64 MHz and increases the adapter speed. Those RCC values remain when the application begins its own clock setup, so the HAL observes a PLL configuration different from the one generated by STM32CubeMX. image.png

Solution

  • For this configuration, use reset halt instead of reset init so that the target script does not rewrite the PLL registers. Some boards rely on other reset-init actions, so check the target script before applying this workaround elsewhere. The operation is pretty simple, change the option in CLion from: image.png to: image.png

Besides

Inspecting peripheral registers through an SVD file in CLion made it much easier to compare RCC state with the reference manual. The TinyGo STM32 SVD repository contains a useful STM32F407 definition; use the file matching the exact target MCU.