pamoja_hal/script.rs
1//! Scripted buses: a part's side of the conversation, for tests with nothing plugged in.
2//!
3//! A driver is the sequence of transfers its datasheet prescribes: read this register,
4//! write that one, wait, read the result. [`I2cScript`] holds that sequence as
5//! [`I2cStep`]s, checks every transfer the driver makes against the next step, and
6//! answers with the bytes the part would have sent. A step can also fail on purpose,
7//! so the error path is tested too. [`PinScript`] and [`DelayLog`] do the same for a
8//! GPIO line and a delay: the levels the driver set and the time it waited are kept
9//! for the test to assert against the datasheet's timing.
10//!
11//! [`block_on`] runs the future a driver's `read` or `apply` returns to completion, so
12//! a test of a scripted driver needs no executor.
13
14use alloc::collections::VecDeque;
15use alloc::vec::Vec;
16use core::fmt;
17use core::future::Future;
18use core::pin::pin;
19use core::task::{Context, Poll, Waker};
20
21use embedded_hal::delay::DelayNs;
22use embedded_hal::digital::{
23 ErrorType as PinErrorType, InputPin, OutputPin, PinState, StatefulOutputPin,
24};
25use embedded_hal::i2c::{ErrorKind, ErrorType, I2c, Operation, SevenBitAddress};
26use embedded_hal::spi::{self, SpiDevice};
27
28/// One transfer a scripted I2C part expects, and what it answers.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub enum I2cStep {
31 /// The driver writes exactly `bytes` to `address`.
32 Write {
33 /// The 7-bit address the write must go to.
34 address: u8,
35 /// The bytes the driver must send.
36 bytes: Vec<u8>,
37 },
38 /// The driver reads from `address` and receives `reply`, whose length is the
39 /// length it must ask for.
40 Read {
41 /// The 7-bit address the read must come from.
42 address: u8,
43 /// The bytes the part answers with.
44 reply: Vec<u8>,
45 },
46 /// The driver writes `bytes` then reads `reply.len()` bytes from `address` in one
47 /// transaction, the shape of a register read.
48 WriteRead {
49 /// The 7-bit address of the part.
50 address: u8,
51 /// The bytes the driver must send first, usually a register address.
52 bytes: Vec<u8>,
53 /// The bytes the part answers with.
54 reply: Vec<u8>,
55 },
56 /// The part fails the next transfer to `address` with `kind`, the way a missing
57 /// or busy part does.
58 Fault {
59 /// The 7-bit address the failing transfer must go to.
60 address: u8,
61 /// The failure the driver sees.
62 kind: ErrorKind,
63 },
64}
65
66impl I2cStep {
67 /// A write of exactly `bytes` to `address`.
68 ///
69 /// # Arguments
70 ///
71 /// * `address` - the 7-bit address.
72 /// * `bytes` - the bytes the driver must send.
73 ///
74 /// # Returns
75 ///
76 /// The step.
77 pub fn write(address: u8, bytes: impl Into<Vec<u8>>) -> I2cStep {
78 I2cStep::Write {
79 address,
80 bytes: bytes.into(),
81 }
82 }
83
84 /// A read from `address` answered with `reply`.
85 ///
86 /// # Arguments
87 ///
88 /// * `address` - the 7-bit address.
89 /// * `reply` - the bytes the part answers with; the driver must ask for exactly
90 /// this many.
91 ///
92 /// # Returns
93 ///
94 /// The step.
95 pub fn read(address: u8, reply: impl Into<Vec<u8>>) -> I2cStep {
96 I2cStep::Read {
97 address,
98 reply: reply.into(),
99 }
100 }
101
102 /// A write of `bytes` followed by a read answered with `reply`, in one transaction.
103 ///
104 /// # Arguments
105 ///
106 /// * `address` - the 7-bit address.
107 /// * `bytes` - the bytes the driver must send first.
108 /// * `reply` - the bytes the part answers with.
109 ///
110 /// # Returns
111 ///
112 /// The step.
113 pub fn write_read(
114 address: u8,
115 bytes: impl Into<Vec<u8>>,
116 reply: impl Into<Vec<u8>>,
117 ) -> I2cStep {
118 I2cStep::WriteRead {
119 address,
120 bytes: bytes.into(),
121 reply: reply.into(),
122 }
123 }
124
125 /// A transfer to `address` that fails with `kind`.
126 ///
127 /// # Arguments
128 ///
129 /// * `address` - the 7-bit address.
130 /// * `kind` - the failure the driver sees.
131 ///
132 /// # Returns
133 ///
134 /// The step.
135 pub fn fault(address: u8, kind: ErrorKind) -> I2cStep {
136 I2cStep::Fault { address, kind }
137 }
138
139 fn address(&self) -> u8 {
140 match self {
141 I2cStep::Write { address, .. }
142 | I2cStep::Read { address, .. }
143 | I2cStep::WriteRead { address, .. }
144 | I2cStep::Fault { address, .. } => *address,
145 }
146 }
147}
148
149/// One operation of a transaction, as the driver issued it.
150#[derive(Clone, Debug, PartialEq, Eq)]
151pub enum Transfer {
152 /// The driver wrote these bytes.
153 Write {
154 /// The 7-bit address written to.
155 address: u8,
156 /// The bytes written.
157 bytes: Vec<u8>,
158 },
159 /// The driver asked to read this many bytes.
160 Read {
161 /// The 7-bit address read from.
162 address: u8,
163 /// How many bytes were asked for.
164 len: usize,
165 },
166}
167
168/// What a scripted I2C part refused.
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub enum ScriptError {
171 /// A transfer arrived that does not match the next step of the script.
172 Mismatch {
173 /// The index of the step the transfer was checked against.
174 step: usize,
175 /// The step that stood there, or `None` once the script had been used up.
176 expected: Option<I2cStep>,
177 /// The whole transaction as the driver issued it.
178 actual: Vec<Transfer>,
179 },
180 /// The next step was an [`I2cStep::Fault`], so the part failed as scripted.
181 Fault(ErrorKind),
182}
183
184impl fmt::Display for ScriptError {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 match self {
187 ScriptError::Mismatch {
188 step,
189 expected,
190 actual,
191 } => write!(
192 f,
193 "i2c script step {step}: expected {expected:?}, the driver issued {actual:?}"
194 ),
195 ScriptError::Fault(kind) => write!(f, "i2c script fault: {kind}"),
196 }
197 }
198}
199
200impl core::error::Error for ScriptError {}
201
202impl embedded_hal::i2c::Error for ScriptError {
203 fn kind(&self) -> ErrorKind {
204 match self {
205 ScriptError::Fault(kind) => *kind,
206 ScriptError::Mismatch { .. } => ErrorKind::Other,
207 }
208 }
209}
210
211/// An I2C bus that plays a script of transfers and replies.
212///
213/// Each transaction the driver issues is matched against the next step. A register
214/// read (a write followed by a read in one transaction) consumes one
215/// [`I2cStep::WriteRead`]; a plain write or read consumes a [`I2cStep::Write`] or
216/// [`I2cStep::Read`]; a longer transaction consumes steps in order. A transfer that
217/// does not match fails with [`ScriptError::Mismatch`], which names the step and what
218/// arrived, and a test ends by checking [`done`](I2cScript::done) so an unfinished
219/// script is a failure too.
220///
221/// # Examples
222///
223/// ```
224/// use pamoja_hal::i2c::I2c;
225/// use pamoja_hal::script::{I2cScript, I2cStep};
226///
227/// // A part at 0x48 whose 16-bit result register 0x00 reads 0x0C80.
228/// let mut bus = I2cScript::new([
229/// I2cStep::write(0x48, [0x01, 0x60, 0x20]),
230/// I2cStep::write_read(0x48, [0x00], [0x0C, 0x80]),
231/// ]);
232///
233/// bus.write(0x48, &[0x01, 0x60, 0x20])?;
234/// let mut result = [0u8; 2];
235/// bus.write_read(0x48, &[0x00], &mut result)?;
236/// assert_eq!(u16::from_be_bytes(result), 0x0C80);
237/// assert!(bus.done());
238/// # Ok::<(), pamoja_hal::script::ScriptError>(())
239/// ```
240#[derive(Clone, Debug, Default)]
241pub struct I2cScript {
242 steps: VecDeque<I2cStep>,
243 consumed: usize,
244}
245
246impl I2cScript {
247 /// Creates a bus that expects `steps` in order.
248 ///
249 /// # Arguments
250 ///
251 /// * `steps` - the transfers the driver is expected to make, and their replies.
252 ///
253 /// # Returns
254 ///
255 /// The scripted bus.
256 pub fn new(steps: impl IntoIterator<Item = I2cStep>) -> I2cScript {
257 I2cScript {
258 steps: steps.into_iter().collect(),
259 consumed: 0,
260 }
261 }
262
263 /// Reports whether every step has been consumed.
264 ///
265 /// # Returns
266 ///
267 /// `true` once the driver has made every transfer the script expected.
268 pub fn done(&self) -> bool {
269 self.steps.is_empty()
270 }
271
272 /// Reports how many steps remain.
273 ///
274 /// # Returns
275 ///
276 /// The number of steps the driver has not yet reached.
277 pub fn remaining(&self) -> usize {
278 self.steps.len()
279 }
280
281 /// Reports how many steps the driver has consumed.
282 ///
283 /// # Returns
284 ///
285 /// The number of steps matched or faulted so far.
286 pub fn consumed(&self) -> usize {
287 self.consumed
288 }
289
290 fn mismatch(&self, actual: Vec<Transfer>) -> ScriptError {
291 ScriptError::Mismatch {
292 step: self.consumed,
293 expected: self.steps.front().cloned(),
294 actual,
295 }
296 }
297
298 fn take(&mut self) -> Option<I2cStep> {
299 let step = self.steps.pop_front();
300 if step.is_some() {
301 self.consumed += 1;
302 }
303 step
304 }
305}
306
307impl ErrorType for I2cScript {
308 type Error = ScriptError;
309}
310
311impl I2c<SevenBitAddress> for I2cScript {
312 fn transaction(
313 &mut self,
314 address: SevenBitAddress,
315 operations: &mut [Operation<'_>],
316 ) -> Result<(), ScriptError> {
317 let issued: Vec<Transfer> = operations
318 .iter()
319 .map(|operation| match operation {
320 Operation::Write(bytes) => Transfer::Write {
321 address,
322 bytes: bytes.to_vec(),
323 },
324 Operation::Read(buffer) => Transfer::Read {
325 address,
326 len: buffer.len(),
327 },
328 })
329 .collect();
330
331 let mut index = 0;
332 while index < operations.len() {
333 let Some(step) = self.steps.front() else {
334 return Err(self.mismatch(issued));
335 };
336 if step.address() != address {
337 return Err(self.mismatch(issued));
338 }
339 if let I2cStep::Fault { kind, .. } = step {
340 let kind = *kind;
341 self.take();
342 return Err(ScriptError::Fault(kind));
343 }
344
345 let (first, rest) = operations[index..].split_first_mut().expect("in range");
346 match (first, rest.first_mut(), step) {
347 (
348 Operation::Write(written),
349 Some(Operation::Read(buffer)),
350 I2cStep::WriteRead { bytes, reply, .. },
351 ) if bytes.as_slice() == *written && reply.len() == buffer.len() => {
352 buffer.copy_from_slice(reply);
353 self.take();
354 index += 2;
355 }
356 (Operation::Write(written), _, I2cStep::Write { bytes, .. })
357 if bytes.as_slice() == *written =>
358 {
359 self.take();
360 index += 1;
361 }
362 (Operation::Read(buffer), _, I2cStep::Read { reply, .. })
363 if reply.len() == buffer.len() =>
364 {
365 buffer.copy_from_slice(reply);
366 self.take();
367 index += 1;
368 }
369 _ => return Err(self.mismatch(issued)),
370 }
371 }
372 Ok(())
373 }
374}
375
376/// One transfer a scripted SPI part expects, and what it answers.
377#[derive(Clone, Debug, PartialEq, Eq)]
378pub enum SpiStep {
379 /// The driver writes exactly `bytes`; whatever the part shifts out meanwhile is
380 /// discarded, as the driver asked.
381 Write {
382 /// The bytes the driver must send.
383 bytes: Vec<u8>,
384 },
385 /// The driver reads `reply.len()` bytes and receives `reply`.
386 Read {
387 /// The bytes the part shifts out.
388 reply: Vec<u8>,
389 },
390 /// The driver writes `bytes` and receives `reply` in the same clocks, so the two
391 /// are the same length.
392 Transfer {
393 /// The bytes the driver must send.
394 bytes: Vec<u8>,
395 /// The bytes the part shifts out at the same time.
396 reply: Vec<u8>,
397 },
398 /// The part fails the next transfer with `kind`.
399 Fault {
400 /// The failure the driver sees.
401 kind: spi::ErrorKind,
402 },
403}
404
405impl SpiStep {
406 /// A write of exactly `bytes`.
407 ///
408 /// # Arguments
409 ///
410 /// * `bytes` - the bytes the driver must send.
411 ///
412 /// # Returns
413 ///
414 /// The step.
415 pub fn write(bytes: impl Into<Vec<u8>>) -> SpiStep {
416 SpiStep::Write {
417 bytes: bytes.into(),
418 }
419 }
420
421 /// A read answered with `reply`.
422 ///
423 /// # Arguments
424 ///
425 /// * `reply` - the bytes the part shifts out; the driver must ask for exactly
426 /// this many.
427 ///
428 /// # Returns
429 ///
430 /// The step.
431 pub fn read(reply: impl Into<Vec<u8>>) -> SpiStep {
432 SpiStep::Read {
433 reply: reply.into(),
434 }
435 }
436
437 /// A full-duplex transfer: `bytes` go out while `reply` comes in.
438 ///
439 /// # Arguments
440 ///
441 /// * `bytes` - the bytes the driver must send.
442 /// * `reply` - the bytes the part shifts out, as many as `bytes`.
443 ///
444 /// # Returns
445 ///
446 /// The step.
447 pub fn transfer(bytes: impl Into<Vec<u8>>, reply: impl Into<Vec<u8>>) -> SpiStep {
448 SpiStep::Transfer {
449 bytes: bytes.into(),
450 reply: reply.into(),
451 }
452 }
453
454 /// A transfer that fails with `kind`.
455 ///
456 /// # Arguments
457 ///
458 /// * `kind` - the failure the driver sees.
459 ///
460 /// # Returns
461 ///
462 /// The step.
463 pub fn fault(kind: spi::ErrorKind) -> SpiStep {
464 SpiStep::Fault { kind }
465 }
466}
467
468/// One operation of an SPI transaction, as the driver issued it.
469#[derive(Clone, Debug, PartialEq, Eq)]
470pub enum SpiTransfer {
471 /// The driver wrote these bytes.
472 Write {
473 /// The bytes written.
474 bytes: Vec<u8>,
475 },
476 /// The driver asked to read this many bytes.
477 Read {
478 /// How many bytes were asked for.
479 len: usize,
480 },
481 /// The driver wrote these bytes and read as many back.
482 Transfer {
483 /// The bytes written.
484 bytes: Vec<u8>,
485 },
486 /// The driver asked the bus to pause inside the transaction.
487 Delay {
488 /// The pause in nanoseconds.
489 ns: u32,
490 },
491}
492
493/// What a scripted SPI part refused.
494#[derive(Clone, Debug, PartialEq, Eq)]
495pub enum SpiScriptError {
496 /// An operation arrived that does not match the next step of the script.
497 Mismatch {
498 /// The index of the step the operation was checked against.
499 step: usize,
500 /// The step that stood there, or `None` once the script had been used up.
501 expected: Option<SpiStep>,
502 /// The whole transaction as the driver issued it.
503 actual: Vec<SpiTransfer>,
504 },
505 /// The next step was a [`SpiStep::Fault`], so the part failed as scripted.
506 Fault(spi::ErrorKind),
507}
508
509impl fmt::Display for SpiScriptError {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 match self {
512 SpiScriptError::Mismatch {
513 step,
514 expected,
515 actual,
516 } => write!(
517 f,
518 "spi script step {step}: expected {expected:?}, the driver issued {actual:?}"
519 ),
520 SpiScriptError::Fault(kind) => write!(f, "spi script fault: {kind}"),
521 }
522 }
523}
524
525impl core::error::Error for SpiScriptError {}
526
527impl spi::Error for SpiScriptError {
528 fn kind(&self) -> spi::ErrorKind {
529 match self {
530 SpiScriptError::Fault(kind) => *kind,
531 SpiScriptError::Mismatch { .. } => spi::ErrorKind::Other,
532 }
533 }
534}
535
536/// An SPI device that plays a script of transfers and replies.
537///
538/// The device is the bus plus the part's chip select, so a transaction is one
539/// chip-select assertion and each operation inside it consumes one step. A delay
540/// operation consumes nothing. A transfer that does not match fails with
541/// [`SpiScriptError::Mismatch`], and a test ends by checking
542/// [`done`](SpiScript::done).
543///
544/// # Examples
545///
546/// ```
547/// use pamoja_hal::script::{SpiScript, SpiStep};
548/// use pamoja_hal::spi::{Operation, SpiDevice};
549///
550/// // A Bosch part read over SPI: the control byte 0xD0 with its read bit set, then
551/// // the chip id shifted out.
552/// let mut device = SpiScript::new([SpiStep::write([0xD0]), SpiStep::read([0x60])]);
553/// let mut id = [0u8; 1];
554/// device.transaction(&mut [Operation::Write(&[0xD0]), Operation::Read(&mut id)])?;
555/// assert_eq!(id, [0x60]);
556/// assert!(device.done());
557/// # Ok::<(), pamoja_hal::script::SpiScriptError>(())
558/// ```
559#[derive(Clone, Debug, Default)]
560pub struct SpiScript {
561 steps: VecDeque<SpiStep>,
562 consumed: usize,
563}
564
565impl SpiScript {
566 /// Creates a device that expects `steps` in order.
567 ///
568 /// # Arguments
569 ///
570 /// * `steps` - the transfers the driver is expected to make, and their replies.
571 ///
572 /// # Returns
573 ///
574 /// The scripted device.
575 pub fn new(steps: impl IntoIterator<Item = SpiStep>) -> SpiScript {
576 SpiScript {
577 steps: steps.into_iter().collect(),
578 consumed: 0,
579 }
580 }
581
582 /// Reports whether every step has been consumed.
583 ///
584 /// # Returns
585 ///
586 /// `true` once the driver has made every transfer the script expected.
587 pub fn done(&self) -> bool {
588 self.steps.is_empty()
589 }
590
591 /// Reports how many steps remain.
592 ///
593 /// # Returns
594 ///
595 /// The number of steps the driver has not yet reached.
596 pub fn remaining(&self) -> usize {
597 self.steps.len()
598 }
599
600 /// Reports how many steps the driver has consumed.
601 ///
602 /// # Returns
603 ///
604 /// The number of steps matched or faulted so far.
605 pub fn consumed(&self) -> usize {
606 self.consumed
607 }
608
609 fn mismatch(&self, actual: Vec<SpiTransfer>) -> SpiScriptError {
610 SpiScriptError::Mismatch {
611 step: self.consumed,
612 expected: self.steps.front().cloned(),
613 actual,
614 }
615 }
616
617 fn take(&mut self) {
618 if self.steps.pop_front().is_some() {
619 self.consumed += 1;
620 }
621 }
622}
623
624impl spi::ErrorType for SpiScript {
625 type Error = SpiScriptError;
626}
627
628impl SpiDevice<u8> for SpiScript {
629 fn transaction(
630 &mut self,
631 operations: &mut [spi::Operation<'_, u8>],
632 ) -> Result<(), SpiScriptError> {
633 let issued: Vec<SpiTransfer> = operations
634 .iter()
635 .map(|operation| match operation {
636 spi::Operation::Write(bytes) => SpiTransfer::Write {
637 bytes: bytes.to_vec(),
638 },
639 spi::Operation::Read(buffer) => SpiTransfer::Read { len: buffer.len() },
640 spi::Operation::Transfer(_, bytes) => SpiTransfer::Transfer {
641 bytes: bytes.to_vec(),
642 },
643 spi::Operation::TransferInPlace(bytes) => SpiTransfer::Transfer {
644 bytes: bytes.to_vec(),
645 },
646 spi::Operation::DelayNs(ns) => SpiTransfer::Delay { ns: *ns },
647 })
648 .collect();
649
650 for operation in operations.iter_mut() {
651 if let spi::Operation::DelayNs(_) = operation {
652 continue;
653 }
654 let Some(step) = self.steps.front() else {
655 return Err(self.mismatch(issued));
656 };
657 if let SpiStep::Fault { kind } = step {
658 let kind = *kind;
659 self.take();
660 return Err(SpiScriptError::Fault(kind));
661 }
662 match (operation, step) {
663 (spi::Operation::Write(written), SpiStep::Write { bytes })
664 if bytes.as_slice() == *written =>
665 {
666 self.take();
667 }
668 (spi::Operation::Read(buffer), SpiStep::Read { reply })
669 if reply.len() == buffer.len() =>
670 {
671 buffer.copy_from_slice(reply);
672 self.take();
673 }
674 (spi::Operation::Transfer(read, written), SpiStep::Transfer { bytes, reply })
675 if bytes.as_slice() == *written && reply.len() == read.len() =>
676 {
677 read.copy_from_slice(reply);
678 self.take();
679 }
680 (spi::Operation::TransferInPlace(buffer), SpiStep::Transfer { bytes, reply })
681 if bytes.as_slice() == *buffer && reply.len() == buffer.len() =>
682 {
683 buffer.copy_from_slice(reply);
684 self.take();
685 }
686 _ => return Err(self.mismatch(issued)),
687 }
688 }
689 Ok(())
690 }
691}
692
693/// A GPIO line that records the levels it is driven to and answers reads from a script.
694///
695/// Driving the pin appends to [`driven`](PinScript::driven). Reading it returns the
696/// next scripted level, or the level it was last driven to once the script is used up,
697/// so a line that is only ever an output never runs dry.
698///
699/// # Examples
700///
701/// ```
702/// use pamoja_hal::digital::{InputPin, OutputPin, PinState};
703/// use pamoja_hal::script::PinScript;
704///
705/// let mut pin = PinScript::new([PinState::Low, PinState::High]);
706/// pin.set_high()?;
707/// pin.set_low()?;
708/// assert_eq!(pin.driven(), [PinState::High, PinState::Low]);
709/// assert!(pin.is_low()?);
710/// assert!(pin.is_high()?);
711/// # Ok::<(), core::convert::Infallible>(())
712/// ```
713#[derive(Clone, Debug)]
714pub struct PinScript {
715 driven: Vec<PinState>,
716 inputs: VecDeque<PinState>,
717 level: PinState,
718}
719
720impl Default for PinScript {
721 fn default() -> Self {
722 PinScript::new([])
723 }
724}
725
726impl PinScript {
727 /// Creates a released (high) line whose reads answer `inputs` in order.
728 ///
729 /// # Arguments
730 ///
731 /// * `inputs` - the levels each read returns, in order.
732 ///
733 /// # Returns
734 ///
735 /// The scripted pin.
736 pub fn new(inputs: impl IntoIterator<Item = PinState>) -> PinScript {
737 PinScript {
738 driven: Vec::new(),
739 inputs: inputs.into_iter().collect(),
740 level: PinState::High,
741 }
742 }
743
744 /// Returns every level the pin was driven to, oldest first.
745 ///
746 /// # Returns
747 ///
748 /// The levels set through [`OutputPin`].
749 pub fn driven(&self) -> &[PinState] {
750 &self.driven
751 }
752
753 /// Returns the level the pin was last driven to.
754 ///
755 /// # Returns
756 ///
757 /// The current output level.
758 pub fn level(&self) -> PinState {
759 self.level
760 }
761
762 /// Reports how many scripted input levels remain unread.
763 ///
764 /// # Returns
765 ///
766 /// The number of reads the script still answers.
767 pub fn remaining(&self) -> usize {
768 self.inputs.len()
769 }
770}
771
772impl PinErrorType for PinScript {
773 type Error = core::convert::Infallible;
774}
775
776impl OutputPin for PinScript {
777 fn set_low(&mut self) -> Result<(), Self::Error> {
778 self.level = PinState::Low;
779 self.driven.push(PinState::Low);
780 Ok(())
781 }
782
783 fn set_high(&mut self) -> Result<(), Self::Error> {
784 self.level = PinState::High;
785 self.driven.push(PinState::High);
786 Ok(())
787 }
788}
789
790impl StatefulOutputPin for PinScript {
791 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
792 Ok(self.level == PinState::High)
793 }
794
795 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
796 Ok(self.level == PinState::Low)
797 }
798}
799
800impl InputPin for PinScript {
801 fn is_high(&mut self) -> Result<bool, Self::Error> {
802 let level = self.inputs.pop_front().unwrap_or(self.level);
803 Ok(level == PinState::High)
804 }
805
806 fn is_low(&mut self) -> Result<bool, Self::Error> {
807 self.is_high().map(|high| !high)
808 }
809}
810
811/// A delay that records every wait instead of sleeping.
812///
813/// # Examples
814///
815/// ```
816/// use pamoja_hal::delay::DelayNs;
817/// use pamoja_hal::script::DelayLog;
818///
819/// let mut delay = DelayLog::new();
820/// delay.delay_us(480);
821/// delay.delay_ms(10);
822/// assert_eq!(delay.total_micros(), 10_480);
823/// ```
824#[derive(Clone, Debug, Default)]
825pub struct DelayLog {
826 waits_ns: Vec<u32>,
827 total_ns: u64,
828}
829
830impl DelayLog {
831 /// Creates a log with nothing waited yet.
832 ///
833 /// # Returns
834 ///
835 /// The empty log.
836 pub fn new() -> DelayLog {
837 DelayLog::default()
838 }
839
840 /// Returns every wait in nanoseconds, oldest first.
841 ///
842 /// # Returns
843 ///
844 /// The waits as the driver requested them.
845 pub fn waits_ns(&self) -> &[u32] {
846 &self.waits_ns
847 }
848
849 /// Returns the total time waited, in nanoseconds.
850 ///
851 /// # Returns
852 ///
853 /// The sum of every wait.
854 pub fn total_ns(&self) -> u64 {
855 self.total_ns
856 }
857
858 /// Returns the total time waited, in whole microseconds.
859 ///
860 /// # Returns
861 ///
862 /// The sum of every wait, rounded down.
863 pub fn total_micros(&self) -> u64 {
864 self.total_ns / 1_000
865 }
866
867 /// Returns the total time waited, in whole milliseconds.
868 ///
869 /// # Returns
870 ///
871 /// The sum of every wait, rounded down.
872 pub fn total_millis(&self) -> u64 {
873 self.total_ns / 1_000_000
874 }
875
876 /// Forgets every recorded wait.
877 pub fn clear(&mut self) {
878 self.waits_ns.clear();
879 self.total_ns = 0;
880 }
881}
882
883impl DelayNs for DelayLog {
884 fn delay_ns(&mut self, ns: u32) {
885 self.waits_ns.push(ns);
886 self.total_ns += u64::from(ns);
887 }
888}
889
890/// Runs a future to completion by polling it, for futures that never wait on I/O.
891///
892/// A driver over a scripted bus finishes its `read` or `apply` in one poll, so a test
893/// needs no executor to await it. A future that is genuinely pending is polled again
894/// without yielding, so this is not for futures that wait on a timer or a socket.
895///
896/// # Arguments
897///
898/// * `future` - the future to run.
899///
900/// # Returns
901///
902/// The future's output.
903///
904/// # Examples
905///
906/// ```
907/// use pamoja_hal::script::block_on;
908///
909/// let answer = block_on(async { 6 * 7 });
910/// assert_eq!(answer, 42);
911/// ```
912pub fn block_on<F: Future>(future: F) -> F::Output {
913 let mut future = pin!(future);
914 let mut context = Context::from_waker(Waker::noop());
915 loop {
916 if let Poll::Ready(output) = future.as_mut().poll(&mut context) {
917 return output;
918 }
919 core::hint::spin_loop();
920 }
921}
922
923#[cfg(test)]
924mod tests {
925 use super::*;
926 use alloc::vec;
927
928 #[test]
929 fn a_register_read_consumes_one_write_read_step() {
930 let mut bus = I2cScript::new([I2cStep::write_read(0x76, [0xF7], [1, 2, 3])]);
931 let mut data = [0u8; 3];
932 bus.write_read(0x76, &[0xF7], &mut data).unwrap();
933 assert_eq!(data, [1, 2, 3]);
934 assert!(bus.done());
935 assert_eq!(bus.consumed(), 1);
936 }
937
938 #[test]
939 fn a_longer_transaction_consumes_steps_in_order() {
940 let mut bus = I2cScript::new([
941 I2cStep::write(0x40, [0x02]),
942 I2cStep::read(0x40, [0xAA, 0xBB]),
943 ]);
944 let mut data = [0u8; 2];
945 bus.transaction(
946 0x40,
947 &mut [Operation::Write(&[0x02]), Operation::Read(&mut data)],
948 )
949 .unwrap();
950 assert_eq!(data, [0xAA, 0xBB]);
951 assert!(bus.done());
952 }
953
954 #[test]
955 fn a_wrong_address_or_payload_is_a_mismatch_naming_the_step() {
956 let mut bus = I2cScript::new([
957 I2cStep::write(0x76, [0xF4, 0x25]),
958 I2cStep::write(0x76, [0xF5, 0x00]),
959 ]);
960 bus.write(0x76, &[0xF4, 0x25]).unwrap();
961 let error = bus.write(0x77, &[0xF5, 0x00]).unwrap_err();
962 assert_eq!(
963 error,
964 ScriptError::Mismatch {
965 step: 1,
966 expected: Some(I2cStep::write(0x76, [0xF5, 0x00])),
967 actual: vec![Transfer::Write {
968 address: 0x77,
969 bytes: vec![0xF5, 0x00]
970 }],
971 }
972 );
973 let error = bus.write(0x76, &[0xF5, 0x04]).unwrap_err();
974 assert!(matches!(error, ScriptError::Mismatch { step: 1, .. }));
975 assert_eq!(bus.remaining(), 1);
976 }
977
978 #[test]
979 fn a_read_of_the_wrong_length_is_a_mismatch() {
980 let mut bus = I2cScript::new([I2cStep::read(0x48, [0x12, 0x34])]);
981 let mut short = [0u8; 1];
982 assert!(bus.read(0x48, &mut short).is_err());
983 }
984
985 #[test]
986 fn an_exhausted_script_refuses_and_says_so() {
987 let mut bus = I2cScript::new([]);
988 let error = bus.write(0x48, &[0x00]).unwrap_err();
989 assert!(matches!(
990 error,
991 ScriptError::Mismatch {
992 step: 0,
993 expected: None,
994 ..
995 }
996 ));
997 }
998
999 #[test]
1000 fn a_fault_step_fails_with_its_kind_and_is_consumed() {
1001 use embedded_hal::i2c::{Error, NoAcknowledgeSource};
1002
1003 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
1004 let mut bus = I2cScript::new([
1005 I2cStep::fault(0x76, kind),
1006 I2cStep::write_read(0x76, [0xD0], [0x60]),
1007 ]);
1008 let mut id = [0u8; 1];
1009 let error = bus.write_read(0x76, &[0xD0], &mut id).unwrap_err();
1010 assert_eq!(error, ScriptError::Fault(kind));
1011 assert_eq!(error.kind(), kind);
1012 bus.write_read(0x76, &[0xD0], &mut id).unwrap();
1013 assert_eq!(id, [0x60]);
1014 assert!(bus.done());
1015 }
1016
1017 #[test]
1018 fn an_spi_transaction_consumes_one_step_per_operation_and_skips_delays() {
1019 let mut device = SpiScript::new([
1020 SpiStep::write([0xF7]),
1021 SpiStep::read([1, 2, 3]),
1022 SpiStep::transfer([0xAA, 0xBB], [0x11, 0x22]),
1023 ]);
1024 let mut data = [0u8; 3];
1025 let mut exchanged = [0xAA, 0xBB];
1026 device
1027 .transaction(&mut [
1028 spi::Operation::Write(&[0xF7]),
1029 spi::Operation::DelayNs(10),
1030 spi::Operation::Read(&mut data),
1031 spi::Operation::TransferInPlace(&mut exchanged),
1032 ])
1033 .unwrap();
1034 assert_eq!(data, [1, 2, 3]);
1035 assert_eq!(exchanged, [0x11, 0x22]);
1036 assert!(device.done());
1037 assert_eq!(device.consumed(), 3);
1038 }
1039
1040 #[test]
1041 fn an_spi_mismatch_or_fault_is_reported_like_the_i2c_ones() {
1042 let mut device = SpiScript::new([
1043 SpiStep::write([0x74, 0x25]),
1044 SpiStep::fault(spi::ErrorKind::ChipSelectFault),
1045 ]);
1046 let error = device.write(&[0x74, 0x26]).unwrap_err();
1047 assert!(matches!(
1048 error,
1049 SpiScriptError::Mismatch {
1050 step: 0,
1051 expected: Some(SpiStep::Write { .. }),
1052 ..
1053 }
1054 ));
1055 device.write(&[0x74, 0x25]).unwrap();
1056 assert_eq!(
1057 device.write(&[0x00]).unwrap_err(),
1058 SpiScriptError::Fault(spi::ErrorKind::ChipSelectFault)
1059 );
1060 assert!(device.done());
1061 }
1062
1063 #[test]
1064 fn a_pin_records_what_it_was_driven_to_and_answers_its_script() {
1065 let mut pin = PinScript::new([PinState::Low]);
1066 pin.set_low().unwrap();
1067 pin.set_high().unwrap();
1068 assert_eq!(pin.driven(), [PinState::Low, PinState::High]);
1069 assert!(pin.is_low().unwrap());
1070 assert!(
1071 pin.is_high().unwrap(),
1072 "the script is used up, so the output level answers"
1073 );
1074 assert!(pin.is_set_high().unwrap());
1075 assert_eq!(pin.remaining(), 0);
1076 }
1077
1078 #[test]
1079 fn a_delay_log_sums_every_unit() {
1080 let mut delay = DelayLog::new();
1081 delay.delay_ns(500);
1082 delay.delay_us(2);
1083 delay.delay_ms(1);
1084 assert_eq!(delay.total_ns(), 1_002_500);
1085 assert_eq!(delay.total_micros(), 1_002);
1086 assert_eq!(delay.total_millis(), 1);
1087 assert!(!delay.waits_ns().is_empty());
1088 delay.clear();
1089 assert_eq!(delay.total_ns(), 0);
1090 }
1091
1092 #[test]
1093 fn block_on_runs_a_ready_future() {
1094 assert_eq!(block_on(async { 7 }), 7);
1095 }
1096}