opentitanlib/test_utils/bitbanging/
uart_rx_sampling.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::iter::Peekable;
6use std::time::Duration;
7use std::vec::Vec;
8
9use anyhow::{Result, bail};
10use thiserror::Error;
11
12use crate::io::gpio::{
13    ClockNature, Edge, MonitoringEvent, MonitoringReadResponse, MonitoringStartResponse,
14};
15use crate::test_utils::bitbanging::uart::{UartBitbangDecoder, UartTransfer};
16
17#[derive(Error, Debug, PartialEq)]
18pub enum UartBitbangError {
19    #[error("Uart bitbanging RX monitoring needs a more reliable clock source")]
20    InaccurateMonitoringClock,
21    #[error("RX monitoring recorded double rising edge")]
22    DoubleRisingEdge,
23    #[error("RX monitoring recorded double falling edge")]
24    DoubleFallingEdge,
25    #[error("RX monitoring provided edges out-of-order")]
26    EdgesOutOfOrder,
27}
28
29/// A wrapper for the `UartBitbangDecoder` which converts the waveform returned
30/// by the `GpioMonitoring` interface into the uniform discrete samples that
31/// are understood by the decoder. This performs uniform sampling according to
32/// the baud rate from the initial response and the edges provided by monitor
33/// reads, optimized to avoid sampling whilst the UART is idle.
34pub struct UartRxMonitoringDecoder {
35    pub decoder: UartBitbangDecoder,
36    clock_resolution: u64,
37    initial_timestamp: u64,
38    last_event: Option<MonitoringEvent>,
39    last_sample_time: Option<u64>,
40}
41
42impl UartRxMonitoringDecoder {
43    pub fn new(
44        decoder: UartBitbangDecoder,
45        clock_nature: ClockNature,
46        start: MonitoringStartResponse,
47    ) -> Result<Self> {
48        let ClockNature::Wallclock { resolution, .. } = clock_nature else {
49            Err(UartBitbangError::InaccurateMonitoringClock)?
50        };
51        Ok(Self {
52            decoder,
53            clock_resolution: resolution,
54            initial_timestamp: start.timestamp,
55            last_event: None,
56            last_sample_time: None,
57        })
58    }
59
60    /// Convert a `u64` timestamp to a time in nanoseconds based on the initial
61    /// monitoring offset timestamp and configured clock resolution.
62    fn timestamp_to_nanos(&self, timestamp: u64) -> u64 {
63        let delta = (timestamp - self.initial_timestamp) as u128;
64        let nanos = delta * 1_000_000_000u128 / self.clock_resolution as u128;
65        nanos as u64
66    }
67
68    /// Calculates the number of samples between two timestamps, assuming a
69    /// sample was taken at the given `from` time.
70    fn samples_since(&self, from: u64, until: u64, period_ns: u64) -> u64 {
71        let time_elapsed = until - from;
72        // Rounding division of time_elapsed / period_ns
73        (time_elapsed + period_ns / 2) / period_ns
74    }
75
76    /// Consume edge events until several identical samples are found between
77    /// edges. Lets us wait for a break condition / idle event if monitoring
78    /// starts mid-transmission. Returns `true` if in a stable state.
79    fn sample_until_stable_state<I: Iterator<Item = MonitoringEvent>>(
80        &mut self,
81        events: &mut Peekable<I>,
82        end_time: u64,
83        period_ns: u64,
84    ) -> Result<bool> {
85        let frame_bit_time = self.decoder.config.bit_time_per_frame() as u64;
86        let last_ts = match self.last_event {
87            Some(last_event) => last_event.timestamp,
88            None => self.initial_timestamp,
89        };
90        let mut last_time = self.timestamp_to_nanos(last_ts);
91
92        while let Some(event) = events.peek() {
93            let timestamp = self.timestamp_to_nanos(event.timestamp);
94            if timestamp < last_time {
95                Err(UartBitbangError::EdgesOutOfOrder)?
96            } else if self.samples_since(last_time, timestamp, period_ns) > frame_bit_time {
97                return Ok(true);
98            }
99            last_time = timestamp;
100            self.last_event = events.next();
101        }
102        Ok(self.samples_since(last_time, end_time, period_ns) > frame_bit_time)
103    }
104
105    /// Find the previous sample time and RX value stored by the decoder. If no
106    /// sampling has been performed previously, wait for a stable RX level
107    /// (no edges for >= UART frame time) and then synchronize with the initial
108    /// falling edge.
109    ///
110    /// This will consume edge events until a stable state is found, but will
111    /// not consume the event corresponding to the first falling edge.
112    fn get_last_state<I: Iterator<Item = MonitoringEvent>>(
113        &mut self,
114        events: &mut Peekable<I>,
115        end_time: u64,
116        period_ns: u64,
117    ) -> Result<Option<(u64, u8)>> {
118        // If we have information stored from a previous sample, retrieve it.
119        if let Some(last_sample_time) = self.last_sample_time {
120            let Some(last_event) = self.last_event else {
121                bail!("Previous sampling time exists but previous event does not");
122            };
123            let value = match last_event.edge {
124                Edge::Rising => 0x01,
125                Edge::Falling => 0x00,
126            };
127            return Ok(Some((last_sample_time, value)));
128        };
129
130        // No previous sampling, so wait for a stable RX level to avoid desync
131        if !self.sample_until_stable_state(events, end_time, period_ns)? {
132            return Ok(None);
133        }
134
135        // Identify & synchronize with the first falling edge
136        let Some(first_event) = events.peek() else {
137            return Ok(None);
138        };
139
140        let edge_time = if first_event.edge == Edge::Falling {
141            self.timestamp_to_nanos(first_event.timestamp)
142        } else {
143            // If we start in a break condition, wait until the break is clear
144            // (the UART goes idle) and sync with the next (falling) edge.
145            events.next();
146            let Some(second_event) = events.peek() else {
147                return Ok(None);
148            };
149            self.timestamp_to_nanos(second_event.timestamp)
150        };
151        Ok(Some((edge_time, 0x01)))
152    }
153
154    /// Uses the bitbanging decoder to decode a given RX pin sample.
155    fn decode_sample(&mut self, sample: u8, decoded: &mut Vec<u8>) -> Result<()> {
156        if let Some(transfer) = self.decoder.decode_sample(sample)? {
157            match transfer {
158                UartTransfer::Byte { data } => decoded.push(data),
159                UartTransfer::Broken { error, .. } => {
160                    Err(error)?;
161                }
162                // TODO: handle decoding incoming break conditions
163                UartTransfer::Break => (),
164            }
165        }
166        Ok(())
167    }
168
169    /// Decode a given RX monitoring waveform edge, calculating & decoding
170    /// uniform RX pin samples between this edge and the previous one.
171    ///
172    /// Args:
173    /// - event: The `MonitoringEvent` representing an edge to decode.
174    /// - decoded: The `Vec` to place decoded UART characters into.
175    /// - period_ns: The sampling period in nanos (according to the Baud rate).
176    /// - last_sample_time: The timestamp (in nanoseconds) at which the
177    ///   previous edge/sample occurred. A sample (or several samples) will be
178    ///   taken if the edge occurs more than 1/2 a period after this time, and
179    ///   the time will then be correspondingly updated.
180    /// - value: The current value of the UART RX signal. Will be updated from
181    ///   the given edge.
182    fn decode_edge(
183        &mut self,
184        event: MonitoringEvent,
185        decoded: &mut Vec<u8>,
186        period_ns: u64,
187        last_sample_time: &mut u64,
188        value: &mut u8,
189    ) -> Result<()> {
190        if event.edge == Edge::Falling && *value == 0 {
191            Err(UartBitbangError::DoubleFallingEdge)?
192        } else if event.edge == Edge::Rising && *value == 1 {
193            Err(UartBitbangError::DoubleRisingEdge)?
194        }
195        let sampling_end = self.timestamp_to_nanos(event.timestamp);
196        if sampling_end < *last_sample_time {
197            Err(UartBitbangError::EdgesOutOfOrder)?
198        }
199
200        // Calculate & decode samples between edges
201        let num_samples = self.samples_since(*last_sample_time, sampling_end, period_ns);
202        *last_sample_time += period_ns * num_samples;
203        for _ in 0..num_samples {
204            if self.decoder.is_idle() && event.edge == Edge::Falling {
205                // Optimization: don't decode idle-high samples between frames
206                break;
207            }
208            self.decode_sample(*value, decoded)?;
209        }
210
211        if self.decoder.is_idle() && event.edge == Edge::Falling {
212            // Reset sampling time at the start of each transaction
213            *last_sample_time = self.timestamp_to_nanos(event.timestamp);
214        }
215        self.last_event = Some(event);
216        *value = if *value == 0x00 { 0x01 } else { 0x00 };
217        Ok(())
218    }
219
220    /// Decode a given RX monitoring waveform  into received bytes, where the
221    /// waveform is an ordered vector of RX edges, and the time at which the
222    /// monitoring was performed (i.e. the end).
223    ///
224    /// Args:
225    /// - events: The list of `MonitoringEvents` in the response waveform.
226    /// - signal_index: The index of the RX signal in the monitoring events.
227    /// - end_time: The final timestamp of the response waveform.
228    /// - period: The period at which to sample (according to the Baud rate).
229    fn decode_waveform(
230        &mut self,
231        events: Vec<MonitoringEvent>,
232        signal_index: u8,
233        end_time: u64,
234        period: &Duration,
235    ) -> Result<Vec<u8>> {
236        let mut decoded = Vec::new();
237        let mut events_iter = events
238            .into_iter()
239            .filter(|event| event.signal_index == signal_index)
240            .peekable();
241        let period_ns = period.as_nanos() as u64;
242        let last_state = self.get_last_state(&mut events_iter, end_time, period_ns)?;
243        let Some((mut last_sample_time, mut value)) = last_state else {
244            // Not enough events recorded to find a starting state.
245            return Ok(decoded);
246        };
247        for event in events_iter {
248            self.decode_edge(
249                event,
250                &mut decoded,
251                period_ns,
252                &mut last_sample_time,
253                &mut value,
254            )?;
255        }
256        self.last_sample_time = Some(last_sample_time);
257        // When a frame finishes, a final rising edge leaves the RX line high,
258        // but our sampling mechanism only decodes samples between edges. To
259        // avoid requiring subsequent transmissions, add idle bits until the
260        // read timestamp (while the decoder is active).
261        if value != 0x00 {
262            while !self.decoder.is_idle() {
263                if (last_sample_time + period_ns / 2) >= end_time {
264                    break;
265                }
266                last_sample_time += period_ns;
267                self.last_sample_time = Some(last_sample_time);
268                self.decode_sample(value, &mut decoded)?;
269            }
270        }
271        Ok(decoded)
272    }
273
274    /// Decode a `MonitoringReadResponse` from the `GpioMonitoring` interface,
275    /// performing uniform sampling and decoding the sampled UART output.
276    /// `signal_index` specifies the `MonitoringReadResponse` signal
277    /// corresponding to the UART RX pin (normally 0).
278    ///
279    /// Expects at least a UART frame time of idle since the initial timestamp
280    /// before it will start sampling (any malformed data is dropped).
281    ///
282    /// Note: it is expected that *all* monitor responses since monitoring
283    /// initialization are fed to the decoder through this function. If any
284    /// monitoring events are lost, this could cause the corruption of some
285    /// received UART data.
286    pub fn decode_response(
287        &mut self,
288        response: MonitoringReadResponse,
289        signal_index: u8,
290        baud_rate: u32,
291    ) -> Result<Vec<u8>> {
292        let sampling_period = Duration::from_nanos(1_000_000_000u64 / baud_rate as u64);
293        let read_end_timestamp = self.timestamp_to_nanos(response.timestamp);
294        self.decode_waveform(
295            response.events,
296            signal_index,
297            read_end_timestamp,
298            &sampling_period,
299        )
300    }
301}
302
303#[cfg(test)]
304mod test {
305    use super::*;
306
307    use std::sync::LazyLock;
308
309    use serialport::Parity;
310
311    use crate::test_utils::bitbanging::uart::{UartBitbangConfig, UartStopBits};
312
313    static CLOCK_NATURE: ClockNature = ClockNature::Wallclock {
314        resolution: 1_000_000,
315        offset: None,
316    };
317    static BAUD_RATE: u32 = 57600;
318    static START_RESPONSE: LazyLock<MonitoringStartResponse> =
319        LazyLock::new(|| MonitoringStartResponse {
320            timestamp: 0,
321            initial_levels: vec![true],
322        });
323    // Example UART RX waveform taken from a bitbanging test sample
324    static EVENT_TIMESTAMPS: [u64; 112] = [
325        5889252340, 5889252358, 5889252375, 5889252392, 5889252410, 5889252427, 5889252445,
326        5889252462, 5889252479, 5889252497, 5889252514, 5889252532, 5889252549, 5889252636,
327        5889252654, 5889252671, 5889252688, 5889252723, 5889252740, 5889252775, 5889252793,
328        5889252810, 5889252827, 5889252845, 5889252862, 5889252914, 5889252932, 5889252949,
329        5889252967, 5889252984, 5889253001, 5889253019, 5889253036, 5889253141, 5889253158,
330        5889253193, 5889253210, 5889253245, 5889253263, 5889253315, 5889253349, 5889253367,
331        5889253386, 5889253402, 5889253419, 5889253454, 5889253471, 5889253489, 5889253523,
332        5889253541, 5889253558, 5889253610, 5889253628, 5889253645, 5889253697, 5889253715,
333        5889253732, 5889253767, 5889253784, 5889253837, 5889253871, 5889253889, 5889253906,
334        5889253924, 5889253941, 5889254011, 5889254046, 5889254063, 5889254080, 5889254115,
335        5889254167, 5889254185, 5889254219, 5889254238, 5889254254, 5889254272, 5889254324,
336        5889254359, 5889254393, 5889254411, 5889254428, 5889254533, 5889254550, 5889254585,
337        5889254603, 5889254655, 5889254672, 5889254689, 5889254741, 5889254759, 5889254776,
338        5889254794, 5889254811, 5889254828, 5889254846, 5889254881, 5889254915, 5889254933,
339        5889254950, 5889254968, 5889255002, 5889255037, 5889255089, 5889255107, 5889255124,
340        5889255176, 5889255194, 5889255211, 5889255264, 5889255281, 5889255298, 5889255455,
341    ];
342    static READ_RESPONSE_TIMESTAMP: u64 = 5889262827;
343    static EXPECTED_RESPONSE: &str = "UART bitbang test\0";
344
345    // A helper to compactly construct `MonitoringEvent` edges.
346    fn edge(signal_index: u8, edge: Edge, timestamp: u64) -> MonitoringEvent {
347        MonitoringEvent {
348            signal_index,
349            edge,
350            timestamp,
351        }
352    }
353
354    fn sample_and_decode(
355        decoder: UartBitbangDecoder,
356        start_idle: bool,
357        start_response: MonitoringStartResponse,
358        edge_timestamps: &[u64],
359        read_timestamp: u64,
360        baud_rate: u32,
361        expected: String,
362    ) -> Result<()> {
363        let edges = if start_idle {
364            [Edge::Falling, Edge::Rising]
365        } else {
366            [Edge::Rising, Edge::Falling]
367        };
368        let events = edge_timestamps
369            .iter()
370            .enumerate()
371            .map(|(i, t)| edge(0, edges[i % 2], *t))
372            .collect::<Vec<_>>();
373        let read_response = MonitoringReadResponse {
374            events,
375            timestamp: read_timestamp,
376        };
377
378        let mut response_decoder =
379            UartRxMonitoringDecoder::new(decoder, CLOCK_NATURE, start_response)?;
380        let decoded = response_decoder.decode_response(read_response, 0, baud_rate)?;
381        assert_eq!(String::from_utf8(decoded), Ok(expected));
382        Ok(())
383    }
384
385    #[test]
386    fn smoke() -> Result<()> {
387        // Decode against a known sample test string
388        let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
389        let decoder = UartBitbangDecoder::new(config);
390        sample_and_decode(
391            decoder,
392            true,
393            START_RESPONSE.clone(),
394            &EVENT_TIMESTAMPS,
395            READ_RESPONSE_TIMESTAMP,
396            BAUD_RATE,
397            EXPECTED_RESPONSE.into(),
398        )
399    }
400
401    #[test]
402    fn baud_rates() -> Result<()> {
403        // Check we can sample at different baud rates
404        fn modify_timestamp(timestamp: u64, first_edge_timestamp: u64, divider: u32) -> u64 {
405            let delta = timestamp - first_edge_timestamp;
406            let new_delta = delta * divider as u64;
407            first_edge_timestamp + new_delta
408        }
409
410        let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
411        let first_edge_timestamp = EVENT_TIMESTAMPS[0];
412        for divider in 1..=10u32 {
413            let modified_timestamps = EVENT_TIMESTAMPS
414                .iter()
415                .map(|timestamp| modify_timestamp(*timestamp, first_edge_timestamp, divider))
416                .collect::<Vec<_>>();
417            let modified_baud = BAUD_RATE / divider;
418            let modified_response_timestamp =
419                modify_timestamp(READ_RESPONSE_TIMESTAMP, first_edge_timestamp, divider);
420            let decoder = UartBitbangDecoder::new(config.clone());
421            sample_and_decode(
422                decoder,
423                true,
424                START_RESPONSE.clone(),
425                &modified_timestamps,
426                modified_response_timestamp,
427                modified_baud,
428                EXPECTED_RESPONSE.into(),
429            )?;
430        }
431        Ok(())
432    }
433
434    #[test]
435    fn clock_jitter_and_skew() -> Result<()> {
436        // Check we can tolerate some small +-5% jitters or skews
437        // Use a smaller baud rate to allow more accurate jitters
438        const BAUD_DIVIDER: u32 = 8;
439
440        fn modify_timestamp(
441            timestamp: u64,
442            first_edge_timestamp: u64,
443            period_ns: u64,
444            skew_pc: i32,
445            jitter_pc: i32,
446        ) -> u64 {
447            let delta = timestamp - first_edge_timestamp;
448            let new_delta = delta * BAUD_DIVIDER as u64;
449            let skew_delta = new_delta * ((100i32 + skew_pc) as u64) / 100u64;
450            let jittered_delta =
451                (skew_delta as i64) + ((period_ns as i64) * jitter_pc as i64) / 100i64;
452            (first_edge_timestamp as i64 + jittered_delta) as u64
453        }
454
455        fn clock_test(skew_pc: i32, jitter_pc: i32) -> Result<()> {
456            let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
457            let modified_baud = BAUD_RATE / BAUD_DIVIDER;
458            let period_ns = 1_000_000_000u64 / modified_baud as u64;
459            let first_edge_timestamp = EVENT_TIMESTAMPS[0];
460
461            let modified_timestamps = EVENT_TIMESTAMPS
462                .iter()
463                .map(|timestamp| {
464                    modify_timestamp(
465                        *timestamp,
466                        first_edge_timestamp,
467                        period_ns,
468                        skew_pc,
469                        jitter_pc,
470                    )
471                })
472                .collect::<Vec<_>>();
473            let modified_response_timestamp = modify_timestamp(
474                READ_RESPONSE_TIMESTAMP,
475                first_edge_timestamp,
476                period_ns,
477                skew_pc,
478                jitter_pc,
479            );
480            let decoder = UartBitbangDecoder::new(config.clone());
481            sample_and_decode(
482                decoder,
483                true,
484                START_RESPONSE.clone(),
485                &modified_timestamps,
486                modified_response_timestamp,
487                modified_baud,
488                EXPECTED_RESPONSE.into(),
489            )?;
490            Ok(())
491        }
492
493        // Test -20% -> 20% jitter (unidirectional only for now)
494        for jitter_pc in -20..=20i32 {
495            clock_test(0i32, jitter_pc)?;
496        }
497
498        // UART frame time = 1 start bit + 8 data bits + 1 stop bit = 10 samples
499        // Rounding gives up to a 50% tolerance, so realistically we can test a
500        // max of -4 -> 4% skew
501        for skew_pc in -4..=4i32 {
502            clock_test(skew_pc, 0i32)?;
503        }
504
505        // Test with small jitter (-5 -> 5%) & skew (-2 -> 2%)
506        for jitter_pc in -5..=5i32 {
507            for skew_pc in -2..=2i32 {
508                clock_test(skew_pc, jitter_pc)?;
509            }
510        }
511
512        Ok(())
513    }
514
515    #[test]
516    fn start_during_break() -> Result<()> {
517        // Check that we can handle starting monitoring during a break
518        let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
519        let decoder = UartBitbangDecoder::new(config);
520
521        // Insert an extra edge just before, an start in a break condition (RX = 0)
522        let bit_time = EVENT_TIMESTAMPS[1] - EVENT_TIMESTAMPS[0];
523        let before_timestamp = EVENT_TIMESTAMPS[0] - bit_time;
524        let mut events = vec![before_timestamp];
525        events.extend_from_slice(&EVENT_TIMESTAMPS);
526        sample_and_decode(
527            decoder,
528            false,
529            START_RESPONSE.clone(),
530            &events,
531            READ_RESPONSE_TIMESTAMP,
532            BAUD_RATE,
533            EXPECTED_RESPONSE.into(),
534        )
535    }
536
537    #[test]
538    fn start_mid_transmission() -> Result<()> {
539        // Check that we can handle starting monitoring mid-transmission
540        let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
541        let decoder = UartBitbangDecoder::new(config.clone());
542
543        // Insert some spurious edges a couple of UART frames before,
544        // corresponding to the end of some partial UART transmission.
545        // The decoder should ignore this and read as normal.
546        let bit_time = EVENT_TIMESTAMPS[1] - EVENT_TIMESTAMPS[0];
547        let start_response = MonitoringStartResponse {
548            timestamp: EVENT_TIMESTAMPS[0] - bit_time * 24,
549            initial_levels: vec![true],
550        };
551        let mut events = vec![
552            EVENT_TIMESTAMPS[0] - bit_time * 23,
553            EVENT_TIMESTAMPS[0] - bit_time * 21,
554            EVENT_TIMESTAMPS[0] - bit_time * 20,
555        ];
556        events.extend_from_slice(&EVENT_TIMESTAMPS);
557        sample_and_decode(
558            decoder,
559            false,
560            start_response,
561            &events,
562            READ_RESPONSE_TIMESTAMP,
563            BAUD_RATE,
564            EXPECTED_RESPONSE.into(),
565        )?;
566
567        // Insert some spurious edges just before, corresponding to the end
568        // of some partial UART transmission. The decoder should decode nothing
569        // (instead of garbage) because it cannot find a stable state.
570        let decoder = UartBitbangDecoder::new(config);
571        let start_response = MonitoringStartResponse {
572            timestamp: EVENT_TIMESTAMPS[0] - bit_time * 5,
573            initial_levels: vec![true],
574        };
575        let mut events = vec![
576            EVENT_TIMESTAMPS[0] - bit_time * 4,
577            EVENT_TIMESTAMPS[0] - bit_time * 2,
578            EVENT_TIMESTAMPS[0] - bit_time,
579        ];
580        events.extend_from_slice(&EVENT_TIMESTAMPS);
581        let empty_response = String::new();
582        sample_and_decode(
583            decoder,
584            false,
585            start_response,
586            &events,
587            READ_RESPONSE_TIMESTAMP,
588            BAUD_RATE,
589            empty_response,
590        )
591    }
592
593    #[test]
594    fn partial_responses() -> Result<()> {
595        // Check that we can decode the data over several partial monitor reads
596        let config = UartBitbangConfig::new(8, UartStopBits::Stop1, 2, Parity::None)?;
597        let decoder = UartBitbangDecoder::new(config.clone());
598        let edges = [Edge::Falling, Edge::Rising];
599        let events = EVENT_TIMESTAMPS
600            .iter()
601            .enumerate()
602            .map(|(i, t)| edge(0, edges[i % 2], *t))
603            .collect::<Vec<_>>();
604        let mut response_decoder =
605            UartRxMonitoringDecoder::new(decoder, CLOCK_NATURE, START_RESPONSE.clone())?;
606
607        // Decode the first 60 edges 3 edges at a time
608        let mut decoded = Vec::new();
609        for partial_edges in events[..60].chunks(3) {
610            // "read" a response at last edge + 1 ns
611            let timestamp = partial_edges.last().unwrap().timestamp + 1;
612            let read_response = MonitoringReadResponse {
613                events: Vec::from(partial_edges),
614                timestamp,
615            };
616            decoded.extend(response_decoder.decode_response(read_response, 0, BAUD_RATE)?);
617        }
618
619        // Decode the remaining edges with up to 20 at a time
620        for partial_edges in events[60..].chunks(20) {
621            let timestamp = partial_edges.last().unwrap().timestamp + 1;
622            let read_response = MonitoringReadResponse {
623                events: Vec::from(partial_edges),
624                timestamp,
625            };
626            decoded.extend(response_decoder.decode_response(read_response, 0, BAUD_RATE)?);
627        }
628
629        // Final read response containing the end timestamp
630        let read_response = MonitoringReadResponse {
631            events: Vec::new(),
632            timestamp: READ_RESPONSE_TIMESTAMP,
633        };
634        decoded.extend(response_decoder.decode_response(read_response, 0, BAUD_RATE)?);
635        assert_eq!(
636            String::from_utf8(decoded),
637            Ok(String::from(EXPECTED_RESPONSE))
638        );
639        Ok(())
640    }
641}