All guides
TECHNICAL GUIDESTM32L4R5CLOCK2026

RCC Clock Tree
MSI / HSI16 / HSE / PLL to 120 MHz

Register-level clock bring-up for the STM32L4R5 (Cortex-M4F, RM0432): PWR Range 1 boost, flash wait states, PLL math and a complete, compilable 120 MHz init.

01 The L4R5 clock tree & why 120 MHz needs boost

Every clock on the STM32L4R5 is derived by the RCC (Reset and Clock Control) block from one of four sources. SYSCLK feeds the AHB prescaler to make HCLK (the CPU/AHB clock), and two APB prescalers derive PCLK1 and PCLK2 for the peripheral buses.

MSI / HSI16 / HSE ---> [PLL] ---> SYSCLK ---> /HPRE  ---> HCLK  (Cortex-M4F, AHB, SysTick)
                                              ---> /PPRE1 ---> PCLK1 (APB1: TIM2-7, USART2/3, I2C, SPI2/3...)
                                              ---> /PPRE2 ---> PCLK2 (APB2: USART1, SPI1, TIM1/8, ADC...)

Reaching the part's maximum 120 MHz is not just "spin up the PLL." Three things must be prepared in the right order before you switch SYSCLK onto the PLL:

1. VoltageThe core regulator must be in Range 1 boost mode. Range 1 normal caps at 80 MHz; Range 2 at 26 MHz.
2. FlashFlash is far slower than 120 MHz. It needs 5 wait states, set and verified before the clock is raised.
3. PLLOnly the main PLL's R output can drive SYSCLK. Configure PLLCFGR while the PLL is off, lock it, then switch.
Reset state

After reset the STM32L4R5 runs on MSI at 4 MHz (MSIRANGE = 6, taken from RCC_CSR), SYSCLK = 4 MHz, Range 1 normal, 0 wait states. All the code below assumes this starting point.

SourceTyp. freqKindNotes
MSI100 kHz – 48 MHzInternal RCDefault SYSCLK; 12 selectable ranges; can be PLL source
HSI1616 MHzInternal RCFactory-trimmed ~1%; fast wake; good PLL source
HSE4 – 48 MHzCrystal / extCrystal, or external clock via HSEBYP
PLLup to 120 MHzMultiplierFed by MSI/HSI16/HSE; only path to 120 MHz
LSE / LSI32.768 kHz / 32 kHzLow speedRTC / watchdog; not a SYSCLK path

02 Oscillators: MSI, HSI16, HSE (RCC_CR)

The three high-speed sources are enabled and observed through RCC_CR (base RCC = 0x4002_1000, offset 0x00). Each has an enable bit and a read-only ready flag — always spin on the ready flag before using a source.

FieldBitsMeaning
MSION / MSIRDY0 / 1MSI enable / ready
MSIPLLEN2Hardware auto-trim MSI against LSE (PLL-mode MSI)
MSIRGSEL30 = range from RCC_CSR (reset); 1 = range from MSIRANGE below
MSIRANGE7:4MSI frequency range (see table); writable only when MSI off or ready
HSION / HSIRDY8 / 10HSI16 enable / ready
HSEON / HSERDY16 / 17HSE enable / ready
HSEBYP181 = external clock on OSC_IN (no crystal); set while HSEON = 0
PLLON / PLLRDY24 / 25Main PLL enable / locked

MSI ranges (RCC_CR.MSIRANGE)

To pick an MSI frequency at runtime you must set MSIRGSEL = 1 so the range is read from MSIRANGE rather than from RCC_CSR. The field encodes:

MSIRANGEFreqMSIRANGEFreq
0000100 kHz01104 MHz (reset)
0001200 kHz01118 MHz
0010400 kHz100016 MHz
0011800 kHz100124 MHz
01001 MHz101032 MHz
01012 MHz101148 MHz
Which source for the PLL?

For 120 MHz any of the three works. MSI @ 4 MHz (used in the full example) needs no extra setup — it is already running at reset. HSI16 is the go-to when you want no external parts and better accuracy than MSI. HSE is required when you need tight ppm accuracy (USB, precise UART baud).

03 PWR voltage scaling: Range 1 boost

The core supply is set by the PWR block (base PWR = 0x4000_7000, on APB1). Higher SYSCLK requires higher core voltage. The L4+ adds a Range 1 boost tier on top of the classic L4 ranges — boost is the only way to exceed 80 MHz.

ModeVOS / R1MODECore VMax HCLK
Range 1 boostVOS=01, R1MODE=01.2 V120 MHz
Range 1 normalVOS=01, R1MODE=1 (reset)1.1 V80 MHz
Range 2VOS=101.0 V26 MHz
RegisterFieldBitsUse
PWR_CR1VOS10:901 = Range 1, 10 = Range 2
PWR_CR5R1MODE80 = Range 1 boost, 1 = Range 1 normal (reset)
PWR_SR2VOSF101 while the regulator is ramping; poll until 0

Note the inverted sense: R1MODE = 0 means boost, and its reset value is 1 (normal). So enabling boost is a bit clear, not a set:

c — enable Range 1 boost
/* PWR is on APB1: its bus clock MUST be enabled before any PWR access */
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
(void)RCC->APB1ENR1;                 /* read-back: guarantee the write landed */

/* Select voltage scaling Range 1 (VOS = 0b01) */
MODIFY_REG(PWR->CR1, PWR_CR1_VOS, (0x1UL << PWR_CR1_VOS_Pos));

/* Enter Range 1 BOOST: clear R1MODE (reset value 1 = Range 1 normal, 80 MHz cap) */
PWR->CR5 &= ~PWR_CR5_R1MODE;

/* Wait until the internal regulator has settled at the new voltage */
while (PWR->SR2 & PWR_SR2_VOSF) { }   /* VOSF = 1 while voltage is changing */
Order matters

Enable the PWR bus clock (RCC_APB1ENR1.PWREN) first — PWR registers read as 0 otherwise. Then always wait on VOSF before raising the clock; switching SYSCLK to 120 MHz before the regulator has stabilised can hang or crash the core.

04 Flash latency & wait states (FLASH_ACR)

Embedded flash cannot be read in a single cycle at 120 MHz, so the flash interface inserts wait states. Set the latency in FLASH_ACR (base FLASH = 0x4002_2000, offset 0x00) and, per RM0432, read it back to confirm the value was accepted before you speed up.

FieldBitsMeaning
LATENCY3:0Number of wait states (0–5 valid on L4R5)
PRFTEN8Prefetch enable
ICEN9Instruction cache enable
DCEN10Data cache enable

Wait states vs. HCLK

The step size depends on the voltage mode. In Range 1 boost each wait state buys another 20 MHz, so 120 MHz needs the maximum of 5:

WS (LATENCY)Range 1 boostRange 1 normalRange 2
0≤ 20 MHz≤ 16 MHz≤ 8 MHz
1≤ 40 MHz≤ 32 MHz≤ 16 MHz
2≤ 60 MHz≤ 48 MHz≤ 26 MHz
3≤ 80 MHz≤ 64 MHz
4≤ 100 MHz≤ 80 MHz
5≤ 120 MHz
c — flash wait states for 120 MHz
/* Raise wait states BEFORE increasing the clock, then read back to confirm.
 * 120 MHz in Range 1 boost needs 5 WS. Writing LATENCY is not enough - the
 * value must be verified, because the flash accepts it only when stable.     */
MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, FLASH_ACR_LATENCY_5WS);
while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_5WS) { }

/* Prefetch + instruction cache + data cache: free performance on the L4+ */
FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
Direction rule

When increasing the clock: raise wait states first, then increase SYSCLK. When decreasing: lower SYSCLK first, then reduce wait states. Getting the order wrong means the CPU fetches from flash faster than it can respond — a hard fault or lock-up.

05 PLL configuration & the 120 MHz math (RCC_PLLCFGR)

The main PLL is programmed through RCC_PLLCFGR (RCC offset 0x0C). The input is divided by M, multiplied by N into the VCO, then divided by R to make SYSCLK (P and Q feed peripherals like SAI/USB).

fVCO    = (fPLLIN / PLLM) * PLLN        constraints: 2.66..16 MHz in,  64..344 MHz VCO
SYSCLK  =  fVCO / PLLR                  PLLR in {2,4,6,8},  SYSCLK <= 120 MHz
FieldBitsEncoding
PLLSRC1:000 none, 01 MSI, 10 HSI16, 11 HSE
PLLM7:4Divider = field + 1 → /1../16 (L4+ is 4 bits wide)
PLLN14:8Multiplier, 8..127
PLLPEN / PLLP16 / 17P output enable / legacy /7 or /17 (see PLLPDIV)
PLLQEN / PLLQ20 / 22:21Q output enable / 00=/2, 01=/4, 10=/6, 11=/8
PLLREN / PLLR24 / 26:25R output enable / 00=/2, 01=/4, 10=/6, 11=/8
PLLPDIV31:27Explicit P divider 2..31 (0 = use PLLP bit)
Two encodings to remember

PLLM holds M−1 (so M=1 → field 0), while PLLR/PLLQ hold a code (0→/2, 1→/4, 2→/6, 3→/8). Also: PLLCFGR is writable only while PLLON = 0.

Worked examples for SYSCLK = 120 MHz

SourcefINMNVCORSYSCLK
MSI4 MHz160240 MHz2120 MHz
HSI1616 MHz230240 MHz2120 MHz
HSE8 MHz130240 MHz2120 MHz

Register-level PLL setup for the MSI case (the full main.c is in section 07):

c — main PLL from MSI (4 MHz to 120 MHz)
/* Ensure the PLL source (MSI, 4 MHz by reset) is running and ready */
RCC->CR |= RCC_CR_MSION;
while (!(RCC->CR & RCC_CR_MSIRDY)) { }

/* PLLCFGR is writable only while the PLL is OFF */
RCC->CR &= ~RCC_CR_PLLON;
while (RCC->CR & RCC_CR_PLLRDY) { }

/* VCO   = (fIN / PLLM) * PLLN = (4 MHz / 1) * 60 = 240 MHz   (64..344 MHz OK)
 * SYSCLK= VCO / PLLR          = 240 MHz / 2      = 120 MHz
 * NOTE: the PLLM field holds (M - 1); the PLLR field encodes /2=0,/4=1,/6=2,/8=3 */
RCC->PLLCFGR =
      (0x1UL << RCC_PLLCFGR_PLLSRC_Pos)   /* PLLSRC = 01 -> MSI              */
    | (0UL   << RCC_PLLCFGR_PLLM_Pos)     /* PLLM   = /1  (field = M-1 = 0)  */
    | (60UL  << RCC_PLLCFGR_PLLN_Pos)     /* PLLN   = x60                    */
    | (0UL   << RCC_PLLCFGR_PLLR_Pos)     /* PLLR   = /2  (field 0b00)       */
    | RCC_PLLCFGR_PLLREN;                 /* enable the PLLCLK (R) output    */

/* Start the PLL and wait for lock */
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) { }

The HSE bypass variant (typical of a Nucleo-L4R5ZI fed by the ST-LINK 8 MHz MCO):

c — main PLL from HSE bypass (8 MHz)
/* HSE variant (e.g. Nucleo-L4R5ZI: 8 MHz from ST-LINK MCO, HSE bypass).
 * 8 MHz /1 x30 /2 = 120 MHz.  For a real crystal, drop HSEBYP.              */
RCC->CR |= RCC_CR_HSEON | RCC_CR_HSEBYP;      /* bypass = external clock in  */
while (!(RCC->CR & RCC_CR_HSERDY)) { }

RCC->PLLCFGR =
      (0x3UL << RCC_PLLCFGR_PLLSRC_Pos)   /* PLLSRC = 11 -> HSE              */
    | (0UL   << RCC_PLLCFGR_PLLM_Pos)     /* /1  (8 MHz PLL input)           */
    | (30UL  << RCC_PLLCFGR_PLLN_Pos)     /* x30 -> VCO 240 MHz              */
    | (0UL   << RCC_PLLCFGR_PLLR_Pos)     /* /2  -> 120 MHz                  */
    | RCC_PLLCFGR_PLLREN;

06 AHB/APB prescalers & peripheral clock enable

SYSCLK is divided down for the CPU and the two peripheral buses in RCC_CFGR (offset 0x08). In Range 1 boost every bus is rated for the full 120 MHz, so all three prescalers stay at /1.

FieldBitsDrivesMax @ boost
SW / SWS1:0 / 3:2SYSCLK source select / status (11 = PLL)
HPRE7:4AHB prescaler → HCLK120 MHz
PPRE110:8APB1 prescaler → PCLK1120 MHz
PPRE213:11APB2 prescaler → PCLK2120 MHz
HPRE codeDividePPRE1/2 codeDivide
0xxx/10xx/1
1000/2100/2
1001/4101/4
1010/8110/8
1011..1111/16../512111/16

Peripheral clock gating

Beyond the bus clocks, each peripheral has an individual gate in an RCC AHBxENR / APBxENR register. GPIO ports live on AHB2 (RCC_AHB2ENR), most timers/UARTs on APB1 (RCC_APB1ENR1/2), and USART1/SPI1/TIM1/ADC on APB2 (RCC_APB2ENR).

c — enable peripheral clocks
/* Peripheral clock gating lives in the RCC AHBxENR / APBxENR registers.
 * A peripheral is DEAD (reads as 0, writes ignored) until its bit is set,
 * and you must read the register back before touching the peripheral so the
 * one- or two-cycle clock-enable latency has elapsed.                        */

RCC->AHB2ENR  |= RCC_AHB2ENR_GPIOAEN            /* GPIO ports A and B        */
              |  RCC_AHB2ENR_GPIOBEN;
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN            /* TIM2 (32-bit) on APB1     */
              |  RCC_APB1ENR1_USART2EN;
RCC->APB2ENR  |= RCC_APB2ENR_SYSCFGEN           /* SYSCFG + USART1 on APB2   */
              |  RCC_APB2ENR_USART1EN;
(void)RCC->APB2ENR;                             /* mandatory dummy read      */
Timer clock quirk

When an APB prescaler is /1, the timers on that bus are clocked at PCLK. As soon as the prescaler is > 1, the RCC doubles the timer clock (2×PCLK). With all buses at /1 here, every timer sees a clean 120 MHz.

07 Complete register-level init (+ HAL variant)

The whole sequence in one file. It is self-contained C for the STM32L4R5 CMSIS headers — PWR boost, flash 5 WS, PLL from MSI, buses at /1, then the SYSCLK switch. Compile with -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -DSTM32L4R5xx.

c — main.c : full 120 MHz bring-up (register level)
#include "stm32l4xx.h"   /* umbrella header: pulls in stm32l4r5xx.h + CMSIS   */

/* ---------------------------------------------------------------------------
 * Bring the STM32L4R5 from its 4 MHz MSI reset state up to 120 MHz.
 *   MSI 4 MHz --> PLL (M=1, N=60, R=2) --> SYSCLK = 120 MHz
 *   HCLK = PCLK1 = PCLK2 = 120 MHz
 * Prerequisites handled in order: PWR boost, flash WS, PLL, bus, clock switch.
 * ------------------------------------------------------------------------- */
void SystemClock_Config_120MHz(void)
{
    /* 1. Power interface clock + Range 1 BOOST voltage scaling ------------- */
    RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
    (void)RCC->APB1ENR1;
    MODIFY_REG(PWR->CR1, PWR_CR1_VOS, (0x1UL << PWR_CR1_VOS_Pos)); /* Range 1 */
    PWR->CR5 &= ~PWR_CR5_R1MODE;                                   /* boost   */
    while (PWR->SR2 & PWR_SR2_VOSF) { }

    /* 2. Flash: 5 wait states (verified) + prefetch and caches ------------- */
    MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, FLASH_ACR_LATENCY_5WS);
    while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_5WS) { }
    FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;

    /* 3. Main PLL: MSI(4) /1 x60 /2 = 120 MHz ----------------------------- */
    RCC->CR |= RCC_CR_MSION;
    while (!(RCC->CR & RCC_CR_MSIRDY)) { }
    RCC->CR &= ~RCC_CR_PLLON;
    while (RCC->CR & RCC_CR_PLLRDY) { }
    RCC->PLLCFGR =
          (0x1UL << RCC_PLLCFGR_PLLSRC_Pos)   /* MSI            */
        | (0UL   << RCC_PLLCFGR_PLLM_Pos)     /* /1  (M-1 = 0)  */
        | (60UL  << RCC_PLLCFGR_PLLN_Pos)     /* x60            */
        | (0UL   << RCC_PLLCFGR_PLLR_Pos)     /* /2             */
        | RCC_PLLCFGR_PLLREN;
    RCC->CR |= RCC_CR_PLLON;
    while (!(RCC->CR & RCC_CR_PLLRDY)) { }

    /* 4. Bus prescalers (all /1) then switch SYSCLK to the PLL ------------- */
    MODIFY_REG(RCC->CFGR,
               RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2,
               RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV1 | RCC_CFGR_PPRE2_DIV1);
    MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) { }

    SystemCoreClock = 120000000UL;
}

int main(void)
{
    SystemClock_Config_120MHz();

    /* SYSCLK is now 120 MHz. Enable a couple of peripheral clocks as proof. */
    RCC->AHB2ENR  |= RCC_AHB2ENR_GPIOAEN;     /* GPIOA on AHB2               */
    RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;   /* USART2 on APB1 (120 MHz)    */
    RCC->APB2ENR  |= RCC_APB2ENR_USART1EN;    /* USART1 on APB2 (120 MHz)    */
    (void)RCC->APB2ENR;                        /* barrier before first access */

    for (;;) { }
}

HAL equivalent

The same hardware steps expressed with STM32Cube HAL. PWR_REGULATOR_VOLTAGE_SCALE1_BOOST performs the R1MODE clear + VOSF wait, and passing FLASH_LATENCY_5 to HAL_RCC_ClockConfig programs the wait states in the correct order:

c — HAL SystemClock_Config (120 MHz)
/* Same 120 MHz target using the STM32Cube HAL. Identical hardware sequence,
 * just wrapped: voltage boost -> flash latency (auto) -> PLL -> bus switch. */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef   osc = {0};
    RCC_ClkInitTypeDef   clk = {0};

    /* 1. Range 1 BOOST. HAL clears PWR_CR5.R1MODE and waits for VOSF. */
    __HAL_RCC_PWR_CLK_ENABLE();
    if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST) != HAL_OK)
        Error_Handler();

    /* 2. MSI 4 MHz -> PLL (M=1, N=60, R=2) = 120 MHz */
    osc.OscillatorType      = RCC_OSCILLATORTYPE_MSI;
    osc.MSIState            = RCC_MSI_ON;
    osc.MSIClockRange       = RCC_MSIRANGE_6;        /* 4 MHz */
    osc.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
    osc.PLL.PLLState        = RCC_PLL_ON;
    osc.PLL.PLLSource       = RCC_PLLSOURCE_MSI;
    osc.PLL.PLLM            = 1;
    osc.PLL.PLLN            = 60;
    osc.PLL.PLLR            = 2;                      /* -> SYSCLK 120 MHz */
    osc.PLL.PLLP            = 2;
    osc.PLL.PLLQ            = 2;
    if (HAL_RCC_OscConfig(&osc) != HAL_OK)
        Error_Handler();

    /* 3. Buses all /1; HAL derives FLASH_LATENCY_5 from the 120 MHz target. */
    clk.ClockType      = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
                       | RCC_CLOCKTYPE_PCLK1  | RCC_CLOCKTYPE_PCLK2;
    clk.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    clk.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    clk.APB1CLKDivider = RCC_HCLK_DIV1;
    clk.APB2CLKDivider = RCC_HCLK_DIV1;
    if (HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_5) != HAL_OK)
        Error_Handler();
}

08 Gotchas & common mistakes

The failures below account for the vast majority of "my board won't run at 120 MHz" reports.

Skipping boostLeaving R1MODE = 1 (Range 1 normal) caps you at 80 MHz. At 120 MHz with normal mode the part is out of spec — it may run, then fail intermittently or over temperature.
Not waiting on VOSFRaising SYSCLK before PWR_SR2.VOSF clears runs the core faster than the regulator can supply — brown-out and lock-up.
Wrong wait-state orderIncreasing SYSCLK before setting 5 WS (or not reading FLASH_ACR back) causes flash fetch faults. Raise WS first when speeding up; lower WS last when slowing down.
Forgetting the PWR bus clockPWR is on APB1. Without RCC_APB1ENR1.PWREN set, PWR_CR1/CR5 reads return 0 and your voltage writes silently do nothing.
PLLM off by onePLLM stores M−1. Writing "1" for M=1 actually selects /2 and halves your VCO → 60 MHz instead of 120. Write 0 for /1.
Writing PLLCFGR while PLL onPLLCFGR is read-only while PLLON=1. Clear PLLON and wait for PLLRDY=0 before reprogramming M/N/R.
Forgetting PLLRENThe PLL can lock (PLLRDY=1) yet output nothing to SYSCLK if the R output gate (PLLREN) is not set. Switching SW to PLL then stalls forever on SWS.
VCO out of rangeKeep the VCO in 64–344 MHz and the post-M input in 2.66–16 MHz. e.g. MSI 4 MHz with M>1 drops the input below spec and the PLL will not lock.
Stale SystemCoreClockUpdate the CMSIS SystemCoreClock global (or call SystemCoreClockUpdate()). SysTick, HAL timeouts and UART baud are all computed from it — leave it at 4 MHz and every delay is 30× too short.
On-the-fly range changeIf you switch Range/boost while already running fast, RM0432 requires briefly forcing HPRE to /2 during the transition. Doing everything from the 4 MHz reset state (as here) avoids this entirely.

120 MHz checklist

  • Enable PWR clock → VOS = Range 1 → clear R1MODE (boost) → wait VOSF = 0
  • FLASH_ACR: LATENCY = 5 (verify read-back) + prefetch/ICEN/DCEN
  • PLL off → PLLCFGR (src, M−1, N, R code, PLLREN) → PLLON → wait PLLRDY
  • HPRE/PPRE1/PPRE2 = /1 → SW = PLL → wait SWS = PLL → set SystemCoreClock