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_async_context pico_async_context
|
||||||
* \defgroup pico_multicore pico_multicore
|
* \defgroup pico_multicore pico_multicore
|
||||||
|
* \defgroup pico_rand pico_rand
|
||||||
* \defgroup pico_stdlib pico_stdlib
|
* \defgroup pico_stdlib pico_stdlib
|
||||||
* \defgroup pico_sync pico_sync
|
* \defgroup pico_sync pico_sync
|
||||||
* \defgroup pico_time pico_time
|
* \defgroup pico_time pico_time
|
||||||
|
@ -49,6 +49,7 @@ if (NOT PICO_BARE_METAL)
|
|||||||
pico_add_subdirectory(pico_mem_ops)
|
pico_add_subdirectory(pico_mem_ops)
|
||||||
pico_add_subdirectory(pico_malloc)
|
pico_add_subdirectory(pico_malloc)
|
||||||
pico_add_subdirectory(pico_printf)
|
pico_add_subdirectory(pico_printf)
|
||||||
|
pico_add_subdirectory(pico_rand)
|
||||||
|
|
||||||
pico_add_subdirectory(pico_stdio)
|
pico_add_subdirectory(pico_stdio)
|
||||||
pico_add_subdirectory(pico_stdio_semihosting)
|
pico_add_subdirectory(pico_stdio_semihosting)
|
||||||
|
@ -70,6 +70,11 @@ typedef volatile uint32_t spin_lock_t;
|
|||||||
#define PICO_SPINLOCK_ID_HARDWARE_CLAIM 11
|
#define PICO_SPINLOCK_ID_HARDWARE_CLAIM 11
|
||||||
#endif
|
#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
|
// 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
|
#ifndef PICO_SPINLOCK_ID_OS1
|
||||||
#define PICO_SPINLOCK_ID_OS1 14
|
#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/tcp_out.c
|
||||||
${PICO_LWIP_PATH}/src/core/timeouts.c
|
${PICO_LWIP_PATH}/src/core/timeouts.c
|
||||||
${PICO_LWIP_PATH}/src/core/udp.c
|
${PICO_LWIP_PATH}/src/core/udp.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lwip_random.c
|
|
||||||
)
|
)
|
||||||
target_include_directories(pico_lwip_core INTERFACE
|
target_include_directories(pico_lwip_core INTERFACE
|
||||||
${PICO_LWIP_PATH}/src/include)
|
${PICO_LWIP_PATH}/src/include)
|
||||||
@ -274,7 +273,8 @@ if (EXISTS ${PICO_LWIP_PATH}/${LWIP_TEST_PATH})
|
|||||||
target_link_libraries(pico_lwip_nosys INTERFACE
|
target_link_libraries(pico_lwip_nosys INTERFACE
|
||||||
pico_async_context_base
|
pico_async_context_base
|
||||||
pico_lwip_arch
|
pico_lwip_arch
|
||||||
pico_lwip)
|
pico_lwip
|
||||||
|
pico_rand)
|
||||||
|
|
||||||
if (NOT PICO_LWIP_CONTRIB_PATH)
|
if (NOT PICO_LWIP_CONTRIB_PATH)
|
||||||
set(PICO_LWIP_CONTRIB_PATH ${PICO_LWIP_PATH}/contrib)
|
set(PICO_LWIP_CONTRIB_PATH ${PICO_LWIP_PATH}/contrib)
|
||||||
|
@ -86,14 +86,8 @@ void pico_lwip_custom_unlock_tcpip_core(void);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LWIP_RAND
|
#ifndef LWIP_RAND
|
||||||
#ifdef __cplusplus
|
#include "pico/rand.h"
|
||||||
extern "C" {
|
// Use the pico_rand library which goes to reasonable lengths to try to provide good entropy
|
||||||
#endif
|
#define LWIP_RAND() get_rand_32()
|
||||||
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
|
|
||||||
#endif
|
#endif
|
||||||
#endif /* __CC_H__ */
|
#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.
|
* 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>`
|
* 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
|
* \param group a string suffix to use in the section name to distinguish groups that can be linker
|
||||||
* garbage-collected independently
|
* 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
|
/*! \brief Section attribute macro for placement in flash even in a COPY_TO_RAM binary
|
||||||
* \ingroup pico_platform
|
* \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_time
|
||||||
pico_unique_id
|
pico_unique_id
|
||||||
pico_util
|
pico_util
|
||||||
|
pico_rand
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(kitchen_sink_options INTERFACE)
|
add_library(kitchen_sink_options INTERFACE)
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include "pico/sync.h"
|
#include "pico/sync.h"
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
#include "pico/unique_id.h"
|
#include "pico/unique_id.h"
|
||||||
|
#include "pico/rand.h"
|
||||||
#if LIB_PICO_CYW43_ARCH
|
#if LIB_PICO_CYW43_ARCH
|
||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user