summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/stm32/Makefile2
-rw-r--r--ports/stm32/boards/pllvalues.py134
-rw-r--r--ports/stm32/modmachine.c5
-rw-r--r--ports/stm32/powerctrl.c127
4 files changed, 213 insertions, 55 deletions
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index 96f10fe98..11db9dfba 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 4a85b5478..2d64876b4 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):
- 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)
+ 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
- # 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] <hse in MHz>")
+ print("usage: pllvalues.py [-c] [-m <mcu_series>] <hse in MHz>")
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 7db8e2964..19f58a98d 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 4c40cffb6..9619e0ea4 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;
}