Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LL driver routine RCC_GetHCLKClockFreq ignores D1CPRE divider from D1CFGR register #80

Open
Ilia-Ko opened this issue Dec 21, 2024 · 1 comment
Assignees
Labels
bug Something isn't working hal HAL-LL driver-related issue or pull-request. rcc Reset and Clock Controller

Comments

@Ilia-Ko
Copy link

Ilia-Ko commented Dec 21, 2024

Hello,
I'm using the STM32H743VIT6 board and arm-none-eabi-gcc compiler version 14.2.0 with -O0 option. I'm not using CubeMX setup or bigger HAL driver in this project, only LL routines mixed with some CMSIS-style register access.

It seems that LL driver routine LL_RCC_GetSystemClocksFreq() incorrectly determines HCLK frequency when non-unit D1 core prescaler (D1CPRE) is set.

The following behavior looks like a potential bug:

  1. Setup PLL1 sourced from HSE so that pll1_p_ck = 96 MHz in integer mode (lines 18-44 in my MRE below)
  2. Set D1 core prescaler = 2, i.e. D1CPRE = 0b1000 in the D1CFGR register of RCC block (line 47 in MRE)
  3. Set dividers for AHB, APB1...APB4 = 1 (lines 48-52 in MRE)
  4. Select system clock sys_ck to be sourced from pll1_p_ck, i.e. 96 MHz (line 53 in MRE)
  5. Call LL_RCC_GetSystemClocksFreq(&clk) (line 67 in MRE)
  6. Now clk.SYSCLK_Frequency = 96 MHz (correct), but clk.HCLK_Frequency = 96 MHz which is incorrect since it should be HCLK = sys_ck / D1CPRE = sys_ck / 2 = 48 MHz. Consequently, all other PCLKx_Frequency are incorrect as well (96 MHz instead of 48 MHz). Also LL_RCC_GetSystemClocksFreq(&clk) does not calculate clk.CPUCLK_Frequency what looks inconsistent too.

Some debugging suggests that this routine Src/stm32h7xx_ll_rcc.c simply ignores D1CPRE as if it were always = 1. It divides SYSCLK by HPRE from D1CFGR register, but does not divide it further by D1CPRE from the same register.

The frequencies are set up indeed as expected, since LED on GPIO PA1 with two LL_mDelay(500) intervals blinks 1 time per second when LL_Init1msTick() is fed with 48 MHz value (see lines 80-92 in MRE).

The ignored prescaler is framed in red at this screenshot from reference manual (RM0433 Rev 8, page 350).
Ignored prescaler

Here is my MRE (the only custom source file 'main.c' in project):

#include "stm32h743xx.h"

#include "stm32h7xx_ll_rcc.h"
#include "stm32h7xx_ll_bus.h"
#include "stm32h7xx_ll_gpio.h"
#include "stm32h7xx_ll_utils.h"

int main(void) {

    // Increase system voltage scale to the VOS1
    PWR->CR3 |= PWR_CR3_LDOEN;          // Ensure that LDO voltage regulator is enabled
    uint32_t d3cr = PWR->D3CR;
    d3cr &= ~PWR_D3CR_VOS;
    d3cr |= (3 << PWR_D3CR_VOS_Pos);    // Set VOS1 voltage scale
    PWR->D3CR = d3cr;
    while (0 == (PWR->D3CR & PWR_D3CR_VOSRDY));

    // Start HSE clock
    LL_RCC_HSE_Enable();
    while (!LL_RCC_HSE_IsReady());  // Wait until HSE becomes ready

    // Configure PLL1
    // 0. HSE @ 25 MHz = REF_CK
    // 1. REF_CK / PLLM = VCO_IN = 1 MHz (thus PLLM = 25)           Must be: 1 MHz <= VCO_IN <= 2 MHz
    // 2. VCO_IN * PLLN = VCO_OUT = 384 MHz (thus PLLN = 384)       Must be: 192 MHz <= VCO_OUT <= 836 MHz
    // 3. VCO_OUT / PLLP = SYS_CK = 96 MHz (thus PLLP = 4)          Must be: PLL_OUT <= 180 MHz
    uint32_t pllM = 25 << RCC_PLLCKSELR_DIVM1_Pos;  // This factor is specific for PLL1
    uint32_t pllN = (384-1) << RCC_PLL1DIVR_N1_Pos; // Multiplier for VCO
    uint32_t pllP = (4-1) << RCC_PLL1DIVR_P1_Pos;   // Divider for SYS_CK
    LL_RCC_PLL1_Disable();
    while (LL_RCC_PLL1_IsReady());
    RCC->PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_Msk;
    RCC->PLLCKSELR |=  RCC_PLLCKSELR_PLLSRC_HSE | pllM;  // Use HSE as input for PLL
    RCC->PLLCFGR &= ~RCC_PLLCFGR_PLL1RGE;   // Select PLL1 input (VCO_IN) between 1 and 2 MHz
    RCC->PLLCFGR |=  RCC_PLLCFGR_PLL1VCOSEL; // Select low-frequency VCO (VCO_IN is 1 to 2 MHz)
    RCC->PLLCFGR &= ~RCC_PLLCFGR_PLL1FRACEN;// Disable sigma-delta modulator (fractional mode)
    RCC->PLLCFGR |=  RCC_PLLCFGR_DIVP1EN;    // Enable P divider
    RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVQ1EN;    // Disable Q divider
    RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVR1EN;    // Disable R divider
    RCC->PLL1DIVR = pllN | pllP;    // Configure PLL dividers and multipliers

    // Start the PLL1
    LL_RCC_PLL1_Enable();
    while (!LL_RCC_PLL1_IsReady()); // Wait until PLL1 becomes ready

    // Configure bus prescalers: CPU 48 MHz, AHBx 48 MHz, APBx 48 MHz
    LL_RCC_SetSysPrescaler(LL_RCC_SYSCLK_DIV_2);    // Divide SYS_CLK by 2 to obtain 48 MHz CPU frequency
    LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_1);
    LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);  
    LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);    
    LL_RCC_SetAPB3Prescaler(LL_RCC_APB3_DIV_1);   
    LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_1);  
    LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1);

    // Decrease the count of FLASH wait states (to accomodate its latency)
    // 1 WS for 70 < HCLK < 140 MHz, 2 WS for 140 < HCLK < 210 MHz (VOS1 voltage range)
    // 1 WS for 45 < HCLK <  90 MHz, 2 WS for  90 < HCLK < 135 MHz (default VOS3 voltage range)
    const uint32_t FLATENCY = FLASH_ACR_LATENCY_0WS;
    uint32_t flACR = FLASH->ACR;
    flACR &= ~FLASH_ACR_LATENCY_Msk;
    flACR |= FLATENCY;
    FLASH->ACR = flACR;
    if ((FLASH->ACR & FLASH_ACR_LATENCY_Msk) != FLATENCY) while (1);

    // Check clock frequencies
    LL_RCC_ClocksTypeDef clk = {};
    LL_RCC_GetSystemClocksFreq(&clk);   // Get all frequencies (probably, there is a bug with HCLK and subsequent freqs)

    const uint32_t SYS_FREQ = 96*1000*1000;
    const uint32_t CPU_FREQ = 48*1000*1000;
    const uint32_t BUS_FREQ = 48*1000*1000;
    while (SYS_FREQ != clk.SYSCLK_Frequency) {}     // OK (96 MHz)
    // while (BUS_FREQ != clk.HCLK_Frequency) {}       // Incorrect 96 MHz (should be 48 MHz)
    // while (BUS_FREQ != clk.PCLK1_Frequency) {}      // Incorrect
    // while (BUS_FREQ != clk.PCLK2_Frequency) {}      // Incorrect
    // while (BUS_FREQ != clk.PCLK3_Frequency) {}      // Incorrect
    // while (BUS_FREQ != clk.PCLK4_Frequency) {}      // Incorrect
    // while (CPU_FREQ != clk.CPUCLK_Frequency) {}     // Incorrect 0 MHz (should be 48 MHz)

    LL_Init1msTick(CPU_FREQ);   // Configure generic millisecond clock

    // Setup GPIO PA1 for LED
    LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
    LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT);

    // Switch LED approx. 2 times per second (each blink ~ +1 sec)
    while (1) {
        LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_1);
        LL_mDelay(500);
        LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_1);
        LL_mDelay(500);
    }
}
@ALABSTM ALABSTM added bug Something isn't working hal HAL-LL driver-related issue or pull-request. rcc Reset and Clock Controller labels Dec 23, 2024
@ALABSTM
Copy link
Collaborator

ALABSTM commented Dec 23, 2024

Hi @Ilia-Ko,

Thank you for this detailed report. We will get back to you after further analysis.

With regards,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working hal HAL-LL driver-related issue or pull-request. rcc Reset and Clock Controller
Projects
Development

No branches or pull requests

3 participants