opentitanlib/util/
vcd.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 anyhow::{Result, bail};
6use std::io;
7use thiserror::Error;
8
9use crate::io::gpio::{Edge, MonitoringEvent, MonitoringReadResponse};
10
11/// A VCD file header, containing timescale info & additional metadata
12#[derive(Debug)]
13pub struct Header {
14    pub timescale_ps: Option<u128>,
15    pub date: Option<String>,
16    pub version: Option<String>,
17}
18
19/// A signal in a VCD file, of some known type & width along with a reference name
20/// e.g. "wire 1 ... rx_en"
21#[derive(Debug)]
22pub struct Signal {
23    pub signal_type: String,
24    pub width: u32,
25    pub reference: String,
26}
27
28/// A member item of some VCD scope, which is either another nested scope or
29/// a variable definition.
30#[derive(Debug)]
31pub enum ScopeItem {
32    Scope {
33        scope_type: String,
34        identifier: String,
35        items: Vec<Self>,
36    },
37    VarDef {
38        signal: Signal,
39        identifier: String,
40    },
41}
42
43impl ScopeItem {
44    /// Get the identifiers of all variables (recursively) defined in this scope.
45    pub fn var_names(&self) -> Vec<&str> {
46        match self {
47            ScopeItem::Scope { items, .. } => {
48                let mut identifiers = Vec::new();
49                for item in items {
50                    identifiers.extend(item.var_names())
51                }
52                identifiers
53            }
54            ScopeItem::VarDef { identifier, .. } => {
55                vec![identifier]
56            }
57        }
58    }
59}
60
61/// A variable definition section of a VCD file, which features a list of top-level
62/// scopes that encapsulate all signal variable definitions (vardefs).
63#[derive(Debug)]
64pub struct VarDefs {
65    scopes: Vec<ScopeItem>,
66}
67
68impl VarDefs {
69    /// Get the identifiers of all variables defined in the entire VarDefs section.
70    pub fn var_names(&self) -> Vec<&str> {
71        let mut identifiers = Vec::new();
72        for scope in &self.scopes {
73            identifiers.extend(scope.var_names());
74        }
75        identifiers
76    }
77}
78
79/// Four-valued scalar logic values
80#[derive(Debug)]
81pub enum ScalarValue {
82    Zero,
83    One,
84    /// Unknown / Don't Care
85    X,
86    /// High Impedance
87    Z,
88}
89
90/// An item in the value change section of a VCD file, which can be a timestamp
91/// entry, a change in the value of a signal (at the last seen timestamp), or
92/// some other command (not currently supported).
93#[derive(Debug)]
94pub enum ValueChangeItem {
95    Timestamp {
96        step: u128,
97    },
98    Scalar {
99        identifier: String,
100        value: ScalarValue,
101    },
102    // TODO: add other VCD signal types (vector, real vars, strings)
103}
104
105/// The value change section of a VCD file, which contains a sequence of
106/// value change item entries.
107#[derive(Debug)]
108pub struct ValueChangeSection {
109    pub changes: Vec<ValueChangeItem>,
110}
111
112/// A full VCD file definition, containing at least a header (with a timescale),
113/// some variable definition(s) and any recorded value change section.
114#[derive(Debug)]
115pub struct Vcd {
116    pub header: Header,
117    pub vardefs: VarDefs,
118    pub value_changes: ValueChangeSection,
119}
120
121impl Vcd {
122    /// Get the identifiers of all variables defined in the VCD file
123    pub fn var_names(&self) -> Vec<&str> {
124        self.vardefs.var_names()
125    }
126}
127
128/// Errors relating to VCD encoding/dumping
129#[derive(Debug, Error)]
130pub enum VcdDumpError {
131    #[error("Cannot represent VCD timescale {0}ps")]
132    InvalidTimescale(u128),
133    #[error("Timestamps out of order: {0} and {1}")]
134    TimestampsOutOfOrder(u128, u128),
135}
136
137/// Struct implementing methods for writing VCD data to some io::Write
138#[derive(Debug)]
139pub struct VcdWriter<W: io::Write> {
140    pub writer: W,
141}
142
143impl<W: io::Write> VcdWriter<W> {
144    pub fn new(writer: W) -> Self {
145        VcdWriter { writer }
146    }
147
148    pub fn flush(&mut self) -> Result<()> {
149        self.writer.flush()?;
150        Ok(())
151    }
152
153    pub fn write_vcd(&mut self, vcd: &Vcd) -> Result<()> {
154        self.write_header(&vcd.header)?;
155        self.write_vardefs(&vcd.vardefs)?;
156        self.write_value_change_section(&vcd.value_changes, 0u128)?;
157        writeln!(self.writer)?;
158        Ok(())
159    }
160
161    pub fn write_header(&mut self, header: &Header) -> Result<()> {
162        // TODO: perhaps avoid hardcoding for picos in the future,
163        // it might be sufficient to e.g. use ns/us/ms in some cases
164        if let Some(timescale_ps) = &header.timescale_ps {
165            if *timescale_ps < 1 {
166                bail!(VcdDumpError::InvalidTimescale(*timescale_ps));
167            }
168            writeln!(self.writer, "$timescale {}ps $end", timescale_ps)?;
169        }
170        if let Some(date) = &header.date {
171            writeln!(self.writer, "$date {} $end", date)?;
172        }
173        if let Some(version) = &header.version {
174            writeln!(self.writer, "$version {} $end", version)?;
175        }
176        Ok(())
177    }
178
179    pub fn write_vardefs(&mut self, vardefs: &VarDefs) -> Result<()> {
180        for scope in &vardefs.scopes {
181            self.write_scope_item(scope)?;
182        }
183        writeln!(self.writer, "$enddefinitions $end")?;
184        Ok(())
185    }
186
187    pub fn write_scope_item(&mut self, scope: &ScopeItem) -> Result<()> {
188        match scope {
189            ScopeItem::Scope {
190                scope_type,
191                identifier,
192                items,
193            } => {
194                writeln!(self.writer, "$scope {} {} $end", &scope_type, &identifier)?;
195                for item in items {
196                    self.write_scope_item(item)?;
197                }
198                writeln!(self.writer, "$upscope $end")?;
199            }
200            ScopeItem::VarDef { signal, identifier } => {
201                writeln!(
202                    self.writer,
203                    "$var {} {} {} {} $end",
204                    &signal.signal_type, signal.width, &identifier, &signal.reference
205                )?;
206            }
207        }
208        Ok(())
209    }
210
211    pub fn write_value_change_section(
212        &mut self,
213        vcs: &ValueChangeSection,
214        initial_timestamp: u128,
215    ) -> Result<()> {
216        let mut current_timestamp = initial_timestamp;
217        for (i, value_change_item) in vcs.changes.iter().enumerate() {
218            if let ValueChangeItem::Timestamp { step } = value_change_item {
219                if *step < current_timestamp {
220                    bail!(VcdDumpError::TimestampsOutOfOrder(current_timestamp, *step));
221                }
222                current_timestamp = *step;
223                if i != 0 {
224                    writeln!(self.writer)?;
225                }
226            }
227            self.write_value_change_item(value_change_item)?;
228            write!(self.writer, " ")?;
229        }
230        Ok(())
231    }
232
233    pub fn write_value_change_item(&mut self, item: &ValueChangeItem) -> Result<()> {
234        match item {
235            ValueChangeItem::Timestamp { step } => {
236                write!(self.writer, "#{}", step)?;
237            }
238            ValueChangeItem::Scalar { identifier, value } => {
239                self.write_scalar_change(identifier, value)?;
240            }
241        }
242        Ok(())
243    }
244
245    pub fn write_scalar_change(&mut self, identifier: &str, value: &ScalarValue) -> Result<()> {
246        let value = match value {
247            ScalarValue::Zero => "0",
248            ScalarValue::One => "1",
249            ScalarValue::X => "X",
250            ScalarValue::Z => "Z",
251        };
252        write!(self.writer, "{}{}", value, identifier)?;
253        Ok(())
254    }
255}
256
257/// A helper function using a `VcdWriter` to directly dump a given Vcd to
258/// a string containing equivalent VCD file contents.
259pub fn dump_vcd(vcd: &Vcd) -> Result<String> {
260    let mut vcd_bytes = Vec::new();
261    let mut writer = VcdWriter::new(&mut vcd_bytes);
262    writer.write_vcd(vcd)?;
263    writer.flush()?;
264    Ok(String::from_utf8(vcd_bytes).expect("Generated VCD should be UTF-8 compatible"))
265}
266
267/// Errors related to VCD decoding/parsing
268#[derive(Debug, Error)]
269pub enum VcdParseError {
270    #[error("Missing required {0} value")]
271    MissingValue(String),
272    #[error("Missing required definition {0}")]
273    MissingDefinition(String),
274    #[error("Missing $end for definition {0}")]
275    MissingDefinitionEnd(String),
276    #[error("Multiple definitions for {0}")]
277    MultipleDefinitions(String),
278    #[error("Found $end when there was no definition to end")]
279    MismatchedEnd,
280    #[error("Found a $scope after already parsing $enddefinitions")]
281    DefinitionAfterEnd,
282    #[error("{0} is not yet supported by VCD parsing")]
283    UnsupportedFeature(String),
284    #[error("VCD timestamps #{0} and #{1} are out of order!")]
285    TimestampsOutOfOrder(u128, u128),
286    #[error("Unrecognized value {0}")]
287    UnknownToken(String),
288    #[error("Supplied identifier {0} does not match any defined identifier")]
289    UnknownVariable(String),
290}
291
292/// Iterator for reading whitespace-separated tokens from a given io::BufRead.
293/// Internally buffers the next line, returning tokens from that line.
294#[derive(Debug)]
295struct WhitespaceTokenizer<R: io::BufRead> {
296    reader: R,
297    line_buf: String,
298    tokens: Vec<String>,
299}
300
301impl<R: io::BufRead> WhitespaceTokenizer<R> {
302    fn new(reader: R) -> Self {
303        Self {
304            reader,
305            line_buf: String::new(),
306            tokens: Vec::new(),
307        }
308    }
309}
310
311impl<R: io::BufRead> Iterator for WhitespaceTokenizer<R> {
312    type Item = io::Result<String>;
313
314    fn next(&mut self) -> Option<Self::Item> {
315        loop {
316            if let Some(token) = self.tokens.pop() {
317                return Some(Ok(token));
318            }
319            self.line_buf.clear();
320            match self.reader.read_line(&mut self.line_buf) {
321                Ok(0) => return None, // EOF
322                Ok(_) => {}
323                Err(e) => return Some(Err(e)),
324            };
325            // Collect tokens in reverse to efficiently pop from the back of the
326            // vec, rather than the front.
327            self.tokens = self
328                .line_buf
329                .split_whitespace()
330                .map(|s| s.to_string())
331                .rev()
332                .collect();
333        }
334    }
335}
336
337/// Struct implementing methods for parsing VCD data from some io::BufRead.
338#[derive(Debug)]
339pub struct VcdParser<R: io::BufRead> {
340    tokenizer: WhitespaceTokenizer<R>,
341    identifiers: Vec<String>,
342}
343
344impl<R: io::BufRead> VcdParser<R> {
345    pub fn new(reader: R) -> Self {
346        VcdParser {
347            tokenizer: WhitespaceTokenizer::new(reader),
348            identifiers: Vec::new(),
349        }
350    }
351
352    /// Get the next whitespace-separated VCD file token
353    pub fn next_token(&mut self) -> Result<Option<String>, io::Error> {
354        self.tokenizer.next().transpose()
355    }
356
357    pub fn parse_vcd(&mut self) -> Result<Vcd> {
358        Ok(Vcd {
359            header: self.parse_header()?,
360            vardefs: self.parse_vardefs()?,
361            value_changes: self.parse_value_change_section()?,
362        })
363    }
364
365    pub fn parse_header(&mut self) -> Result<Header> {
366        let mut header = Header {
367            timescale_ps: None,
368            date: None,
369            version: None,
370        };
371        while let Some(token) = self.next_token()?.as_deref() {
372            match token {
373                "$timescale" => {
374                    if header.timescale_ps.is_some() {
375                        bail!(VcdParseError::MultipleDefinitions("timescale".into()));
376                    }
377                    // TODO: add a custom `Duration` type that supports parsing
378                    // picoseconds (`ps`) rather than hacking slightly here
379                    let timescale_str = self.parse_header_str("timescale")?;
380                    header.timescale_ps = if timescale_str.ends_with("ps") {
381                        Some(
382                            timescale_str
383                                .strip_suffix("ps")
384                                .unwrap()
385                                .trim()
386                                .parse::<u128>()?,
387                        )
388                    } else {
389                        Some(humantime::parse_duration(&timescale_str)?.as_nanos() * 1000)
390                    }
391                }
392                "$date" => {
393                    if header.date.is_some() {
394                        bail!(VcdParseError::MultipleDefinitions("date".into()));
395                    }
396                    header.date = Some(self.parse_header_str("date")?)
397                }
398                "$version" => {
399                    if header.version.is_some() {
400                        bail!(VcdParseError::MultipleDefinitions("version".into()));
401                    }
402                    header.version = Some(self.parse_header_str("version")?)
403                }
404                "$scope" => {
405                    return Ok(header);
406                }
407                _ => (),
408            }
409        }
410        bail!(VcdParseError::MissingDefinition("scope".into()))
411    }
412
413    pub fn parse_header_str(&mut self, field: &str) -> Result<String> {
414        let Some(mut value) = self.next_token()? else {
415            bail!(VcdParseError::MissingValue(field.into()));
416        };
417        loop {
418            match self.next_token()?.as_deref() {
419                Some("$end") => return Ok(value),
420                Some(next_val) => {
421                    value.push(' ');
422                    value.push_str(next_val);
423                }
424                None => bail!(VcdParseError::MissingDefinitionEnd(field.into())),
425            }
426        }
427    }
428
429    pub fn parse_vardefs(&mut self) -> Result<VarDefs> {
430        let mut scopes = Vec::new();
431        scopes.push(self.parse_scope()?);
432        let mut end_seen = false;
433        while let Some(token) = self.next_token()?.as_deref() {
434            match (token, end_seen) {
435                ("$scope", false) => {
436                    scopes.push(self.parse_scope()?);
437                }
438                ("$scope", true) => bail!(VcdParseError::DefinitionAfterEnd),
439                ("$enddefinitions", false) => {
440                    end_seen = true;
441                }
442                ("$enddefinitions", true) => {
443                    bail!(VcdParseError::MultipleDefinitions("$enddefinitions".into()))
444                }
445                ("$end", false) => bail!(VcdParseError::MismatchedEnd),
446                ("$end", true) => return Ok(VarDefs { scopes }),
447                _ => (),
448            }
449        }
450        bail!(VcdParseError::MissingDefinition("$enddefinitions".into()))
451    }
452
453    pub fn parse_scope(&mut self) -> Result<ScopeItem> {
454        let Some(scope_type) = self.next_token()? else {
455            bail!(VcdParseError::MissingValue("scope type".into()));
456        };
457        let Some(identifier) = self.next_token()? else {
458            bail!(VcdParseError::MissingValue("scope name".into()));
459        };
460        match self.next_token()?.as_deref() {
461            Some("$end") => (),
462            _ => bail!(VcdParseError::MissingDefinitionEnd("scope".into())),
463        };
464        let mut items = Vec::new();
465        while let Some(token) = self.next_token()?.as_deref() {
466            match token {
467                "$scope" => {
468                    items.push(self.parse_scope()?);
469                }
470                "$var" => {
471                    items.push(self.parse_variable()?);
472                }
473                "$end" => {
474                    return Ok(ScopeItem::Scope {
475                        scope_type,
476                        identifier,
477                        items,
478                    });
479                }
480                _ => (),
481            }
482        }
483        bail!(VcdParseError::MissingDefinitionEnd("scope".into()))
484    }
485
486    pub fn parse_variable(&mut self) -> Result<ScopeItem> {
487        let Some(signal_type) = self.next_token()? else {
488            bail!(VcdParseError::MissingValue("signal type".into()));
489        };
490        let Some(var_width) = self.next_token()? else {
491            bail!(VcdParseError::MissingValue("signal width".into()));
492        };
493        let width = var_width.parse::<u32>()?;
494        let Some(identifier) = self.next_token()? else {
495            bail!(VcdParseError::MissingValue("identifier".into()));
496        };
497        let Some(reference) = self.next_token()? else {
498            bail!(VcdParseError::MissingValue("signal name".into()));
499        };
500        match self.next_token()?.as_deref() {
501            Some("$end") => {
502                self.identifiers.push(identifier.to_string());
503                Ok(ScopeItem::VarDef {
504                    signal: Signal {
505                        signal_type,
506                        width,
507                        reference,
508                    },
509                    identifier,
510                })
511            }
512            _ => bail!(VcdParseError::MissingDefinitionEnd("var".into())),
513        }
514    }
515
516    pub fn parse_value_change_section(&mut self) -> Result<ValueChangeSection> {
517        let mut changes = Vec::new();
518        let current_step: u128 = 0;
519        while let Some(token) = self.next_token()? {
520            match token.as_str() {
521                // Commands
522                "$dumpvars" => {
523                    bail!(VcdParseError::UnsupportedFeature("$dumpvars".into()));
524                }
525                "$end" => bail!(VcdParseError::MismatchedEnd),
526                v if v.starts_with("$") => {
527                    bail!(VcdParseError::UnsupportedFeature("commands".into()));
528                }
529                // Timesteps
530                v if v.starts_with("#") => {
531                    let step = v.strip_prefix("#").unwrap().parse::<u128>()?;
532                    if step < current_step {
533                        bail!(VcdParseError::TimestampsOutOfOrder(current_step, step));
534                    }
535                    changes.push(ValueChangeItem::Timestamp { step });
536                }
537                // Values
538                v if v.starts_with(['0', '1', 'z', 'Z', 'x', 'X']) => {
539                    changes.push(self.parse_scalar(token)?);
540                }
541                // TODO: add support for more VCD signal types
542                v if v.starts_with(['b', 'B']) => {
543                    bail!(VcdParseError::UnsupportedFeature("vector signals".into()))
544                }
545                v if v.starts_with(['r', 'R']) => {
546                    bail!(VcdParseError::UnsupportedFeature("real vars".into()))
547                }
548                v if v.starts_with(['s', 'S']) => {
549                    bail!(VcdParseError::UnsupportedFeature("strings".into()))
550                }
551                v => bail!(VcdParseError::UnknownToken(v.into())),
552            }
553        }
554        Ok(ValueChangeSection { changes })
555    }
556
557    pub fn parse_scalar(&self, token: String) -> Result<ValueChangeItem> {
558        let value = match &token[0..1] {
559            "0" => ScalarValue::Zero,
560            "1" => ScalarValue::One,
561            "z" | "Z" => ScalarValue::Z,
562            "x" | "X" => ScalarValue::X,
563            v => bail!(VcdParseError::UnknownToken(v.into())),
564        };
565        let identifier = token[1..].to_string();
566        if !self.identifiers.contains(&identifier) {
567            bail!(VcdParseError::UnknownVariable(identifier));
568        }
569        Ok(ValueChangeItem::Scalar { identifier, value })
570    }
571}
572
573/// A helper function using a `VcdParser` to directly load given VCD file
574/// contents (as a &str) to an equivalent Vcd.
575pub fn load_vcd(vcd: &str) -> Result<Vcd> {
576    let cursor = io::Cursor::new(vcd.as_bytes());
577    let mut parser = VcdParser::new(cursor);
578    parser.parse_vcd()
579}
580
581/// Dump a single discrete waveform sample as a VCD value change based on the
582/// previous sample. Samples are given as a slice of bytes, where each bit
583/// refers to 1 value. `pin_vars` describes the variables to use for each
584/// signal in LSB order (e.g. pin_vars[0] is used for byte 0 LSB, etc.)
585fn dump_vcd_sample(
586    pin_vars: &[String],
587    timestamp: u128,
588    sample: &[u8],
589    prev_sample: Option<&[u8]>,
590) -> Vec<ValueChangeItem> {
591    let mut changes = Vec::with_capacity(pin_vars.len());
592    for (i, var) in pin_vars.iter().enumerate() {
593        // For each bit, calculate changes from the previous sample
594        let byte_index = i / 8;
595        let bit_index = i % 8;
596        let sample_byte = sample.get(byte_index).unwrap_or(&0x00);
597        let bit = (sample_byte >> bit_index) & 0x01;
598        if let Some(prev_bytes) = prev_sample {
599            let prev_byte = prev_bytes.get(byte_index).unwrap_or(&0x00);
600            let prev_bit = (prev_byte >> bit_index) & 0x01;
601            if prev_bit == bit {
602                continue;
603            }
604        }
605        // There has been a change in value (or this is the first sample),
606        // so record the change.
607        if changes.is_empty() {
608            changes.push(ValueChangeItem::Timestamp { step: timestamp });
609        }
610        changes.push(ValueChangeItem::Scalar {
611            identifier: var.to_string(),
612            value: if bit != 0 {
613                ScalarValue::One
614            } else {
615                ScalarValue::Zero
616            },
617        });
618    }
619    changes
620}
621
622/// Generate a vardefs section resulting from dumping a list of scalar (1 width)
623/// write signals into a single named scope, as a helper for easily exporting
624/// basic VCD files.
625pub fn dump_vcd_wire_vardefs(scope: String, pin_vars: Vec<(String, Option<String>)>) -> VarDefs {
626    let opentitanlib_scope = ScopeItem::Scope {
627        scope_type: String::from("module"),
628        identifier: scope,
629        items: pin_vars
630            .into_iter()
631            .enumerate()
632            .map(|(i, (identifier, pin))| ScopeItem::VarDef {
633                signal: Signal {
634                    signal_type: String::from("wire"),
635                    width: 1,
636                    reference: pin.unwrap_or(format!("pin_{}", i)),
637                },
638                identifier,
639            })
640            .collect::<Vec<_>>(),
641    };
642    VarDefs {
643        scopes: vec![opentitanlib_scope],
644    }
645}
646
647/// Construct a VCD from a uniformly (discretely) sampled waveform, represented by
648/// a list of samples (pin values over time) and a `timescale` (sampling period).
649/// Each sample is defined by a slice of bytes, where each bit index in a slice
650/// refers to a single pin over each sample.
651/// Variables are defined in order, corresponding to LSB-first sample order.
652pub fn vcd_from_samples(
653    pin_names: Vec<Option<String>>,
654    timescale_ps: u128,
655    samples: &[&[u8]],
656) -> Result<Vcd> {
657    // Generate a header sections, sequentially assigning vars '0, '1, '2, etc.
658    let header = Header {
659        timescale_ps: Some(timescale_ps),
660        date: None,
661        version: None,
662    };
663    let vars = &(0..pin_names.len())
664        .map(|c| format!("'{}", c))
665        .collect::<Vec<_>>();
666    let vardefs = dump_vcd_wire_vardefs(
667        String::from("opentitanlib"),
668        vars.clone().into_iter().zip(pin_names).collect::<Vec<_>>(),
669    );
670
671    // Compute value changes across the uniform samples to construct the
672    // value change section of the VCD
673    let mut changes = Vec::new();
674    let mut prev_sample = None;
675    let mut last_sample_index = 0;
676    let num_samples = samples.len();
677    for (i, sample) in samples.iter().enumerate() {
678        let value_change_items = dump_vcd_sample(vars, i as u128, sample, prev_sample);
679        prev_sample = Some(sample);
680        if !value_change_items.is_empty() {
681            changes.extend(value_change_items);
682            last_sample_index = i;
683        }
684    }
685
686    // Make sure we include an ending timestamp for the final sample
687    if (last_sample_index + 1) < num_samples {
688        changes.push(ValueChangeItem::Timestamp {
689            step: num_samples as u128 - 1,
690        });
691    }
692    let value_changes = ValueChangeSection { changes };
693
694    // Dump the VCD
695    Ok(Vcd {
696        header,
697        vardefs,
698        value_changes,
699    })
700}
701
702/// Transform a `u64` timestamp to a time in picoseconds given some initial
703/// offset timestamp measurement and a clock resolution
704fn timestamp_to_picos(initial: u64, timestamp: u64, clock_res: u64) -> u128 {
705    let delta = (timestamp - initial) as u128;
706    delta * 1_000_000_000_000u128 / (clock_res as u128)
707}
708
709/// Transform a time in picoseconds to a `u64` timestamp relative to some
710/// initial offset timestamp measurement and a clock resolution.
711fn timestamp_from_picos(initial: u64, picos: u128, clock_res: u64) -> u64 {
712    let delta = picos * clock_res as u128 / 1_000_000_000_000u128;
713    initial + delta as u64
714}
715
716/// Construct a VCD of a waveform represented by a chronological sequence of
717/// rising / falling edges (i.e. value changes) as is returned by the
718/// `GpioMonitoring` interface. This includes an initial & final timestamp,
719/// some initial values, and a list of edge `events`.
720pub fn vcd_from_edges(
721    pin_names: Vec<Option<String>>,
722    clock_resolution: u64,
723    initial_timestamp: u64,
724    initial_values: &[bool],
725    events: &[MonitoringEvent],
726    final_timestamp: u64,
727) -> Result<Vcd> {
728    // Generate a header sections, sequentially assigning vars '0, '1, '2, etc.
729    // We use a timescale of 1 picosecond to accurately represent different
730    // monitoring clock resolutions.
731    let header = Header {
732        timescale_ps: Some(1),
733        date: None,
734        version: None,
735    };
736    let vars = &(0..pin_names.len())
737        .map(|c| format!("'{}", c))
738        .collect::<Vec<_>>();
739    let vardefs = dump_vcd_wire_vardefs(
740        String::from("opentitanlib"),
741        vars.clone().into_iter().zip(pin_names).collect::<Vec<_>>(),
742    );
743
744    // Compute the value change section, which is mostly a 1-to-1 mapping
745    let mut changes = Vec::new();
746    // Dump the initial values
747    changes.push(ValueChangeItem::Timestamp { step: 0 });
748    for (&bit, var) in initial_values.iter().zip(vars.iter()) {
749        changes.push(ValueChangeItem::Scalar {
750            identifier: var.to_string(),
751            value: if bit {
752                ScalarValue::One
753            } else {
754                ScalarValue::Zero
755            },
756        });
757    }
758    // Edge events are essentially 1-to-1 with the VCD format
759    let mut timestamp: u128 = 0;
760    for event in events.iter() {
761        let edge_time = timestamp_to_picos(initial_timestamp, event.timestamp, clock_resolution);
762        if edge_time > timestamp {
763            timestamp = edge_time;
764            changes.push(ValueChangeItem::Timestamp { step: edge_time });
765        }
766        let index = event.signal_index as usize;
767        changes.push(ValueChangeItem::Scalar {
768            identifier: vars[index].clone(),
769            value: if event.edge == Edge::Rising {
770                ScalarValue::One
771            } else {
772                ScalarValue::Zero
773            },
774        });
775    }
776
777    // Include a final timestamp marking the final measurement
778    let end_time = timestamp_to_picos(initial_timestamp, final_timestamp, clock_resolution);
779    if end_time > timestamp {
780        changes.push(ValueChangeItem::Timestamp { step: end_time });
781    }
782    let value_changes = ValueChangeSection { changes };
783
784    // Dump the VCD
785    Ok(Vcd {
786        header,
787        vardefs,
788        value_changes,
789    })
790}
791
792/// An iterator over a VCD file, which returns uniform samples of values of
793/// VCD signals at every VCD time step. Depending on the delay between value
794/// changes, this could result in very long iteration counts.
795pub struct UniformVcdSampler {
796    pub step: u128,
797    pin_vars: Vec<String>,
798    value_changes: std::vec::IntoIter<ValueChangeItem>,
799    current_timestamp: u128,
800    next_timestamp: u128,
801    current_values: Vec<u8>,
802    depleted: bool,
803}
804
805impl UniformVcdSampler {
806    /// Create a new uniform VCD sampler. Samples every `step` steps in the VCD.
807    pub fn new(vcd: Vcd, step: u128) -> Self {
808        let pin_vars = vcd
809            .var_names()
810            .iter()
811            .map(|n| n.to_string())
812            .collect::<Vec<_>>();
813        let num_bytes = pin_vars.len().div_ceil(8);
814        Self {
815            step,
816            pin_vars,
817            value_changes: vcd.value_changes.changes.into_iter(),
818            current_timestamp: 0u128,
819            next_timestamp: 0u128,
820            current_values: std::iter::repeat_n(0x00, num_bytes).collect::<Vec<_>>(),
821            depleted: false,
822        }
823    }
824}
825
826impl Iterator for UniformVcdSampler {
827    type Item = Result<Vec<u8>>;
828
829    fn next(&mut self) -> Option<Self::Item> {
830        loop {
831            // Handle sampling in-between parsed timestamps
832            if self.next_timestamp > self.current_timestamp {
833                self.current_timestamp += self.step;
834                return Some(Ok(self.current_values.clone()));
835            }
836            match self.value_changes.next() {
837                // If we read a timestamp, sample in steps until it is reached
838                Some(ValueChangeItem::Timestamp { step }) => {
839                    self.next_timestamp = step;
840                }
841                // If we read a value, update the internal `current_values` state
842                Some(ValueChangeItem::Scalar { identifier, value }) => {
843                    let value = match value {
844                        ScalarValue::Zero => 0,
845                        ScalarValue::One => 1,
846                        _ => {
847                            return Some(Err(VcdParseError::UnsupportedFeature(
848                                "non-binary scalars".into(),
849                            )
850                            .into()));
851                        }
852                    };
853                    let Some(index) = self.pin_vars.iter().position(|id| *id == identifier) else {
854                        return Some(Err(VcdParseError::UnknownVariable(identifier).into()));
855                    };
856                    let byte_index = index / 8;
857                    let bit_index = index % 8;
858                    self.current_values[byte_index] &= !(1 << bit_index);
859                    self.current_values[byte_index] |= value << bit_index;
860                }
861                // If there are no more value changes to read, output one final
862                // sample of the current values. Otherwise return `None`.
863                None => {
864                    if self.depleted {
865                        return None;
866                    }
867                    self.depleted = true;
868                    return Some(Ok(self.current_values.clone()));
869                }
870            }
871        }
872    }
873}
874
875/// The output of parsing a VCD to a list of edges as a `MonitoringReadResponse`,
876/// which will include any parsed pin variables.
877pub struct ParsedVcdEdges {
878    pub pin_vars: Vec<String>,
879    pub events: MonitoringReadResponse,
880}
881
882/// Parses a VCD (given its contents) to a Vec of edges (value changes), with
883/// timestamps relative to the `clock_resolution` and `initial_timestamp`.
884pub fn vcd_to_edges(
885    vcd: Vcd,
886    clock_resolution: u64,
887    initial_timestamp: u64,
888) -> Result<ParsedVcdEdges> {
889    let pin_vars = vcd.var_names();
890    let timescale = vcd
891        .header
892        .timescale_ps
893        .expect("VCD must contain a timescale to parse to edges");
894
895    // Parse edges from the value changes
896    let mut events = Vec::new();
897    let mut current_time: u128 = 0;
898    let num_bytes = pin_vars.len().div_ceil(8);
899    // Assume all values are initialized at 0 if not given a value at t=0.
900    let mut current_values: Vec<u8> = std::iter::repeat_n(0x00, num_bytes).collect();
901
902    for change in &vcd.value_changes.changes {
903        match change {
904            ValueChangeItem::Timestamp { step } => {
905                current_time = *step;
906            }
907            ValueChangeItem::Scalar { identifier, value } => {
908                let value = match value {
909                    ScalarValue::Zero => 0,
910                    ScalarValue::One => 1,
911                    _ => bail!(VcdParseError::UnsupportedFeature(
912                        "non-binary scalars".into()
913                    )),
914                };
915                let Some(index) = pin_vars.iter().position(|id| id == identifier) else {
916                    bail!(VcdParseError::UnknownVariable(identifier.into()));
917                };
918                let byte_index = index / 8;
919                let bit_index = index % 8;
920                let current_value = current_values[byte_index] >> bit_index & 0x01;
921                // Only create an edge on a change of value
922                if value != current_value {
923                    current_values[byte_index] &= !(1 << bit_index);
924                    current_values[byte_index] |= value << bit_index;
925                    // Don't generate edges for initial signal values (t=0)
926                    if current_time == 0 {
927                        // TODO: maybe this should be parsed from a `$dumpvars` instead
928                        // of `#0`, so we can have an edge at `t=0`?
929                        continue;
930                    }
931                    let picos = current_time * timescale;
932                    let timestamp =
933                        timestamp_from_picos(initial_timestamp, picos, clock_resolution);
934                    events.push(MonitoringEvent {
935                        signal_index: index as u8,
936                        edge: if value == 1 {
937                            Edge::Rising
938                        } else {
939                            Edge::Falling
940                        },
941                        timestamp,
942                    });
943                }
944            }
945        }
946    }
947
948    // Determine a final timestamp for the waveform from the last step seen
949    let last_picos = current_time * timescale;
950    let last_timestamp = timestamp_from_picos(initial_timestamp, last_picos, clock_resolution);
951    let events = MonitoringReadResponse {
952        events,
953        timestamp: last_timestamp,
954    };
955    Ok(ParsedVcdEdges {
956        pin_vars: pin_vars.iter().map(|n| n.to_string()).collect(),
957        events,
958    })
959}
960
961#[cfg(test)]
962mod test {
963    use super::*;
964    use std::time::Duration;
965
966    // A helper to compactly construct `MonitoringEvent` edges.
967    fn edge(signal_index: u8, edge: Edge, timestamp: u64) -> MonitoringEvent {
968        MonitoringEvent {
969            signal_index,
970            edge,
971            timestamp,
972        }
973    }
974
975    // Test by dumping a given uniformly sampled waveform to a VCD and parsing
976    // it back, to check the decoded contents match the original.
977    fn samples_encode_decode(
978        samples: &[&[u8]],
979        pin_names: Vec<Option<String>>,
980        clock_tick: Duration,
981    ) -> Result<()> {
982        let vcd = dump_vcd(&vcd_from_samples(
983            pin_names,
984            clock_tick.as_nanos() * 1000,
985            samples,
986        )?)?;
987        assert!(!vcd.is_empty());
988        // For now this test assumes the order of bits is retained (i.e. vars
989        // are parsed into sample bit indexes in the same order they are defined).
990        let vcd = load_vcd(&vcd)?;
991        let decoded = UniformVcdSampler::new(vcd, 1).collect::<Result<Vec<_>>>()?;
992        for (decoded_sample, sample) in decoded.iter().zip(samples) {
993            assert_eq!(decoded_sample, sample);
994        }
995        Ok(())
996    }
997
998    // Test by dumping a given edge-represented waveform to a VCD and parsing it
999    // back, to check the decoded contents match the original.
1000    fn edges_encode_decode(
1001        clock_resolution: u64,
1002        initial_timestamp: u64,
1003        initial_values: &[bool],
1004        events: &[MonitoringEvent],
1005        final_timestamp: u64,
1006        pin_names: Vec<Option<String>>,
1007    ) -> Result<()> {
1008        let vcd = dump_vcd(&vcd_from_edges(
1009            pin_names,
1010            clock_resolution,
1011            initial_timestamp,
1012            initial_values,
1013            events,
1014            final_timestamp,
1015        )?)?;
1016        assert!(!vcd.is_empty());
1017        // For now this test assumes the order of signals is retained (i.e.
1018        // vars are parsed into signal indexes in the order they are defined).
1019        let vcd = load_vcd(&vcd)?;
1020        let decoded = vcd_to_edges(vcd, clock_resolution, initial_timestamp)?;
1021        assert_eq!(decoded.events.timestamp, final_timestamp);
1022        assert_eq!(decoded.events.events, events);
1023        Ok(())
1024    }
1025
1026    #[test]
1027    fn samples_waveform() -> Result<()> {
1028        // Random waveform over 3 pins, with a clock_tick of 3 us.
1029        let samples = [
1030            4, 5, 3, 4, 1, 3, 6, 0, 2, 4, 5, 1, 7, 2, 3, 4, 0, 0, 1, 4, 0, 1, 2, 3, 4, 6,
1031        ];
1032        let sample_slices = samples.iter().map(std::slice::from_ref).collect::<Vec<_>>();
1033        let pin_names = Vec::from([const { None }; 3]);
1034        let clock_tick = Duration::from_nanos(3000);
1035        samples_encode_decode(&sample_slices, pin_names.clone(), clock_tick)?;
1036
1037        // Example SPI waveform over 3 pins, again with a clock_tick of 3 us.
1038        let pin_names = vec![
1039            Some("spi_sck".into()),
1040            Some("spi_cs".into()),
1041            Some("spi_copi".into()),
1042        ];
1043        let samples = [
1044            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 4,
1045            5, 4, 5, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1046            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1047            1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1048            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1049            1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1050            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1051            1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1052            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1053            1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
1054            2,
1055        ];
1056        let sample_slices = samples.iter().map(std::slice::from_ref).collect::<Vec<_>>();
1057        samples_encode_decode(&sample_slices, pin_names, clock_tick)
1058    }
1059
1060    #[test]
1061    fn raw_vcd_to_samples() -> Result<()> {
1062        let vcd = "
1063$timescale 1345ns $end\n\
1064$scope module opentitanlib $end\n\
1065$var wire 1 # CN_04_2 $end\n\
1066$var wire 1 ! CN_05_3 $end\n\
1067$var wire 1 ; CN_05_4 $end\n\
1068$var wire 1 ? CN_15_4 $end\n\
1069$upscope $end\n\
1070$enddefinitions $end\n\
1071#0 0# 0! 1; 0?\n\
1072#1 1# 1! 0;\n\
1073#2 0#\n\
1074#3 1# 0!\n\
1075#4 0# 1! 1;\n\
1076#5 1#\n\
1077#6 0# 0! 0; 1?\n\
1078#7 1# 1! 1;\n\
1079#8 0#\n\
1080#9 1# 0; 0?\n\
1081#10 0# 0! 1;\n\
1082#11 1# 1! 0;\n\
1083#12 0# 0!\n\
1084#13 1# 1!\n\
1085#14 0# 0! 1; 1?\n\
1086#15 1# 0;\n\
1087#16 0#\n\
1088#17 1# 1! 0?\n\
1089#18 0# 0!\n\
1090#19 1#\n\
1091#20 0#\n\
1092#21 1#\n\
1093#22 0#\n\
1094#23 1#\n\
1095#24 0#\n\
1096#33 1;\n\
1097#34 1# 1! 1?\n\
1098#35 0#\n\
1099#36 1# 0! 0; 0?\n\
1100#37 0#";
1101        let expected_pin_vars = [
1102            String::from("#"),
1103            String::from("!"),
1104            String::from(";"),
1105            String::from("?"),
1106        ];
1107        let expected_timescale = Some(1345 * 1000); // 1345 ns
1108        let expected_samples: [u8; 38] = [
1109            4, 3, 2, 1, 6, 7, 8, 15, 14, 3, 4, 3, 0, 3, 12, 9, 8, 3, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0,
1110            0, 0, 0, 0, 0, 4, 15, 14, 1, 0,
1111        ];
1112
1113        // Load & decode the VCD, and check it matches expected contents
1114        let vcd = load_vcd(vcd)?;
1115        let decoded_pin_vars = vcd
1116            .var_names()
1117            .iter()
1118            .map(|n| n.to_string())
1119            .collect::<Vec<_>>();
1120        let decoded_timescale_ps = vcd.header.timescale_ps;
1121        let decoded_samples = UniformVcdSampler::new(vcd, 1).collect::<Result<Vec<_>>>()?;
1122        for expected in expected_pin_vars {
1123            assert!(decoded_pin_vars.contains(&expected));
1124        }
1125        assert_eq!(decoded_timescale_ps, expected_timescale);
1126        for (decoded_sample, sample) in decoded_samples.iter().zip(expected_samples) {
1127            assert_eq!(decoded_sample, std::slice::from_ref(&sample));
1128        }
1129        Ok(())
1130    }
1131
1132    #[test]
1133    fn edges_waveform() -> Result<()> {
1134        // Random waveform over 3 pins, with a clock_tick of 1 us.
1135        let clock_resolution = 1_000_000;
1136        let initial_timestamp = 536;
1137        let initial_values = vec![true, false, true];
1138        let events = vec![
1139            edge(2, Edge::Falling, 562),
1140            edge(1, Edge::Rising, 621),
1141            edge(0, Edge::Falling, 754),
1142            edge(2, Edge::Rising, 832),
1143            edge(2, Edge::Falling, 855),
1144            edge(2, Edge::Rising, 912),
1145            edge(0, Edge::Rising, 1012),
1146            edge(1, Edge::Falling, 1058),
1147            edge(2, Edge::Falling, 1123),
1148            edge(2, Edge::Rising, 1157),
1149            edge(0, Edge::Falling, 1210),
1150            edge(1, Edge::Rising, 1258),
1151            edge(0, Edge::Rising, 1315),
1152            edge(2, Edge::Falling, 1415),
1153            edge(1, Edge::Falling, 1510),
1154            edge(0, Edge::Falling, 1633),
1155            edge(0, Edge::Rising, 1901),
1156        ];
1157        let final_timestamp = 1947;
1158        let pin_names = Vec::from([const { None }; 3]);
1159        edges_encode_decode(
1160            clock_resolution,
1161            initial_timestamp,
1162            &initial_values,
1163            &events,
1164            final_timestamp,
1165            pin_names,
1166        )?;
1167
1168        // Example UART RX waveform with 1 pin taken from a bitbanging test sample
1169        let initial_timestamp = 0;
1170        let initial_values = vec![true];
1171        let event_timestamps = [
1172            5889252340, 5889252358, 5889252375, 5889252392, 5889252410, 5889252427, 5889252445,
1173            5889252462, 5889252479, 5889252497, 5889252514, 5889252532, 5889252549, 5889252636,
1174            5889252654, 5889252671, 5889252688, 5889252723, 5889252740, 5889252775, 5889252793,
1175            5889252810, 5889252827, 5889252845, 5889252862, 5889252914, 5889252932, 5889252949,
1176            5889252967, 5889252984, 5889253001, 5889253019, 5889253036, 5889253141, 5889253158,
1177            5889253193, 5889253210, 5889253245, 5889253263, 5889253315, 5889253349, 5889253367,
1178            5889253386, 5889253402, 5889253419, 5889253454, 5889253471, 5889253489, 5889253523,
1179            5889253541, 5889253558, 5889253610, 5889253628, 5889253645, 5889253697, 5889253715,
1180            5889253732, 5889253767, 5889253784, 5889253837, 5889253871, 5889253889, 5889253906,
1181            5889253924, 5889253941, 5889254011, 5889254046, 5889254063, 5889254080, 5889254115,
1182            5889254167, 5889254185, 5889254219, 5889254238, 5889254254, 5889254272, 5889254324,
1183            5889254359, 5889254393, 5889254411, 5889254428, 5889254533, 5889254550, 5889254585,
1184            5889254603, 5889254655, 5889254672, 5889254689, 5889254741, 5889254759, 5889254776,
1185            5889254794, 5889254811, 5889254828, 5889254846, 5889254881, 5889254915, 5889254933,
1186            5889254950, 5889254968, 5889255002, 5889255037, 5889255089, 5889255107, 5889255124,
1187            5889255176, 5889255194, 5889255211, 5889255264, 5889255281, 5889255298, 5889255455,
1188        ];
1189        let edges = [Edge::Falling, Edge::Rising];
1190        let events = event_timestamps
1191            .into_iter()
1192            .enumerate()
1193            .map(|(i, t)| edge(0, edges[i % 2], t))
1194            .collect::<Vec<_>>();
1195        let final_timestamp = 5889262827;
1196        let pin_names = vec![Some("uart_rx".into())];
1197        edges_encode_decode(
1198            clock_resolution,
1199            initial_timestamp,
1200            &initial_values,
1201            &events,
1202            final_timestamp,
1203            pin_names,
1204        )
1205    }
1206
1207    #[test]
1208    fn raw_vcd_to_edges() -> Result<()> {
1209        let vcd = "
1210$timescale 1ns $end\n\
1211$scope module opentitanlib $end\n\
1212$var wire 1 # a $end\n\
1213$var wire 1 ! b $end\n\
1214$var wire 1 ; c $end\n\
1215$var wire 1 ? d $end\n\
1216$upscope $end\n\
1217$enddefinitions $end\n\
1218#0 0# 0! 1; 0?\n\
1219#10000 1# 1! 0;\n\
1220#20000 0#\n\
1221#30000 1# 0!\n\
1222#40000 0# 1! 1;\n\
1223#50000 1#\n\
1224#60000 0# 0! 0; 1?\n\
1225#70000 1# 1! 1;\n\
1226#80000 0#\n\
1227#90000 1# 0; 0?\n\
1228#100000 0# 0! 1;\n\
1229#110000 1# 1! 0;\n\
1230#120000 0# 0!\n\
1231#130000 1# 1!\n\
1232#140000 0# 0! 1; 1?\n\
1233#150000 1# 0;\n\
1234#160000 0#\n\
1235#170000 1# 1! 0?\n\
1236#180000 0# 0!\n\
1237#190000 1#\n\
1238#200000 0#\n\
1239#210000 1#\n\
1240#220000 0#\n\
1241#230000 1#\n\
1242#240000 0#\n\
1243#330000 1;\n\
1244#340000 1# 1! 1?\n\
1245#350000 0#\n\
1246#360000 1# 0! 0; 0?\n\
1247#100000000 0#";
1248        let clock_resolution = 1_000_000; // Each clock tick is 1 us.
1249        let initial_timestamp = 536;
1250        let vcd = load_vcd(vcd)?;
1251        let decoded = vcd_to_edges(vcd, clock_resolution, initial_timestamp)?;
1252
1253        let expected_pin_vars = [
1254            String::from("#"),
1255            String::from("!"),
1256            String::from(";"),
1257            String::from("?"),
1258        ];
1259        let expected_edges = vec![
1260            edge(0, Edge::Rising, 546),
1261            edge(1, Edge::Rising, 546),
1262            edge(2, Edge::Falling, 546),
1263            edge(0, Edge::Falling, 556),
1264            edge(0, Edge::Rising, 566),
1265            edge(1, Edge::Falling, 566),
1266            edge(0, Edge::Falling, 576),
1267            edge(1, Edge::Rising, 576),
1268            edge(2, Edge::Rising, 576),
1269            edge(0, Edge::Rising, 586),
1270            edge(0, Edge::Falling, 596),
1271            edge(1, Edge::Falling, 596),
1272            edge(2, Edge::Falling, 596),
1273            edge(3, Edge::Rising, 596),
1274            edge(0, Edge::Rising, 606),
1275            edge(1, Edge::Rising, 606),
1276            edge(2, Edge::Rising, 606),
1277            edge(0, Edge::Falling, 616),
1278            edge(0, Edge::Rising, 626),
1279            edge(2, Edge::Falling, 626),
1280            edge(3, Edge::Falling, 626),
1281            edge(0, Edge::Falling, 636),
1282            edge(1, Edge::Falling, 636),
1283            edge(2, Edge::Rising, 636),
1284            edge(0, Edge::Rising, 646),
1285            edge(1, Edge::Rising, 646),
1286            edge(2, Edge::Falling, 646),
1287            edge(0, Edge::Falling, 656),
1288            edge(1, Edge::Falling, 656),
1289            edge(0, Edge::Rising, 666),
1290            edge(1, Edge::Rising, 666),
1291            edge(0, Edge::Falling, 676),
1292            edge(1, Edge::Falling, 676),
1293            edge(2, Edge::Rising, 676),
1294            edge(3, Edge::Rising, 676),
1295            edge(0, Edge::Rising, 686),
1296            edge(2, Edge::Falling, 686),
1297            edge(0, Edge::Falling, 696),
1298            edge(0, Edge::Rising, 706),
1299            edge(1, Edge::Rising, 706),
1300            edge(3, Edge::Falling, 706),
1301            edge(0, Edge::Falling, 716),
1302            edge(1, Edge::Falling, 716),
1303            edge(0, Edge::Rising, 726),
1304            edge(0, Edge::Falling, 736),
1305            edge(0, Edge::Rising, 746),
1306            edge(0, Edge::Falling, 756),
1307            edge(0, Edge::Rising, 766),
1308            edge(0, Edge::Falling, 776),
1309            edge(2, Edge::Rising, 866),
1310            edge(0, Edge::Rising, 876),
1311            edge(1, Edge::Rising, 876),
1312            edge(3, Edge::Rising, 876),
1313            edge(0, Edge::Falling, 886),
1314            edge(0, Edge::Rising, 896),
1315            edge(1, Edge::Falling, 896),
1316            edge(2, Edge::Falling, 896),
1317            edge(3, Edge::Falling, 896),
1318            edge(0, Edge::Falling, 100536),
1319        ];
1320        for expected in expected_pin_vars {
1321            assert!(decoded.pin_vars.contains(&expected));
1322        }
1323        assert_eq!(decoded.events.events, expected_edges);
1324        assert!(decoded.events.timestamp >= expected_edges.last().unwrap().timestamp);
1325        Ok(())
1326    }
1327
1328    #[test]
1329    fn many_signals() -> Result<()> {
1330        // Test with a VCD containing 10 samples with 1024 different signals,
1331        // to be sure we can handle large VCDs and large samples without restriction
1332        const NUM_PINS: usize = 1024;
1333        const NUM_SAMPLES: usize = 10;
1334        let mut samples = Vec::new();
1335        let mut prng = 0x0ACECAFE;
1336        for _ in 0..NUM_SAMPLES {
1337            let mut sample = Vec::new();
1338            for _ in 0..NUM_PINS.div_ceil(32) {
1339                // xorshift32 PRNG to distribute changes across sample bitmaps.
1340                prng ^= prng << 13;
1341                prng ^= prng >> 17;
1342                prng ^= prng << 5;
1343                sample.push(prng as u8);
1344                sample.push((prng >> 8) as u8);
1345                sample.push((prng >> 16) as u8);
1346                sample.push((prng >> 24) as u8);
1347            }
1348            samples.push(sample);
1349        }
1350        let sample_slices = samples.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
1351        let pin_names = Vec::from([const { None }; NUM_PINS]);
1352        let clock_tick = Duration::from_nanos(3000);
1353        samples_encode_decode(&sample_slices, pin_names, clock_tick)
1354    }
1355}