Search code examples
embeddedstm32stm32f4

STM32F411CEU6 program halts when switching system clock to PLL


I am trying to achieve high clock speeds on my STM32F411CEU6 board (96 MHZ) by tweaking the PLL clock without HAL. I have set the PLL clock source to HSE (25 MHz). My code halts whenever my debugger reaches this line:

setBits(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
halt((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

I cannot figure out why. I have set the flash latency to the absolute maximum hoping it would help, since it did not work when I set it to 3WS (>90 MHz, 3.3V).

Implementation:

  • Macros:
inline void spin (volatile u32 pCount)
{
    while (pCount--) asm("nop");
}
#define halt(cond) while (cond) { spin(1); }

#define setBits(x, msk, v) x = ((x) & ~((u32)(msk))) | (u32)(v)
#define clearBits(x, msk) x = ((x) & ~(u32)(msk))
  • Init function:
void fhInit()
{
    fhInitPower();
    fhMspInit();
    fhHseInit();
    fhPllInit();
    fhCpuClockInit();

    systickInit();
}
  • Functions:
void fhMspInit()
{
    setBits(FLASH->ACR, FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY, FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_7WS);
}

void fhHseInit()
{
    setBits(RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON);
    halt(RCC->CR & RCC_CR_HSERDY == 0);
}

void fhPllDisable()
{
    setBits(RCC->CR, RCC_CR_PLLON, 0);
    halt(RCC->CR & RCC_CR_PLLRDY);
}

void fhPllEnable()
{
    setBits(RCC->CR, RCC_CR_PLLON, 1);
    halt(RCC->CR & RCC_CR_PLLRDY == 0);
}

void fhPllInit()
{
    fhPllDisable();

    auto m = 25;
    auto n = 192;
    auto p = 2;
    auto q = 4;

    auto pllReg = RCC_PLLCFGR_PLLSRC_HSE;
    pllReg |= m << RCC_PLLCFGR_PLLM_Pos;
    pllReg |= n << RCC_PLLCFGR_PLLN_Pos;
    pllReg |= (p >> 1) - 1 << RCC_PLLCFGR_PLLP_Pos;
    pllReg |= q <<  RCC_PLLCFGR_PLLQ_Pos;

    setBits(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC | RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLP | RCC_PLLCFGR_PLLQ, pllReg);

    fhPllEnable();
}

void fhInitPower()
{
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    setBits(PWR->CR, PWR_CR_VOS, 0b11 << PWR_CR_VOS_Pos);
}

void fhUpdateCoreClock()
{
    u32 pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
    u32 pllvco = (uint64_t)HSE_VALUE * (uint64_t)((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos) / (uint64_t)pllm;
    u32 pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >> RCC_PLLCFGR_PLLP_Pos) + 1U) * 2U;
    u32 sysCfkFreq = pllvco / pllp;
    SystemCoreClock = sysCfkFreq >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];
}

void fhCpuClockInit()
{
    fhPllEnable();

    setBits(RCC->CFGR, RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2 | RCC_CFGR_HPRE, RCC_CFGR_PPRE2_DIV1 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_HPRE_DIV1);

    setBits(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
    halt((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

    fhUpdateCoreClock();
}

This clock configuration works perfectly in the Cube IDE when using HAL, so it is not a hardware problem. Clock diagram: enter image description here Here is the github repo containing the whole project

I tried to find people with similar problems, but did not found anyone having this exact issue. Everybody's system clock just seems to switch to PLL without problem.


Solution

  • Fixed it. Turns out I was using setBits when I should have used writeBits: setBits does not set the register value directly, it only touches the bits provided by the mask. I should have instead used writeBits, which is equivalent to X = Y, instead of setBits's X = (X & ~M) | Y.

    #define setBits(x, msk, v) x = ((x) & ~((u32)(msk))) | (u32)(v)
    #define setMask(x, msk) x |= msk
    #define writeBits(x, v) x = (u32)v
    #define clearBits(x, msk) x = ((x) & ~(u32)(msk))
    
    void fhHseInit()
    {
        setBits(RCC->CR, RCC_CR_HSEON, RCC_CR_HSEON);
        halt((RCC->CR & RCC_CR_HSERDY) == 0);
    }
    
    void fhPllDisable()
    {
        setBits(RCC->CR, RCC_CR_PLLON, 0);
        halt(RCC->CR & RCC_CR_PLLRDY);
    }
    
    void fhPllEnable()
    {
        setBits(RCC->CR, RCC_CR_PLLON, RCC_CR_PLLON);
        halt((RCC->CR & RCC_CR_PLLRDY) == 0);
    }
    
    #define HSE_VALUE 25000000
    
    void fhUpdateCoreClock()
    {
        u32 pllm        = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
        u32 pllvco      = (uint64_t)HSE_VALUE * (uint64_t)((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos) / (uint64_t)pllm;
        u32 pllp        = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >> RCC_PLLCFGR_PLLP_Pos) + 1U) * 2U;
        u32 sysCfkFreq  = pllvco / pllp;
        SystemCoreClock = sysCfkFreq >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];
    }
    
    void fhPllInit()
    {
        fhPllDisable();
    
        u8 m = 25;
        u8 n = 192;
        u8 p = 2;
        u8 q = 4;
    
        u32 pllReg = RCC_PLLCFGR_PLLSRC_HSE;
        pllReg |= m << RCC_PLLCFGR_PLLM_Pos;
        pllReg |= n << RCC_PLLCFGR_PLLN_Pos;
        pllReg |= ((p >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos;
        pllReg |= q << RCC_PLLCFGR_PLLQ_Pos;
    
        writeBits(RCC->PLLCFGR, pllReg);
    
        fhPllEnable();
    }
    
    void fhCpuInit()
    {
        u32 latency = FLASH_ACR_LATENCY_3WS;
    
        if (latency > fhFlashGetLatency())
        {
            setBits(FLASH->ACR, FLASH_ACR_LATENCY, latency);
        }
    
        setBits(RCC->CFGR, RCC_CFGR_PPRE1, RCC_CFGR_PPRE1_DIV16);
        setBits(RCC->CFGR, RCC_CFGR_PPRE2, RCC_CFGR_PPRE2_DIV16);
        setBits(RCC->CFGR, RCC_CFGR_HPRE, RCC_CFGR_HPRE_DIV1);
    
        setBits(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
        halt((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
    
        setBits(RCC->CFGR, RCC_CFGR_PPRE1, RCC_CFGR_PPRE1_DIV2);
        setBits(RCC->CFGR, RCC_CFGR_PPRE2, RCC_CFGR_PPRE2_DIV2); // APB2 clock = 96 MHz / 2 = 48 MHz (max for sd card is 25MHz, lowest prescaler is DIV2, so the maximum frequency is 24MHz, the lowest is 187.5khZ)
    
        fhUpdateCoreClock();
    
        if (latency < fhFlashGetLatency())
        {
            setBits(FLASH->ACR, FLASH_ACR_LATENCY, latency);
        }
    }
    
    void fhInitMsp()
    {
        setBits(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN, RCC_APB2ENR_SYSCFGEN);
        setBits(RCC->APB1ENR, RCC_APB1ENR_PWREN, RCC_APB1ENR_PWREN);
        setBits(PWR->CR, PWR_CR_VOS, PWR_CR_VOS);
    }
    
    void fhInit()
    {
        fhFlashInit();
        fhInitMsp();
        fhHseInit();
        fhPllInit();
        fhCpuInit();
    
        systickInit();
    }