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:
parent
67af83f069
commit
b70f984f2a
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
13
src/rp2_common/pico_rand/CMakeLists.txt
Normal file
13
src/rp2_common/pico_rand/CMakeLists.txt
Normal 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)
|
173
src/rp2_common/pico_rand/include/pico/rand.h
Normal file
173
src/rp2_common/pico_rand/include/pico/rand.h
Normal 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
|
303
src/rp2_common/pico_rand/rand.c
Normal file
303
src/rp2_common/pico_rand/rand.c
Normal 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();
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user