diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 96f10fe98a..11db9dfbab 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -649,7 +649,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 $(if $(filter $(MCU_SERIES),f7),--relax-pll48,) file:$(BOARD_DIR)/stm32$(MCU_SERIES)xx_hal_conf.h > $@ + $(Q)$(PYTHON) $(PLLVALUES) -c -m $(MCU_SERIES) 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 4a85b5478a..2d64876b46 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -7,6 +7,36 @@ for the machine.freq() function. from __future__ import print_function import re +class MCU: + def __init__(self, range_sysclk, range_m, range_n, range_p, range_q, range_vco_in, range_vco_out): + self.range_sysclk = range_sysclk + self.range_m = range_m + self.range_n = range_n + self.range_p = range_p + self.range_q = range_q + self.range_vco_in = range_vco_in + self.range_vco_out = range_vco_out + +mcu_default = MCU( + range_sysclk=range(2, 216 + 1, 2), + range_m=range(2, 63 + 1), + range_n=range(192, 432 + 1), + range_p=range(2, 8 + 1, 2), + range_q=range(2, 15 + 1), + range_vco_in=range(1, 2 + 1), + range_vco_out=range(192, 432 + 1), +) + +mcu_h7 = MCU( + range_sysclk=range(2, 400 + 1, 2), # above 400MHz currently unsupported + range_m=range(1, 63 + 1), + range_n=range(4, 512 + 1), + range_p=range(2, 128 + 1, 2), + range_q=range(1, 128 + 1), + range_vco_in=range(1, 16 + 1), + range_vco_out=range(150, 960 + 1), # 150-420=medium, 192-960=wide +) + def close_int(x): return abs(x - round(x)) < 0.01 @@ -42,41 +72,40 @@ def compute_pll(hse, sys): # improved version that doesn't require N/M to be an integer 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. + # that gives the desired "sys" frequency. fallback = None - for P in (2.0, 4.0, 6.0, 8.0): - NbyM = sys * P / hse + for P in mcu.range_p: # VCO_OUT must be between 192MHz and 432MHz - if not (192 <= hse * NbyM <= 432): + if not sys * P in mcu.range_vco_out: continue + NbyM = float(sys * P) / hse # float for Python 2 # scan M - M = int(192 // NbyM) # starting value - while 2 * M < hse: - M += 1 - # VCO_IN must be between 1MHz and 2MHz (2MHz recommended) - for M in range(M, hse + 1): - if NbyM * M < 191.99 or not close_int(NbyM * M): - continue + M_min = mcu.range_n[0] // int(round(NbyM)) # starting value + while mcu.range_vco_in[-1] * M_min < hse: + M_min += 1 + # VCO_IN must be >=1MHz, but higher is better for stability so start high (low M) + for M in range(M_min, hse + 1): # compute N N = NbyM * M # N must be an integer if not close_int(N): continue + N = round(N) # N is restricted - if not (192 <= N <= 432): + if N not in mcu.range_n: continue - Q = (sys * P / 48) + Q = float(sys * P) / 48 # float for Python 2 # Q must be an integer in a set range - if not (2 <= Q <= 15): + if close_int(Q) and round(Q) in mcu.range_q: + # found valid values + return (M, N, P, Q) + # Re-try Q to get at most 48MHz + Q = (sys * P + 47) // 48 + if Q not in mcu.range_q: 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 fallback is None: + # the values don't give 48MHz on PLL48 but are otherwise OK + fallback = M, N, P, Q if relax_pll48: # might have found values which don't give 48MHz on PLL48 return fallback @@ -85,6 +114,7 @@ def compute_pll2(hse, sys, relax_pll48): return None def compute_derived(hse, pll): + hse = float(hse) # float for Python 2 M, N, P, Q = pll vco_in = hse / M vco_out = hse * N / M @@ -103,16 +133,16 @@ def verify_pll(hse, pll): assert close_int(Q) # verify range - assert 2 <= M <= 63 - assert 192 <= N <= 432 - assert P in (2, 4, 6, 8) - assert 2 <= Q <= 15 - assert 1 <= vco_in <= 2 - assert 192 <= vco_out <= 432 + assert M in mcu.range_m + assert N in mcu.range_n + assert P in mcu.range_p + assert Q in mcu.range_q + assert mcu.range_vco_in[0] <= vco_in <= mcu.range_vco_in[-1] + assert mcu.range_vco_out[0] <= vco_out <= mcu.range_vco_out[-1] def compute_pll_table(source_clk, relax_pll48): valid_plls = [] - for sysclk in range(2, 217, 2): + for sysclk in mcu.range_sysclk: pll = compute_pll2(source_clk, sysclk, relax_pll48) if pll is not None: verify_pll(source_clk, pll) @@ -121,10 +151,34 @@ def compute_pll_table(source_clk, relax_pll48): def generate_c_table(hse, valid_plls): valid_plls.sort() + if mcu.range_sysclk[-1] <= 0xff and mcu.range_m[-1] <= 0x3f and mcu.range_p[-1] // 2 - 1 <= 0x3: + typedef = 'uint16_t' + sys_mask = 0xff + m_shift = 10 + m_mask = 0x3f + p_shift = 8 + p_mask = 0x3 + else: + typedef = 'uint32_t' + sys_mask = 0xffff + m_shift = 24 + m_mask = 0xff + p_shift = 16 + p_mask = 0xff + print("#define PLL_FREQ_TABLE_SYS(pll) ((pll) & %d)" % (sys_mask,)) + print("#define PLL_FREQ_TABLE_M(pll) (((pll) >> %d) & %d)" % (m_shift, m_mask)) + print("#define PLL_FREQ_TABLE_P(pll) (((((pll) >> %d) & %d) + 1) * 2)" % (p_shift, p_mask)) + print("typedef %s pll_freq_table_t;" % (typedef,)) print("// (M, P/2-1, SYS) values for %u MHz source" % hse) - print("static const uint16_t pll_freq_table[%u] = {" % len(valid_plls)) + print("static const pll_freq_table_t pll_freq_table[%u] = {" % (len(valid_plls),)) for sys, (M, N, P, Q) in valid_plls: - print(" (%u << 10) | (%u << 8) | %u," % (M, P // 2 - 1, sys)) + print(" (%u << %u) | (%u << %u) | %u," % (M, m_shift, P // 2 - 1, p_shift, sys), end='') + if M >= 2: + vco_in, vco_out, pllck, pll48ck = compute_derived(hse, (M, N, P, Q)) + print(" // M=%u N=%u P=%u Q=%u vco_in=%.2f vco_out=%.2f pll48=%.2f" + % (M, N, P, Q, vco_in, vco_out, pll48ck), end='' + ) + print() print("};") def print_table(hse, valid_plls): @@ -157,6 +211,7 @@ def search_header_for_hsx_values(filename, vals): return vals def main(): + global mcu global out_format # parse input args @@ -164,7 +219,7 @@ def main(): argv = sys.argv[1:] c_table = False - relax_pll48 = False + mcu_series = 'f4' hse = None hsi = None @@ -172,14 +227,14 @@ def main(): if argv[0] == '-c': c_table = True argv.pop(0) - elif argv[0] == '--relax-pll48': - relax_pll48 = True + elif argv[0] == '-m': argv.pop(0) + mcu_series = argv.pop(0).lower() else: break if len(argv) != 1: - print("usage: pllvalues.py [-c] ") + print("usage: pllvalues.py [-c] [-m ] ") sys.exit(1) if argv[0].startswith("file:"): @@ -194,6 +249,15 @@ def main(): # HSE given directly as an integer hse = int(argv[0]) + # Select MCU parameters + if mcu_series == 'h7': + mcu = mcu_h7 + else: + mcu = mcu_default + + # Relax constraight on PLLQ being 48MHz on F7 and H7 MCUs, which have separate PLLs for 48MHz + relax_pll48 = mcu_series in ('f7', 'h7') + hse_valid_plls = compute_pll_table(hse, relax_pll48) if hsi is not None: hsi_valid_plls = compute_pll_table(hsi, relax_pll48) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 7db8e29643..19f58a98db 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -310,6 +310,11 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { #else mp_int_t sysclk = mp_obj_get_int(args[0]); mp_int_t ahb = sysclk; + #if defined (STM32H7) + if (ahb > 200000000) { + ahb /= 2; + } + #endif mp_int_t apb1 = ahb / 4; mp_int_t apb2 = ahb / 2; if (n_args > 1) { diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 4c40cffb60..9619e0ea47 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -93,12 +93,48 @@ void powerctrl_check_enter_bootloader(void) { #if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) +typedef struct _sysclk_scaling_table_entry_t { + uint16_t mhz; + uint16_t value; +} sysclk_scaling_table_entry_t; + +#if defined(STM32F7) +STATIC const sysclk_scaling_table_entry_t volt_scale_table[] = { + { 151, PWR_REGULATOR_VOLTAGE_SCALE3 }, + { 180, PWR_REGULATOR_VOLTAGE_SCALE2 }, + // Above 180MHz uses default PWR_REGULATOR_VOLTAGE_SCALE1 +}; +#elif defined(STM32H7) +STATIC const sysclk_scaling_table_entry_t volt_scale_table[] = { + // See table 55 "Kernel clock distribution overview" of RM0433. + {200, PWR_REGULATOR_VOLTAGE_SCALE3}, + {300, PWR_REGULATOR_VOLTAGE_SCALE2}, + // Above 300MHz uses default PWR_REGULATOR_VOLTAGE_SCALE1 + // (above 400MHz needs special handling for overdrive, currently unsupported) +}; +#endif + +STATIC int powerctrl_config_vos(uint32_t sysclk_mhz) { + #if defined(STM32F7) || defined(STM32H7) + uint32_t volt_scale = PWR_REGULATOR_VOLTAGE_SCALE1; + for (int i = 0; i < MP_ARRAY_SIZE(volt_scale_table); ++i) { + if (sysclk_mhz <= volt_scale_table[i].mhz) { + volt_scale = volt_scale_table[i].value; + break; + } + } + if (HAL_PWREx_ControlVoltageScaling(volt_scale) != HAL_OK) { + return -MP_EIO; + } + #endif + return 0; +} + // Assumes that PLL is used as the SYSCLK source int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai) { uint32_t flash_latency; #if defined(STM32F7) - if (need_pllsai) { // Configure PLLSAI at 48MHz for those peripherals that need this freq // (calculation assumes it can get an integral value of PLLSAIN) @@ -118,20 +154,16 @@ int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk } RCC->DCKCFGR2 |= RCC_DCKCFGR2_CK48MSEL; } + #endif // If possible, scale down the internal voltage regulator to save power - uint32_t volt_scale; - if (sysclk_mhz <= 151) { - volt_scale = PWR_REGULATOR_VOLTAGE_SCALE3; - } else if (sysclk_mhz <= 180) { - volt_scale = PWR_REGULATOR_VOLTAGE_SCALE2; - } else { - volt_scale = PWR_REGULATOR_VOLTAGE_SCALE1; - } - if (HAL_PWREx_ControlVoltageScaling(volt_scale) != HAL_OK) { - return -MP_EIO; + int ret = powerctrl_config_vos(sysclk_mhz); + if (ret) { + return ret; } + #if defined(STM32F7) + // These flash_latency values assume a supply voltage between 2.7V and 3.6V if (sysclk_mhz <= 30) { flash_latency = FLASH_LATENCY_0; @@ -172,6 +204,17 @@ int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk #if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) STATIC uint32_t calc_ahb_div(uint32_t wanted_div) { + #if defined(STM32H7) + if (wanted_div <= 1) { return RCC_HCLK_DIV1; } + else if (wanted_div <= 2) { return RCC_HCLK_DIV2; } + else if (wanted_div <= 4) { return RCC_HCLK_DIV4; } + else if (wanted_div <= 8) { return RCC_HCLK_DIV8; } + else if (wanted_div <= 16) { return RCC_HCLK_DIV16; } + else if (wanted_div <= 64) { return RCC_HCLK_DIV64; } + else if (wanted_div <= 128) { return RCC_HCLK_DIV128; } + else if (wanted_div <= 256) { return RCC_HCLK_DIV256; } + else { return RCC_HCLK_DIV512; } + #else if (wanted_div <= 1) { return RCC_SYSCLK_DIV1; } else if (wanted_div <= 2) { return RCC_SYSCLK_DIV2; } else if (wanted_div <= 4) { return RCC_SYSCLK_DIV4; } @@ -181,14 +224,35 @@ STATIC uint32_t calc_ahb_div(uint32_t wanted_div) { else if (wanted_div <= 128) { return RCC_SYSCLK_DIV128; } else if (wanted_div <= 256) { return RCC_SYSCLK_DIV256; } else { return RCC_SYSCLK_DIV512; } + #endif } -STATIC uint32_t calc_apb_div(uint32_t wanted_div) { +STATIC uint32_t calc_apb1_div(uint32_t wanted_div) { + #if defined(STM32H7) + if (wanted_div <= 1) { return RCC_APB1_DIV1; } + else if (wanted_div <= 2) { return RCC_APB1_DIV2; } + else if (wanted_div <= 4) { return RCC_APB1_DIV4; } + else if (wanted_div <= 8) { return RCC_APB1_DIV8; } + else { return RCC_APB1_DIV16; } + #else if (wanted_div <= 1) { return RCC_HCLK_DIV1; } else if (wanted_div <= 2) { return RCC_HCLK_DIV2; } else if (wanted_div <= 4) { return RCC_HCLK_DIV4; } else if (wanted_div <= 8) { return RCC_HCLK_DIV8; } - else { return RCC_SYSCLK_DIV16; } + else { return RCC_HCLK_DIV16; } + #endif +} + +STATIC uint32_t calc_apb2_div(uint32_t wanted_div) { + #if defined(STM32H7) + if (wanted_div <= 1) { return RCC_APB2_DIV1; } + else if (wanted_div <= 2) { return RCC_APB2_DIV2; } + else if (wanted_div <= 4) { return RCC_APB2_DIV4; } + else if (wanted_div <= 8) { return RCC_APB2_DIV8; } + else { return RCC_APB2_DIV16; } + #else + return calc_apb1_div(wanted_div); + #endif } int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2) { @@ -207,11 +271,11 @@ int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t // Search for a valid PLL configuration that keeps USB at 48MHz uint32_t sysclk_mhz = sysclk / 1000000; - for (const uint16_t *pll = &pll_freq_table[MP_ARRAY_SIZE(pll_freq_table) - 1]; pll >= &pll_freq_table[0]; --pll) { - uint32_t sys = *pll & 0xff; + for (const pll_freq_table_t *pll = &pll_freq_table[MP_ARRAY_SIZE(pll_freq_table) - 1]; pll >= &pll_freq_table[0]; --pll) { + uint32_t sys = PLL_FREQ_TABLE_SYS(*pll); if (sys <= sysclk_mhz) { - m = (*pll >> 10) & 0x3f; - p = ((*pll >> 7) & 0x6) + 2; + m = PLL_FREQ_TABLE_M(*pll); + p = PLL_FREQ_TABLE_P(*pll); if (m == 0) { // special entry for using HSI directly sysclk_source = RCC_SYSCLKSOURCE_HSI; @@ -259,8 +323,13 @@ set_clk: #if !defined(STM32H7) ahb = sysclk >> AHBPrescTable[RCC_ClkInitStruct.AHBCLKDivider >> RCC_CFGR_HPRE_Pos]; #endif - RCC_ClkInitStruct.APB1CLKDivider = calc_apb_div(ahb / apb1); - RCC_ClkInitStruct.APB2CLKDivider = calc_apb_div(ahb / apb2); + RCC_ClkInitStruct.APB1CLKDivider = calc_apb1_div(ahb / apb1); + RCC_ClkInitStruct.APB2CLKDivider = calc_apb2_div(ahb / apb2); + #if defined(STM32H7) + RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; + RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; + #endif #if MICROPY_HW_CLK_LAST_FREQ // Save the bus dividers for use later @@ -294,6 +363,26 @@ set_clk: RCC_OscInitStruct.PLL.PLLN = n; RCC_OscInitStruct.PLL.PLLP = p; RCC_OscInitStruct.PLL.PLLQ = q; + + #if defined(STM32H7) + RCC_OscInitStruct.PLL.PLLR = 0; + if (MICROPY_HW_CLK_VALUE / 1000000 <= 2 * m) { + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_0; // 1-2MHz + } else if (MICROPY_HW_CLK_VALUE / 1000000 <= 4 * m) { + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1; // 2-4MHz + } else if (MICROPY_HW_CLK_VALUE / 1000000 <= 8 * m) { + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; // 4-8MHz + } else { + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; // 8-16MHz + } + if (MICROPY_HW_CLK_VALUE / 1000000 * n <= 420 * m) { + RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOMEDIUM; // 150-420MHz + } else { + RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; // 192-960MHz + } + RCC_OscInitStruct.PLL.PLLFRACN = 0; + #endif + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { return -MP_EIO; }