Add pico_rand library (#1111)

Add a general purpose random number generator via pico_rand library. which tries to use as much real entropy as possible mixed into a PRNG

Co-authored-by: graham sanderson <graham.sanderson@raspberrypi.com>
This commit is contained in:
andygpz11 2023-01-26 19:25:27 +00:00 committed by GitHub
parent 67af83f069
commit b70f984f2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 505 additions and 42 deletions

View File

@ -43,6 +43,7 @@
* @{
* \defgroup pico_async_context pico_async_context
* \defgroup pico_multicore pico_multicore
* \defgroup pico_rand pico_rand
* \defgroup pico_stdlib pico_stdlib
* \defgroup pico_sync pico_sync
* \defgroup pico_time pico_time

View File

@ -49,6 +49,7 @@ if (NOT PICO_BARE_METAL)
pico_add_subdirectory(pico_mem_ops)
pico_add_subdirectory(pico_malloc)
pico_add_subdirectory(pico_printf)
pico_add_subdirectory(pico_rand)
pico_add_subdirectory(pico_stdio)
pico_add_subdirectory(pico_stdio_semihosting)

View File

@ -70,6 +70,11 @@ typedef volatile uint32_t spin_lock_t;
#define PICO_SPINLOCK_ID_HARDWARE_CLAIM 11
#endif
// PICO_CONFIG: PICO_SPINLOCK_ID_RAND, Spinlock ID for Random Number Generator, min=0, max=31, default=12, group=hardware_sync
#ifndef PICO_SPINLOCK_ID_RAND
#define PICO_SPINLOCK_ID_RAND 12
#endif
// PICO_CONFIG: PICO_SPINLOCK_ID_OS1, First Spinlock ID reserved for use by low level OS style software, min=0, max=31, default=14, group=hardware_sync
#ifndef PICO_SPINLOCK_ID_OS1
#define PICO_SPINLOCK_ID_OS1 14

View File

@ -46,7 +46,6 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
${PICO_LWIP_PATH}/src/core/tcp_out.c
${PICO_LWIP_PATH}/src/core/timeouts.c
${PICO_LWIP_PATH}/src/core/udp.c
${CMAKE_CURRENT_LIST_DIR}/lwip_random.c
)
target_include_directories(pico_lwip_core INTERFACE
${PICO_LWIP_PATH}/src/include)
@ -274,7 +273,8 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
target_link_libraries(pico_lwip_nosys INTERFACE
pico_async_context_base
pico_lwip_arch
pico_lwip)
pico_lwip
pico_rand)
if (NOT PICO_LWIP_CONTRIB_PATH)
set(PICO_LWIP_CONTRIB_PATH ${PICO_LWIP_PATH}/contrib)

View File

@ -86,14 +86,8 @@ void pico_lwip_custom_unlock_tcpip_core(void);
#endif
#ifndef LWIP_RAND
#ifdef __cplusplus
extern "C" {
#endif
unsigned int pico_lwip_rand(void);
#ifdef __cplusplus
}
#endif
// Use ROSC based random number generation, more for the fact that rand() may not be seeded, than anything else
#define LWIP_RAND pico_lwip_rand
#include "pico/rand.h"
// Use the pico_rand library which goes to reasonable lengths to try to provide good entropy
#define LWIP_RAND() get_rand_32()
#endif
#endif /* __CC_H__ */

View File

@ -1,29 +0,0 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
#include "hardware/structs/rosc.h"
static uint8_t pico_lwip_random_byte(int cycles) {
static uint8_t byte;
assert(cycles >= 8);
assert(rosc_hw->status & ROSC_STATUS_ENABLED_BITS);
for(int i=0;i<cycles;i++) {
// picked a fairly arbitrary polynomial of 0x35u - this doesn't have to be crazily uniform.
byte = ((byte << 1) | rosc_hw->randombit) ^ (byte & 0x80u ? 0x35u : 0);
// delay a little because the random bit is a little slow
busy_wait_at_least_cycles(30);
}
return byte;
}
unsigned int pico_lwip_rand(void) {
uint32_t value = 0;
for (int i = 0; i < 4; i++) {
value = (value << 8u) | pico_lwip_random_byte(32);
}
return value;
}

View File

@ -153,14 +153,14 @@ extern "C" {
*
* For example a `uint32_t` foo that will retain its value if the program is restarted by reset.
*
* uint32_t __uninitialized_ram("my_group_name") foo;
* uint32_t __uninitialized_ram("foo");
*
* The section attribute is `.uninitialized_ram.<group>`
*
* \param group a string suffix to use in the section name to distinguish groups that can be linker
* garbage-collected independently
*/
#define __uninitialized_ram(group) __attribute__((section(".uninitialized_ram." #group))) group
#define __uninitialized_ram(group) __attribute__((section(".uninitialized_data." #group))) group
/*! \brief Section attribute macro for placement in flash even in a COPY_TO_RAM binary
* \ingroup pico_platform

View File

@ -0,0 +1,13 @@
pico_add_impl_library(pico_rand)
target_sources(pico_rand INTERFACE
${CMAKE_CURRENT_LIST_DIR}/rand.c
)
target_include_directories(pico_rand INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_rand INTERFACE
pico_unique_id
hardware_clocks
hardware_timer
hardware_sync)

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_RAND_H
#define _PICO_RAND_H
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file pico/rand.h
* \defgroup pico_rand pico_rand
*
* Random Number Generator API
*
* This module generates random numbers at runtime via a number of possible entropy
* sources and uses those sources to modify the state of a 128-bit 'Pseudo
* Random Number Generator' implemented in software.
*
* The random numbers (32 to 128 bit) to be supplied are read from the PRNG which is used
* to help provide a large number space.
*
* The following (multiple) sources of entropy are available of varying quality, each enabled by a #define:
*
* - The Ring Oscillator (ROSC) (\ref PICO_RAND_ENTROPY_SRC_ROSC == 1):
* \ref PICO_RAND_ROSC_BIT_SAMPLE_COUNT bits are gathered from the ring oscillator "random bit" and mixed in each
* time. This should not be used if the ROSC is off, or the processor is running from
* the ROSC.
* \note the maximum throughput of ROSC bit sampling is controlled by PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US which defaults
* to 10us, i.e. 100,000 bits per second.
* - Time (\ref PICO_RAND_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed in each time.
* - Bus Performance Counter (\ref PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER == 1): One of the bus fabric's performance
* counters is mixed in each time.
*
* \note All entropy sources are hashed before application to the PRNG state machine.
*
* \note The \em first time a random number is requested, the 128-bit PRNG state
* must be seeded. Multiple entropy sources are also available for the seeding operation:
*
* - The Ring Oscillator (ROSC) (\ref PICO_RAND_SEED_ENTROPY_SRC_ROSC == 1):
* 64 bits are gathered from the ring oscillator "random bit" and mixed into the seed.
* - Time (\ref PICO_RAND_SEED_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed into the seed.
* - Board Identifier (PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID == 1): The board id via \ref pico_get_unique_board_id
* is mixed into the seed.
* - RAM hash (\ref PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH (\ref PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH): The hashed contents of a
* subset of RAM are mixed in. Initial RAM contents are undefined on power up, so provide a reasonable source of entropy.
* By default the last 1K of RAM (which usually contains the core 0 stack) is hashed, which may also provide for differences
* after each warm reset.
*
* With default settings, the seed generation takes approximately 1 millisecond while
* subsequent random numbers generally take between 10 and 20 microseconds to generate.
*/
// ---------------
// ENTROPY SOURCES
// ---------------
// PICO_CONFIG: PICO_RAND_ENTROPY_SRC_ROSC, Enable/disable use of ROSC as an entropy source, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_ENTROPY_SRC_ROSC
#define PICO_RAND_ENTROPY_SRC_ROSC 1
#endif
// PICO_CONFIG: PICO_RAND_ENTROPY_SRC_TIME, Enable/disable use of hardware timestamp as an entropy source, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_ENTROPY_SRC_TIME
#define PICO_RAND_ENTROPY_SRC_TIME 1
#endif
// PICO_CONFIG: PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER, Enable/disable use of a bus performance counter as an entropy source, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
#define PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER 1
#endif
// --------------------
// SEED ENTROPY SOURCES
// --------------------
// PICO_CONFIG: PICO_RAND_SEED_ENTROPY_SRC_ROSC, Enable/disable use of ROSC as an entropy source for the random seed, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_SEED_ENTROPY_SRC_ROSC
#define PICO_RAND_SEED_ENTROPY_SRC_ROSC PICO_RAND_ENTROPY_SRC_ROSC
#endif
// PICO_CONFIG: PICO_RAND_SEED_ENTROPY_SRC_TIME, Enable/disable use of hardware timestamp as an entropy source for the random seed, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_SEED_ENTROPY_SRC_TIME
#define PICO_RAND_SEED_ENTROPY_SRC_TIME PICO_RAND_ENTROPY_SRC_TIME
#endif
// PICO_CONFIG: PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID, Enable/disable use of board id as part of the random seed, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID
#define PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID 1
#endif
// PICO_CONFIG: PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH, Enable/disable use of a RAM hash as an entropy source for the random seed, type=bool, default=1, group=pico_rand
#ifndef PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
#define PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH 1
#endif
// ---------------------------------
// PICO_RAND_ENTROPY_SRC_ROSC CONFIG
// ---------------------------------
// PICO_CONFIG: PICO_RAND_ROSC_BIT_SAMPLE_COUNT, Number of samples to take of the ROSC random bit per random number generation , min=1, max=64, default=1, group=pico_rand
#ifndef PICO_RAND_ROSC_BIT_SAMPLE_COUNT
#define PICO_RAND_ROSC_BIT_SAMPLE_COUNT 1
#endif
// PICO_CONFIG: PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US, Define a default minimum time between sampling the ROSC random bit, min=5, max=20, default=10, group=pico_rand
#ifndef PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US
// (Arbitrary / tested) minimum time between sampling the ROSC random bit
#define PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US 10u
#endif
// ---------------------------------------------
// PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER CONFIG
// ---------------------------------------------
// PICO_CONFIG: PICO_RAND_BUS_PERF_COUNTER_INDEX, Bus performance counter index to use for sourcing entropy, min=0, max=3, group=pico_rand
// this is deliberately undefined by default, meaning the code will pick that appears unused
//#define PICO_RAND_BUS_PERF_COUNTER_INDEX 0
// PICO_CONFIG: PICO_RAND_BUS_PERF_COUNTER_EVENT, Bus performance counter event to use for sourcing entropy, default=arbiter_sram5_perf_event_access, group=pico_rand
#ifndef PICO_RAND_BUS_PERF_COUNTER_EVENT
#define PICO_RAND_BUS_PERF_COUNTER_EVENT arbiter_sram5_perf_event_access
#endif
// ------------------------------------------
// PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH CONFIG
// ------------------------------------------
// PICO_CONFIG: PICO_RAND_RAM_HASH_END, end of address in RAM (non-inclusive) to hash during pico_rand seed initialization, default=SRAM_END, group=pico_rand
#ifndef PICO_RAND_RAM_HASH_END
#define PICO_RAND_RAM_HASH_END SRAM_END
#endif
// PICO_CONFIG: PICO_RAND_RAM_HASH_START, start of address in RAM (inclusive) to hash during pico_rand seed initialization, default=PICO_RAND_RAM_HASH_END-1024, group=pico_rand
#ifndef PICO_RAND_RAM_HASH_START
#define PICO_RAND_RAM_HASH_START (PICO_RAND_RAM_HASH_END - 1024u)
#endif
// We provide a maximum of 128 bits entropy in one go
typedef struct rng_128 {
uint64_t r[2];
} rng_128_t;
/*! \brief Get 128-bit random number
* \ingroup pico_rand
*
* \param rand128 Pointer to storage to accept a 128-bit random number
*/
void get_rand_128(rng_128_t *rand128);
/*! \brief Get 64-bit random number
* \ingroup pico_rand
*
* \return 64-bit random number
*/
uint64_t get_rand_64(void);
/*! \brief Get 32-bit random number
* \ingroup pico_rand
*
* \return 32-bit random number
*/
uint32_t get_rand_32(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,303 @@
/*
* 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/bus_ctrl.h"
#include "hardware/sync.h"
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
}
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;
}
return hash;
}
#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);
} else {
sleep_until(next_sample_time);
}
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;
}
}
spin_unlock(lock, save);
} while (!bit_done);
}
return samples;
}
#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_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
spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
uint32_t save = spin_lock_blocking(lock);
if (!rng_initialised) {
#if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
#if !PICO_RAND_BUSCTRL_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;
}
}
hard_assert(idx != -1);
bus_counter_idx = (uint8_t)idx;
#else
bus_counter_idx = (uint8_t)PICO_RAND_BUSCTRL_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;
}
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();
}
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_BUS_PERF_COUNTER
uint32_t bus_counter_value = bus_ctrl_hw->counter[bus_counter_idx].value;
// counter is saturating, so clear it if it has reached saturation
if (bus_counter_value == BUSCTRL_PERFCTR0_BITS) {
bus_ctrl_hw->counter[bus_counter_idx].value = 0;
}
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];
}
// 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();
}

View File

@ -40,6 +40,7 @@ target_link_libraries(kitchen_sink_libs INTERFACE
pico_time
pico_unique_id
pico_util
pico_rand
)
add_library(kitchen_sink_options INTERFACE)

View File

@ -47,6 +47,7 @@
#include "pico/sync.h"
#include "pico/time.h"
#include "pico/unique_id.h"
#include "pico/rand.h"
#if LIB_PICO_CYW43_ARCH
#include "pico/cyw43_arch.h"
#endif