diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-09-17 22:17:23 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-17 22:17:23 +0000 |
| commit | d12e98aaf420cb40664c1ac3660e47124f19965e (patch) | |
| tree | 5432c7fd04e4dc587f0548e59bb1c74217b4bb16 | |
| parent | e597c6b959c8c79078d5bc7126a29cab6f3df46c (diff) | |
| parent | 2bc49763c6c36026feeaf681078d06106b73e0b0 (diff) | |
Merge pull request #3338 from ionspin/add-rp2350-trng-support
RP2350 TRNG support
| -rw-r--r-- | embassy-rp/src/lib.rs | 4 | ||||
| -rw-r--r-- | embassy-rp/src/trng.rs | 405 | ||||
| -rw-r--r-- | examples/rp23/src/bin/trng.rs | 64 |
3 files changed, 473 insertions, 0 deletions
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c357c14c2..d402cf793 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -42,6 +42,8 @@ pub mod rtc; | |||
| 42 | pub mod spi; | 42 | pub mod spi; |
| 43 | #[cfg(feature = "time-driver")] | 43 | #[cfg(feature = "time-driver")] |
| 44 | pub mod time_driver; | 44 | pub mod time_driver; |
| 45 | #[cfg(feature = "_rp235x")] | ||
| 46 | pub mod trng; | ||
| 45 | pub mod uart; | 47 | pub mod uart; |
| 46 | pub mod usb; | 48 | pub mod usb; |
| 47 | pub mod watchdog; | 49 | pub mod watchdog; |
| @@ -402,6 +404,8 @@ embassy_hal_internal::peripherals! { | |||
| 402 | 404 | ||
| 403 | WATCHDOG, | 405 | WATCHDOG, |
| 404 | BOOTSEL, | 406 | BOOTSEL, |
| 407 | |||
| 408 | TRNG | ||
| 405 | } | 409 | } |
| 406 | 410 | ||
| 407 | #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] | 411 | #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] |
diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs new file mode 100644 index 000000000..9f2f33c4b --- /dev/null +++ b/embassy-rp/src/trng.rs | |||
| @@ -0,0 +1,405 @@ | |||
| 1 | //! True Random Number Generator (TRNG) driver. | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::ops::Not; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | use embassy_hal_internal::Peripheral; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | use rand_core::Error; | ||
| 11 | |||
| 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 13 | use crate::peripherals::TRNG; | ||
| 14 | use crate::{interrupt, pac}; | ||
| 15 | |||
| 16 | trait SealedInstance { | ||
| 17 | fn regs() -> pac::trng::Trng; | ||
| 18 | fn waker() -> &'static AtomicWaker; | ||
| 19 | } | ||
| 20 | |||
| 21 | /// TRNG peripheral instance. | ||
| 22 | #[allow(private_bounds)] | ||
| 23 | pub trait Instance: SealedInstance { | ||
| 24 | /// Interrupt for this peripheral. | ||
| 25 | type Interrupt: Interrupt; | ||
| 26 | } | ||
| 27 | |||
| 28 | impl SealedInstance for TRNG { | ||
| 29 | fn regs() -> rp_pac::trng::Trng { | ||
| 30 | pac::TRNG | ||
| 31 | } | ||
| 32 | |||
| 33 | fn waker() -> &'static AtomicWaker { | ||
| 34 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 35 | &WAKER | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl Instance for TRNG { | ||
| 40 | type Interrupt = interrupt::typelevel::TRNG_IRQ; | ||
| 41 | } | ||
| 42 | |||
| 43 | #[derive(Copy, Clone, Debug)] | ||
| 44 | #[allow(missing_docs)] | ||
| 45 | /// TRNG ROSC Inverter chain length options. | ||
| 46 | pub enum InverterChainLength { | ||
| 47 | None = 0, | ||
| 48 | One, | ||
| 49 | Two, | ||
| 50 | Three, | ||
| 51 | Four, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl From<InverterChainLength> for u8 { | ||
| 55 | fn from(value: InverterChainLength) -> Self { | ||
| 56 | value as u8 | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Configuration for the TRNG. | ||
| 61 | /// | ||
| 62 | /// - Three built in entropy checks | ||
| 63 | /// - ROSC frequency controlled by selecting one of ROSC chain lengths | ||
| 64 | /// - Sample period in terms of system clock ticks | ||
| 65 | /// | ||
| 66 | /// | ||
| 67 | /// Default configuration is based on the following from documentation: | ||
| 68 | /// | ||
| 69 | /// ---- | ||
| 70 | /// | ||
| 71 | /// RP2350 Datasheet 12.12.2 | ||
| 72 | /// | ||
| 73 | /// ... | ||
| 74 | /// | ||
| 75 | /// When configuring the TRNG block, consider the following principles: | ||
| 76 | /// • As average generation time increases, result quality increases and failed entropy checks decrease. | ||
| 77 | /// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and | ||
| 78 | /// failed entropy checks. | ||
| 79 | /// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or | ||
| 80 | /// 1 and sample count settings of 20-25. | ||
| 81 | /// | ||
| 82 | /// --- | ||
| 83 | /// | ||
| 84 | /// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly | ||
| 85 | /// by setting the sample period to 0. Random data collected this way is then passed through | ||
| 86 | /// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!). | ||
| 87 | #[non_exhaustive] | ||
| 88 | #[derive(Copy, Clone, Debug)] | ||
| 89 | pub struct Config { | ||
| 90 | /// Bypass TRNG autocorrelation test | ||
| 91 | pub disable_autocorrelation_test: bool, | ||
| 92 | /// Bypass CRNGT test | ||
| 93 | pub disable_crngt_test: bool, | ||
| 94 | /// When set, the Von-Neuman balancer is bypassed (including the | ||
| 95 | /// 32 consecutive bits test) | ||
| 96 | pub disable_von_neumann_balancer: bool, | ||
| 97 | /// Sets the number of rng_clk cycles between two consecutive | ||
| 98 | /// ring oscillator samples. | ||
| 99 | /// Note: If the von Neumann decorrelator is bypassed, the minimum value for | ||
| 100 | /// sample counter must not be less than seventeen | ||
| 101 | pub sample_count: u32, | ||
| 102 | /// Selects the number of inverters (out of four possible | ||
| 103 | /// selections) in the ring oscillator (the entropy source). Higher values select | ||
| 104 | /// longer inverter chain lengths. | ||
| 105 | pub inverter_chain_length: InverterChainLength, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl Default for Config { | ||
| 109 | fn default() -> Self { | ||
| 110 | Config { | ||
| 111 | disable_autocorrelation_test: true, | ||
| 112 | disable_crngt_test: true, | ||
| 113 | disable_von_neumann_balancer: true, | ||
| 114 | sample_count: 25, | ||
| 115 | inverter_chain_length: InverterChainLength::One, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// True Random Number Generator Driver for RP2350 | ||
| 121 | /// | ||
| 122 | /// This driver provides async and blocking options. | ||
| 123 | /// | ||
| 124 | /// See [Config] for configuration details. | ||
| 125 | /// | ||
| 126 | /// Usage example: | ||
| 127 | /// ```no_run | ||
| 128 | /// use embassy_executor::Spawner; | ||
| 129 | /// use embassy_rp::trng::Trng; | ||
| 130 | /// use embassy_rp::peripherals::TRNG; | ||
| 131 | /// use embassy_rp::bind_interrupts; | ||
| 132 | /// | ||
| 133 | /// bind_interrupts!(struct Irqs { | ||
| 134 | /// TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | ||
| 135 | /// }); | ||
| 136 | /// | ||
| 137 | /// #[embassy_executor::main] | ||
| 138 | /// async fn main(spawner: Spawner) { | ||
| 139 | /// let peripherals = embassy_rp::init(Default::default()); | ||
| 140 | /// let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); | ||
| 141 | /// | ||
| 142 | /// let mut randomness = [0u8; 58]; | ||
| 143 | /// loop { | ||
| 144 | /// trng.fill_bytes(&mut randomness).await; | ||
| 145 | /// assert_ne!(randomness, [0u8; 58]); | ||
| 146 | /// } | ||
| 147 | ///} | ||
| 148 | /// ``` | ||
| 149 | pub struct Trng<'d, T: Instance> { | ||
| 150 | phantom: PhantomData<&'d mut T>, | ||
| 151 | } | ||
| 152 | |||
| 153 | /// 12.12.1. Overview | ||
| 154 | /// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of | ||
| 155 | /// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC). | ||
| 156 | const TRNG_BLOCK_SIZE_BITS: usize = 192; | ||
| 157 | const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8; | ||
| 158 | |||
| 159 | impl<'d, T: Instance> Trng<'d, T> { | ||
| 160 | /// Create a new TRNG driver. | ||
| 161 | pub fn new( | ||
| 162 | _trng: impl Peripheral<P = T> + 'd, | ||
| 163 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 164 | config: Config, | ||
| 165 | ) -> Self { | ||
| 166 | let regs = T::regs(); | ||
| 167 | |||
| 168 | regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); | ||
| 169 | |||
| 170 | let trng_config_register = regs.trng_config(); | ||
| 171 | trng_config_register.write(|w| { | ||
| 172 | w.set_rnd_src_sel(config.inverter_chain_length.clone().into()); | ||
| 173 | }); | ||
| 174 | |||
| 175 | let sample_count_register = regs.sample_cnt1(); | ||
| 176 | sample_count_register.write(|w| { | ||
| 177 | *w = config.sample_count; | ||
| 178 | }); | ||
| 179 | |||
| 180 | let debug_control_register = regs.trng_debug_control(); | ||
| 181 | debug_control_register.write(|w| { | ||
| 182 | w.set_auto_correlate_bypass(config.disable_autocorrelation_test); | ||
| 183 | w.set_trng_crngt_bypass(config.disable_crngt_test); | ||
| 184 | w.set_vnc_bypass(config.disable_von_neumann_balancer) | ||
| 185 | }); | ||
| 186 | |||
| 187 | Trng { phantom: PhantomData } | ||
| 188 | } | ||
| 189 | |||
| 190 | fn start_rng(&self) { | ||
| 191 | let regs = T::regs(); | ||
| 192 | let source_enable_register = regs.rnd_source_enable(); | ||
| 193 | // Enable TRNG ROSC | ||
| 194 | source_enable_register.write(|w| w.set_rnd_src_en(true)); | ||
| 195 | } | ||
| 196 | |||
| 197 | fn stop_rng(&self) { | ||
| 198 | let regs = T::regs(); | ||
| 199 | let source_enable_register = regs.rnd_source_enable(); | ||
| 200 | source_enable_register.write(|w| w.set_rnd_src_en(false)); | ||
| 201 | let reset_bits_counter_register = regs.rst_bits_counter(); | ||
| 202 | reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); | ||
| 203 | } | ||
| 204 | |||
| 205 | fn enable_irq(&self) { | ||
| 206 | unsafe { T::Interrupt::enable() } | ||
| 207 | } | ||
| 208 | |||
| 209 | fn disable_irq(&self) { | ||
| 210 | T::Interrupt::disable(); | ||
| 211 | } | ||
| 212 | |||
| 213 | fn blocking_wait_for_successful_generation(&self) { | ||
| 214 | let regs = T::regs(); | ||
| 215 | |||
| 216 | let trng_busy_register = regs.trng_busy(); | ||
| 217 | let trng_valid_register = regs.trng_valid(); | ||
| 218 | |||
| 219 | let mut success = false; | ||
| 220 | while success.not() { | ||
| 221 | while trng_busy_register.read().trng_busy() {} | ||
| 222 | if trng_valid_register.read().ehr_valid().not() { | ||
| 223 | if regs.rng_isr().read().autocorr_err() { | ||
| 224 | regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); | ||
| 225 | } else { | ||
| 226 | panic!("RNG not busy, but ehr is not valid!") | ||
| 227 | } | ||
| 228 | } else { | ||
| 229 | success = true | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { | ||
| 235 | let regs = T::regs(); | ||
| 236 | let ehr_data_regs = [ | ||
| 237 | regs.ehr_data0(), | ||
| 238 | regs.ehr_data1(), | ||
| 239 | regs.ehr_data2(), | ||
| 240 | regs.ehr_data3(), | ||
| 241 | regs.ehr_data4(), | ||
| 242 | regs.ehr_data5(), | ||
| 243 | ]; | ||
| 244 | |||
| 245 | for (i, reg) in ehr_data_regs.iter().enumerate() { | ||
| 246 | buffer[i * 4..i * 4 + 4].copy_from_slice(®.read().to_ne_bytes()); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { | ||
| 251 | self.blocking_wait_for_successful_generation(); | ||
| 252 | self.read_ehr_registers_into_array(buffer); | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Fill the buffer with random bytes, async version. | ||
| 256 | pub async fn fill_bytes(&mut self, destination: &mut [u8]) { | ||
| 257 | if destination.is_empty() { | ||
| 258 | return; // Nothing to fill | ||
| 259 | } | ||
| 260 | |||
| 261 | self.start_rng(); | ||
| 262 | self.enable_irq(); | ||
| 263 | |||
| 264 | let mut bytes_transferred = 0usize; | ||
| 265 | let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; | ||
| 266 | |||
| 267 | let regs = T::regs(); | ||
| 268 | |||
| 269 | let trng_busy_register = regs.trng_busy(); | ||
| 270 | let trng_valid_register = regs.trng_valid(); | ||
| 271 | |||
| 272 | let waker = T::waker(); | ||
| 273 | |||
| 274 | let destination_length = destination.len(); | ||
| 275 | |||
| 276 | poll_fn(|context| { | ||
| 277 | waker.register(context.waker()); | ||
| 278 | if bytes_transferred == destination_length { | ||
| 279 | self.stop_rng(); | ||
| 280 | self.disable_irq(); | ||
| 281 | Poll::Ready(()) | ||
| 282 | } else { | ||
| 283 | if trng_busy_register.read().trng_busy() { | ||
| 284 | Poll::Pending | ||
| 285 | } else { | ||
| 286 | if trng_valid_register.read().ehr_valid().not() { | ||
| 287 | panic!("RNG not busy, but ehr is not valid!") | ||
| 288 | } | ||
| 289 | self.read_ehr_registers_into_array(&mut buffer); | ||
| 290 | let remaining = destination_length - bytes_transferred; | ||
| 291 | if remaining > TRNG_BLOCK_SIZE_BYTES { | ||
| 292 | destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES] | ||
| 293 | .copy_from_slice(&buffer); | ||
| 294 | bytes_transferred += TRNG_BLOCK_SIZE_BYTES | ||
| 295 | } else { | ||
| 296 | destination[bytes_transferred..bytes_transferred + remaining] | ||
| 297 | .copy_from_slice(&buffer[0..remaining]); | ||
| 298 | bytes_transferred += remaining | ||
| 299 | } | ||
| 300 | if bytes_transferred == destination_length { | ||
| 301 | self.stop_rng(); | ||
| 302 | self.disable_irq(); | ||
| 303 | Poll::Ready(()) | ||
| 304 | } else { | ||
| 305 | Poll::Pending | ||
| 306 | } | ||
| 307 | } | ||
| 308 | } | ||
| 309 | }) | ||
| 310 | .await | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Fill the buffer with random bytes, blocking version. | ||
| 314 | pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) { | ||
| 315 | if destination.is_empty() { | ||
| 316 | return; // Nothing to fill | ||
| 317 | } | ||
| 318 | self.start_rng(); | ||
| 319 | |||
| 320 | let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; | ||
| 321 | |||
| 322 | for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) { | ||
| 323 | self.blocking_wait_for_successful_generation(); | ||
| 324 | self.blocking_read_ehr_registers_into_array(&mut buffer); | ||
| 325 | chunk.copy_from_slice(&buffer[..chunk.len()]) | ||
| 326 | } | ||
| 327 | self.stop_rng() | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Return a random u32, blocking. | ||
| 331 | pub fn blocking_next_u32(&mut self) -> u32 { | ||
| 332 | let regs = T::regs(); | ||
| 333 | self.start_rng(); | ||
| 334 | self.blocking_wait_for_successful_generation(); | ||
| 335 | // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to | ||
| 336 | // clear all of the result registers. | ||
| 337 | let result = regs.ehr_data5().read(); | ||
| 338 | self.stop_rng(); | ||
| 339 | result | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Return a random u64, blocking. | ||
| 343 | pub fn blocking_next_u64(&mut self) -> u64 { | ||
| 344 | let regs = T::regs(); | ||
| 345 | self.start_rng(); | ||
| 346 | self.blocking_wait_for_successful_generation(); | ||
| 347 | |||
| 348 | let low = regs.ehr_data4().read() as u64; | ||
| 349 | // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to | ||
| 350 | // clear all of the result registers. | ||
| 351 | let result = (regs.ehr_data5().read() as u64) << 32 | low; | ||
| 352 | self.stop_rng(); | ||
| 353 | result | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { | ||
| 358 | fn next_u32(&mut self) -> u32 { | ||
| 359 | self.blocking_next_u32() | ||
| 360 | } | ||
| 361 | |||
| 362 | fn next_u64(&mut self) -> u64 { | ||
| 363 | self.blocking_next_u64() | ||
| 364 | } | ||
| 365 | |||
| 366 | fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 367 | self.blocking_fill_bytes(dest) | ||
| 368 | } | ||
| 369 | |||
| 370 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
| 371 | self.blocking_fill_bytes(dest); | ||
| 372 | Ok(()) | ||
| 373 | } | ||
| 374 | } | ||
| 375 | /// TRNG interrupt handler. | ||
| 376 | pub struct InterruptHandler<T: Instance> { | ||
| 377 | _trng: PhantomData<T>, | ||
| 378 | } | ||
| 379 | |||
| 380 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 381 | unsafe fn on_interrupt() { | ||
| 382 | let regs = T::regs(); | ||
| 383 | let isr = regs.rng_isr().read(); | ||
| 384 | // Clear ehr bit | ||
| 385 | regs.rng_icr().write(|w| { | ||
| 386 | w.set_ehr_valid(true); | ||
| 387 | }); | ||
| 388 | if isr.ehr_valid() { | ||
| 389 | T::waker().wake(); | ||
| 390 | } else { | ||
| 391 | // 12.12.5. List of Registers | ||
| 392 | // ... | ||
| 393 | // TRNG: RNG_ISR Register | ||
| 394 | // ... | ||
| 395 | // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. | ||
| 396 | // When set, RNG ceases functioning until next reset | ||
| 397 | if isr.autocorr_err() { | ||
| 398 | warn!("TRNG Autocorrect error! Resetting TRNG"); | ||
| 399 | regs.trng_sw_reset().write(|w| { | ||
| 400 | w.set_trng_sw_reset(true); | ||
| 401 | }); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs new file mode 100644 index 000000000..e146baa2e --- /dev/null +++ b/examples/rp23/src/bin/trng.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example shows TRNG usage | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::gpio::{Level, Output}; | ||
| 11 | use embassy_rp::peripherals::TRNG; | ||
| 12 | use embassy_rp::trng::Trng; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use rand::RngCore; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 20 | |||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | bind_interrupts!(struct Irqs { | ||
| 32 | TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | ||
| 33 | }); | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async fn main(_spawner: Spawner) { | ||
| 37 | let peripherals = embassy_rp::init(Default::default()); | ||
| 38 | |||
| 39 | // Initialize the TRNG with default configuration | ||
| 40 | let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); | ||
| 41 | // A buffer to collect random bytes in. | ||
| 42 | let mut randomness = [0u8; 58]; | ||
| 43 | |||
| 44 | let mut led = Output::new(peripherals.PIN_25, Level::Low); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | trng.fill_bytes(&mut randomness).await; | ||
| 48 | info!("Random bytes async {}", &randomness); | ||
| 49 | trng.blocking_fill_bytes(&mut randomness); | ||
| 50 | info!("Random bytes blocking {}", &randomness); | ||
| 51 | let random_u32 = trng.next_u32(); | ||
| 52 | let random_u64 = trng.next_u64(); | ||
| 53 | info!("Random u32 {} u64 {}", random_u32, random_u64); | ||
| 54 | // Random number of blinks between 0 and 31 | ||
| 55 | let blinks = random_u32 % 32; | ||
| 56 | for _ in 0..blinks { | ||
| 57 | led.set_high(); | ||
| 58 | Timer::after_millis(20).await; | ||
| 59 | led.set_low(); | ||
| 60 | Timer::after_millis(20).await; | ||
| 61 | } | ||
| 62 | Timer::after_millis(1000).await; | ||
| 63 | } | ||
| 64 | } | ||
