Crate vcd_ng

source ·
Expand description

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

Example

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

/// Write out a clocked signal to a VCD file
fn write_clocked_vcd(shift_reg: u32, w: &mut impl io::Write) -> io::Result<()> {
  let mut writer = vcd_ng::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 impl io::Read) -> io::Result<u32> {
   let mut parser = vcd_ng::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 {
     use vcd_ng::Command::*;
     let command = command_result?;
     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);

Structs

A value change token.
Fast token stream of timestamp and value changes. See the module-level documentation for details.
Structure containing the data from the header of a VCD file.
An ID used within the file to refer to a particular variable.
Error wrapping a static string message explaining why parsing failed.
VCD parser. Wraps an io::Read and acts as an iterator of Commands.
Information on a VCD scope as represented by a $scope command and its children.
Information on a VCD variable as represented by a $var command.
A fixed box of Values with bit compression. It is more efficient than Vec<Value>.
An iterator over bits in a VecValue.
Struct wrapping an io::Write with methods for writing VCD commands and data.

Enums

An element in a VCD file.
An enum of tokens that fast flow supports.
Index of a VCD variable reference, either a bit select index [i] or a range index [msb:lsb]
An item in a scope – either a child scope or a variable.
A type of scope, as used in the $scope command.
A simulation command type, used in Command::Begin and Command::End.
A unit of time for the $timescale command.
A four-valued logic scalar value.
A type of variable, as used in the $var command.