Skip to content

clock_stm32_ll_h7.c: VOS0 mode improperly set #104536

@maidnl

Description

@maidnl

Describe the bug

The stm32 clock driver does not set the VOS0 mode as it claims to do.
In the function prepare_regulator_voltage_scale(void) there is the following code:

/* Make sure to put the CPU in highest Voltage scale during clock configuration */
/* Highest voltage is SCALE0 */
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0);

The comments clearly say the purpose of the function to set VOS0 and also the value used LL_PWR_REGU_VOLTAGE_SCALE0 means VOS0, however the LL_PWR_SetRegulVoltageScaling function is unable to do so:

/**
  * @brief  Set the main internal Regulator output voltage
  * @rmtoll D3CR    VOS       LL_PWR_SetRegulVoltageScaling
  * @param  VoltageScaling This parameter can be one of the following values:
  *         @arg @ref LL_PWR_REGU_VOLTAGE_SCALE0
  *         @arg @ref LL_PWR_REGU_VOLTAGE_SCALE1
  *         @arg @ref LL_PWR_REGU_VOLTAGE_SCALE2
  *         @arg @ref LL_PWR_REGU_VOLTAGE_SCALE3
  * @note   For all H7 lines except STM32H7Axxx and STM32H7Bxxx lines, VOS0
  *         is applied when PWR_D3CR_VOS[1:0] = 0b11 and  SYSCFG_PWRCR_ODEN = 0b1.
  * @retval None
  */
__STATIC_INLINE void LL_PWR_SetRegulVoltageScaling(uint32_t VoltageScaling)
{
#if defined (PWR_CPUCR_PDDS_D2)
  MODIFY_REG(PWR->D3CR, PWR_D3CR_VOS, VoltageScaling);
#else
  MODIFY_REG(PWR->SRDCR, PWR_SRDCR_VOS, VoltageScaling);
#endif /* PWR_CPUCR_PDDS_D2 */
}

The note correctly states that to use VOS0 is necessary also to set the ODEN bit, but this function does not modify it.
To actually activate the VOS0 is necessary to use something like:

__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

This macro correctly sets VOS1 and then sets the ODEN bit which eventually set VOS0 correctly.

The effect of this bug is that in case of highly demanding fw (many peripherals used at the same time), the fw could crash because it fails to correctly read from ram (especially during initialization phase). It is a subtle bug because in many cases the VOS1 setting is enough to keep things working also in case high AXI Interface clock frequency (225MHz,240MHz) is used. (See table 16 of STM32H747 Reference manual, RM0399, pag. 166).

Below I classified the bug as "major" because it could be very problematic to detect and lead to unusable boards: perfectly fine application crashes only when many peripheral are use together. Nevertheless it is very easy to reproduce and systematic.

Regression

  • This is a regression.

Steps to reproduce

Use the following function to print the vos status:

#include <soc.h>

/**
 * @brief Prints the current STM32H7 Voltage Scaling (VOS) state.
 * This reads the raw hardware registers from the PWR and SYSCFG
 * peripherals to determine the exact voltage being supplied to the core.
 */
void print_vos_setting(void)
{
   /* Ensure the SYSCFG clock is enabled so we can read its registers */
   __HAL_RCC_SYSCFG_CLK_ENABLE();

   /* Read the hardware registers */
   uint32_t pwr_d3cr = PWR->D3CR;
   uint32_t syscfg_pwrcr = SYSCFG->PWRCR;

   /* Extract the specific bits */
   /* VOS is located at bits 14 and 15 of PWR->D3CR */
   uint32_t vos_bits = (pwr_d3cr & PWR_D3CR_VOS) >> PWR_D3CR_VOS_Pos;
   /* ODEN is bit 0 of SYSCFG->PWRCR */
   uint32_t oden_bit = (syscfg_pwrcr & SYSCFG_PWRCR_ODEN) ? 1 : 0;

   printk("\n========================================\n");
   printk("       DIAGNOSTIC: VOLTAGE SCALE        \n");
   printk("========================================\n");
   printk("PWR_D3CR (VOS bits) : %u\n", vos_bits);
   printk("SYSCFG_PWRCR (ODEN) : %u\n", oden_bit);

   /* 4. Interpret the results based on the STM32H7 datasheet */
   if (vos_bits == 3) {
      if (oden_bit == 1) {
         printk("Current State       : VOS0 (Overdrive / 1.35V)\n");
      } else {
         printk("Current State       : VOS1 (1.25V)\n");
      }
   } else if (vos_bits == 2) {
      printk("Current State       : VOS2 (1.15V)\n");
   } else if (vos_bits == 1) {
      printk("Current State       : VOS3 (1.05V)\n");
   } else {
      printk("Current State       : Unknown\n");
   }
   printk("========================================\n\n");
}

The function will print:

=======================================
       DIAGNOSTIC: VOLTAGE SCALE
========================================
PWR_D3CR (VOS bits) : 3
SYSCFG_PWRCR (ODEN) : 0
Current State       : VOS1 (1.25V)
========================================

This means that the current code calling LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0); does not work properly.

Relevant log output

Impact

Major – Severely degrades functionality; workaround is difficult or unavailable.

Environment

OS: Linux (Ubuntu 22.04)
Zephyr SDK version: 0.17.4
Zephyr version: tag v4.3.0 (SHA: 3568e1b)

Additional Context

No response

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions