This commit is contained in:
el-bert0 2025-07-28 19:00:41 +02:00 committed by GitHub
parent 4635b37e68
commit 3f6f0fc0ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 276 additions and 0 deletions

View file

@ -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)

View 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",
],
)

View 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
)

View 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
View 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();
}