This issue is a tracking for the refactoring of the clock configuration and initialization on STM32 cpus.
Current situation
In the current situation, the whole clock configuration is done at board level although a lot parameters are CPU level specific.
Here is the list of clock parameters that doesn't depend on the board configuration:
sysclk source clock: it can be either HSE, HSI, MSI (only on L0,L1,L4,WB families) or PLL but in RIOT only the PLL is used.
the PLL parameters (M,N,P,Q factors or PREDIV,MUL factors, depending on the families
the AHB/APBx dividers
There are other problems with the current clock configuration mechanism: it's not easy to change the parameters.
For example, one would have to write his own config header and use pre-processor conditionals to skip the default values. This custom header would have to define all parameters, even if only a few needs to be tweaked.
For AHB/APBx dividers, there's no connection between the define of the divider value (it must contain the corresponding bitfields to put in the configuration register) and actual output clock of the bus. Example:
https://github.com/RIOT-OS/RIOT/blob/27e4c19748c95105cbd2dc993fb687dbfa5d215a/boards/common/stm32/include/f0/cfg_clock_default.h#L48-L52
Here, CLOCK_APB1 is CLOCK_CORECLOCK divided by 1, but CLOCK_APB1_DIV is not 1, it's the corresponding bitfield. It would be simpler to directly compute CLOCK_APB1 from CLOCK_CORECLOCK and CLOCK_APB1_DIV:
And since APB1 corresponds to HCLK / CLOCK_APB1_DIV, it should even be CLOCK_AHB / CLOCK_APB1_DIV
Another issue with the current design is that CLOCK_CORECLOCK is hardcoded although it could easily be computed automatically, at build time, as a combination of the input clock frequency (either HSI or HSE) and the PLL factors.
The stm32fx.c file is mixing two different families of clocks initialization: those with M/N/P PLL (f2/f4/f7) and those with PREDIV/MUL PLL (F0/F1/F3)
About the selection of the 48MHz clock (used by USB): some 100MHz and 180MHz capable CPUs are forced to run at 96MHz and 168MHz to get a 48MHz clock PLLQ even if it's unused (no USB required for instance).
It is not possible to define the AHB divider to something different than 1, otherwise xtimer test applications are not working properly.
Expected situation
In general, the expected situation should provide a way to easily configure the clocks of a CPU: by easily, we mean no need to know the bitfields in advance and the parameters can be overridden via CFLAGS (command line or Makefile), a custom config header and Kconfig.
The only parameters that should depend on the board configuration are:
whether the board provides an HSE to clock the CPU
if there's one, the frequency of the HSE
whether the board provides an LSE (and eventually it's frequency)
All other parameters should be only exposed at the CPU level: SYSCLK source, PLL parameters, AHB/APBx dividers, MCO sources, enabling and config of 48MHz clocks
Doing this will allow a simpler and centralized clock configuration: less code duplication and more flexibility.
The CLOCK_CORECLOCK and all sub-clock values should be computed at build time with good defaults.
For the 48MHz source clocks, they should only be enabled if required by the application. For the moment, this is only the case when the USB peripheral feature is required.
On F4 and F7 families, several 48MHz clock source are possible (from PLLQ, PLLI2S or PLLSAI). The idea is to fall back to PLLI2S/PLLSAI when PLLQ cannot output 48MHz and to fail at build time if no 48MHz clock is available.
For 100MHz and 180MHz capable CPUs (typically on F4), we should by default configure them to reach their maximum clock frequency by default and adapt it when 48MHz is needed (and then their core clock would decrease to 96/168MHz).
After that rework, the cpu/dist/clk_conf tool will become obsolete and will not longer be needed.
Work to be done
Achieving what's described above is a lot of work and cannot be done in a single PR. To prepare the work needed for Kconfig, all configuration constants will be prefixed by CONFIG_.
Here are the steps needed (not necessary in order, for some there are already opened PRs and some PRs were already merged):
Prerequisites changes:
[x] For each STM32 families, provide the clock configuration of all boards in common headers: see #14891, #14892, #14870 for details on F0/F1/F3/L1.
L0 and L1 specific required changes:
[x] Rework the initialization and configuration of L0 and L1 families. These families are already sharing the same initialization code, but only PLL can be used. #14945
[x] Implement MCO configuration for L0/L1 #15073
[x] Implement 48MHz clock configuration/initialization for L0/L1 #14945
L4/WB specific required changes:
[x] Rework the initialization and configuration of L4 and WB families. #14866
[x] Implement MCO configuration for L4/WB, #15064
[x] Implement 48MHz clock configuration/initialization for L4/WB: #15036
[ ] Implement PLLSAI1/2 clock initialization and configuration (could be inspired by the work done in #14946)
F0/F1/F3 specific required changes:
[x] Rework the initialization and configuration of F0 family: #14921.
Here, since f0 is sharing with F1/F2/F4/F7 the same implementation of the clock initialization (in stmclk_fx.c), we have to split the f0 part from stmclk_fx.c and work on it.
[x] Rework the initialization and configuration of F1 and F3 families: #14923.
Since F0/F1/F3 have a very similar clock configuration (HSE/HSI/PLL possible SYSCLK sources and PREDIV/MUL PLL parameters, only one MCO output), this PR merge their clock initialization code.
[x] Better handle the HSI PLL input that is divided by 2 and cannot be configured by the user (it's hard-wired) on some f0 and f3 cpu lines #15001
[x] Implement MCO configuration for F0/F1/F3 #15078
[ ] Implement 48MHz clock configuration/initialization for F0/F1/F3 (HSI48 on F0 only available on STM32F04x, STM32F07x and STM32F09x lines)
[x] Merge the F0/F1/F3 clock configuration headers #15650
F2/F4/F7 specific required changes:
[x] Rework the initialization and configuration of F2, F4 and F7 families: #14946.
This is a big one because these families can provide several PLL (Main PLL, PLLI2S, PLLSAI), 48MHz possible clock from 2 sources, 2 MCO (MCO1 and MCO2) and CPUs can work at different max clock speeds.
[ ] Remove cpu/dist/clk_conf tool that will be obsolete after #14946 gets merged. Also update the github actions check (will be required by the CI anyway)
[x] Remove 96MHz and 168MHz default clock configuration headers and merge them with the 100MHz and 180MHz headers. 96MHz and 180MHz clock should only be set by default when a 48MHz clock source is required, otherwise the CPU is configured to 100MHz and 180MHz by default. #15043
[x] Manage overdrive mode for high clock speeds (>168MHz on F4 and > 180MHz on F7) #15223
G0/G4 specific required changes:
[ ] Improve 48MHz clock configuration (there are multiple available sources
[x] Implement MCO initialization and configuration #15084
Possible improvements once clock configurations are consistent across STM32 CPU families:
[x] Move all clock configuration headers from boards/ to cpu/ and only keep in boards the default values for HSE and LSE #15273
[ ] CLOCK_CORECLOCK, CLOCK_AHB, CLOCK_APBx should only be defined in stmclk.h
[x] Common ifdefs in the clock configurations could be factorized #15657
[x] Find a way to handle in a generic way good PLL defaults for any value of HSE clock: not possible but custom configuration can be provided at board level (both in periph_conf.h and in Kconfig)
[x] Do some cleanup in stmclk_common.c (in disable_clock_hsi for instance, there's a check on CLOCK_HSE that should be removed) #15259
[x] Replace the CLOCK_LSE with CONFIG_BOARD_HAS_LSE in the periph_rtc implementation and in the configuration headers #15260
[ ] Implement static inline functions to return the core clock, clock_apb, etc
[ ] Add pre-processor checks for the PLL parameters and for the intermediate PLL clock values that should be kept within certain bounds (that depends on families and/or cpu lines)
[ ] Fix the timer issue when CLOCK_AHB_DIV is different from 1
[ ] Add configuration for AHB prescaler (fixed to 1 for the moment). This requires the previous point to be fixed first.
[ ] Implement calibration for internal oscillators (for HSI, LSI, HSI48 for example)
[x] Document the clock configuration #15265
Kconfig: add Kconfig files for configuring the clocks at CPU level
[x] Expose CPU lines config in Kconfig: #14998
[x] Move Kconfig file of stm32gx to cpu/stm32 #15286
[x] F0: #14967
[x] F1/F3: #15001
[x] F2/F4/F7 #15632
[x] L0/L1: #15000
[x] L4/WB: #14968
[ ] MP1
[x] Expose MCO configuration in Kconfig #15706
This is is not exhaustive and is subject to change over time.
Description
This issue is a tracking for the refactoring of the clock configuration and initialization on STM32 cpus.
Current situation
In the current situation, the whole clock configuration is done at board level although a lot parameters are CPU level specific.
Here is the list of clock parameters that doesn't depend on the board configuration:
There are other problems with the current clock configuration mechanism: it's not easy to change the parameters. For example, one would have to write his own config header and use pre-processor conditionals to skip the default values. This custom header would have to define all parameters, even if only a few needs to be tweaked.
For AHB/APBx dividers, there's no connection between the define of the divider value (it must contain the corresponding bitfields to put in the configuration register) and actual output clock of the bus. Example: https://github.com/RIOT-OS/RIOT/blob/27e4c19748c95105cbd2dc993fb687dbfa5d215a/boards/common/stm32/include/f0/cfg_clock_default.h#L48-L52 Here,
CLOCK_APB1
isCLOCK_CORECLOCK
divided by 1, but CLOCK_APB1_DIV is not 1, it's the corresponding bitfield. It would be simpler to directly computeCLOCK_APB1
fromCLOCK_CORECLOCK
andCLOCK_APB1_DIV
:And since APB1 corresponds to
HCLK / CLOCK_APB1_DIV
, it should even beCLOCK_AHB / CLOCK_APB1_DIV
Another issue with the current design is that
CLOCK_CORECLOCK
is hardcoded although it could easily be computed automatically, at build time, as a combination of the input clock frequency (either HSI or HSE) and the PLL factors.The stm32fx.c file is mixing two different families of clocks initialization: those with M/N/P PLL (f2/f4/f7) and those with PREDIV/MUL PLL (F0/F1/F3)
About the selection of the 48MHz clock (used by USB): some 100MHz and 180MHz capable CPUs are forced to run at 96MHz and 168MHz to get a 48MHz clock PLLQ even if it's unused (no USB required for instance).
It is not possible to define the AHB divider to something different than 1, otherwise
xtimer
test applications are not working properly.Expected situation
In general, the expected situation should provide a way to easily configure the clocks of a CPU: by easily, we mean no need to know the bitfields in advance and the parameters can be overridden via CFLAGS (command line or Makefile), a custom config header and Kconfig.
Doing this will allow a simpler and centralized clock configuration: less code duplication and more flexibility. The CLOCK_CORECLOCK and all sub-clock values should be computed at build time with good defaults.
For the 48MHz source clocks, they should only be enabled if required by the application. For the moment, this is only the case when the USB peripheral feature is required. On F4 and F7 families, several 48MHz clock source are possible (from PLLQ, PLLI2S or PLLSAI). The idea is to fall back to PLLI2S/PLLSAI when PLLQ cannot output 48MHz and to fail at build time if no 48MHz clock is available. For 100MHz and 180MHz capable CPUs (typically on F4), we should by default configure them to reach their maximum clock frequency by default and adapt it when 48MHz is needed (and then their core clock would decrease to 96/168MHz).
After that rework, the
cpu/dist/clk_conf
tool will become obsolete and will not longer be needed.Work to be done
Achieving what's described above is a lot of work and cannot be done in a single PR. To prepare the work needed for Kconfig, all configuration constants will be prefixed by
CONFIG_
.Here are the steps needed (not necessary in order, for some there are already opened PRs and some PRs were already merged):
stmclk_fx.c
), we have to split the f0 part fromstmclk_fx.c
and work on it.cpu/dist/clk_conf
tool that will be obsolete after #14946 gets merged. Also update the github actions check (will be required by the CI anyway)CLOCK_CORECLOCK
,CLOCK_AHB
,CLOCK_APBx
should only be defined instmclk.h
disable_clock_hsi
for instance, there's a check on CLOCK_HSE that should be removed) #15259CLOCK_LSE
withCONFIG_BOARD_HAS_LSE
in the periph_rtc implementation and in the configuration headers #15260CLOCK_AHB_DIV
is different from 1cpu/stm32
#15286This is is not exhaustive and is subject to change over time.