Select one of the symbols to view example projects that use it.
 
Outline
#include "pico/rand.h"
#include "pico/unique_id.h"
#include "pico/time.h"
#include "hardware/clocks.h"
#include "hardware/structs/rosc.h"
#include "hardware/structs/busctrl.h"
#include "hardware/sync.h"
rng_initialised
rng_state
splitmix64(uint64_t)
rotl(const uint64_t, int)
xoroshiro128ss(rng_128_t *)
#include "hardware/structs/trng.h"
trng_sample_words
trng_sample_word_count
capture_additional_trng_samples()
#include "pico/bootrom.h"
initialise_rand()
get_rand_64()
get_rand_128(rng_128_t *)
get_rand_32()
Files
loading...
SourceVuRaspberry Pi Pico SDK and ExamplesPicoSDKsrc/rp2_common/pico_rand/rand.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause *//* ... */ /* xoroshiro128ss(), rotl(): Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See <http://creativecommons.org/publicdomain/zero/1.0/> splitmix64() implementation: Written in 2015 by Sebastiano Vigna (vigna@acm.org) To the extent possible under law, the author has dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See <http://creativecommons.org/publicdomain/zero/1.0/> *//* ... */ #include "pico/rand.h" #include "pico/unique_id.h" #include "pico/time.h" #include "hardware/clocks.h" #include "hardware/structs/rosc.h" #include "hardware/structs/busctrl.h" #include "hardware/sync.h" 7 includes static bool rng_initialised = false; // Note: By design, do not initialise any of the variables that hold entropy, // they may have useful junk in them, either from power-up or a previous boot. static rng_128_t __uninitialized_ram(rng_state); #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH static uint64_t __uninitialized_ram(ram_hash); #endif #if PICO_RAND_ENTROPY_SRC_ROSC | PICO_RAND_SEED_ENTROPY_SRC_ROSC static uint64_t __uninitialized_ram(rosc_samples); #endif #if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER static uint8_t bus_counter_idx; #endif /* From the original source: This is a fixed-increment version of Java 8's SplittableRandom generator See http://dx.doi.org/10.1145/2714064.2660195 and http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html It is a very fast generator passing BigCrush, and it can be useful if for some reason you absolutely want 64 bits of state; otherwise, we rather suggest to use a xoroshiro128+ (for moderately parallel computations) or xorshift1024* (for massively parallel computations) generator. Note: This can be called with any value (i.e. including 0) *//* ... */ static __noinline uint64_t splitmix64(uint64_t x) { uint64_t z = x + 0x9E3779B97F4A7C15ull; z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; return z ^ (z >> 31); }{ ... } /* From the original source: This is xoroshiro128** 1.0, one of our all-purpose, rock-solid, small-state generators. It is extremely (sub-ns) fast and it passes all tests we are aware of, but its state space is large enough only for mild parallelism. For generating just floating-point numbers, xoroshiro128+ is even faster (but it has a very mild bias, see notes in the comments). The state must be seeded so that it is not everywhere zero. If you have a 64-bit seed, we suggest to seed a splitmix64 generator and use its output to fill s. *//* ... */ static inline uint64_t rotl(const uint64_t x, int k) { return (x << k) | (x >> (64 - k)); }{ ... } static __noinline uint64_t xoroshiro128ss(rng_128_t *local_rng_state) { const uint64_t s0 = local_rng_state->r[0]; uint64_t s1 = local_rng_state->r[1]; // Because the state is *modified* outside of this function, there is a // 1 in 2^128 chance that it could be all zeroes (which is not allowed). while (s0 == 0 && s1 == 0) { s1 = time_us_64(); // should not be 0, but loop anyway }while (s0 == 0 && s1 == 0) { ... } const uint64_t result = rotl(s0 * 5, 7) * 9; s1 ^= s0; local_rng_state->r[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b local_rng_state->r[1] = rotl(s1, 37); // c return result; }{ ... } #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH static uint64_t sdbm_hash64_sram(uint64_t hash) { // save some time by hashing a word at a time for (uint i = (PICO_RAND_RAM_HASH_START + 3) & ~3; i < PICO_RAND_RAM_HASH_END; i+=4) { uint32_t c = *(uint32_t *) i; hash = (uint64_t) c + (hash << 6) + (hash << 16) - hash; }for (uint i = (PICO_RAND_RAM_HASH_START + 3) & ~3; i < PICO_RAND_RAM_HASH_END; i+=4) { ... } return hash; }sdbm_hash64_sram (uint64_t hash) { ... } /* ... */#endif #if PICO_RAND_SEED_ENTROPY_SRC_TRNG | PICO_RAND_ENTROPY_SRC_TRNG #if !HAS_RP2350_TRNG #error PICO_RAND_SEED_ENTROPY_SRC_TRNG and PICO_RAND_ENTROPY_SRC_TRNG are only valid on RP2350 #endif #include "hardware/structs/trng.h" uint32_t trng_sample_words[count_of(trng_hw->ehr_data)]; static_assert(count_of(trng_hw->ehr_data) >= 2 && count_of(trng_hw->ehr_data) < 255, ""); uint8_t trng_sample_word_count; static uint64_t capture_additional_trng_samples(void) { spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); uint32_t save = spin_lock_blocking(lock); if (trng_sample_word_count < 2) { // Sample one ROSC bit into EHR every cycle, subject to CPU keeping up. // More temporal resolution to measure ROSC phase noise is better, if we // use a high quality hash function instead of naive VN decorrelation. // (Also more metastability events, which are a secondary noise source) // // This is out of the loop because writing to this register seems to // restart the sampling, slowing things down. We don't care if this write // is skipped as that would just make sampling take longer. trng_hw->sample_cnt1 = 0; // TRNG setup is inside loop in case it is skipped. Disable checks and // bypass decorrelators, to stream raw TRNG ROSC samples: trng_hw->trng_debug_control = -1u; // Start ROSC if it is not already started trng_hw->rnd_source_enable = -1u; // Clear all interrupts (including EHR_VLD) -- we will check this // later, after seeding RCP. trng_hw->rng_icr = -1u; // Wait for 192 ROSC samples to fill EHR, this should take constant time: while (trng_hw->trng_busy); for (uint i = 0; i < count_of(trng_sample_words); i++) { trng_sample_words[i] = trng_hw->ehr_data[i]; }for (uint i = 0; i < count_of(trng_sample_words); i++) { ... } trng_sample_word_count = count_of(trng_sample_words); // TRNG is now sampling again, having started after we read the last // EHR word. Grab some random bits and use them to modulate // the chain length, to reduce chance of injection locking: trng_hw->trng_config = rng_state.r[0]; }if (trng_sample_word_count < 2) { ... } trng_sample_word_count -= 2; uint64_t rc = trng_sample_words[trng_sample_word_count] | (((uint64_t)trng_sample_words[trng_sample_word_count + 1]) << 32); spin_unlock(lock, save); return rc; }{ ... } /* ... */#endif #if PICO_RAND_SEED_ENTROPY_SRC_ROSC | PICO_RAND_ENTROPY_SRC_ROSC /* gather an additional n bits of entropy, and shift them into the 64 bit entropy counter */ static uint64_t capture_additional_rosc_samples(uint n) { static absolute_time_t next_sample_time; // provide an override if someone really wants it, but disabling ROSC as an entropy source makes more sense #if !PICO_RAND_DISABLE_ROSC_CHECK // check that the ROSC is running but that the processors are NOT running from it hard_assert((rosc_hw->status & ROSC_STATUS_ENABLED_BITS) && ((clocks_hw->clk[clk_sys].ctrl & CLOCKS_CLK_SYS_CTRL_AUXSRC_BITS) != (CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC << CLOCKS_CLK_SYS_CTRL_AUXSRC_LSB)));/* ... */ #endif bool in_exception = __get_current_exception(); assert(n); // save us having to special case samples for this uint64_t samples = 0; for(uint i=0; i<n; i++) { bool bit_done = false; do { // Ensure that the ROSC random bit is not sampled too quickly, // ROSC may be ticking only a few times a microsecond. // Note: In general (i.e. sporadic) use, very often there will be no delay here. // note this is not read under lock, so the two 32 bit halves could be skewed, but in that // case we'll fail the check later, which is fine in this rare case absolute_time_t cached_next_sample_time = next_sample_time; // we support being called from IRQ, so be careful about sleeping... still not // ideal, but not much that can be done if (in_exception) { busy_wait_until(next_sample_time); }if (in_exception) { ... } else { sleep_until(next_sample_time); }else { ... } spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); uint32_t save = spin_lock_blocking(lock); if (!absolute_time_diff_us(cached_next_sample_time, next_sample_time)) { // we won the race (if any) for the bit, so we collect it locally samples <<= 1; samples |= rosc_hw->randombit & 1u; // use of relative time to now, rather than offset from before makes things // a bit less predictable at the cost of some speed. next_sample_time = make_timeout_time_us(PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US); bit_done = true; if (i == n - 1) { // samples has our random bits, so let's mix them in now samples = rosc_samples = (rosc_samples << n) | samples; }if (i == n - 1) { ... } }if (!absolute_time_diff_us(cached_next_sample_time, next_sample_time)) { ... } spin_unlock(lock, save); ...} while (!bit_done); }for (uint i=0; i return samples; }capture_additional_rosc_samples (uint n) { ... } /* ... */#endif #if PICO_RAND_SEED_ENTROPY_SRC_BOOT_RANDOM #include "pico/bootrom.h" #endif static void initialise_rand(void) { rng_128_t local_rng_state = local_rng_state; uint which = 0; #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH ram_hash = sdbm_hash64_sram(ram_hash); local_rng_state.r[which] ^= splitmix64(ram_hash); which ^= 1;/* ... */ #endif #if PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID static_assert(PICO_UNIQUE_BOARD_ID_SIZE_BYTES == sizeof(uint64_t), "Code below requires that 'board_id' is 64-bits in size"); // Note! The safety of the length assumption here is protected by a 'static_assert' above union unique_id_u { pico_unique_board_id_t board_id_native; uint64_t board_id_u64; ...} unique_id; // Note! The safety of the length assumption here is protected by a 'static_assert' above pico_get_unique_board_id(&unique_id.board_id_native); local_rng_state.r[which] ^= splitmix64(unique_id.board_id_u64); which ^= 1;/* ... */ #endif #if PICO_RAND_SEED_ENTROPY_SRC_ROSC // this is really quite slow (10ms per iteration), and I'm not sure that it adds value over the 64 random bits // uint ref_khz = clock_get_hz(clk_ref) / 100; // for (int i = 0; i < 5; i++) { // // Apply hash of the rosc frequency, limited but still 'extra' entropy // uint measurement = frequency_count_raw(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC, ref_khz); // local_rng_state.r[which] ^= splitmix64(measurement); // (void) xoroshiro128ss(&local_rng_state); //churn to mix seed sources // } // Gather a full ROSC sample array with sample bits local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(8 * sizeof(rosc_samples))); which ^= 1;/* ... */ #endif #if PICO_RAND_SEED_ENTROPY_SRC_BOOT_RANDOM // Mix in boot random. union { uint64_t u64[2]; uint32_t u32[4]; ...} br; rom_get_boot_random(br.u32); local_rng_state.r[which] ^= splitmix64(br.u64[0]); local_rng_state.r[which ^ 1] ^= splitmix64(br.u64[1]);/* ... */ #endif #if PICO_RAND_SEED_ENTROPY_SRC_TIME // Mix in hashed time. This is [possibly] predictable boot-to-boot // but will vary application-to-application. local_rng_state.r[which] ^= splitmix64(time_us_64()); which ^= 1;/* ... */ #endif #if PICO_RAND_SEED_ENTROPY_SRC_TRNG local_rng_state.r[which] ^= splitmix64(capture_additional_trng_samples()); which ^= 1;/* ... */ #endif spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); uint32_t save = spin_lock_blocking(lock); if (!rng_initialised) { #if PICO_RAND_SEED_ENTROPY_SRC_BUS_PERF_COUNTER #if !PICO_RAND_BUS_PERF_COUNTER_INDEX int idx = -1; for(uint i = 0; i < count_of(bus_ctrl_hw->counter); i++) { if (bus_ctrl_hw->counter[i].sel == BUSCTRL_PERFSEL0_RESET) { idx = (int)i; break; }if (bus_ctrl_hw->counter[i].sel == BUSCTRL_PERFSEL0_RESET) { ... } }for (uint i = 0; i < count_of(bus_ctrl_hw->counter); i++) { ... } hard_assert(idx != -1); bus_counter_idx = (uint8_t)idx;/* ... */ #else bus_counter_idx = (uint8_t)PICO_RAND_BUS_PERF_COUNTER_INDEX; #endif bus_ctrl_hw->counter[bus_counter_idx].sel = PICO_RAND_BUS_PERF_COUNTER_EVENT;/* ... */ #endif (void) xoroshiro128ss(&local_rng_state); rng_state = local_rng_state; rng_initialised = true; }if (!rng_initialised) { ... } spin_unlock(lock, save); }{ ... } uint64_t get_rand_64(void) { if (!rng_initialised) { // Do not provide 'RNs' until the system has been initialised. Note: // The first initialisation can be quite time-consuming depending on // the amount of RAM hashed, see RAM_HASH_START and RAM_HASH_END initialise_rand(); }if (!rng_initialised) { ... } static volatile uint8_t check_byte; rng_128_t local_rng_state = rng_state; uint8_t local_check_byte = check_byte; // Modify PRNG state with the run-time entropy sources, // hashed to reduce correlation with previous modifications. uint which = 0; #if PICO_RAND_ENTROPY_SRC_TIME local_rng_state.r[which] ^= splitmix64(time_us_64()); which ^= 1;/* ... */ #endif #if PICO_RAND_ENTROPY_SRC_ROSC local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(PICO_RAND_ROSC_BIT_SAMPLE_COUNT)); which ^= 1;/* ... */ #endif #if PICO_RAND_ENTROPY_SRC_TRNG uint64_t foo = capture_additional_trng_samples(); local_rng_state.r[which] ^= splitmix64(foo); which ^= 1;/* ... */ #endif #if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER uint32_t bus_counter_value = busctrl_hw->counter[bus_counter_idx].value; // counter is saturating, so clear it if it has reached saturation if (bus_counter_value == BUSCTRL_PERFCTR0_BITS) { busctrl_hw->counter[bus_counter_idx].value = 0; }if (bus_counter_value == BUSCTRL_PERFCTR0_BITS) { ... } local_rng_state.r[which] ^= splitmix64(bus_counter_value); which ^= 1;/* ... */ #endif spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); uint32_t save = spin_lock_blocking(lock); if (local_check_byte != check_byte) { // someone got a random number in the interim, so mix it in local_rng_state.r[0] ^= rng_state.r[0]; local_rng_state.r[1] ^= rng_state.r[1]; }if (local_check_byte != check_byte) { ... } // Generate a 64-bit RN from the modified PRNG state. // Note: This also "churns" the 128-bit state for next time. uint64_t rand64 = xoroshiro128ss(&local_rng_state); rng_state = local_rng_state; check_byte++; spin_unlock(lock, save); return rand64; }{ ... } void get_rand_128(rng_128_t *ptr128) { ptr128->r[0] = get_rand_64(); ptr128->r[1] = get_rand_64(); }{ ... } uint32_t get_rand_32(void) { return (uint32_t) get_rand_64(); }{ ... }
Details