8 releases (breaking)

0.7.0 Jul 15, 2023
0.6.1 Sep 12, 2020
0.6.0 Jul 12, 2020
0.5.0 Oct 3, 2019
0.1.0 May 4, 2015

#917 in Parser implementations

Download history 292/week @ 2023-12-11 209/week @ 2023-12-18 140/week @ 2023-12-25 228/week @ 2024-01-01 253/week @ 2024-01-08 308/week @ 2024-01-15 529/week @ 2024-01-22 593/week @ 2024-01-29 825/week @ 2024-02-05 669/week @ 2024-02-12 1107/week @ 2024-02-19 728/week @ 2024-02-26 795/week @ 2024-03-04 754/week @ 2024-03-11 831/week @ 2024-03-18 664/week @ 2024-03-25

3,083 downloads per month
Used in 40 crates (12 directly)

MIT license

73KB
2K SLoC

VCD

Documentation | Changelog

This crate reads and writes VCD (Value Change Dump) files, a common format used with logic analyzers, HDL simulators, and other EDA tools. It provides streaming wrappers around the io::Read and io::Write traits to read and write VCD commands and data.


lib.rs:

This crate reads and writes VCD (Value Change Dump) files, a common format used with logic analyzers, HDL simulators, and other EDA tools.

It provides:

Example

use std::io;
use std::io::ErrorKind::InvalidInput;
use vcd::{ self, Value, TimescaleUnit, SimulationCommand };

/// Write out a clock and data signal to a VCD file
fn write_clocked_vcd(shift_reg: u32, w: &mut dyn io::Write) -> io::Result<()> {
  let mut writer = vcd::Writer::new(w);

  // Write the header
  writer.timescale(1, TimescaleUnit::US)?;
  writer.add_module("top")?;
  let clock = writer.add_wire(1, "clock")?;
  let data = writer.add_wire(1, "data")?;
  writer.upscope()?;
  writer.enddefinitions()?;

  // Write the initial values
  writer.begin(SimulationCommand::Dumpvars)?;
  writer.change_scalar(clock, Value::V0)?;
  writer.change_scalar(data, Value::V0)?;
  writer.end()?;

  // Write the data values
  let mut t = 0;
  for i in 0..32 {
    t += 4;
    writer.timestamp(t)?;
    writer.change_scalar(clock, Value::V1)?;
    writer.change_scalar(data, ((shift_reg >> i) & 1) != 0)?;

    t += 4;
    writer.timestamp(t)?;
    writer.change_scalar(clock, Value::V0)?;
  }
  Ok(())
}

/// Parse a VCD file containing a clocked signal and decode the signal
fn read_clocked_vcd(r: &mut dyn io::BufRead) -> io::Result<u32> {
   let mut parser = vcd::Parser::new(r);

   // Parse the header and find the wires
   let header = parser.parse_header()?;
   let clock = header.find_var(&["top", "clock"])
      .ok_or_else(|| io::Error::new(InvalidInput, "no wire top.clock"))?.code;
   let data = header.find_var(&["top", "data"])
      .ok_or_else(|| io::Error::new(InvalidInput, "no wire top.data"))?.code;

   // Iterate through the remainder of the file and decode the data
   let mut shift_reg = 0;
   let mut data_val = Value::X;
   let mut clock_val = Value::X;

   for command_result in parser {
     let command = command_result?;
     use vcd::Command::*;
     match command {
       ChangeScalar(i, v) if i == clock => {
         if clock_val == Value::V1 && v == Value::V0 { // falling edge on clock
            let shift_bit = match data_val { Value::V1 => (1 << 31), _ => 0 };
            shift_reg = (shift_reg >> 1) | shift_bit;
         }
         clock_val = v;
       }
       ChangeScalar(i, v) if i == data => {
         data_val = v;
       }
       _ => (),
     }
   }

   Ok(shift_reg)
}

let mut buf = Vec::new();
let data = 0xC0DE1234;
write_clocked_vcd(data, &mut buf).expect("Failed to write");
let value = read_clocked_vcd(&mut &buf[..]).expect("Failed to read");
assert_eq!(value, data);

No runtime deps