mirror of
https://github.com/raspberrypi/pico-sdk.git
synced 2025-12-10 07:14:36 +01:00
pico_rand (#2598)
This commit is contained in:
parent
4635b37e68
commit
3f6f0fc0ef
5 changed files with 276 additions and 0 deletions
|
|
@ -31,6 +31,7 @@ include (${CMAKE_DIR}/no_hardware.cmake)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_divider)
|
pico_add_subdirectory(${HOST_DIR}/pico_divider)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_multicore)
|
pico_add_subdirectory(${HOST_DIR}/pico_multicore)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_platform)
|
pico_add_subdirectory(${HOST_DIR}/pico_platform)
|
||||||
|
pico_add_subdirectory(${HOST_DIR}/pico_rand)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_runtime)
|
pico_add_subdirectory(${HOST_DIR}/pico_runtime)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_printf)
|
pico_add_subdirectory(${HOST_DIR}/pico_printf)
|
||||||
pico_add_subdirectory(${HOST_DIR}/pico_stdio)
|
pico_add_subdirectory(${HOST_DIR}/pico_stdio)
|
||||||
|
|
|
||||||
13
src/host/pico_rand/BUILD.bazel
Normal file
13
src/host/pico_rand/BUILD.bazel
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "pico_rand",
|
||||||
|
srcs = ["rand.c"],
|
||||||
|
hdrs = ["include/pico/rand.h"],
|
||||||
|
includes = ["include"],
|
||||||
|
target_compatible_with = ["//bazel/constraint:host"],
|
||||||
|
deps = [
|
||||||
|
"//src/host/hardware_sync",
|
||||||
|
"//src/host/hardware_timer",
|
||||||
|
],
|
||||||
|
)
|
||||||
12
src/host/pico_rand/CMakeLists.txt
Normal file
12
src/host/pico_rand/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
pico_add_library(pico_rand)
|
||||||
|
|
||||||
|
target_sources(pico_rand INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/rand.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(pico_rand_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
|
||||||
|
pico_mirrored_target_link_libraries(pico_rand INTERFACE
|
||||||
|
hardware_timer
|
||||||
|
hardware_sync
|
||||||
|
)
|
||||||
89
src/host/pico_rand/include/pico/rand.h
Normal file
89
src/host/pico_rand/include/pico/rand.h
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PICO_RAND_H
|
||||||
|
#define _PICO_RAND_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \file pico/rand.h
|
||||||
|
* \defgroup pico_rand pico_rand
|
||||||
|
*
|
||||||
|
* \brief Random Number Generator API
|
||||||
|
*
|
||||||
|
* This module generates random numbers at runtime utilizing 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:
|
||||||
|
*
|
||||||
|
* - Time (\ref PICO_RAND_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed in each time.
|
||||||
|
*
|
||||||
|
* \note All entropy sources are hashed before application to the PRNG state machine.
|
||||||
|
*
|
||||||
|
* 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:
|
||||||
|
*
|
||||||
|
* - Time (\ref PICO_RAND_SEED_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed into the seed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// ENTROPY SOURCES
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// SEED ENTROPY SOURCES
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
// 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=PICO_RAND_ENTROPY_SRC_TIME, group=pico_rand
|
||||||
|
#ifndef PICO_RAND_SEED_ENTROPY_SRC_TIME
|
||||||
|
#define PICO_RAND_SEED_ENTROPY_SRC_TIME PICO_RAND_ENTROPY_SRC_TIME
|
||||||
|
#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
|
||||||
161
src/host/pico_rand/rand.c
Normal file
161
src/host/pico_rand/rand.c
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
#if PICO_RAND_ENTROPY_SRC_TIME
|
||||||
|
#include "hardware/timer.h"
|
||||||
|
#endif
|
||||||
|
#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 rng_state;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialise_rand(void) {
|
||||||
|
rng_128_t local_rng_state = local_rng_state;
|
||||||
|
uint which = 0;
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
(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
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue