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}