imxrt_hal/chip/drivers/trng.rs
1//! True random number generator.
2//!
3//! Provides basic support for the True Random Number Generator. The TRNG generates truly random
4//! data and is intended for use as a generator of entropy.
5//!
6//! The TRNG is fairly slow - 15 minutes to an hour to generate 1 megabyte - so it probably
7//! should only be used to generate a relatively small amount of entropy for a cryptographic
8//! algorithm. Occasionally retrieving entropy from it won't necessarily need to block, as
9//! this driver retrieves 512 bits at a time.
10//!
11//! # Example
12//!
13//! Enable the TRNG clock gate, wait to generate random data.
14//!
15//! ```no_run
16//! use core::task::Poll;
17//! use imxrt_hal as hal;
18//! use imxrt_ral as ral;
19//!
20//! # || -> Option<()> {
21//! let mut ccm = unsafe { ral::ccm::CCM::instance() };
22//! hal::ccm::clock_gate::trng().set(&mut ccm, hal::ccm::clock_gate::ON);
23//!
24//! let mut trng = hal::trng::Trng::new(
25//! unsafe { ral::trng::TRNG::instance() },
26//! hal::trng::SampleMode::default(),
27//! hal::trng::RetryCount::default(),
28//! );
29//!
30//! let random_data = loop {
31//! if let Poll::Ready(result) = trng.next_u32() {
32//! break result.ok()?;
33//! }
34//! };
35//! # Some(()) }();
36//! ```
37
38use core::fmt;
39use core::task::{Poll, ready};
40
41use crate::ral::trng;
42use crate::ral::{modify_reg, read_reg, write_reg};
43
44/// TRNG sampling mode
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46#[derive(Copy, Clone, Debug, PartialEq, Eq)]
47#[repr(u32)]
48pub enum SampleMode {
49 /// von Neumann data in both entropy shifter and statistical checks. Approximately 4x slower
50 /// than the other modes.
51 VonNeumann = trng::MCTL::SAMP_MODE::RW::SAMP_MODE_0,
52 /// Raw data in both entropy shifter and statistical checks. Likely lower quality than the
53 /// other two modes.
54 Raw = trng::MCTL::SAMP_MODE::RW::SAMP_MODE_1,
55 /// von Neumann data in entropy shifter, raw data in statistical checks
56 VonNeumannRaw = trng::MCTL::SAMP_MODE::RW::SAMP_MODE_2,
57}
58
59impl Default for SampleMode {
60 /// Returns `VonNeumannRaw`.
61 fn default() -> Self {
62 // "Set sample mode of the TRNG ring oscillator to Von Neumann, for better random data.
63 // It is optional." <- SDK, explaining why they set sample mode to 0 (VN)
64 // Teensyduino uses VNRaw, the reason appears to be this post:
65 // https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=195000&viewfull=1#post195000
66 // VNRaw appears to produce equivalent quality output and is ~4x faster than VN.
67 // As such, VonNeumannRaw is the default SampleMode here.
68 Self::VonNeumannRaw
69 }
70}
71
72/// The true random number generator.
73pub struct Trng {
74 reg: trng::TRNG,
75 block: [u32; 16],
76 index: usize,
77}
78
79impl fmt::Debug for Trng {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 f.debug_struct("TRNG")
82 .field("block", &self.block)
83 .field("index", &self.index)
84 .finish()
85 }
86}
87
88#[cfg(feature = "defmt")]
89impl defmt::Format for Trng {
90 fn format(&self, f: defmt::Formatter) {
91 defmt::write!(f, "Trng {{ block: {}, index: {} }}", self.block, self.index)
92 }
93}
94
95/// The number of retry attempts.
96///
97/// Describes the number of times to retry
98/// after a test failure before an error is declared. Valid
99/// range `1..=15`. The default retry count is the largest
100/// possible value.
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct RetryCount(u32);
104
105impl RetryCount {
106 /// The default reset count.
107 pub const DEFAULT: u32 = 15;
108
109 /// Create a new retry count.
110 ///
111 /// Returns `None` if `retry_count` is not in the half-closed range
112 /// `1..=15`.
113 pub fn new(retry_count: u32) -> Option<Self> {
114 (1..=15)
115 .contains(&retry_count)
116 .then_some(RetryCount(retry_count))
117 }
118}
119
120impl Default for RetryCount {
121 fn default() -> Self {
122 RetryCount(Self::DEFAULT)
123 }
124}
125
126impl Trng {
127 /// Create a TRNG given a sampling mode and retry count.
128 ///
129 /// To select the default sampling mode and retry counts, use `Default::default()`.
130 pub fn new(reg: trng::TRNG, sample_mode: SampleMode, retry_count: RetryCount) -> Self {
131 // Not supported: locking TRNG to prevent programmability.
132 // A number of things here are likely configurable if you have access to the SRM.
133 // Without it only limited configuration is safe. Garbage configs lead to endless errors.
134
135 modify_reg!(trng, reg, MCTL, PRGM: 1);
136 modify_reg!(trng, reg, MCTL, RST_DEF: 1);
137 // enter program mode and reset to defaults
138 // it isn't clear what defaults it actually sets, so let's set the tests manually
139
140 // All these values are sourced only from the #defines in the MCUXpresso TRNG driver for the
141 // IMXRT1062. The doc comments contain both typos and completely wrong values.
142
143 write_reg!(trng, reg, SCMISC, RTY_CT: retry_count.0, LRUN_MAX: 34); // _RUN_MAX_LIMIT
144
145 // Note: The SDK uses _MAX and _MIN for values, but the registers use the max value and a
146 // range. The SDK _MIN values are expressed as (max - range), making it easy to ensure
147 // that these values are correct.
148 write_reg!(trng, reg, SCML, MONO_MAX: 1384, MONO_RNG: 268); // _MONOBIT_
149 write_reg!(trng, reg, SCR1L, RUN1_MAX: 405, RUN1_RNG: 178); // _RUNBIT1_
150 write_reg!(trng, reg, SCR2L, RUN2_MAX: 220, RUN2_RNG: 122); // _RUNBIT2_
151 write_reg!(trng, reg, SCR3L, RUN3_MAX: 125, RUN3_RNG: 88); // _RUNBIT3_
152 write_reg!(trng, reg, SCR4L, RUN4_MAX: 75, RUN4_RNG: 64); // _RUNBIT4_
153 write_reg!(trng, reg, SCR5L, RUN5_MAX: 47, RUN5_RNG: 46); // _RUNBIT5_
154 write_reg!(trng, reg, SCR6PL, RUN6P_MAX: 47, RUN6P_RNG: 46); // _RUNBIT6PLUS_
155
156 write_reg!(trng, reg, PKRMAX, PKR_MAX: 26912); // _POKER_MAXIMUM
157 write_reg!(trng, reg, PKRRNG, PKR_RNG: 2467);
158
159 write_reg!(trng, reg, FRQMAX, FRQ_MAX: 25600); // _FREQUENCY_MAXIMUM
160 write_reg!(trng, reg, FRQMIN, FRQ_MIN: 1600); // _FREQUENCY_MINIMUM
161
162 write_reg!(trng, reg, SDCTL, SAMP_SIZE: 2500, ENT_DLY: 3200); // _SAMPLE_SIZE, _ENTROPY_DELAY
163 write_reg!(trng, reg, SBLIM, SB_LIM: 63); // _SPARSE_BIT_LIMIT
164
165 // set sample mode, exit program mode
166 modify_reg!(trng, reg, MCTL, SAMP_MODE: sample_mode as u32);
167 modify_reg!(trng, reg, MCTL, PRGM: 0);
168 // for 1015, 1021, maybe other non i.MX chips: set TRNG_ACC to 1 here
169 read_reg!(trng, reg, ENT[15]);
170 // reading ENT15 triggers new entropy generation
171
172 Self {
173 reg,
174 block: [0; 16],
175 index: 16, // equal to len, to trigger immediate retrieval
176 }
177 }
178
179 /// Return the next randomly-generated `u32`. May need to retrieve another block of random numbers.
180 ///
181 /// Returns `Poll::Pending` if we're not ready to read entropy; try again. See the module-level
182 /// example for how to block.
183 pub fn next_u32(&mut self) -> Poll<Result<u32, Error>> {
184 ready!(self.retrieve_if_needed())?;
185 let data = self.block[self.index];
186 self.index += 1;
187 Poll::Ready(Ok(data))
188 }
189
190 /// Retrieve another block of random numbers if we've used them all up.
191 fn retrieve_if_needed(&mut self) -> Poll<Result<(), Error>> {
192 if self.index >= self.block.len() {
193 ready!(self.retrieve())?;
194 self.index = 0;
195 }
196 Poll::Ready(Ok(()))
197 }
198
199 /// Retrieve another block of random numbers.
200 fn retrieve(&mut self) -> Poll<Result<(), Error>> {
201 let mctl = read_reg!(trng, self.reg, MCTL);
202 if (mctl & trng::MCTL::ERR::mask) != 0 {
203 let flags = self.get_error_flags();
204 write_reg!(trng, self.reg, MCTL, mctl); // write reg back to clear error
205 return Poll::Ready(Err(Error(flags)));
206 }
207 if (mctl & trng::MCTL::ENT_VAL::mask) == 0 {
208 return Poll::Pending; // not ready to read entropy
209 }
210 for idx in 0..self.reg.ENT.len() {
211 self.block[idx] = read_reg!(trng, self.reg, ENT[idx]);
212 }
213
214 read_reg!(trng, self.reg, ENT[0]);
215 // SDK (fsl_trng.c):
216 // Dummy read. Defect workaround.
217 // TRNG could not clear ENT_VAL flag automatically, application
218 // had to do a dummy reading operation for anyone TRNG register
219 // to clear it firstly, then to read the RTENT0 to RTENT15 again
220 // This appears unnecessary on the 1062? done anyway in case it's necessary for another chip
221 Poll::Ready(Ok(()))
222 }
223
224 /// Retrieve all known error flags.
225 fn get_error_flags(&self) -> ErrorFlags {
226 let status = read_reg!(trng, self.reg, STATUS) & 0xFFFF;
227 // all the error flags in STATUS are in the low 16 bits
228 let mut flags = ErrorFlags::from_bits_truncate(status);
229 flags.set(
230 ErrorFlags::FCT_FAIL,
231 read_reg!(trng, self.reg, MCTL, FCT_FAIL) == 1,
232 );
233 flags
234 }
235
236 /// Release the TRNG in a disabled state.
237 ///
238 /// This preserves any previously set retry count, sample mode,
239 /// and peripheral settings. However, the register block is returned
240 /// in a disabled state.
241 pub fn release_disabled(self) -> trng::TRNG {
242 modify_reg!(trng, self.reg, MCTL, PRGM: 1);
243 while read_reg!(trng, self.reg, MCTL, TSTOP_OK) == 0 {
244 core::hint::spin_loop();
245 }
246 self.reg
247 }
248}
249
250/// A TRNG error occurred, such as a statistical test failing.
251#[cfg_attr(feature = "defmt", derive(defmt::Format))]
252#[derive(Copy, Clone, Debug, PartialEq, Eq)]
253pub struct Error(pub ErrorFlags);
254
255bitflags::bitflags! {
256 /// Specific errors that may occur during entropy generation
257 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
258 pub struct ErrorFlags : u32 {
259 // STATUS register starts here (automatically set from bits)
260 /// 1-bit run sampling 0s test failed
261 const TF1BR0 = 1 << 0;
262 /// 1-bit run sampling 1s test failed
263 const TF1BR1 = 1 << 1;
264 /// 2-bit run sampling 0s test failed
265 const TF2BR0 = 1 << 2;
266 /// 2-bit run sampling 1s test failed
267 const TF2BR1 = 1 << 3;
268 /// 3-bit run sampling 0s test failed
269 const TF3BR0 = 1 << 4;
270 /// 3-bit run sampling 1s test failed
271 const TF3BR1 = 1 << 5;
272 /// 4-bit run sampling 0s test failed
273 const TF4BR0 = 1 << 6;
274 /// 4-bit run sampling 1s test failed
275 const TF4BR1 = 1 << 7;
276 /// 5-bit run sampling 0s test failed
277 const TF5BR0 = 1 << 8;
278 /// 5-bit run sampling 1s test failed
279 const TF5BR1 = 1 << 9;
280 /// 6-plus-bit run sampling 0s test failed
281 const TF6PBR0 = 1 << 10;
282 /// 6-plus-bit run sampling 1s test failed
283 const TF6PBR1 = 1 << 11;
284 /// Sparse bit test failed
285 const TFSB = 1 << 12;
286 /// Long run test failed
287 const TFLR = 1 << 13;
288 /// Poker test failed
289 const TFP = 1 << 14;
290 /// Mono bit test failed
291 const TFMB = 1 << 15;
292 // MCTL register starts here (set manually)
293 /// Count taken during entropy generation was outside the defined range of FRQ_MIN to FRQ_MAX
294 const FCT_FAIL = 1 << 16;
295 }
296}
297
298#[cfg(feature = "defmt")]
299impl defmt::Format for ErrorFlags {
300 fn format(&self, f: defmt::Formatter) {
301 defmt::write!(f, "ErrorFlags({=u32:#x})", self.bits());
302 }
303}
304
305impl fmt::Display for Error {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 write!(f, "An error occurred in the TRNG module")
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::RetryCount;
314
315 #[test]
316 fn retry_count() {
317 assert!(RetryCount::new(0).is_none());
318 for count in 1..16 {
319 assert!(RetryCount::new(count).is_some());
320 }
321 assert!(RetryCount::new(16).is_none());
322 }
323}