all
Chapter 5 of 20

RCC Peripheral — Tiva C

Eslam El Hefny Apr 5, 2025 6 min read
25% done

RCC Peripheral — Tiva C

Overview

The System Control (SYSCTL) block on the TM4C123 manages all clock sources, the PLL, peripheral clock gating, reset control, and power modes. Configuring the clock correctly is the very first thing any firmware does, because every peripheral’s timing depends on knowing the exact system clock frequency.


Beginner Level — What & Why

What is the RCC / System Clock?

Every digital circuit needs a heartbeat — a regular tick that drives all operations. The RCC (Reset and Clock Control, called SYSCTL in TivaWare) configures that heartbeat. You choose the clock source, multiply it with the PLL to reach higher speeds, and then divide the result to get the exact frequency your firmware needs.

Real-World Analogy

Think of the clock system like a car’s gearbox. The engine (crystal oscillator, 16 MHz) runs at a fixed speed. The PLL is like a turbocharger that multiplies that speed to 400 MHz internally. The SYSDIV divider is like the gearbox ratio that reduces it back down to a usable road speed — 80 MHz.

What Problem Does It Solve?

  • Defines the execution speed of all CPU instructions
  • Gates (enables/disables) individual peripheral clocks to save power
  • Provides a stable, accurate frequency for baud-rate generation, PWM periods, and timer intervals

Key Terms

Term Meaning
MOSC Main Oscillator — external 16 MHz crystal on LaunchPad
PIOSC Precision Internal OSC — 16 MHz ±3%, no crystal needed
PLL Phase-Locked Loop — multiplies clock to 400 MHz
SYSDIV System Divider — divides PLL output to final clock
RCGC Running Clock Gate Control — enables peripheral clocks
PRGPIO Peripheral Ready register — confirms clock is stable

Intermediate Level — How It Works

Clock Tree

16 MHz Crystal (MOSC)
        |
       PLL × 25 = 400 MHz (internal VCO)
        |
    ÷ 2 (fixed predivide) = 200 MHz
        |
    SYSDIV (1–16) → 200 / SYSDIV = system clock

Example: SYSDIV = 2.5 → 200 / 2.5 = 80 MHz

Key Registers

Register Address Description
RCC 0x400FE060 Run-Mode Clock Config (use when ≤ 16 MHz)
RCC2 0x400FE070 Extended RCC — needed for PLL and SYSDIV > 16
MOSCCTL 0x400FE07C Main Oscillator Control
PLLSTAT 0x400FE168 PLL lock status (bit 0 = locked)
RCGCGPIO 0x400FE608 GPIO clock gate (bits 0-5 = Port A-F)
RCGCUART 0x400FE618 UART clock gate
RCGCTIMER 0x400FE604 Timer clock gate
RCGCADC 0x400FE638 ADC clock gate
PRGPIO 0x400FEA08 GPIO peripheral ready flags

TivaWare Clock Configuration

/* Set 80 MHz: PLL from 16 MHz crystal, divide by 2.5 */
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | // ÷2.5 → 80 MHz
               SYSCTL_USE_PLL     | // use PLL (400 MHz)
               SYSCTL_OSC_MAIN    | // use main oscillator
               SYSCTL_XTAL_16MHZ);  // 16 MHz crystal

/* Verify the result */
uint32_t ui32SysClock = SysCtlClockGet();  // should return 80000000

/* Enable peripheral clocks */
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

/* Wait until peripherals are ready */
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

Common SYSCTL_SYSDIV Macros

Macro Divider Resulting Clock
SYSCTL_SYSDIV_1 1 200 MHz (too fast, limited by flash wait states)
SYSCTL_SYSDIV_2_5 2.5 80 MHz (recommended maximum)
SYSCTL_SYSDIV_4 4 50 MHz
SYSCTL_SYSDIV_5 5 40 MHz
SYSCTL_SYSDIV_8 8 25 MHz
SYSCTL_SYSDIV_10 10 20 MHz
SYSCTL_SYSDIV_16 16 12.5 MHz

Advanced Level — Deep Dive

Bare-Metal PLL Configuration

#include "inc/hw_types.h"
#include "inc/hw_sysctl.h"

void Clock_Init80MHz(void)
{
    /* Step 1: Enable MOSC (main oscillator) */
    /* MOSCCTL register: clear NOXTAL bit to enable 16 MHz crystal */
    HWREG(SYSCTL_MOSCCTL) &= ~(SYSCTL_MOSCCTL_NOXTAL);
    /* Optionally set crystal verification */
    HWREG(SYSCTL_MOSCCTL) |= SYSCTL_MOSCCTL_CVAL;

    /* Step 2: Switch to MOSC temporarily while we configure PLL */
    /* RCC2: set USERCC2 so RCC2 overrides RCC */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_USERCC2;

    /* Bypass PLL (use oscillator directly while PLL locks) */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_BYPASS2;

    /* Select MOSC as clock source */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_OSCSRC2_M;
    HWREG(SYSCTL_RCC2) |=  SYSCTL_RCC2_OSCSRC2_MO;  // main osc

    /* Clear XTAL field and set to 16 MHz in RCC */
    HWREG(SYSCTL_RCC) &= ~SYSCTL_RCC_XTAL_M;
    HWREG(SYSCTL_RCC) |=  SYSCTL_RCC_XTAL_16MHZ;

    /* Step 3: Enable PLL by clearing PWRDN bit */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_PWRDN2;

    /* Step 4: Set divisor for 80 MHz using DIV400 trick
     * With DIV400=1, effective divisor = (SYSDIV2 + 1 + SYSDIV2LSB*0.5)
     * For 80 MHz: 400/(4+1) = 80 → SYSDIV2 = 4, LSB = 1 */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_DIV400;

    /* Clear SYSDIV2 field [28:23] */
    HWREG(SYSCTL_RCC2) &= ~(SYSCTL_RCC2_SYSDIV2_M | SYSCTL_RCC2_SYSDIV2LSB);

    /* Set SYSDIV2 = 4 (bits [28:23]) and SYSDIV2LSB = 1 (bit 22) */
    HWREG(SYSCTL_RCC2) |= (4 << 23) | SYSCTL_RCC2_SYSDIV2LSB;

    /* Step 5: Wait for PLL to lock */
    while (!(HWREG(SYSCTL_PLLSTAT) & SYSCTL_PLLSTAT_LOCK));

    /* Step 6: Switch CPU to use the PLL output */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_BYPASS2;
}

Peripheral Clock Gating (Bare Metal)

/* Enable GPIO Port F clock: bit 5 of RCGCGPIO */
HWREG(SYSCTL_RCGCGPIO) |= (1U << 5);

/* Busy-wait until the peripheral is ready */
while (!(HWREG(SYSCTL_PRGPIO) & (1U << 5)));

SysCtlClockSet vs SysCtlClockSetxtal

SysCtlClockSet is the legacy function used on TM4C123. It accepts combined flags:

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
               SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

For TM4C129x (different device), use SysCtlClockFreqSet which takes a target frequency directly. Do not mix them.

Power and Sleep Modes

/* Enable peripheral in Sleep mode (keeps clock while CPU sleeps) */
SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);

/* Enable peripheral in Deep Sleep mode */
SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_UART0);

/* Enter Sleep mode (WFI) — wakes on any interrupt */
SysCtlSleep();

/* Enter Deep Sleep mode — wakes on specific interrupts */
SysCtlDeepSleep();

Gotchas

  • Never read from a peripheral before enabling its clock — bus fault will occur.
  • PLLSTAT must show LOCK before switching away from bypass — otherwise you get an unstable clock.
  • SysCtlClockGet() relies on the same flags you passed to SysCtlClockSet() — it does not read hardware. If you use bare-metal setup, SysCtlClockGet() will return wrong values.
  • Flash wait states are automatically managed by the TivaWare SysCtlClockSet() call.

Step-by-Step Example

/*
 * rcc_clock_demo.c
 * Configures 80 MHz clock, reads it back, and blinks at exact 1Hz
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"

int main(void)
{
    uint32_t ui32SysClock;

    /* Step 1: Configure 80 MHz system clock from 16 MHz crystal + PLL */
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* Step 2: Read back actual clock frequency */
    ui32SysClock = SysCtlClockGet();
    /* ui32SysClock should equal 80,000,000 */

    /* Step 3: Enable Port F clock for LED */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    /* Step 4: Configure PF2 (Blue LED) as output */
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

    /* Step 5: Blink at exactly 1 Hz using SysCtlDelay
     * SysCtlDelay(n) burns 3n CPU cycles.
     * Half period = 500 ms = 0.5 * 80,000,000 cycles = 40,000,000 cycles
     * SysCtlDelay count = 40,000,000 / 3 = 13,333,333 */
    uint32_t ui32HalfPeriod = ui32SysClock / 6;  // = 13,333,333

    while (1)
    {
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2); // ON
        SysCtlDelay(ui32HalfPeriod);                           // 500 ms

        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);          // OFF
        SysCtlDelay(ui32HalfPeriod);                           // 500 ms
    }
}

Summary

Key Point Details
Main oscillator 16 MHz crystal (MOSC) on LaunchPad
PLL VCO 400 MHz internal
Recommended clock 80 MHz (SYSDIV_2_5)
SysCtlClockSet Configures PLL, source, and divider in one call
SysCtlClockGet Returns current system clock frequency
Peripheral gating SysCtlPeripheralEnable / Disable
Peripheral ready SysCtlPeripheralReady polls PRGPIO/PRUART etc.
PLLSTAT Must show LOCK before removing bypass

Next Chapter

SysTick Peripheral

Share: