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 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}