From 76cf98c35b81dded1dc623d07f25e28713a16b63 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 24 Jan 2023 20:36:19 +0100 Subject: [PATCH] samd/mcu: Implement a hardware seed for the SAMD21 random module. By using the phase jitter between the DFLL48M clock and the FDPLL96M clock. Even if both use the same reference source, they have a different jitter. SysTick is driven by FDPLL96M, the us counter by DFLL48M. As a random source, the us counter is read out on every SysTick and the value is used to accumulate a simple multiply, add and xor register. According to tests it creates about 30 bit random bit-flips per second. That mechanism will pass quite a few RNG tests, has a suitable frequency distribution and serves better than just the time after boot to seed the PRNG. --- docs/samd/quickref.rst | 6 ++++-- ports/samd/mcu/samd21/clock_config.c | 5 +++-- ports/samd/mcu/samd21/mpconfigmcu.h | 3 +++ ports/samd/samd_isr.c | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index aa3d563f7c..cdfb0a4750 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -42,8 +42,10 @@ The :mod:`machine` module:: machine.freq(96_000_000) # set the CPU frequency to 96 MHz The range accepted by the function call is 1_000_000 to 200_000_000 (1 MHz to 200 MHz) -for SAMD51 and 1_000_000 to 48_000_000 (1 MHz to 48 MHz) for SAMD21. The safe -range for SAMD51 according to the data sheet is 96 MHz to 120 MHz. +for SAMD51 and 1_000_000 to 54_000_000 (1 MHz to 54 MHz) for SAMD21. The safe +range for SAMD51 according to the data sheet is up to 120 MHz, for the SAMD21 up to 48Mhz. +Frequencies below 48Mhz are set by dividing 48Mhz by an integer, limiting the number of +discrete frequencies to 24Mhz, 16Mhz, 12MHz, and so on. At frequencies below 8 MHz USB will be disabled. Changing the frequency below 48 MHz impacts the baud rates of UART, I2C and SPI. These have to be set again after changing the CPU frequency. The ms and µs timers are not affected by the frequency diff --git a/ports/samd/mcu/samd21/clock_config.c b/ports/samd/mcu/samd21/clock_config.c index 4473be5afc..630b5c0a4a 100644 --- a/ports/samd/mcu/samd21/clock_config.c +++ b/ports/samd/mcu/samd21/clock_config.c @@ -78,8 +78,9 @@ void set_cpu_freq(uint32_t cpu_freq_arg) { // CtrlB: Set the ref ource to GCLK, set the Wakup-Fast Flag. SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_REFCLK_GCLK | SYSCTRL_DPLLCTRLB_WUF; // Set the FDPLL ratio and enable the DPLL. - int ldr = cpu_freq_arg / FDPLL_REF_FREQ - 1; - SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(ldr); + int ldr = cpu_freq / FDPLL_REF_FREQ; + int frac = ((cpu_freq - ldr * FDPLL_REF_FREQ) / (FDPLL_REF_FREQ / 16)) & 0x0f; + SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR((frac << 16 | ldr) - 1); SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE; // Wait for the DPLL lock. while (!SYSCTRL->DPLLSTATUS.bit.LOCK) { diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index 7d787cacfe..150a378a3e 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -22,6 +22,9 @@ #define MICROPY_PY_CMATH (0) #endif +#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32()) +unsigned long trng_random_u32(void); + #define VFS_BLOCK_SIZE_BYTES (1536) // 24x 64B flash pages; #ifndef MICROPY_HW_UART_TXBUF diff --git a/ports/samd/samd_isr.c b/ports/samd/samd_isr.c index 1d6febaaa2..838800bdbb 100644 --- a/ports/samd/samd_isr.c +++ b/ports/samd/samd_isr.c @@ -42,6 +42,9 @@ extern void EIC_Handler(void); const ISR isr_vector[]; volatile uint32_t systick_ms; volatile uint32_t ticks_us64_upper; +#if defined(MCU_SAMD21) +volatile uint32_t rng_state; +#endif void Reset_Handler(void) __attribute__((naked)); void Reset_Handler(void) { @@ -91,6 +94,12 @@ void Default_Handler(void) { } void SysTick_Handler(void) { + #if defined(MCU_SAMD21) + // Use the phase jitter between the clocks to get some entropy + // and accumulate the random number register. + rng_state = (rng_state * 32310901 + 1) ^ (REG_TC4_COUNT32_COUNT >> 1); + #endif + uint32_t next_tick = systick_ms + 1; systick_ms = next_tick; @@ -99,6 +108,13 @@ void SysTick_Handler(void) { } } +#if defined(MCU_SAMD21) +uint32_t trng_random_u32(void) { + mp_hal_delay_ms(320); // wait for ten cycles of the rng_seed register + return rng_state; +} +#endif + void us_timer_IRQ(void) { #if defined(MCU_SAMD21) if (TC4->COUNT32.INTFLAG.reg & TC_INTFLAG_OVF) {