Skip to content

Commit 76cf98c

Browse files
robert-hhdpgeorge
authored andcommitted
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.
1 parent 7e0b1bc commit 76cf98c

File tree

4 files changed

+26
-4
lines changed

4 files changed

+26
-4
lines changed

docs/samd/quickref.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ The :mod:`machine` module::
4242
machine.freq(96_000_000) # set the CPU frequency to 96 MHz
4343

4444
The range accepted by the function call is 1_000_000 to 200_000_000 (1 MHz to 200 MHz)
45-
for SAMD51 and 1_000_000 to 48_000_000 (1 MHz to 48 MHz) for SAMD21. The safe
46-
range for SAMD51 according to the data sheet is 96 MHz to 120 MHz.
45+
for SAMD51 and 1_000_000 to 54_000_000 (1 MHz to 54 MHz) for SAMD21. The safe
46+
range for SAMD51 according to the data sheet is up to 120 MHz, for the SAMD21 up to 48Mhz.
47+
Frequencies below 48Mhz are set by dividing 48Mhz by an integer, limiting the number of
48+
discrete frequencies to 24Mhz, 16Mhz, 12MHz, and so on.
4749
At frequencies below 8 MHz USB will be disabled. Changing the frequency below 48 MHz
4850
impacts the baud rates of UART, I2C and SPI. These have to be set again after
4951
changing the CPU frequency. The ms and µs timers are not affected by the frequency

ports/samd/mcu/samd21/clock_config.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ void set_cpu_freq(uint32_t cpu_freq_arg) {
7878
// CtrlB: Set the ref ource to GCLK, set the Wakup-Fast Flag.
7979
SYSCTRL->DPLLCTRLB.reg = SYSCTRL_DPLLCTRLB_REFCLK_GCLK | SYSCTRL_DPLLCTRLB_WUF;
8080
// Set the FDPLL ratio and enable the DPLL.
81-
int ldr = cpu_freq_arg / FDPLL_REF_FREQ - 1;
82-
SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR(ldr);
81+
int ldr = cpu_freq / FDPLL_REF_FREQ;
82+
int frac = ((cpu_freq - ldr * FDPLL_REF_FREQ) / (FDPLL_REF_FREQ / 16)) & 0x0f;
83+
SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDR((frac << 16 | ldr) - 1);
8384
SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE;
8485
// Wait for the DPLL lock.
8586
while (!SYSCTRL->DPLLSTATUS.bit.LOCK) {

ports/samd/mcu/samd21/mpconfigmcu.h

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#define MICROPY_PY_CMATH (0)
2323
#endif
2424

25+
#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32())
26+
unsigned long trng_random_u32(void);
27+
2528
#define VFS_BLOCK_SIZE_BYTES (1536) // 24x 64B flash pages;
2629

2730
#ifndef MICROPY_HW_UART_TXBUF

ports/samd/samd_isr.c

+16
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ extern void EIC_Handler(void);
4242
const ISR isr_vector[];
4343
volatile uint32_t systick_ms;
4444
volatile uint32_t ticks_us64_upper;
45+
#if defined(MCU_SAMD21)
46+
volatile uint32_t rng_state;
47+
#endif
4548

4649
void Reset_Handler(void) __attribute__((naked));
4750
void Reset_Handler(void) {
@@ -91,6 +94,12 @@ void Default_Handler(void) {
9194
}
9295

9396
void SysTick_Handler(void) {
97+
#if defined(MCU_SAMD21)
98+
// Use the phase jitter between the clocks to get some entropy
99+
// and accumulate the random number register.
100+
rng_state = (rng_state * 32310901 + 1) ^ (REG_TC4_COUNT32_COUNT >> 1);
101+
#endif
102+
94103
uint32_t next_tick = systick_ms + 1;
95104
systick_ms = next_tick;
96105

@@ -99,6 +108,13 @@ void SysTick_Handler(void) {
99108
}
100109
}
101110

111+
#if defined(MCU_SAMD21)
112+
uint32_t trng_random_u32(void) {
113+
mp_hal_delay_ms(320); // wait for ten cycles of the rng_seed register
114+
return rng_state;
115+
}
116+
#endif
117+
102118
void us_timer_IRQ(void) {
103119
#if defined(MCU_SAMD21)
104120
if (TC4->COUNT32.INTFLAG.reg & TC_INTFLAG_OVF) {

0 commit comments

Comments
 (0)