use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::soc::stm32h743::generated::rng::{Register, cr, sr};
use crate::soc::{Access, FieldSpec, Layout, WriteModify};
use crate::time::{EventId, VirtualNanos};
const IT: usize = 0;
pub const KERNEL_HZ: u64 = 48_000_000;
pub const RESET_AHB_HZ: u64 = 64_000_000;
pub const SEED: u64 = 0x0123_4567_89AB_CDEF;
const NANOS_PER_SECOND: u128 = 1_000_000_000;
const ROUND_FLOOR_CYCLES: u128 = 213;
const ROUND_RNG_CYCLES: u128 = 16;
const FIRST_SLOW: (u128, u128) = (128, 426);
const FIRST_FAST: (u128, u128) = (192, 213);
const THRESHOLD: (u128, u128) = (213, 64);
const CLOCK_RATIO: u128 = 32;
const WORDS: usize = 4;
pub static SEIS: FieldSpec = FieldSpec {
access: Access::ReadWrite,
write: WriteModify::ZeroToClear,
..*sr::SEIS
};
pub static CEIS: FieldSpec = FieldSpec {
access: Access::ReadWrite,
write: WriteModify::ZeroToClear,
..*sr::CEIS
};
const FORBIDDEN_READ: &str = "RNG_DR read with DRDY clear or an error flag set: it must not be read, RM0433 rev 9 section 34.3.5";
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Work {
First,
Round,
Resample,
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Job {
work: Work,
left: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Stage {
Starting,
Steady,
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
struct Faults {
seed: bool,
clock: bool,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Rng {
ahb_hz: u64,
kernel_hz: u64,
stream: u64,
enabled: bool,
ced: bool,
stage: Stage,
faults: Faults,
buffer: [u32; WORDS],
waiting: usize,
register: Option<[u32; WORDS]>,
job: Option<Job>,
run: Option<(EventId, VirtualNanos)>,
raised: Level,
told: bool,
}
impl Rng {
#[must_use]
pub const fn new(ahb_hz: u64, kernel_hz: u64) -> Self {
Self::seeded(ahb_hz, kernel_hz, SEED)
}
#[must_use]
pub const fn seeded(ahb_hz: u64, kernel_hz: u64, seed: u64) -> Self {
Self {
ahb_hz,
kernel_hz,
stream: seed,
enabled: false,
ced: false,
stage: Stage::Starting,
faults: Faults {
seed: false,
clock: false,
},
buffer: [0; WORDS],
waiting: 0,
register: None,
job: None,
run: None,
raised: Level::Low,
told: false,
}
}
pub fn seed_error(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.faults.seed {
return;
}
self.pause(context.now());
self.faults.seed = true;
registers.set(Register::Sr, sr::SEIS.set(registers.get(Register::Sr), 1));
self.sync(registers, context);
}
pub fn seed_recovered(
&mut self,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !self.faults.seed {
return;
}
self.faults.seed = false;
self.resume(context);
self.sync(registers, context);
}
fn deal(&mut self) -> u32 {
loop {
self.stream = self.stream.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut mixed = self.stream;
mixed = (mixed ^ (mixed >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
mixed = (mixed ^ (mixed >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
mixed ^= mixed >> 31;
let word = u32::try_from(mixed >> 32).expect("the top half is 32 bits");
if word != 0 {
return word;
}
}
}
fn deal_round(&mut self) -> [u32; WORDS] {
std::array::from_fn(|_| self.deal())
}
fn clocks_run(&self) -> bool {
self.ahb_hz > 0 && self.kernel_hz > 0
}
fn duration(&self, work: Work) -> u64 {
if !self.clocks_run() {
return 0;
}
let (ahb, kernel) = (u128::from(self.ahb_hz), u128::from(self.kernel_hz));
let nanos = match work {
Work::Round | Work::Resample => {
let cycles = (ROUND_RNG_CYCLES * ahb)
.div_ceil(kernel)
.max(ROUND_FLOOR_CYCLES);
let sampling = if work == Work::Resample {
ROUND_RNG_CYCLES
} else {
0
};
((sampling * ahb + cycles * kernel) * NANOS_PER_SECOND).div_ceil(kernel * ahb)
}
Work::First => {
let (rng_cycles, ahb_cycles) = if ahb * THRESHOLD.1 < THRESHOLD.0 * kernel {
FIRST_SLOW
} else {
FIRST_FAST
};
((rng_cycles * ahb + ahb_cycles * kernel) * NANOS_PER_SECOND).div_ceil(kernel * ahb)
}
};
u64::try_from(nanos).expect("a few cycles at a clock fit a u64 of nanoseconds")
}
fn begin(&mut self, work: Work, context: &mut DeviceContext<'_>) {
self.job = Some(Job {
work,
left: self.duration(work),
});
self.resume(context);
}
fn resume(&mut self, context: &mut DeviceContext<'_>) {
if !self.enabled || self.faults.seed || !self.clocks_run() || self.run.is_some() {
return;
}
if let Some(job) = self.job {
let due = VirtualNanos(context.now().0 + job.left);
self.run = Some((context.schedule(due), due));
}
}
fn pause(&mut self, now: VirtualNanos) {
if let Some((_, due)) = self.run.take()
&& let Some(job) = &mut self.job
{
job.left = due.0.saturating_sub(now.0);
}
}
fn finish(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let Some(job) = self.job.take() else {
return;
};
if job.work == Work::First {
self.stage = Stage::Steady;
}
let round = self.deal_round();
if self.waiting == 0 {
self.buffer = round;
self.waiting = WORDS;
} else {
self.register = Some(round);
}
if self.register.is_none() {
self.begin(Work::Round, context);
}
self.sync(registers, context);
}
fn take(&mut self, context: &mut DeviceContext<'_>) -> u32 {
if self.waiting == 0 {
return 0;
}
let word = self.buffer[WORDS - self.waiting];
self.waiting -= 1;
if self.waiting == 0
&& let Some(round) = self.register.take()
{
self.buffer = round;
self.waiting = WORDS;
self.begin(Work::Round, context);
}
word
}
fn clock_check_fails(&self) -> bool {
self.enabled
&& !self.ced
&& u128::from(self.kernel_hz) * CLOCK_RATIO <= u128::from(self.ahb_hz)
}
fn sync(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let failing = self.clock_check_fails();
let mut status = registers.get(Register::Sr);
if failing && !self.faults.clock {
status = sr::CEIS.set(status, 1);
}
self.faults.clock = failing;
status = sr::DRDY.set(status, u64::from(self.waiting > 0));
status = sr::CECS.set(status, u64::from(self.faults.clock));
status = sr::SECS.set(status, u64::from(self.faults.seed));
registers.set(Register::Sr, status);
let pending = sr::DRDY.is_set(status) || sr::SEIS.is_set(status) || sr::CEIS.is_set(status);
let wanted =
Level::from(self.enabled && cr::IE.is_set(registers.get(Register::Cr)) && pending);
if wanted != self.raised {
self.raised = wanted;
context.interrupt(IT, wanted);
}
}
fn control(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let landed = registers.get(Register::Cr);
let enable = cr::RNGEN.is_set(landed);
if self.enabled && cr::CED.is_set(landed) != self.ced {
registers.set(Register::Cr, cr::CED.set(landed, u64::from(self.ced)));
} else {
self.ced = cr::CED.is_set(landed);
}
if enable && !self.enabled {
self.enabled = true;
match self.stage {
Stage::Starting if self.job.is_none() => self.begin(Work::First, context),
Stage::Starting => self.resume(context),
Stage::Steady if self.register.is_none() => self.begin(Work::Resample, context),
Stage::Steady => {}
}
} else if !enable && self.enabled {
self.pause(context.now());
self.enabled = false;
}
self.sync(registers, context);
}
}
impl Device for Rng {
type Register = Register;
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
(register == Register::Dr).then(|| {
let status = registers.get(Register::Sr);
let unfit = self.waiting == 0 || sr::SEIS.is_set(status) || sr::CEIS.is_set(status);
if unfit && !self.told {
context.tell(Register::Dr.spec().name, FORBIDDEN_READ);
}
self.told = unfit;
let word = self.take(context);
self.sync(registers, context);
u64::from(word)
})
}
fn peek(
&self,
register: Register,
_registers: &RegisterFile,
_now: VirtualNanos,
) -> Option<u64> {
(register == Register::Dr).then(|| {
if self.waiting == 0 {
0
} else {
u64::from(self.buffer[WORDS - self.waiting])
}
})
}
fn write(
&mut self,
register: Register,
_value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Cr => self.control(registers, context),
Register::Sr => self.sync(registers, context),
Register::Dr => {}
}
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.run.map(|(id, _)| id) != Some(event) {
return;
}
self.run = None;
self.finish(registers, context);
}
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let before = self.job.map(|job| (job.work, self.duration(job.work)));
self.pause(context.now());
self.ahb_hz = hz;
if let Some((work, was)) = before {
let now = self.duration(work);
if let Some(job) = &mut self.job
&& was > 0
&& now > 0
{
let left = u128::from(job.left) * u128::from(now) / u128::from(was);
job.left = u64::try_from(left).expect("a fraction of a duration fits");
}
}
self.resume(context);
self.sync(registers, context);
}
fn reset(&mut self) {
*self = Self {
stream: self.stream,
..Self::new(self.ahb_hz, self.kernel_hz)
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::device::ResetCause;
use crate::mcu::{Mcu, Operate, Single};
use crate::soc::Width;
use crate::soc::stm32h743::builder;
use crate::soc::stm32h743::generated::RNG;
const FAST_AHB: u64 = 240_000_000;
const FIRST_FAST_NANOS: u64 = 4888;
const ROUND_FAST_NANOS: u64 = 888;
const RESAMPLE_FAST_NANOS: u64 = 1221;
const FIRST_SLOW_NANOS: u64 = 9323;
fn rng_at(ahb_hz: u64, kernel_hz: u64) -> Single<Rng> {
Single::new(builder(), "RNG", Rng::new(ahb_hz, kernel_hz)).expect("the bus builds")
}
fn rng() -> Single<Rng> {
rng_at(FAST_AHB, KERNEL_HZ)
}
fn address(register: Register) -> u64 {
RNG.base + u64::from(register.spec().offset)
}
fn at(nanos: u64) -> VirtualNanos {
VirtualNanos(nanos)
}
fn read(soc: &mut Single<Rng>, now: u64, register: Register) -> u64 {
soc.advance_to(at(now));
soc.read(at(now), address(register), Width::W32)
.expect("the register answers")
}
fn write(soc: &mut Single<Rng>, now: u64, register: Register, value: u64) {
soc.advance_to(at(now));
soc.write(at(now), address(register), Width::W32, value)
.expect("the register takes the write");
}
fn enable(soc: &mut Single<Rng>, now: u64) {
write(soc, now, Register::Cr, cr::RNGEN.mask());
}
fn drdy(soc: &mut Single<Rng>, now: u64) -> bool {
sr::DRDY.is_set(read(soc, now, Register::Sr))
}
fn lines(soc: &mut Single<Rng>) -> Vec<(u16, Level)> {
soc.drain_interrupts()
.into_iter()
.map(|change| (change.interrupt, change.level))
.collect()
}
#[test]
fn every_register_resets_to_the_manuals_value() {
let mut soc = rng();
for register in Register::ALL {
assert_eq!(
read(&mut soc, 0, register),
0,
"{}, 34.7",
register.spec().name
);
}
}
#[test]
fn the_first_word_comes_after_the_first_sample_time() {
let mut soc = rng();
enable(&mut soc, 1000);
let ready = 1000 + FIRST_FAST_NANOS;
assert!(!drdy(&mut soc, ready - 1), "34.5: not before");
assert_eq!(read(&mut soc, ready - 1, Register::Dr), 0);
assert!(drdy(&mut soc, ready), "192 RNG cycles and 213 AHB cycles");
assert_ne!(read(&mut soc, ready, Register::Dr), 0);
}
#[test]
fn a_slow_ahb_clock_takes_the_longer_first_sample() {
let mut soc = rng_at(64_000_000, KERNEL_HZ);
enable(&mut soc, 0);
assert!(
!drdy(&mut soc, FIRST_SLOW_NANOS - 1),
"128 RNG cycles and 426 AHB cycles, 34.5"
);
assert!(drdy(&mut soc, FIRST_SLOW_NANOS));
}
#[test]
fn each_read_takes_a_new_word_and_eight_words_drain_the_pipeline() {
let mut soc = rng();
enable(&mut soc, 0);
let both = FIRST_FAST_NANOS + ROUND_FAST_NANOS;
let mut words = Vec::new();
for _ in 0..8 {
assert!(
drdy(&mut soc, both),
"DRDY holds through the register's four, 34.3.5"
);
words.push(read(&mut soc, both, Register::Dr));
}
assert!(
!drdy(&mut soc, both),
"the pipeline is empty after eight, 34.3.3"
);
assert_eq!(read(&mut soc, both, Register::Dr), 0, "34.7.3");
words.sort_unstable();
words.dedup();
assert_eq!(words.len(), 8, "every word is new");
assert!(words.iter().all(|&word| word != 0));
}
#[test]
fn drdy_returns_once_a_round_refills_the_buffer() {
let mut soc = rng();
enable(&mut soc, 0);
for _ in 0..4 {
read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
}
assert!(
!drdy(&mut soc, FIRST_FAST_NANOS),
"the register's round is still running"
);
assert!(
!drdy(&mut soc, FIRST_FAST_NANOS + ROUND_FAST_NANOS - 1),
"213 AHB cycles, 34.3.3"
);
assert!(drdy(&mut soc, FIRST_FAST_NANOS + ROUND_FAST_NANOS));
}
#[test]
fn a_slow_kernel_clock_stretches_the_round() {
let mut soc = rng_at(FAST_AHB, 1_000_000);
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::CED.mask());
let first = 192_000 + ROUND_FAST_NANOS;
assert!(
drdy(&mut soc, first),
"192 RNG cycles at 1 MHz and 213 AHB cycles"
);
for _ in 0..4 {
read(&mut soc, first, Register::Dr);
}
let round = 16_000;
assert!(
!drdy(&mut soc, first + round - 1),
"16 x fAHB / fRNG is 3840 AHB cycles, past the floor of 213, 34.5"
);
assert!(drdy(&mut soc, first + round));
}
#[test]
fn dr_reads_zero_while_no_word_is_ready() {
let mut soc = rng();
assert_eq!(read(&mut soc, 0, Register::Dr), 0, "disabled");
enable(&mut soc, 0);
assert_eq!(
read(&mut soc, 100, Register::Dr),
0,
"34.7.3: zero when DRDY is clear"
);
}
#[test]
fn a_read_of_dr_the_manual_forbids_is_told_once() {
let mut soc = rng();
enable(&mut soc, 0);
read(&mut soc, 100, Register::Dr);
read(&mut soc, 200, Register::Dr);
let told = soc.drain_told();
assert_eq!(told.len(), 1, "DRDY was clear, 34.3.5: {told:?}");
assert_eq!(told[0].instance, "RNG");
assert_eq!(told[0].register, "DR");
read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
assert!(soc.drain_told().is_empty(), "a read with DRDY set is fine");
for _ in 0..3 {
read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
}
read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
assert_eq!(
soc.drain_told().len(),
1,
"told again on the next transition"
);
let mut soc = rng_at(FAST_AHB, 7_000_000);
enable(&mut soc, 0);
read(&mut soc, FIRST_FAST_NANOS + ROUND_FAST_NANOS, Register::Dr);
assert_eq!(
soc.drain_told().len(),
1,
"a word is ready, but CEIS stands: it must not be read either"
);
}
#[test]
fn a_peek_shows_the_next_word_and_takes_nothing() {
let mut soc = rng();
enable(&mut soc, 0);
soc.advance_to(at(FIRST_FAST_NANOS));
let peeked = soc
.peek(at(FIRST_FAST_NANOS), address(Register::Dr), Width::W32)
.expect("the register answers");
assert_ne!(peeked, 0);
let again = soc
.peek(at(FIRST_FAST_NANOS), address(Register::Dr), Width::W32)
.expect("the register answers");
assert_eq!(again, peeked, "a second look sees the same word");
assert_eq!(
read(&mut soc, FIRST_FAST_NANOS, Register::Dr),
peeked,
"and the read takes it"
);
}
#[test]
fn a_disabled_generator_keeps_its_words_and_makes_no_more() {
let mut soc = rng();
enable(&mut soc, 0);
let ready = FIRST_FAST_NANOS;
read(&mut soc, ready, Register::Dr);
write(&mut soc, ready, Register::Cr, 0);
for _ in 0..3 {
assert_ne!(
read(&mut soc, ready + 10_000, Register::Dr),
0,
"34.3.8: the buffer stays readable"
);
}
assert!(!drdy(&mut soc, ready + 10_000), "and empties");
assert!(
!drdy(&mut soc, ready + 1_000_000),
"no round completes while it's disabled, 34.3.8"
);
enable(&mut soc, ready + 1_000_000);
assert!(
!drdy(&mut soc, ready + 1_000_000 + RESAMPLE_FAST_NANOS - 1),
"re-enabled, it samples anew and runs a round, 34.3.8"
);
assert!(drdy(&mut soc, ready + 1_000_000 + RESAMPLE_FAST_NANOS));
}
#[test]
fn disabling_during_the_first_sample_resumes_where_it_stopped() {
let mut soc = rng();
enable(&mut soc, 0);
write(&mut soc, 2000, Register::Cr, 0);
assert!(!drdy(&mut soc, 100_000), "disabled, it doesn't progress");
enable(&mut soc, 100_000);
let remaining = FIRST_FAST_NANOS - 2000;
assert!(
!drdy(&mut soc, 100_000 + remaining - 1),
"34.3.4: it resumes from where it stopped"
);
assert!(drdy(&mut soc, 100_000 + remaining));
}
#[test]
fn a_stale_event_is_ignored() {
let mut soc = rng();
enable(&mut soc, 0);
write(&mut soc, 1000, Register::Cr, 0);
enable(&mut soc, 3000);
let second = 3000 + FIRST_FAST_NANOS - 1000;
assert!(
!drdy(&mut soc, FIRST_FAST_NANOS),
"the first run's event fires here and is no longer the work's end"
);
assert!(drdy(&mut soc, second));
}
#[test]
fn the_interrupt_follows_drdy_while_enabled_and_unmasked() {
let mut soc = rng();
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::IE.mask());
soc.advance_to(at(FIRST_FAST_NANOS - 1));
assert!(lines(&mut soc).is_empty(), "no word, no interrupt");
soc.advance_to(at(FIRST_FAST_NANOS));
assert_eq!(
lines(&mut soc),
[(80, Level::High)],
"an interrupt when DRDY rises, 34.4"
);
let both = FIRST_FAST_NANOS + ROUND_FAST_NANOS;
soc.advance_to(at(both));
assert!(
lines(&mut soc).is_empty(),
"the register filling changes nothing"
);
for _ in 0..7 {
read(&mut soc, both, Register::Dr);
}
assert!(lines(&mut soc).is_empty(), "a word still waits");
read(&mut soc, both, Register::Dr);
assert_eq!(
lines(&mut soc),
[(80, Level::Low)],
"the interrupt clears itself when the last word is read, Table 281"
);
let next = both + ROUND_FAST_NANOS;
soc.advance_to(at(next));
assert_eq!(lines(&mut soc), [(80, Level::High)]);
write(&mut soc, next, Register::Cr, cr::RNGEN.mask());
assert_eq!(lines(&mut soc), [(80, Level::Low)], "IE clear lowers it");
write(&mut soc, next, Register::Cr, cr::IE.mask());
assert!(
lines(&mut soc).is_empty(),
"34.4: interrupts are generated only when the generator is enabled, though DRDY stands"
);
}
#[test]
fn the_interrupt_is_line_80() {
let soc = builder().build().unwrap();
assert_eq!(
soc.peripheral("RNG").expect("an instance").interrupts[IT],
80,
"Table 147"
);
}
#[test]
fn ced_cannot_change_while_the_generator_is_enabled() {
let mut soc = rng();
enable(&mut soc, 0);
write(
&mut soc,
10,
Register::Cr,
cr::RNGEN.mask() | cr::CED.mask(),
);
assert!(
!cr::CED.is_set(read(&mut soc, 10, Register::Cr)),
"34.7.1: the clock error detection can't be disabled on the fly"
);
write(&mut soc, 20, Register::Cr, 0);
write(&mut soc, 30, Register::Cr, cr::CED.mask());
assert!(
cr::CED.is_set(read(&mut soc, 30, Register::Cr)),
"disabled, it can"
);
write(
&mut soc,
40,
Register::Cr,
cr::RNGEN.mask() | cr::CED.mask(),
);
write(&mut soc, 50, Register::Cr, cr::RNGEN.mask());
assert!(
cr::CED.is_set(read(&mut soc, 50, Register::Cr)),
"and enabled it can't be enabled either"
);
}
#[test]
fn a_slow_kernel_clock_raises_the_clock_error() {
let mut soc = rng_at(FAST_AHB, 7_000_000);
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::IE.mask());
let status = read(&mut soc, 0, Register::Sr);
assert!(
sr::CECS.is_set(status),
"7 MHz x 32 is under 240 MHz, 34.3.6"
);
assert!(sr::CEIS.is_set(status), "34.3.7: both are set");
assert_eq!(
lines(&mut soc),
[(80, Level::High)],
"an interrupt for the error"
);
write(&mut soc, 5, Register::Sr, !sr::CEIS.mask());
let status = read(&mut soc, 5, Register::Sr);
assert!(!sr::CEIS.is_set(status), "a written zero clears CEIS");
assert!(
sr::CECS.is_set(status),
"while the condition stands, CECS does"
);
assert_eq!(lines(&mut soc), [(80, Level::Low)]);
write(&mut soc, 6, Register::Sr, sr::CEIS.mask());
assert!(
!sr::CEIS.is_set(read(&mut soc, 6, Register::Sr)),
"a written one does nothing"
);
let mut soc = rng_at(FAST_AHB, 7_000_000);
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::CED.mask());
assert_eq!(
read(&mut soc, 0, Register::Sr) & (sr::CECS.mask() | sr::CEIS.mask()),
0,
"CED turns the check off"
);
}
#[test]
fn a_change_of_the_ahb_clock_rescales_the_work_and_the_clock_check() {
let mut soc = rng_at(FAST_AHB, 7_000_000);
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask());
assert!(sr::CECS.is_set(read(&mut soc, 0, Register::Sr)));
soc.drive(at(1000), |rng, registers, context| {
rng.on_clock(200_000_000, registers, context);
});
let status = read(&mut soc, 1000, Register::Sr);
assert!(
!sr::CECS.is_set(status),
"7 MHz x 32 is 224 MHz, over 200 MHz: CECS clears itself, 34.3.6"
);
assert!(
sr::CEIS.is_set(status),
"and CEIS stays until software clears it"
);
let mut soc = rng();
enable(&mut soc, 0);
soc.drive(at(2000), |rng, registers, context| {
rng.on_clock(200_000_000, registers, context);
});
let due = 2000 + 2888 * 5065 / 4888;
assert!(
!drdy(&mut soc, due - 1),
"a slower clock stretches what was left"
);
assert!(drdy(&mut soc, due), "by the new total's fraction");
}
#[test]
fn a_seed_error_sets_both_flags_stops_generation_and_recovers() {
let mut soc = rng();
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::IE.mask());
let ready = FIRST_FAST_NANOS;
soc.advance_to(at(ready));
lines(&mut soc);
for _ in 0..4 {
read(&mut soc, ready, Register::Dr);
}
soc.drive(at(ready), |rng, registers, context| {
rng.seed_error(registers, context);
});
let status = read(&mut soc, ready, Register::Sr);
assert!(
sr::SEIS.is_set(status) && sr::SECS.is_set(status),
"34.3.7: both set"
);
assert!(
!drdy(&mut soc, ready + 100_000),
"generation stopped: the round that was running never lands"
);
write(&mut soc, ready + 200_000, Register::Sr, !sr::SEIS.mask());
let status = read(&mut soc, ready + 200_000, Register::Sr);
assert!(!sr::SEIS.is_set(status), "SEIS clears by a written zero");
assert!(sr::SECS.is_set(status), "SECS stays while the fault does");
soc.drive(at(ready + 300_000), |rng, registers, context| {
rng.seed_recovered(registers, context);
});
let status = read(&mut soc, ready + 300_000, Register::Sr);
assert!(!sr::SECS.is_set(status), "recovered");
assert!(!drdy(&mut soc, ready + 300_000 + ROUND_FAST_NANOS - 1));
assert!(
drdy(&mut soc, ready + 300_000 + ROUND_FAST_NANOS),
"and generation resumes"
);
}
#[test]
fn a_reset_empties_the_pipeline_and_goes_on_with_the_stream() {
let mut soc = rng();
enable(&mut soc, 0);
let first = read(&mut soc, FIRST_FAST_NANOS, Register::Dr);
soc.reset(at(FIRST_FAST_NANOS), ResetCause::Pin);
assert_eq!(read(&mut soc, FIRST_FAST_NANOS, Register::Cr), 0);
assert!(!drdy(&mut soc, FIRST_FAST_NANOS));
enable(&mut soc, 100_000);
let after = read(&mut soc, 100_000 + FIRST_FAST_NANOS, Register::Dr);
assert_eq!(first, 0x157A_3807, "the first word of the stream");
assert_eq!(
after, 0x0140_4CE9,
"the stream goes on past the four words the first round dealt, so the fifth follows"
);
}
#[test]
fn the_stream_is_the_seeds_and_nothing_else() {
let words = |seed: u64| {
let mut rng = Rng::seeded(FAST_AHB, KERNEL_HZ, seed);
(0..12).map(|_| rng.deal()).collect::<Vec<u32>>()
};
assert_eq!(words(SEED), words(SEED), "the same seed, the same words");
assert_ne!(words(SEED), words(SEED + 1), "another seed, other words");
assert_eq!(
words(SEED)[..5],
[
0x157A_3807,
0xD573_529B,
0x2F90_B72E,
0xA2D4_1933,
0x0140_4CE9
],
"SplitMix64's top halves from the seed, which a port of the generator reproduces"
);
}
#[test]
fn no_word_is_zero() {
let mut rng = Rng::seeded(FAST_AHB, KERNEL_HZ, 0);
assert!(
(0..100_000).all(|_| rng.deal() != 0),
"a zero in RNG_DR means a seed error, 34.7.3"
);
rng.stream = 0_u64.wrapping_sub(0x9E37_79B9_7F4A_7C15);
let mut next = Rng::seeded(FAST_AHB, KERNEL_HZ, 0);
next.stream = 0;
assert_eq!(
rng.deal(),
next.deal(),
"the zero is skipped and the word after it is dealt"
);
}
#[test]
fn a_snapshot_goes_on_with_the_same_stream() {
let mut one = Rng::new(FAST_AHB, KERNEL_HZ);
let first: Vec<u32> = (0..5).map(|_| one.deal()).collect();
let bytes = postcard::to_stdvec(&one).expect("the state serializes");
let mut two: Rng = postcard::from_bytes(&bytes).expect("the state deserializes");
let rest_one: Vec<u32> = (0..7).map(|_| one.deal()).collect();
let rest_two: Vec<u32> = (0..7).map(|_| two.deal()).collect();
assert_eq!(
rest_one, rest_two,
"a restored generator deals the words that follow"
);
assert_ne!(first, rest_one);
}
#[test]
fn two_runs_of_one_script_are_the_same_run() {
fn run() -> (Vec<u64>, Vec<(u16, Level)>, u64) {
let mut soc = rng();
write(&mut soc, 0, Register::Cr, cr::RNGEN.mask() | cr::IE.mask());
let mut words = Vec::new();
for step in 1..=12_u64 {
let now = step * 600;
if drdy(&mut soc, now) {
words.push(read(&mut soc, now, Register::Dr));
}
}
let status = read(&mut soc, 8000, Register::Sr);
(words, lines(&mut soc), status)
}
let (first, second) = (run(), run());
assert!(!first.0.is_empty());
assert_eq!(first, second);
}
#[test]
fn ardupilots_reads_get_their_words_within_their_timeouts() {
let mut soc = rng();
let current = read(&mut soc, 0, Register::Cr);
write(&mut soc, 0, Register::Cr, cr::IE.set(current, 1));
let current = read(&mut soc, 0, Register::Cr);
write(&mut soc, 0, Register::Cr, cr::RNGEN.set(current, 1));
let generate = |soc: &mut Single<Rng>, now: u64| {
let status = read(soc, now, Register::Sr);
let errors = sr::SEIS.mask() | sr::CEIS.mask();
(status & errors == 0 && sr::DRDY.is_set(status)).then(|| read(soc, now, Register::Dr))
};
assert_eq!(
generate(&mut soc, 100),
None,
"the nonblocking call before the first sample returns none"
);
let timeout_nanos = 10_000_u64;
let mut now = 100;
let mut words = Vec::new();
while words.len() < 4 && now < timeout_nanos {
if let Some(word) = generate(&mut soc, now) {
words.push(word);
}
now += 100;
}
assert_eq!(
words.len(),
4,
"four words, a blocking call for sixteen bytes, within ten microseconds"
);
assert!(words.iter().all(|&word| word != 0));
let mut taken = 0;
while generate(&mut soc, 7000).is_some() {
taken += 1;
}
assert_eq!(
taken, 8,
"34.3.5: the buffer's four and the register's four"
);
}
}