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