CPU determining its own clock speed - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: April 28, 2023, 10:33:01 am
content: In my project, 32F417, 168MHz, for oddball reasons I have to define the clock speed, 168000000, in two places.This is OK because I've documented itbut has anyone tried to self-determine it?One way would be to use the RTC (32768Hz xtal) together with the CPU doing some timing against the RTC incrementing by say 1 second.Has anyone done this?Obviously it would be stupid to calculate a figure of say 168034567Hz when it is actually 168000000Hz, so maybe one should just have a selection of options and choose one of these. The obvious example would be 168MHz (32F417) against 180MHz (32F4 something else). Then if your production uses both chips, at different max speeds, the same code could be used.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: April 28, 2023, 10:47:54 am
content: Are you running from external source? For internal sources there was a function (in older SPLs at least) calculating core frequency from RCC regs content (take the source freq and mul/div it by all muls and divs in the clock path).But, since we are embedded, #define could’ve been chosen for size/speed reasons.

author: wek
author_rank: Frequent Contributor
timestamp: April 28, 2023, 11:00:11 am
content: > For internal sources there was a function (in older SPLs at least) calculating core frequency from RCC regs content (take the source freq and mul/div it by all muls and divs in the clock path).There's the same in Cube/HAL (which I don't use so don't know its name). It also takes into account the switch which selects primary source to PLL (HSE/HSI) so it anticipates a #define with crystal frequency for HSE.If you prefer to be fancy (e.g. for absolute flexibility even in choice of HSE crystal and in absence of LSE crystal), you can e.g. upon startup measure HSE against HSI by switching system clock to HSI and in TIM11_OR select HSE_RTC as TIM11_CH1 input. Or, if you have LSE, you can measure it against whatever the system (=>timer) clock is, by selecting TIM5_CH4 to be LSE in TIM5_OR.Or just simply keep a variable which holds the system clock frequency and update it whenever you change anything which impacts that frequency.JW

author: hans
author_rank: Super Contributor
timestamp: April 28, 2023, 01:23:34 pm
content: I think STM32s HAL defines a global variable SystemCoreClock which gets updated in SystemInit.SystemInit derives the clock based on the live clock settings, and if HSE is used, the crystal frequency defined during compilation.Namely if HSE is used, the MCU only knows its clock source, divider/PLL settings, and so it needs to know the external input clock to determine the system clock. It is very likely that a STM32F4 at 168 and 180MHz will use different PLL settings as well. Both could run from the same crystal frequency.edit: I'm not sure if for example a STM32F407 and STM32F427 are fully pin and binary compatible. Theoretically you could change BSP routines depending on the chip though. But that then also answers your question w.r.t. the clock being different (or identical if you use the same PLL settings)If you want to measure the oscillator in run-time, then do what @wek described. Crossreference the 2 clocks with HSE/HSI to systemclock and a timer, and you can work it all out. I expect that if you measure long enough it will be pretty accurate. And if not, you could always round up/down the oscillator clocks because commonly people only use round MHz numbers these days (8MHz, 12MHz, 25MHz, etc.)

author: peter-h
author_rank: Super Contributor
timestamp: April 28, 2023, 09:50:39 pm
content: Yes; there is this codeCode:/*** @brief Update SystemCoreClock variable according to Clock Register Values.* The SystemCoreClock variable contains the core clock (HCLK), it can* be used by the user application to setup the SysTick timer or configure* other parameters.** @note Each time the core clock (HCLK) changes, this function must be called* to update SystemCoreClock variable value. Otherwise, any configuration* based on this variable will be incorrect.** @note - The system frequency computed by this function is not the real* frequency in the chip. It is calculated based on the predefined* constant and the selected clock source:** - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)** - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)** - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)* or HSI_VALUE(*) multiplied/divided by the PLL factors.** (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file (default value* 16 MHz) but the real value may vary depending on the variations* in voltage and temperature.** (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file (its value* depends on the application requirements), user has to ensure that HSE_VALUE* is same as the real frequency of the crystal used. Otherwise, this function* may have wrong result.** - The result of this function could be not correct when using fractional* value for HSE crystal.** @param None* @retval None*/void SystemCoreClockUpdate(void){uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;/* Get SYSCLK source -------------------------------------------------------*/tmp = RCC->CFGR & RCC_CFGR_SWS;switch (tmp){case 0x00: /* HSI used as system clock source */SystemCoreClock = HSI_VALUE;break;case 0x04: /* HSE used as system clock source */SystemCoreClock = HSE_VALUE;break;case 0x08: /* PLL used as system clock source *//* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_NSYSCLK = PLL_VCO / PLL_P*/pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;if (pllsource != 0){/* HSE used as PLL clock source */pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);}else{/* HSI used as PLL clock source */pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);}pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;SystemCoreClock = pllvco/pllp;break;default:SystemCoreClock = HSI_VALUE;break;}/* Compute HCLK frequency --------------------------------------------------*//* Get HCLK prescaler */tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];/* HCLK frequency */SystemCoreClock >>= tmp;}I got rid of all that stuff (some of which was done twice) and extracted just the code I needed. This appears to use two #defines HSI_VALUE and HSE_VALUE and it back-calculates the CPU clock from the various register bitfields. That's not really what I was after, and anyway is a horrible way to set things up. But a lot of ST code works stuff out by reading back register fields - registers whichyouset up earlier!I thinkwek'sidea is the most universal.

author: hans
author_rank: Super Contributor
timestamp: April 29, 2023, 08:13:57 am
content: True, but this way the code stays generic and universally applicable even if "an end user" writes its own PLL setup code.In the end, when operated within its limits, the PLL is just a frequency multiplier. If you use different STM32F4 parts, but the same crystal and same PLL settings, then it will output the same frequency.If you want to choose optimum PLL settings in run-time, I guess you could measure HSE first, determine part type from internal CPU ID registers to a target clock speed (e.g. 100, 168 or 180MHz), and then calculate PLL input divider/multiplier settings accordingly.I think this is what STs bootloader also does if it needs to support USB DFU. After all, an end user may use various oscillator frequencies, while the USB peripheral needs a 48/60MHz clock.

author: wek
author_rank: Frequent Contributor
timestamp: April 29, 2023, 08:34:19 am
content: Quote from: hans on April 29, 2023, 08:13:57 am... and it alsofails at it.JW

author: peter-h
author_rank: Super Contributor
timestamp: April 29, 2023, 10:46:22 am
content: QuoteThat's clever - forgot about thatThe bits which need the CPU clock value include hardware-related delays likeCode:// Wait for a time in microseconds__attribute__((noinline))void hang_around_us(uint32_t delay){extern uint32_t SystemCoreClock;delay *= (SystemCoreClock/4100000L);asm volatile ("1: subs %, %, #1 \n"" nop \n"" bne 1b \n":  "+l"(delay));}even though even the above runs some 20% slower (yes slower) if running out of RAM, than FLASH.In most cases one ought to err on the safe side with things like chip select pre- and hold times, but one needs a lot more regression testing then.

author: bugnate
author_rank: Regular Contributor
timestamp: May 02, 2023, 06:18:17 pm
content: Quote from: peter-h on April 28, 2023, 10:33:01 amYes, but for calibration purposes instead of identification. I used exactly this method to calibrate the hilariously bad HSI on a 32F103 product years ago. It was useful only to bump +/- 1 HSI trim level, but it was more than enough for my purposes (and allowed for a faster UART).