diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index c543f01598..92f3648e1c 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -582,7 +582,7 @@ CMSIS_MCU_HDR = $(CMSIS_DIR)/$(CMSIS_MCU_LOWER).h modmachine.c: $(GEN_PLLFREQTABLE_HDR) $(GEN_PLLFREQTABLE_HDR): $(PLLVALUES) | $(HEADER_BUILD) $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(PLLVALUES) -c file:$(BOARD_DIR)/stm32$(MCU_SERIES)xx_hal_conf.h > $@ + $(Q)$(PYTHON) $(PLLVALUES) -c $(if $(filter $(MCU_SERIES),f7),--relax-pll48,) file:$(BOARD_DIR)/stm32$(MCU_SERIES)xx_hal_conf.h > $@ $(BUILD)/modstm.o: $(GEN_STMCONST_HDR) # Use a pattern rule here so that make will only call make-stmconst.py once to diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index befd6cfa0d..4b090c455b 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -39,38 +39,49 @@ def compute_pll(hse, sys): return None # improved version that doesn't require N/M to be an integer -def compute_pll2(hse, sys): +def compute_pll2(hse, sys, relax_pll48): # Loop over the allowed values of P, looking for a valid PLL configuration # that gives the desired "sys" frequency. We use floats for P to force # floating point arithmetic on Python 2. + fallback = None for P in (2.0, 4.0, 6.0, 8.0): - Q = sys * P / 48 - # Q must be an integer in a set range - if not (close_int(Q) and 2 <= Q <= 15): - continue NbyM = sys * P / hse # VCO_OUT must be between 192MHz and 432MHz if not (192 <= hse * NbyM <= 432): continue - # compute M - M = 192 // NbyM # starting value - while hse > 2 * M or NbyM * M < 192 or not close_int(NbyM * M): + # scan M + M = int(192 // NbyM) # starting value + while 2 * M < hse: M += 1 # VCO_IN must be between 1MHz and 2MHz (2MHz recommended) - if not (M <= hse): - continue - # compute N - N = NbyM * M - # N must be an integer - if not close_int(N): - continue - # N is restricted - if not (192 <= N <= 432): - continue - # found valid values - return (M, N, P, Q) - # no valid values found - return None + for M in range(M, hse + 1): + if NbyM * M < 191.99 or not close_int(NbyM * M): + continue + # compute N + N = NbyM * M + # N must be an integer + if not close_int(N): + continue + # N is restricted + if not (192 <= N <= 432): + continue + Q = (sys * P / 48) + # Q must be an integer in a set range + if not (2 <= Q <= 15): + continue + if not close_int(Q): + if int(M) == int(hse) and fallback is None: + # the values don't give 48MHz on PLL48 but are otherwise OK + fallback = M, N, P, int(Q) + continue + # found valid values + return (M, N, P, Q) + if relax_pll48: + # might have found values which don't give 48MHz on PLL48 + return fallback + else: + # no valid values found which give 48MHz on PLL48 + return None def compute_derived(hse, pll): M, N, P, Q = pll @@ -125,9 +136,17 @@ def main(): argv = sys.argv[1:] c_table = False - if argv[0] == '-c': - c_table = True - argv.pop(0) + relax_pll48 = False + + while True: + if argv[0] == '-c': + c_table = True + argv.pop(0) + elif argv[0] == '--relax-pll48': + relax_pll48 = True + argv.pop(0) + else: + break if len(argv) != 1: print("usage: pllvalues.py [-c] ") @@ -150,8 +169,8 @@ def main(): hse = int(argv[0]) valid_plls = [] - for sysclk in range(1, 217): - pll = compute_pll2(hse, sysclk) + for sysclk in range(2, 217, 2): + pll = compute_pll2(hse, sysclk, relax_pll48) if pll is not None: verify_pll(hse, pll) valid_plls.append((sysclk, pll)) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 3da85c1876..6e0c086052 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -324,6 +324,9 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { // default PLL parameters that give 48MHz on PLL48CK uint32_t m = HSE_VALUE / 1000000, n = 336, p = 2, q = 7; uint32_t sysclk_source; + #if defined(STM32F7) + bool need_pllsai = false; + #endif // search for a valid PLL configuration that keeps USB at 48MHz for (const uint16_t *pll = &pll_freq_table[MP_ARRAY_SIZE(pll_freq_table) - 1]; pll >= &pll_freq_table[0]; --pll) { @@ -345,6 +348,9 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { uint32_t vco_out = sys * p; n = vco_out * m / (HSE_VALUE / 1000000); q = vco_out / 48; + #if defined(STM32F7) + need_pllsai = vco_out % 48 != 0; + #endif goto set_clk; } } @@ -394,6 +400,11 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { goto fail; } + #if defined(STM32F7) + // Turn PLLSAI off because we are changing PLLM (which drives PLLSAI) + RCC->CR &= ~RCC_CR_PLLSAION; + #endif + // re-configure PLL // even if we don't use the PLL for the system clock, we still need it for USB, RNG and SDIO RCC_OscInitTypeDef RCC_OscInitStruct; @@ -409,6 +420,28 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { goto fail; } + #if defined(STM32F7) + if (need_pllsai) { + // Configure PLLSAI at 48MHz for those peripherals that need this freq + const uint32_t pllsain = 192; + const uint32_t pllsaip = 4; + const uint32_t pllsaiq = 2; + RCC->PLLSAICFGR = pllsaiq << RCC_PLLSAICFGR_PLLSAIQ_Pos + | (pllsaip / 2 - 1) << RCC_PLLSAICFGR_PLLSAIP_Pos + | pllsain << RCC_PLLSAICFGR_PLLSAIN_Pos; + RCC->CR |= RCC_CR_PLLSAION; + uint32_t ticks = mp_hal_ticks_ms(); + while (!(RCC->CR & RCC_CR_PLLSAIRDY)) { + if (mp_hal_ticks_ms() - ticks > 200) { + goto fail; + } + } + RCC->DCKCFGR2 |= RCC_DCKCFGR2_CK48MSEL; + } else { + RCC->DCKCFGR2 &= ~RCC_DCKCFGR2_CK48MSEL; + } + #endif + // set PLL as system clock source if wanted if (sysclk_source == RCC_SYSCLKSOURCE_PLLCLK) { uint32_t flash_latency; diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index 0cf0753bdf..5d9a1b6622 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -371,6 +371,13 @@ void SystemInit(void) */ void SystemClock_Config(void) { + #if defined(STM32F7) + // The DFU bootloader changes the clocksource register from its default power + // on reset value, so we set it back here, so the clocksources are the same + // whether we were started from DFU or from a power on reset. + RCC->DCKCFGR2 = 0; + #endif + RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; #if defined(STM32H7) @@ -506,6 +513,28 @@ void SystemClock_Config(void) __fatal_error("HAL_RCC_OscConfig"); } + #if defined(STM32F7) + uint32_t vco_out = RCC_OscInitStruct.PLL.PLLN * (HSE_VALUE / 1000000) / RCC_OscInitStruct.PLL.PLLM; + bool need_pllsai = vco_out % 48 != 0; + if (need_pllsai) { + // Configure PLLSAI at 48MHz for those peripherals that need this freq + const uint32_t pllsain = 192; + const uint32_t pllsaip = 4; + const uint32_t pllsaiq = 2; + RCC->PLLSAICFGR = pllsaiq << RCC_PLLSAICFGR_PLLSAIQ_Pos + | (pllsaip / 2 - 1) << RCC_PLLSAICFGR_PLLSAIP_Pos + | pllsain << RCC_PLLSAICFGR_PLLSAIN_Pos; + RCC->CR |= RCC_CR_PLLSAION; + uint32_t ticks = mp_hal_ticks_ms(); + while (!(RCC->CR & RCC_CR_PLLSAIRDY)) { + if (mp_hal_ticks_ms() - ticks > 200) { + __fatal_error("PLLSAIRDY timeout"); + } + } + RCC->DCKCFGR2 |= RCC_DCKCFGR2_CK48MSEL; + } + #endif + #if defined(STM32H7) /* PLL3 for USB Clock */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; @@ -554,13 +583,6 @@ void SystemClock_Config(void) HAL_PWREx_EnableUSBVoltageDetector(); #endif -#if defined(STM32F7) - // The DFU bootloader changes the clocksource register from its default power - // on reset value, so we set it back here, so the clocksources are the same - // whether we were started from DFU or from a power on reset. - - RCC->DCKCFGR2 = 0; -#endif #if defined(STM32L4) // Enable MSI-Hardware auto calibration mode with LSE HAL_RCCEx_EnableMSIPLLMode();