Skip to main content

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}