stmhal: Compute PLL freq table during build instead of at run time.
Allows for simpler, smaller and faster code at run time when selecting the boards frequency, and allows more customisation opportunities for the PLL values depending on the target MCU.
This commit is contained in:
parent
b84268d49c
commit
41b4686dd7
@ -397,6 +397,7 @@ $(BUILD)/firmware.elf: $(OBJ)
|
|||||||
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||||
$(Q)$(SIZE) $@
|
$(Q)$(SIZE) $@
|
||||||
|
|
||||||
|
PLLVALUES = boards/pllvalues.py
|
||||||
MAKE_PINS = boards/make-pins.py
|
MAKE_PINS = boards/make-pins.py
|
||||||
BOARD_PINS = boards/$(BOARD)/pins.csv
|
BOARD_PINS = boards/$(BOARD)/pins.csv
|
||||||
PREFIX_FILE = boards/stm32f4xx_prefix.c
|
PREFIX_FILE = boards/stm32f4xx_prefix.c
|
||||||
@ -443,12 +444,18 @@ $(BUILD)/%_$(BOARD).c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%_af_const.h $(BUILD)/
|
|||||||
$(BUILD)/pins_$(BOARD).o: $(BUILD)/pins_$(BOARD).c
|
$(BUILD)/pins_$(BOARD).o: $(BUILD)/pins_$(BOARD).c
|
||||||
$(call compile_c)
|
$(call compile_c)
|
||||||
|
|
||||||
|
GEN_PLLFREQTABLE_HDR = $(HEADER_BUILD)/pllfreqtable.h
|
||||||
GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h
|
GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h
|
||||||
GEN_STMCONST_QSTR = $(BUILD)/modstm_qstr.h
|
GEN_STMCONST_QSTR = $(BUILD)/modstm_qstr.h
|
||||||
GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h
|
GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h
|
||||||
CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]')
|
CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]')
|
||||||
CMSIS_MCU_HDR = cmsis/$(CMSIS_MCU_LOWER).h
|
CMSIS_MCU_HDR = cmsis/$(CMSIS_MCU_LOWER).h
|
||||||
|
|
||||||
|
modmachine.c: $(GEN_PLLFREQTABLE_HDR)
|
||||||
|
$(GEN_PLLFREQTABLE_HDR): $(PLLVALUES) | $(HEADER_BUILD)
|
||||||
|
$(ECHO) "Create $@"
|
||||||
|
$(Q)$(PYTHON) $(PLLVALUES) -c file:boards/$(BOARD)/stm32$(MCU_SERIES)xx_hal_conf.h > $@
|
||||||
|
|
||||||
$(BUILD)/modstm.o: $(GEN_STMCONST_HDR)
|
$(BUILD)/modstm.o: $(GEN_STMCONST_HDR)
|
||||||
# Use a pattern rule here so that make will only call make-stmconst.py once to
|
# Use a pattern rule here so that make will only call make-stmconst.py once to
|
||||||
# make both modstm_const.h and modstm_qstr.h
|
# make both modstm_const.h and modstm_qstr.h
|
||||||
|
@ -67,14 +67,17 @@ def compute_pll2(hse, sys):
|
|||||||
# no valid values found
|
# no valid values found
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def verify_and_print_pll(hse, sys, pll):
|
def compute_derived(hse, pll):
|
||||||
M, N, P, Q = pll
|
M, N, P, Q = pll
|
||||||
|
|
||||||
# compute derived quantities
|
|
||||||
vco_in = hse / M
|
vco_in = hse / M
|
||||||
vco_out = hse * N / M
|
vco_out = hse * N / M
|
||||||
pllck = hse / M * N / P
|
pllck = hse / M * N / P
|
||||||
pll48ck = hse / M * N / Q
|
pll48ck = hse / M * N / Q
|
||||||
|
return (vco_in, vco_out, pllck, pll48ck)
|
||||||
|
|
||||||
|
def verify_pll(hse, pll):
|
||||||
|
M, N, P, Q = pll
|
||||||
|
vco_in, vco_out, pllck, pll48ck = compute_derived(hse, pll)
|
||||||
|
|
||||||
# verify ints
|
# verify ints
|
||||||
assert close_int(M)
|
assert close_int(M)
|
||||||
@ -90,26 +93,68 @@ def verify_and_print_pll(hse, sys, pll):
|
|||||||
assert 1 <= vco_in <= 2
|
assert 1 <= vco_in <= 2
|
||||||
assert 192 <= vco_out <= 432
|
assert 192 <= vco_out <= 432
|
||||||
|
|
||||||
# print out values
|
def generate_c_table(hse, valid_plls):
|
||||||
print(out_format % (sys, M, N, P, Q, vco_in, vco_out, pllck, pll48ck))
|
valid_plls = valid_plls + [(16, (0, 0, 2, 0))]
|
||||||
|
if hse < 16:
|
||||||
|
valid_plls.append((hse, (1, 0, 2, 0)))
|
||||||
|
valid_plls.sort()
|
||||||
|
print("// (M, P/2-1, SYS) values for %u MHz HSE" % hse)
|
||||||
|
print("static const uint16_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("};")
|
||||||
|
|
||||||
|
def print_table(hse, valid_plls):
|
||||||
|
print("HSE =", hse, "MHz")
|
||||||
|
print("sys : M N P Q : VCO_IN VCO_OUT PLLCK PLL48CK")
|
||||||
|
out_format = "%3u : %2u %.1f %.2f %.2f : %5.2f %6.2f %6.2f %6.2f"
|
||||||
|
for sys, pll in valid_plls:
|
||||||
|
print(out_format % ((sys,) + pll + compute_derived(hse, pll)))
|
||||||
|
print("found %u valid configurations" % len(valid_plls))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global out_format
|
global out_format
|
||||||
|
|
||||||
|
# parse input args
|
||||||
import sys
|
import sys
|
||||||
if len(sys.argv) != 2:
|
argv = sys.argv[1:]
|
||||||
print("usage: pllvalues.py <hse in MHz>")
|
|
||||||
|
c_table = False
|
||||||
|
if argv[0] == '-c':
|
||||||
|
c_table = True
|
||||||
|
argv.pop(0)
|
||||||
|
|
||||||
|
if len(argv) != 1:
|
||||||
|
print("usage: pllvalues.py [-c] <hse in MHz>")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
hse_value = int(sys.argv[1])
|
|
||||||
print("HSE =", hse_value, "MHz")
|
if argv[0].startswith("file:"):
|
||||||
print("sys : M N P Q : VCO_IN VCO_OUT PLLCK PLL48CK")
|
# extract HSE_VALUE from header file
|
||||||
out_format = "%3u : %2u %.1f %.2f %.2f : %5.2f %6.2f %6.2f %6.2f"
|
with open(argv[0][5:]) as f:
|
||||||
n_valid = 0
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("#define") and line.find("HSE_VALUE") != -1:
|
||||||
|
idx_start = line.find("((uint32_t)") + 11
|
||||||
|
idx_end = line.find(")", idx_start)
|
||||||
|
hse = int(line[idx_start:idx_end]) // 1000000
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("%s does not contain a definition of HSE_VALUE" % argv[0])
|
||||||
|
else:
|
||||||
|
# HSE given directly as an integer
|
||||||
|
hse = int(argv[0])
|
||||||
|
|
||||||
|
valid_plls = []
|
||||||
for sysclk in range(1, 217):
|
for sysclk in range(1, 217):
|
||||||
pll = compute_pll2(hse_value, sysclk)
|
pll = compute_pll2(hse, sysclk)
|
||||||
if pll is not None:
|
if pll is not None:
|
||||||
n_valid += 1
|
verify_pll(hse, pll)
|
||||||
verify_and_print_pll(hse_value, sysclk, pll)
|
valid_plls.append((sysclk, pll))
|
||||||
print("found %u valid configurations" % n_valid)
|
|
||||||
|
if c_table:
|
||||||
|
generate_c_table(hse, valid_plls)
|
||||||
|
else:
|
||||||
|
print_table(hse, valid_plls)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
#include "spi.h"
|
#include "spi.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include "wdt.h"
|
#include "wdt.h"
|
||||||
|
#include "genhdr/pllfreqtable.h"
|
||||||
|
|
||||||
#if defined(MCU_SERIES_F4)
|
#if defined(MCU_SERIES_F4)
|
||||||
// the HAL does not define these constants
|
// the HAL does not define these constants
|
||||||
@ -276,61 +277,31 @@ STATIC mp_obj_t machine_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
|||||||
uint32_t m = HSE_VALUE / 1000000, n = 336, p = 2, q = 7;
|
uint32_t m = HSE_VALUE / 1000000, n = 336, p = 2, q = 7;
|
||||||
uint32_t sysclk_source;
|
uint32_t sysclk_source;
|
||||||
|
|
||||||
// the following logic assumes HSE < HSI
|
// search for a valid PLL configuration that keeps USB at 48MHz
|
||||||
if (HSE_VALUE / 1000000 <= wanted_sysclk && wanted_sysclk < HSI_VALUE / 1000000) {
|
for (const uint16_t *pll = &pll_freq_table[MP_ARRAY_SIZE(pll_freq_table) - 1]; pll >= &pll_freq_table[0]; --pll) {
|
||||||
// use HSE as SYSCLK
|
uint32_t sys = *pll & 0xff;
|
||||||
sysclk_source = RCC_SYSCLKSOURCE_HSE;
|
if (sys <= wanted_sysclk) {
|
||||||
} else if (HSI_VALUE / 1000000 <= wanted_sysclk && wanted_sysclk < 24) {
|
m = (*pll >> 10) & 0x3f;
|
||||||
// use HSI as SYSCLK
|
p = ((*pll >> 7) & 0x6) + 2;
|
||||||
sysclk_source = RCC_SYSCLKSOURCE_HSI;
|
if (m == 0) {
|
||||||
} else {
|
// special entry for using HSI directly
|
||||||
// search for a valid PLL configuration that keeps USB at 48MHz
|
sysclk_source = RCC_SYSCLKSOURCE_HSI;
|
||||||
for (; wanted_sysclk > 0; wanted_sysclk--) {
|
goto set_clk;
|
||||||
for (p = 2; p <= 8; p += 2) {
|
} else if (m == 1) {
|
||||||
// compute VCO_OUT
|
// special entry for using HSE directly
|
||||||
mp_uint_t vco_out = wanted_sysclk * p;
|
sysclk_source = RCC_SYSCLKSOURCE_HSE;
|
||||||
// make sure VCO_OUT is between 192MHz and 432MHz
|
goto set_clk;
|
||||||
if (vco_out < 192 || vco_out > 432) {
|
} else {
|
||||||
continue;
|
// use PLL
|
||||||
}
|
|
||||||
// make sure Q is an integer
|
|
||||||
if (vco_out % 48 != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// solve for Q to get PLL48CK at 48MHz
|
|
||||||
q = vco_out / 48;
|
|
||||||
// make sure Q is in range
|
|
||||||
if (q < 2 || q > 15) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// make sure N/M is an integer
|
|
||||||
if (vco_out % (HSE_VALUE / 1000000) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// solve for N/M
|
|
||||||
mp_uint_t n_by_m = vco_out / (HSE_VALUE / 1000000);
|
|
||||||
// solve for M, making sure VCO_IN (=HSE/M) is between 1MHz and 2MHz
|
|
||||||
m = 192 / n_by_m;
|
|
||||||
while (m < (HSE_VALUE / 2000000) || n_by_m * m < 192) {
|
|
||||||
m += 1;
|
|
||||||
}
|
|
||||||
if (m > (HSE_VALUE / 1000000)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// solve for N
|
|
||||||
n = n_by_m * m;
|
|
||||||
// make sure N is in range
|
|
||||||
if (n < 192 || n > 432) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// found values!
|
|
||||||
sysclk_source = RCC_SYSCLKSOURCE_PLLCLK;
|
sysclk_source = RCC_SYSCLKSOURCE_PLLCLK;
|
||||||
|
uint32_t vco_out = sys * p;
|
||||||
|
n = vco_out * m / (HSE_VALUE / 1000000);
|
||||||
|
q = vco_out / 48;
|
||||||
goto set_clk;
|
goto set_clk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mp_raise_ValueError("can't make valid freq");
|
|
||||||
}
|
}
|
||||||
|
mp_raise_ValueError("can't make valid freq");
|
||||||
|
|
||||||
set_clk:
|
set_clk:
|
||||||
//printf("%lu %lu %lu %lu %lu\n", sysclk_source, m, n, p, q);
|
//printf("%lu %lu %lu %lu %lu\n", sysclk_source, m, n, p, q);
|
||||||
|
Loading…
Reference in New Issue
Block a user