Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 496ffbd

Browse files
authoredJun 28, 2024··
Merge pull request #895 from pennam/trng
Enable (t)rng support for STM32H7 and NANO RP2040
2 parents 508d85e + 447062f commit 496ffbd

File tree

2 files changed

+758
-0
lines changed

2 files changed

+758
-0
lines changed
 

‎cores/arduino/WMath.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,36 @@
2323

2424
extern "C" {
2525
#include "stdlib.h"
26+
#include "hal/trng_api.h"
2627
}
28+
#if defined(ARDUINO_NANO_RP2040_CONNECT) || \
29+
defined(ARDUINO_PORTENTA_H7_M7) || \
30+
defined(ARDUINO_NICLA_VISION) || \
31+
defined(ARDUINO_OPTA) || \
32+
defined(ARDUINO_GIGA)
33+
#define MBED_TRNG_SUPPORT 1
34+
static long trng()
35+
{
36+
trng_t trng_obj;
37+
trng_init(&trng_obj);
38+
long value;
39+
size_t olen;
40+
if (trng_get_bytes(&trng_obj, (uint8_t*)&value, sizeof(value), &olen) != 0)
41+
return -1;
42+
trng_free(&trng_obj);
43+
return value >= 0 ? value : -value;
44+
}
45+
#endif
46+
47+
#if (MBED_TRNG_SUPPORT == 1)
48+
static bool useTRNG = true;
49+
#endif
2750

2851
void randomSeed(unsigned long seed)
2952
{
53+
#if (MBED_TRNG_SUPPORT == 1)
54+
useTRNG = false;
55+
#endif
3056
if (seed != 0) {
3157
srandom(seed);
3258
}
@@ -37,6 +63,11 @@ long random(long howbig)
3763
if (howbig == 0) {
3864
return 0;
3965
}
66+
#if (MBED_TRNG_SUPPORT == 1)
67+
if (useTRNG == true) {
68+
return trng() % howbig;
69+
}
70+
#endif
4071
return random() % howbig;
4172
}
4273

@@ -48,3 +79,5 @@ long random(long howsmall, long howbig)
4879
long diff = howbig - howsmall;
4980
return random(diff) + howsmall;
5081
}
82+
83+
#undef MBED_TRNG_SUPPORT
Lines changed: 725 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,725 @@
1+
From ac5c21fad54214e10a3558f29195d7ea70b4ccec Mon Sep 17 00:00:00 2001
2+
From: pennam <m.pennasilico@arduino.cc>
3+
Date: Mon, 17 Jun 2024 10:19:14 +0200
4+
Subject: [PATCH] RP2040: add pico_rand support
5+
6+
---
7+
.../TARGET_RP2040/CMakeLists.txt | 2 +
8+
.../TARGET_RP2040/objects.h | 7 +
9+
.../pico-sdk/rp2_common/CMakeLists.txt | 1 +
10+
.../hardware_sync/include/hardware/sync.h | 5 +
11+
.../pico_platform/include/pico/platform.h | 6 +-
12+
.../rp2_common/pico_rand/CMakeLists.txt | 13 +
13+
.../rp2_common/pico_rand/include/pico/rand.h | 185 +++++++++++
14+
.../pico-sdk/rp2_common/pico_rand/rand.c | 305 ++++++++++++++++++
15+
.../TARGET_RP2040/trng_api.c | 64 ++++
16+
targets/targets.json | 3 +-
17+
10 files changed, 587 insertions(+), 4 deletions(-)
18+
create mode 100644 targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/CMakeLists.txt
19+
create mode 100644 targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/include/pico/rand.h
20+
create mode 100644 targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/rand.c
21+
create mode 100644 targets/TARGET_RASPBERRYPI/TARGET_RP2040/trng_api.c
22+
23+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/CMakeLists.txt b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/CMakeLists.txt
24+
index 4fadf6091e..4e9bae47ce 100644
25+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/CMakeLists.txt
26+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/CMakeLists.txt
27+
@@ -36,6 +36,7 @@ target_include_directories(mbed-rp2040
28+
pico-sdk/common/pico_base/include
29+
pico-sdk/common/pico_binary_info/include
30+
pico-sdk/common/pico_util/include
31+
+ pico-sdk/common/pico_rand/include
32+
pico-sdk/boards/include
33+
pico-sdk/generated
34+
.
35+
@@ -71,6 +72,7 @@ target_sources(mbed-rp2040
36+
pico-sdk/rp2_common/pico_bootrom/bootrom.c
37+
pico-sdk/rp2_common/pico_runtime/runtime.c
38+
pico-sdk/rp2_common/pico_platform/platform.c
39+
+ pico-sdk/rp2_common/pico_rand/rand.c
40+
pico-sdk/common/pico_sync/mutex.c
41+
pico-sdk/common/pico_time/time.c
42+
pico-sdk/common/pico_sync/lock_core.c
43+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/objects.h b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/objects.h
44+
index f5f44a58f4..0231968bb5 100644
45+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/objects.h
46+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/objects.h
47+
@@ -33,6 +33,7 @@ extern "C" {
48+
#include "pico/assert.h"
49+
#include "pico/time.h"
50+
#include "pico/types.h"
51+
+#include "pico/rand.h"
52+
#include "hardware/pwm.h"
53+
#include "hardware/adc.h"
54+
#include "hardware/resets.h"
55+
@@ -122,6 +123,12 @@ struct flash_s {
56+
uint32_t dummy;
57+
};
58+
59+
+#if DEVICE_TRNG
60+
+struct trng_s {
61+
+ uint8_t not_used;
62+
+};
63+
+#endif
64+
+
65+
typedef struct gpio_s gpio_t;
66+
typedef struct serial_s serial_t;
67+
68+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/CMakeLists.txt b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/CMakeLists.txt
69+
index 4ca55becba..ec89c3ff71 100644
70+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/CMakeLists.txt
71+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/CMakeLists.txt
72+
@@ -49,6 +49,7 @@ if (NOT PICO_BARE_METAL)
73+
pico_add_subdirectory(pico_mem_ops)
74+
pico_add_subdirectory(pico_malloc)
75+
pico_add_subdirectory(pico_printf)
76+
+ pico_add_subdirectory(pico_rand)
77+
78+
pico_add_subdirectory(pico_stdio)
79+
pico_add_subdirectory(pico_stdio_semihosting)
80+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_sync/include/hardware/sync.h b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_sync/include/hardware/sync.h
81+
index 8f91d55955..4f076aba02 100644
82+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_sync/include/hardware/sync.h
83+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/hardware_sync/include/hardware/sync.h
84+
@@ -70,6 +70,11 @@ typedef volatile uint32_t spin_lock_t;
85+
#define PICO_SPINLOCK_ID_HARDWARE_CLAIM 11
86+
#endif
87+
88+
+// PICO_CONFIG: PICO_SPINLOCK_ID_RAND, Spinlock ID for Random Number Generator, min=0, max=31, default=12, group=hardware_sync
89+
+#ifndef PICO_SPINLOCK_ID_RAND
90+
+#define PICO_SPINLOCK_ID_RAND 12
91+
+#endif
92+
+
93+
// 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
94+
#ifndef PICO_SPINLOCK_ID_OS1
95+
#define PICO_SPINLOCK_ID_OS1 14
96+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_platform/include/pico/platform.h b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_platform/include/pico/platform.h
97+
index ee1d360cee..ea16d9734e 100644
98+
--- a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_platform/include/pico/platform.h
99+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_platform/include/pico/platform.h
100+
@@ -151,14 +151,14 @@ extern "C" {
101+
*
102+
* For example a `uint32_t` foo that will retain its value if the program is restarted by reset.
103+
*
104+
- * uint32_t __uninitialized_ram("my_group_name") foo;
105+
+ * uint32_t __uninitialized_ram(foo);
106+
*
107+
- * The section attribute is `.uninitialized_ram.<group>`
108+
+ * The section attribute is `.uninitialized_data.<group>`
109+
*
110+
* \param group a string suffix to use in the section name to distinguish groups that can be linker
111+
* garbage-collected independently
112+
*/
113+
-#define __uninitialized_ram(group) __attribute__((section(".uninitialized_ram." #group))) group
114+
+#define __uninitialized_ram(group) __attribute__((section(".uninitialized_data." #group))) group
115+
116+
/*! \brief Section attribute macro for placement in flash even in a COPY_TO_RAM binary
117+
* \ingroup pico_platform
118+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/CMakeLists.txt b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/CMakeLists.txt
119+
new file mode 100644
120+
index 0000000000..0e72bb5ab6
121+
--- /dev/null
122+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/CMakeLists.txt
123+
@@ -0,0 +1,13 @@
124+
+pico_add_library(pico_rand)
125+
+
126+
+target_sources(pico_rand INTERFACE
127+
+ ${CMAKE_CURRENT_LIST_DIR}/rand.c
128+
+)
129+
+
130+
+target_include_directories(pico_rand_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
131+
+
132+
+pico_mirrored_target_link_libraries(pico_rand INTERFACE
133+
+ pico_unique_id
134+
+ hardware_clocks
135+
+ hardware_timer
136+
+ hardware_sync)
137+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/include/pico/rand.h b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/include/pico/rand.h
138+
new file mode 100644
139+
index 0000000000..20fc6d6cb1
140+
--- /dev/null
141+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/include/pico/rand.h
142+
@@ -0,0 +1,185 @@
143+
+/*
144+
+ * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
145+
+ *
146+
+ * SPDX-License-Identifier: BSD-3-Clause
147+
+ */
148+
+
149+
+#ifndef _PICO_RAND_H
150+
+#define _PICO_RAND_H
151+
+
152+
+#include "pico.h"
153+
+
154+
+#ifdef __cplusplus
155+
+extern "C" {
156+
+#endif
157+
+
158+
+/** \file pico/rand.h
159+
+ * \defgroup pico_rand pico_rand
160+
+ *
161+
+ * Random Number Generator API
162+
+ *
163+
+ * This module generates random numbers at runtime utilizing a number of possible entropy
164+
+ * sources and uses those sources to modify the state of a 128-bit 'Pseudo
165+
+ * Random Number Generator' implemented in software.
166+
+ *
167+
+ * The random numbers (32 to 128 bit) to be supplied are read from the PRNG which is used
168+
+ * to help provide a large number space.
169+
+ *
170+
+ * The following (multiple) sources of entropy are available (of varying quality), each enabled by a #define:
171+
+ *
172+
+ * - The Ring Oscillator (ROSC) (\ref PICO_RAND_ENTROPY_SRC_ROSC == 1):
173+
+ * \ref PICO_RAND_ROSC_BIT_SAMPLE_COUNT bits are gathered from the ring oscillator "random bit" and mixed in each
174+
+ * time. This should not be used if the ROSC is off, or the processor is running from
175+
+ * the ROSC.
176+
+ * \note the maximum throughput of ROSC bit sampling is controlled by PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US which defaults
177+
+ * to 10us, i.e. 100,000 bits per second.
178+
+ * - Time (\ref PICO_RAND_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed in each time.
179+
+ * - Bus Performance Counter (\ref PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER == 1): One of the bus fabric's performance
180+
+ * counters is mixed in each time.
181+
+ *
182+
+ * \note All entropy sources are hashed before application to the PRNG state machine.
183+
+ *
184+
+ * The \em first time a random number is requested, the 128-bit PRNG state
185+
+ * must be seeded. Multiple entropy sources are also available for the seeding operation:
186+
+ *
187+
+ * - The Ring Oscillator (ROSC) (\ref PICO_RAND_SEED_ENTROPY_SRC_ROSC == 1):
188+
+ * 64 bits are gathered from the ring oscillator "random bit" and mixed into the seed.
189+
+ * - Time (\ref PICO_RAND_SEED_ENTROPY_SRC_TIME == 1): The 64-bit microsecond timer is mixed into the seed.
190+
+ * - Board Identifier (PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID == 1): The board id via \ref pico_get_unique_board_id
191+
+ * is mixed into the seed.
192+
+ * - RAM hash (\ref PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH (\ref PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH): The hashed contents of a
193+
+ * subset of RAM are mixed in. Initial RAM contents are undefined on power up, so provide a reasonable source of entropy.
194+
+ * By default the last 1K of RAM (which usually contains the core 0 stack) is hashed, which may also provide for differences
195+
+ * after each warm reset.
196+
+ *
197+
+ * With default settings, the seed generation takes approximately 1 millisecond while
198+
+ * subsequent random numbers generally take between 10 and 20 microseconds to generate.
199+
+ *
200+
+ * pico_rand methods may be safely called from either core or from an IRQ, but be careful in the latter case as
201+
+ * the calls may block for a number of microseconds waiting on more entropy.
202+
+ */
203+
+
204+
+// ---------------
205+
+// ENTROPY SOURCES
206+
+// ---------------
207+
+
208+
+// PICO_CONFIG: PICO_RAND_ENTROPY_SRC_ROSC, Enable/disable use of ROSC as an entropy source, type=bool, default=1, group=pico_rand
209+
+#ifndef PICO_RAND_ENTROPY_SRC_ROSC
210+
+#define PICO_RAND_ENTROPY_SRC_ROSC 1
211+
+#endif
212+
+
213+
+// PICO_CONFIG: PICO_RAND_ENTROPY_SRC_TIME, Enable/disable use of hardware timestamp as an entropy source, type=bool, default=1, group=pico_rand
214+
+#ifndef PICO_RAND_ENTROPY_SRC_TIME
215+
+#define PICO_RAND_ENTROPY_SRC_TIME 1
216+
+#endif
217+
+
218+
+// 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
219+
+#ifndef PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
220+
+#define PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER 1
221+
+#endif
222+
+
223+
+// --------------------
224+
+// SEED ENTROPY SOURCES
225+
+// --------------------
226+
+
227+
+// 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
228+
+#ifndef PICO_RAND_SEED_ENTROPY_SRC_ROSC
229+
+#define PICO_RAND_SEED_ENTROPY_SRC_ROSC PICO_RAND_ENTROPY_SRC_ROSC
230+
+#endif
231+
+
232+
+// 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
233+
+#ifndef PICO_RAND_SEED_ENTROPY_SRC_TIME
234+
+#define PICO_RAND_SEED_ENTROPY_SRC_TIME PICO_RAND_ENTROPY_SRC_TIME
235+
+#endif
236+
+
237+
+// 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
238+
+#ifndef PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID
239+
+#define PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID 0
240+
+#endif
241+
+
242+
+// 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
243+
+#ifndef PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
244+
+#define PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH 1
245+
+#endif
246+
+
247+
+// ---------------------------------
248+
+// PICO_RAND_ENTROPY_SRC_ROSC CONFIG
249+
+// ---------------------------------
250+
+
251+
+// 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
252+
+#ifndef PICO_RAND_ROSC_BIT_SAMPLE_COUNT
253+
+#define PICO_RAND_ROSC_BIT_SAMPLE_COUNT 1
254+
+#endif
255+
+
256+
+// 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
257+
+#ifndef PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US
258+
+// (Arbitrary / tested) minimum time between sampling the ROSC random bit
259+
+#define PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US 10u
260+
+#endif
261+
+
262+
+// ---------------------------------------------
263+
+// PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER CONFIG
264+
+// ---------------------------------------------
265+
+
266+
+// PICO_CONFIG: PICO_RAND_BUS_PERF_COUNTER_INDEX, Bus performance counter index to use for sourcing entropy, min=0, max=3, group=pico_rand
267+
+// this is deliberately undefined by default, meaning the code will pick that appears unused
268+
+//#define PICO_RAND_BUS_PERF_COUNTER_INDEX 0
269+
+
270+
+// 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
271+
+#ifndef PICO_RAND_BUS_PERF_COUNTER_EVENT
272+
+#define PICO_RAND_BUS_PERF_COUNTER_EVENT arbiter_sram5_perf_event_access
273+
+#endif
274+
+
275+
+// ------------------------------------------
276+
+// PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH CONFIG
277+
+// ------------------------------------------
278+
+
279+
+// 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
280+
+#ifndef PICO_RAND_RAM_HASH_END
281+
+#define PICO_RAND_RAM_HASH_END SRAM_END
282+
+#endif
283+
+// 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
284+
+#ifndef PICO_RAND_RAM_HASH_START
285+
+#define PICO_RAND_RAM_HASH_START (PICO_RAND_RAM_HASH_END - 1024u)
286+
+#endif
287+
+
288+
+// We provide a maximum of 128 bits entropy in one go
289+
+typedef struct rng_128 {
290+
+ uint64_t r[2];
291+
+} rng_128_t;
292+
+
293+
+/*! \brief Get 128-bit random number
294+
+ * \ingroup pico_rand
295+
+ *
296+
+ * This method may be safely called from either core or from an IRQ, but be careful in the latter case as
297+
+ * the call may block for a number of microseconds waiting on more entropy.
298+
+ *
299+
+ * \param rand128 Pointer to storage to accept a 128-bit random number
300+
+ */
301+
+void get_rand_128(rng_128_t *rand128);
302+
+
303+
+/*! \brief Get 64-bit random number
304+
+ * \ingroup pico_rand
305+
+ *
306+
+ * This method may be safely called from either core or from an IRQ, but be careful in the latter case as
307+
+ * the call may block for a number of microseconds waiting on more entropy.
308+
+ *
309+
+ * \return 64-bit random number
310+
+ */
311+
+uint64_t get_rand_64(void);
312+
+
313+
+/*! \brief Get 32-bit random number
314+
+ * \ingroup pico_rand
315+
+ *
316+
+ * This method may be safely called from either core or from an IRQ, but be careful in the latter case as
317+
+ * the call may block for a number of microseconds waiting on more entropy.
318+
+ *
319+
+ * \return 32-bit random number
320+
+ */
321+
+uint32_t get_rand_32(void);
322+
+
323+
+#ifdef __cplusplus
324+
+}
325+
+#endif
326+
+
327+
+#endif
328+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/rand.c b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/rand.c
329+
new file mode 100644
330+
index 0000000000..c73b80321e
331+
--- /dev/null
332+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_rand/rand.c
333+
@@ -0,0 +1,305 @@
334+
+/*
335+
+ * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
336+
+ *
337+
+ * SPDX-License-Identifier: BSD-3-Clause
338+
+ */
339+
+
340+
+/* xoroshiro128ss(), rotl():
341+
+
342+
+ Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
343+
+
344+
+ To the extent possible under law, the author has dedicated all copyright
345+
+ and related and neighboring rights to this software to the public domain
346+
+ worldwide. This software is distributed without any warranty.
347+
+
348+
+ See <http://creativecommons.org/publicdomain/zero/1.0/>
349+
+
350+
+ splitmix64() implementation:
351+
+
352+
+ Written in 2015 by Sebastiano Vigna (vigna@acm.org)
353+
+ To the extent possible under law, the author has dedicated all copyright
354+
+ and related and neighboring rights to this software to the public domain
355+
+ worldwide. This software is distributed without any warranty.
356+
+
357+
+ See <http://creativecommons.org/publicdomain/zero/1.0/>
358+
+*/
359+
+
360+
+#include "pico/rand.h"
361+
+#if PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID
362+
+#include "pico/unique_id.h"
363+
+#endif
364+
+#include "pico/time.h"
365+
+#include "hardware/clocks.h"
366+
+#include "hardware/structs/rosc.h"
367+
+#include "hardware/structs/bus_ctrl.h"
368+
+#include "hardware/sync.h"
369+
+
370+
+static bool rng_initialised = false;
371+
+
372+
+// Note: By design, do not initialise any of the variables that hold entropy,
373+
+// they may have useful junk in them, either from power-up or a previous boot.
374+
+static rng_128_t __uninitialized_ram(rng_state);
375+
+#if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
376+
+static uint64_t __uninitialized_ram(ram_hash);
377+
+#endif
378+
+
379+
+#if PICO_RAND_ENTROPY_SRC_ROSC | PICO_RAND_SEED_ENTROPY_SRC_ROSC
380+
+static uint64_t __uninitialized_ram(rosc_samples);
381+
+#endif
382+
+
383+
+#if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
384+
+static uint8_t bus_counter_idx;
385+
+#endif
386+
+
387+
+/* From the original source:
388+
+
389+
+ This is a fixed-increment version of Java 8's SplittableRandom generator
390+
+ See http://dx.doi.org/10.1145/2714064.2660195 and
391+
+ http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
392+
+
393+
+ It is a very fast generator passing BigCrush, and it can be useful if
394+
+ for some reason you absolutely want 64 bits of state; otherwise, we
395+
+ rather suggest to use a xoroshiro128+ (for moderately parallel
396+
+ computations) or xorshift1024* (for massively parallel computations)
397+
+ generator.
398+
+
399+
+ Note: This can be called with any value (i.e. including 0)
400+
+*/
401+
+static __noinline uint64_t splitmix64(uint64_t x) {
402+
+ uint64_t z = x + 0x9E3779B97F4A7C15ull;
403+
+ z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull;
404+
+ z = (z ^ (z >> 27)) * 0x94D049BB133111EBull;
405+
+ return z ^ (z >> 31);
406+
+}
407+
+
408+
+/* From the original source:
409+
+
410+
+ This is xoroshiro128** 1.0, one of our all-purpose, rock-solid,
411+
+ small-state generators. It is extremely (sub-ns) fast and it passes all
412+
+ tests we are aware of, but its state space is large enough only for
413+
+ mild parallelism.
414+
+
415+
+ For generating just floating-point numbers, xoroshiro128+ is even
416+
+ faster (but it has a very mild bias, see notes in the comments).
417+
+
418+
+ The state must be seeded so that it is not everywhere zero. If you have
419+
+ a 64-bit seed, we suggest to seed a splitmix64 generator and use its
420+
+ output to fill s.
421+
+*/
422+
+static inline uint64_t rotl(const uint64_t x, int k) {
423+
+ return (x << k) | (x >> (64 - k));
424+
+}
425+
+
426+
+static __noinline uint64_t xoroshiro128ss(rng_128_t *local_rng_state) {
427+
+ const uint64_t s0 = local_rng_state->r[0];
428+
+ uint64_t s1 = local_rng_state->r[1];
429+
+
430+
+ // Because the state is *modified* outside of this function, there is a
431+
+ // 1 in 2^128 chance that it could be all zeroes (which is not allowed).
432+
+ while (s0 == 0 && s1 == 0) {
433+
+ s1 = time_us_64(); // should not be 0, but loop anyway
434+
+ }
435+
+
436+
+ const uint64_t result = rotl(s0 * 5, 7) * 9;
437+
+
438+
+ s1 ^= s0;
439+
+ local_rng_state->r[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b
440+
+ local_rng_state->r[1] = rotl(s1, 37); // c
441+
+
442+
+ return result;
443+
+}
444+
+
445+
+#if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
446+
+static uint64_t sdbm_hash64_sram(uint64_t hash) {
447+
+ // save some time by hashing a word at a time
448+
+ for (uint i = (PICO_RAND_RAM_HASH_START + 3) & ~3; i < PICO_RAND_RAM_HASH_END; i+=4) {
449+
+ uint32_t c = *(uint32_t *) i;
450+
+ hash = (uint64_t) c + (hash << 6) + (hash << 16) - hash;
451+
+ }
452+
+ return hash;
453+
+}
454+
+#endif
455+
+
456+
+#if PICO_RAND_SEED_ENTROPY_SRC_ROSC | PICO_RAND_ENTROPY_SRC_ROSC
457+
+/* gather an additional n bits of entropy, and shift them into the 64 bit entropy counter */
458+
+static uint64_t capture_additional_rosc_samples(uint n) {
459+
+ static absolute_time_t next_sample_time;
460+
+
461+
+ // provide an override if someone really wants it, but disabling ROSC as an entropy source makes more sense
462+
+#if !PICO_RAND_DISABLE_ROSC_CHECK
463+
+ // check that the ROSC is running but that the processors are NOT running from it
464+
+ hard_assert((rosc_hw->status & ROSC_STATUS_ENABLED_BITS) &&
465+
+ ((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)));
466+
+#endif
467+
+
468+
+ bool in_exception = __get_current_exception();
469+
+ assert(n); // save us having to special case samples for this
470+
+ uint64_t samples = 0;
471+
+ for(uint i=0; i<n; i++) {
472+
+ bool bit_done = false;
473+
+ do {
474+
+ // Ensure that the ROSC random bit is not sampled too quickly,
475+
+ // ROSC may be ticking only a few times a microsecond.
476+
+ // Note: In general (i.e. sporadic) use, very often there will be no delay here.
477+
+
478+
+ // note this is not read under lock, so the two 32 bit halves could be skewed, but in that
479+
+ // case we'll fail the check later, which is fine in this rare case
480+
+ absolute_time_t cached_next_sample_time = next_sample_time;
481+
+ // we support being called from IRQ, so be careful about sleeping... still not
482+
+ // ideal, but not much that can be done
483+
+ if (in_exception) {
484+
+ busy_wait_until(next_sample_time);
485+
+ } else {
486+
+ sleep_until(next_sample_time);
487+
+ }
488+
+ spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
489+
+ uint32_t save = spin_lock_blocking(lock);
490+
+ if (!absolute_time_diff_us(cached_next_sample_time, next_sample_time)) {
491+
+ // we won the race (if any) for the bit, so we collect it locally
492+
+ samples <<= 1;
493+
+ samples |= rosc_hw->randombit & 1u;
494+
+ // use of relative time to now, rather than offset from before makes things
495+
+ // a bit less predictable at the cost of some speed.
496+
+ next_sample_time = make_timeout_time_us(PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US);
497+
+ bit_done = true;
498+
+ if (i == n - 1) {
499+
+ // samples has our random bits, so let's mix them in now
500+
+ samples = rosc_samples = (rosc_samples << n) | samples;
501+
+ }
502+
+ }
503+
+ spin_unlock(lock, save);
504+
+ } while (!bit_done);
505+
+ }
506+
+ return samples;
507+
+}
508+
+#endif
509+
+
510+
+static void initialise_rand(void) {
511+
+ rng_128_t local_rng_state = local_rng_state;
512+
+ uint which = 0;
513+
+#if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
514+
+ ram_hash = sdbm_hash64_sram(ram_hash);
515+
+ local_rng_state.r[which] ^= splitmix64(ram_hash);
516+
+ which ^= 1;
517+
+#endif
518+
+
519+
+#if PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID
520+
+ static_assert(PICO_UNIQUE_BOARD_ID_SIZE_BYTES == sizeof(uint64_t),
521+
+ "Code below requires that 'board_id' is 64-bits in size");
522+
+
523+
+ // Note! The safety of the length assumption here is protected by a 'static_assert' above
524+
+ union unique_id_u {
525+
+ pico_unique_board_id_t board_id_native;
526+
+ uint64_t board_id_u64;
527+
+ } unique_id;
528+
+ // Note! The safety of the length assumption here is protected by a 'static_assert' above
529+
+ pico_get_unique_board_id(&unique_id.board_id_native);
530+
+ local_rng_state.r[which] ^= splitmix64(unique_id.board_id_u64);
531+
+ which ^= 1;
532+
+#endif
533+
+
534+
+#if PICO_RAND_SEED_ENTROPY_SRC_ROSC
535+
+ // this is really quite slow (10ms per iteration), and I'm not sure that it adds value over the 64 random bits
536+
+// uint ref_khz = clock_get_hz(clk_ref) / 100;
537+
+// for (int i = 0; i < 5; i++) {
538+
+// // Apply hash of the rosc frequency, limited but still 'extra' entropy
539+
+// uint measurement = frequency_count_raw(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC, ref_khz);
540+
+// local_rng_state.r[which] ^= splitmix64(measurement);
541+
+// (void) xoroshiro128ss(&local_rng_state); //churn to mix seed sources
542+
+// }
543+
+
544+
+ // Gather a full ROSC sample array with sample bits
545+
+ local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(8 * sizeof(rosc_samples)));
546+
+ which ^= 1;
547+
+#endif
548+
+
549+
+#if PICO_RAND_SEED_ENTROPY_SRC_TIME
550+
+ // Mix in hashed time. This is [possibly] predictable boot-to-boot
551+
+ // but will vary application-to-application.
552+
+ local_rng_state.r[which] ^= splitmix64(time_us_64());
553+
+ which ^= 1;
554+
+#endif
555+
+
556+
+ spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
557+
+ uint32_t save = spin_lock_blocking(lock);
558+
+ if (!rng_initialised) {
559+
+#if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
560+
+#if !PICO_RAND_BUSCTRL_COUNTER_INDEX
561+
+ int idx = -1;
562+
+ for(uint i = 0; i < count_of(bus_ctrl_hw->counter); i++) {
563+
+ if (bus_ctrl_hw->counter[i].sel == BUSCTRL_PERFSEL0_RESET) {
564+
+ idx = (int)i;
565+
+ break;
566+
+ }
567+
+ }
568+
+ hard_assert(idx != -1);
569+
+ bus_counter_idx = (uint8_t)idx;
570+
+#else
571+
+ bus_counter_idx = (uint8_t)PICO_RAND_BUSCTRL_COUNTER_INDEX;
572+
+#endif
573+
+ bus_ctrl_hw->counter[bus_counter_idx].sel = PICO_RAND_BUS_PERF_COUNTER_EVENT;
574+
+#endif
575+
+ (void) xoroshiro128ss(&local_rng_state);
576+
+ rng_state = local_rng_state;
577+
+ rng_initialised = true;
578+
+ }
579+
+ spin_unlock(lock, save);
580+
+}
581+
+
582+
+uint64_t get_rand_64(void) {
583+
+ if (!rng_initialised) {
584+
+ // Do not provide 'RNs' until the system has been initialised. Note:
585+
+ // The first initialisation can be quite time-consuming depending on
586+
+ // the amount of RAM hashed, see RAM_HASH_START and RAM_HASH_END
587+
+ initialise_rand();
588+
+ }
589+
+
590+
+ static volatile uint8_t check_byte;
591+
+ rng_128_t local_rng_state = rng_state;
592+
+ uint8_t local_check_byte = check_byte;
593+
+ // Modify PRNG state with the run-time entropy sources,
594+
+ // hashed to reduce correlation with previous modifications.
595+
+ uint which = 0;
596+
+#if PICO_RAND_ENTROPY_SRC_TIME
597+
+ local_rng_state.r[which] ^= splitmix64(time_us_64());
598+
+ which ^= 1;
599+
+#endif
600+
+#if PICO_RAND_ENTROPY_SRC_ROSC
601+
+ local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(PICO_RAND_ROSC_BIT_SAMPLE_COUNT));
602+
+ which ^= 1;
603+
+#endif
604+
+#if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
605+
+ uint32_t bus_counter_value = bus_ctrl_hw->counter[bus_counter_idx].value;
606+
+ // counter is saturating, so clear it if it has reached saturation
607+
+ if (bus_counter_value == BUSCTRL_PERFCTR0_BITS) {
608+
+ bus_ctrl_hw->counter[bus_counter_idx].value = 0;
609+
+ }
610+
+ local_rng_state.r[which] ^= splitmix64(bus_counter_value);
611+
+ which ^= 1;
612+
+#endif
613+
+
614+
+ spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
615+
+ uint32_t save = spin_lock_blocking(lock);
616+
+ if (local_check_byte != check_byte) {
617+
+ // someone got a random number in the interim, so mix it in
618+
+ local_rng_state.r[0] ^= rng_state.r[0];
619+
+ local_rng_state.r[1] ^= rng_state.r[1];
620+
+ }
621+
+ // Generate a 64-bit RN from the modified PRNG state.
622+
+ // Note: This also "churns" the 128-bit state for next time.
623+
+ uint64_t rand64 = xoroshiro128ss(&local_rng_state);
624+
+ rng_state = local_rng_state;
625+
+ check_byte++;
626+
+ spin_unlock(lock, save);
627+
+
628+
+ return rand64;
629+
+}
630+
+
631+
+void get_rand_128(rng_128_t *ptr128) {
632+
+ ptr128->r[0] = get_rand_64();
633+
+ ptr128->r[1] = get_rand_64();
634+
+}
635+
+
636+
+uint32_t get_rand_32(void) {
637+
+ return (uint32_t) get_rand_64();
638+
+}
639+
diff --git a/targets/TARGET_RASPBERRYPI/TARGET_RP2040/trng_api.c b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/trng_api.c
640+
new file mode 100644
641+
index 0000000000..b0886badcc
642+
--- /dev/null
643+
+++ b/targets/TARGET_RASPBERRYPI/TARGET_RP2040/trng_api.c
644+
@@ -0,0 +1,64 @@
645+
+/* mbed Microcontroller Library
646+
+ * Copyright (c) 2018 GigaDevice Semiconductor Inc.
647+
+ *
648+
+ * SPDX-License-Identifier: Apache-2.0
649+
+ *
650+
+ * Licensed under the Apache License, Version 2.0 (the "License");
651+
+ * you may not use this file except in compliance with the License.
652+
+ * You may obtain a copy of the License at
653+
+ *
654+
+ * http://www.apache.org/licenses/LICENSE-2.0
655+
+ *
656+
+ * Unless required by applicable law or agreed to in writing, software
657+
+ * distributed under the License is distributed on an "AS IS" BASIS,
658+
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
659+
+ * See the License for the specific language governing permissions and
660+
+ * limitations under the License.
661+
+ */
662+
+#if DEVICE_TRNG
663+
+
664+
+#include "trng_api.h"
665+
+
666+
+/** Initialize the TRNG peripheral
667+
+ *
668+
+ * @param obj The TRNG object
669+
+ */
670+
+void trng_init(trng_t *obj)
671+
+{
672+
+ (void)obj;
673+
+}
674+
+
675+
+/** Deinitialize the TRNG peripheral
676+
+ *
677+
+ * @param obj The TRNG object
678+
+ */
679+
+void trng_free(trng_t *obj)
680+
+{
681+
+ (void)obj;
682+
+}
683+
+
684+
+/** Get random data from TRNG peripheral
685+
+ *
686+
+ * @param obj The TRNG object
687+
+ * @param output The pointer to an output array
688+
+ * @param length The size of output data, to avoid buffer overwrite
689+
+ * @param output_length The length of generated data
690+
+ * @return 0 success, -1 fail
691+
+ */
692+
+int trng_get_bytes(trng_t *obj, uint8_t *output, size_t length, size_t *output_length)
693+
+{
694+
+ (void)obj;
695+
+ *output_length = 0;
696+
+ uint32_t random[16];
697+
+
698+
+ while (*output_length < length) {
699+
+ get_rand_128((rng_128_t*)random);
700+
+ for (uint8_t i = 0; (i < 16) && (*output_length < length) ; i++) {
701+
+ *output++ = random[i];
702+
+ *output_length += 1;
703+
+ random[i] = 0;
704+
+ }
705+
+ }
706+
+ return 0;
707+
+}
708+
+#endif /* DEVICE_TRNG */
709+
diff --git a/targets/targets.json b/targets/targets.json
710+
index 9a12422336..83fa8c930a 100644
711+
--- a/targets/targets.json
712+
+++ b/targets/targets.json
713+
@@ -9700,7 +9700,8 @@
714+
"USTICKER",
715+
"WATCHDOG",
716+
"USBDEVICE",
717+
- "RESET_REASON"
718+
+ "RESET_REASON",
719+
+ "TRNG"
720+
]
721+
},
722+
"NANO_RP2040_CONNECT": {
723+
--
724+
2.43.0
725+

0 commit comments

Comments
 (0)
Please sign in to comment.