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:
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))
void fhInit()
{
fhInitPower();
fhMspInit();
fhHseInit();
fhPllInit();
fhCpuClockInit();
systickInit();
}
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: 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.
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();
}