Saturday, January 18, 2014

Overclocking the STM32F4

I've been doing some tests with the STM32F407 to see how fast it can go, STMicro has released an almost identical one that runs at 180MHz, is it a marketing thing ? will they release a 200MHz version in a few months? who cares, anyway, I was able to run the STM32F407 at 240MHz without any "obvious" problems, in addition to overclocking, the code listed below lets you set some different frequencies, which could be useful for frequency scaling.

On the STM32F4 the clocks are controlled via the RCC (Reset and Clock Control) block, it's easy enough to change the frequency, the tricky part however, is understanding all the different dividers and getting them right. According to the datasheet, the following are the maximum clock frequencies for the core and peripheral buses:
SYSCLK: 168MHz
PLLC48: 48MHz (feeds the USB OTG FS and RNG)
APB1 clock: 42MHz
APB2 clock: 84MHz
Based on that, I used multiples of 42MHz to get the maximum possible frequencies for the peripheral buses (APB1 and APB2) for frequencies lower than 168MHz (this doesn't always result in the maximum USB frequency, which is required to get the full 12Mbps), for frequencies higher than 168MHz, I used 200MHz and 240MHz mainly because they are convenient to my application, you might want to use different frequencies based on yours:
enum sysclk_freq {
    SYSCLK_42_MHZ=0,
    SYSCLK_84_MHZ,
    SYSCLK_168_MHZ,
    SYSCLK_200_MHZ,
    SYSCLK_240_MHZ,
};

void rcc_set_frequency(enum sysclk_freq freq)
{
    int freqs[]   = {42, 84, 168, 200, 240};

    /* USB freqs: 42MHz, 42Mhz, 48MHz, 50MHz, 48MHz */
    int pll_div[] = {2, 4, 7, 10, 10}; 

    /* PLL_VCO = (HSE_VALUE / PLL_M) * PLL_N */
    /* SYSCLK = PLL_VCO / PLL_P */
    /* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */ 
    uint32_t PLL_P = 2;
    uint32_t PLL_N = freqs[freq] * 2;
    uint32_t PLL_M = (HSE_VALUE/1000000);
    uint32_t PLL_Q = pll_div[freq];

    RCC_DeInit();

    /* Enable HSE osscilator */
    RCC_HSEConfig(RCC_HSE_ON);

    if (RCC_WaitForHSEStartUp() == ERROR) {
        return;
    }

    /* Configure PLL clock M, N, P, and Q dividers */
    RCC_PLLConfig(RCC_PLLSource_HSE, PLL_M, PLL_N, PLL_P, PLL_Q);

    /* Enable PLL clock */
    RCC_PLLCmd(ENABLE);

    /* Wait until PLL clock is stable */
    while ((RCC->CR & RCC_CR_PLLRDY) == 0);

    /* Set PLL_CLK as system clock source SYSCLK */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Set AHB clock divider */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* Set APBx clock dividers */
    switch (freq) {
        /* Max freq APB1: 42MHz APB2: 84MHz */
        case SYSCLK_42_MHZ:
            RCC_PCLK1Config(RCC_HCLK_Div1); /* 42MHz */
            RCC_PCLK2Config(RCC_HCLK_Div1); /* 42MHz */
            break;
        case SYSCLK_84_MHZ:
            RCC_PCLK1Config(RCC_HCLK_Div2); /* 42MHz */
            RCC_PCLK2Config(RCC_HCLK_Div1); /* 84MHz */
            break;
        case SYSCLK_168_MHZ:
            RCC_PCLK1Config(RCC_HCLK_Div4); /* 42MHz */
            RCC_PCLK2Config(RCC_HCLK_Div2); /* 84MHz */
            break;
        case SYSCLK_200_MHZ:
            RCC_PCLK1Config(RCC_HCLK_Div4); /* 50MHz */
            RCC_PCLK2Config(RCC_HCLK_Div2); /* 100MHz */
            break;
        case SYSCLK_240_MHZ:
            RCC_PCLK1Config(RCC_HCLK_Div4); /* 60MHz */
            RCC_PCLK2Config(RCC_HCLK_Div2); /* 120MHz */
            break;
    }

    /* Update SystemCoreClock variable */ 
    SystemCoreClockUpdate();
}

Note: after calling this function, you will probably need to re-enable all the clocks ...

Note on Overclocking:
There's no telling if overclocking will always work, it might fail at some temperature, critical path or just randomly, but it is nice to know that you have the option to run (burn?) the micro at higher speeds if needed... One thing I can confirm though, is that it doesn't overheat too much, obviously touching the micro with your finger tip is not an accurate way to determine that, so I took this a step further and made a series of tests using the internal core temperature sensor.

I ran the micro at different frequencies while collecting samples from the internal temperature sensor, which is connected to one of the ADC's channels, for each frequency I collected a number of samples and then I plotted the average vs the frequencies and here's the result:
One important thing to note here, the internal temperature sensor readings varies from chip to chip up to 45 degrees, which means those are NOT absolute temperature values but should only be used to detect temperature variations. Now, the conclusion, it looks like overclocking the STM32F4 from 168MHz to 240MHz increases the core temperature by ~4 degrees.
Read more ...