vcd/
lib.rs

1//! This crate reads and writes [VCD (Value Change Dump)][wp] files, a common
2//! format used with logic analyzers, HDL simulators, and other EDA tools.
3//!
4//! [wp]: https://en.wikipedia.org/wiki/Value_change_dump
5//! 
6//! It provides:
7//!  * A [`Parser`] wraps [`std::io::BufRead`] and provides the ability to parse a
8//!    VCD [`Header`] and [`Command`]s.
9//!  * A [`Writer`] that allows writing VCD to a [`std::io::Write`].
10//!  * Several structs and enums representing the elements of a VCD file that
11//!    can be used with the `Parser` or `Writer`, or individually by
12//!    implementing [`FromStr`][`std::str::FromStr`] and [`Display`].
13//!
14//! ## Example
15//!
16//! ```
17//! use std::io;
18//! use std::io::ErrorKind::InvalidInput;
19//! use vcd::{ self, Value, TimescaleUnit, SimulationCommand };
20//!
21//! /// Write out a clock and data signal to a VCD file
22//! fn write_clocked_vcd(shift_reg: u32, w: &mut dyn io::Write) -> io::Result<()> {
23//!   let mut writer = vcd::Writer::new(w);
24//!
25//!   // Write the header
26//!   writer.timescale(1, TimescaleUnit::US)?;
27//!   writer.add_module("top")?;
28//!   let clock = writer.add_wire(1, "clock")?;
29//!   let data = writer.add_wire(1, "data")?;
30//!   writer.upscope()?;
31//!   writer.enddefinitions()?;
32//!
33//!   // Write the initial values
34//!   writer.begin(SimulationCommand::Dumpvars)?;
35//!   writer.change_scalar(clock, Value::V0)?;
36//!   writer.change_scalar(data, Value::V0)?;
37//!   writer.end()?;
38//!
39//!   // Write the data values
40//!   let mut t = 0;
41//!   for i in 0..32 {
42//!     t += 4;
43//!     writer.timestamp(t)?;
44//!     writer.change_scalar(clock, Value::V1)?;
45//!     writer.change_scalar(data, ((shift_reg >> i) & 1) != 0)?;
46//!
47//!     t += 4;
48//!     writer.timestamp(t)?;
49//!     writer.change_scalar(clock, Value::V0)?;
50//!   }
51//!   Ok(())
52//! }
53//!
54//! /// Parse a VCD file containing a clocked signal and decode the signal
55//! fn read_clocked_vcd(r: &mut dyn io::BufRead) -> io::Result<u32> {
56//!    let mut parser = vcd::Parser::new(r);
57//!
58//!    // Parse the header and find the wires
59//!    let header = parser.parse_header()?;
60//!    let clock = header.find_var(&["top", "clock"])
61//!       .ok_or_else(|| io::Error::new(InvalidInput, "no wire top.clock"))?.code;
62//!    let data = header.find_var(&["top", "data"])
63//!       .ok_or_else(|| io::Error::new(InvalidInput, "no wire top.data"))?.code;
64//!
65//!    // Iterate through the remainder of the file and decode the data
66//!    let mut shift_reg = 0;
67//!    let mut data_val = Value::X;
68//!    let mut clock_val = Value::X;
69//!
70//!    for command_result in parser {
71//!      let command = command_result?;
72//!      use vcd::Command::*;
73//!      match command {
74//!        ChangeScalar(i, v) if i == clock => {
75//!          if clock_val == Value::V1 && v == Value::V0 { // falling edge on clock
76//!             let shift_bit = match data_val { Value::V1 => (1 << 31), _ => 0 };
77//!             shift_reg = (shift_reg >> 1) | shift_bit;
78//!          }
79//!          clock_val = v;
80//!        }
81//!        ChangeScalar(i, v) if i == data => {
82//!          data_val = v;
83//!        }
84//!        _ => (),
85//!      }
86//!    }
87//!
88//!    Ok(shift_reg)
89//! }
90//!
91//! let mut buf = Vec::new();
92//! let data = 0xC0DE1234;
93//! write_clocked_vcd(data, &mut buf).expect("Failed to write");
94//! let value = read_clocked_vcd(&mut &buf[..]).expect("Failed to read");
95//! assert_eq!(value, data);
96//! ```
97#![warn(missing_docs)]
98
99use std::{fmt::{self, Display}, borrow::Borrow};
100
101mod parser;
102pub use parser::{ Parser, ParseError, ParseErrorKind };
103
104mod write;
105pub use write::Writer;
106
107mod idcode;
108pub use idcode::{IdCode, InvalidIdCode};
109
110mod value;
111pub use value::{ Value, Vector, VectorIter, InvalidValue };
112
113mod timescale;
114pub use timescale::{ TimescaleUnit, InvalidTimescaleUnit };
115
116mod scope;
117pub use scope::{
118    Scope,
119    ScopeItem,
120    ScopeType,
121    InvalidScopeType,
122    Var,
123    VarType,
124    InvalidVarType,
125    ReferenceIndex,
126    InvalidReferenceIndex,
127};
128
129macro_rules! unit_error_struct {
130    ($name:ident, $err:literal) => {
131        #[doc = concat!("Parse error for ", $err, ".")]
132        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
133        #[non_exhaustive]
134        pub struct $name;
135
136        impl Display for $name {
137            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138                write!(f, $err)
139            }
140        }
141
142        impl std::error::Error for $name {}
143    };
144}
145pub(crate) use unit_error_struct;
146
147/// An element in a VCD file.
148#[derive(Debug, PartialEq, Clone)]
149#[non_exhaustive]
150pub enum Command {
151    /// A `$comment` command
152    Comment(String),
153
154    /// A `$date` command
155    Date(String),
156
157    /// A `$version` command
158    Version(String),
159
160    /// A `$timescale` command
161    Timescale(u32, TimescaleUnit),
162
163    /// A `$scope` command
164    ScopeDef(ScopeType, String),
165
166    /// An `$upscope` command
167    Upscope,
168
169    /// A `$var` command
170    VarDef(VarType, u32, IdCode, String, Option<ReferenceIndex>),
171
172    /// An `$enddefinitions` command
173    Enddefinitions,
174
175    /// A `#xxx` timestamp
176    Timestamp(u64),
177
178    /// A `0a` change to a scalar variable
179    ChangeScalar(IdCode, Value),
180
181    /// A `b0000 a` change to a vector variable
182    ChangeVector(IdCode, Vector),
183
184    /// A `r1.234 a` change to a real variable
185    ChangeReal(IdCode, f64),
186
187    /// A `sSTART a` change to a string variable
188    ChangeString(IdCode, String),
189
190    /// A beginning of a simulation command. Unlike header commands, which are parsed atomically,
191    /// simulation commands emit a Begin, followed by the data changes within them, followed by
192    /// End.
193    Begin(SimulationCommand),
194
195    /// An end of a simulation command.
196    End(SimulationCommand),
197}
198
199/// A simulation command type, used in `Command::Begin` and `Command::End`.
200#[derive(Debug, Copy, Clone, Eq, PartialEq)]
201#[non_exhaustive]
202#[allow(missing_docs)]
203pub enum SimulationCommand {
204    Dumpall,
205    Dumpoff,
206    Dumpon,
207    Dumpvars,
208}
209
210impl Display for SimulationCommand {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        use SimulationCommand::*;
213        write!(
214            f,
215            "{}",
216            match *self {
217                Dumpall => "dumpall",
218                Dumpoff => "dumpoff",
219                Dumpon => "dumpon",
220                Dumpvars => "dumpvars",
221            }
222        )
223    }
224}
225
226/// Structure containing the data from the header of a VCD file.
227/// 
228/// A `Header` can be parsed from VCD with [`Parser::parse_header`], or create an
229/// empty `Header` with [`Header::default`].
230#[derive(Debug, Default)]
231#[non_exhaustive]
232pub struct Header {
233    /// `$date` text
234    pub date: Option<String>,
235
236    /// `$version` text
237    pub version: Option<String>,
238
239    /// Parsed `$timescale` indicating the time unit used in the file
240    pub timescale: Option<(u32, TimescaleUnit)>,
241
242    /// Top-level variables, scopes, and comments
243    pub items: Vec<ScopeItem>,
244}
245
246fn find_parent_scope<'a>(
247    mut items: &'a [ScopeItem],
248    path: &[impl Borrow<str>]
249) -> Option<&'a [ScopeItem]> {
250    for name in path {
251        items = items.iter().find_map(|item| match item {
252            ScopeItem::Scope(scope) if scope.identifier == name.borrow() => {
253                Some(&scope.items[..])
254            }
255            _ => None,
256        })?;
257    }
258    Some(items)
259}
260
261impl Header {
262    /// Find the scope object at a specified path.
263    ///
264    /// ## Example
265    ///
266    /// ```rust
267    /// let mut parser = vcd::Parser::new(&b"
268    /// $scope module a $end
269    /// $scope module b $end
270    /// $var integer 16 n0 counter $end
271    /// $upscope $end
272    /// $upscope $end
273    /// $enddefinitions $end
274    /// "[..]);
275    /// let header = parser.parse_header().unwrap();
276    /// let scope = header.find_scope(&["a", "b"]).unwrap();
277    /// assert_eq!(scope.identifier, "b");
278    /// ```
279    pub fn find_scope<S>(&self, path: &[S]) -> Option<&Scope>
280    where
281        S: std::borrow::Borrow<str>,
282    {
283        let (name, parent_path) = path.split_last()?;
284        let parent = find_parent_scope(&self.items, parent_path)?;
285
286        parent.iter().find_map(|item| match item {
287            ScopeItem::Scope(scope) if scope.identifier == name.borrow() => Some(scope),
288            _ => None,
289        })
290    }
291
292    /// Find the variable object at a specified path.
293    ///
294    /// ## Example
295    ///
296    /// ```rust
297    /// let mut parser = vcd::Parser::new(&b"
298    /// $scope module a $end
299    /// $scope module b $end
300    /// $var integer 16 n0 counter $end
301    /// $upscope $end
302    /// $upscope $end
303    /// $enddefinitions $end
304    /// "[..]);
305    /// let header = parser.parse_header().unwrap();
306    /// let var = header.find_var(&["a", "b", "counter"]).unwrap();
307    /// assert_eq!(var.reference, "counter");
308    /// ```
309    pub fn find_var<S>(&self, path: &[S]) -> Option<&Var>
310    where
311        S: std::borrow::Borrow<str>,
312    {
313        let (name, parent_path) = path.split_last()?;
314        let parent = find_parent_scope(&self.items, parent_path)?;
315
316        parent.iter().find_map(|item| match item {
317            ScopeItem::Var(v) if v.reference == name.borrow() => Some(v),
318            _ => None,
319        })
320    }
321}