Some time ago, we discussed how you can build a scope within your own logic, and even how to get the contents of that scope back out of your FPGA. Today, we’ll finish the discussion by describing what it takes to turn that data into a Value-Change Dump (VCD) file.

The VCD file format structure is a common data format that can be used to store digital logic traces, so that you can then later view it with a waveform viewing tool. GTKWave ingests VCD files easily. Indeed, two of my favorite tools, Verilator and the wishbone scope, will both output VCD traces for this purpose.

One of the really nice parts of this standard is that it is a text-based standard. This means you can view, review, and even edit a VCD file if you really need to with your favorite text editor. While I don’t recommend it, I have needed to load VCD files into gvim from time to time to figure out what’s wrong (or right) with the file.

The standard

The actual standard for the VCD format is part of The IEEE Standard for (the) Verilog Hardware Description Language. My copy is IEEE Std 1364-2005, where the Value Change Dump (VCD) file format is discussed starting on page 325.

While this article will discuss some basic components of the standard, it will by no means by exhaustive. For further information, you’ll want to look up the standard itself.

The VCD Header

The first part of any VCD file is a header. There are two primary components to the VCD file header: basic file meta-data, and variable declarations. White space is used to separate commands, and to make the file more human legible.

The basic file meta-data identifies the program, or program version, that created the VCD file, as well as the date the trace was created on and the timescale it uses. As an example, a recent Verilator VCD output file from the wbuart32 distribution started with the following text:

$version Generated by VerilatedVcd $end
$date Wed Jun  7 11:35:32 2017
 $end
$timescale 1ns $end

The first line identifies the tool that created the VCD file and what it’s version is. This line begins with $version, and ends with $end.

The second line identifies when this file was created. This field begins with $date and ends with $end.

The third line identifies the timescale. The timescale includes a time number (1, 10, or 100) followed by a unit (s, ms, us, ns, ps, or fs). Time integers within the file may then be multiplied by this unit to turn them into engineering units in a display. I have typically used a time scale of 1ns, although I suspect autofpga files will use more precise time scales so as to be able to handle multiple dissimilar clocks.

This ends the necessary file meta-data, but not the end of the header yet.

The next section of the header declares your variables. Variables are defined within a hierarchical scope. Hence, you’ll seen sections of variables defined by a $scope line and ending with an $upscope line. The first line ($scope) defines the name of the scope the variables are found within. For example, variables at the top level of a Verilator produced VCD file will have a top-level scope of TOP.

 $scope module TOP $end

 .... (variables defined here)

 $upscope $end

The $scope line has two words within it. The first is the type of scope being referenced. In this case, it references a module. The second is the name of the scope, TOP, being Verilator’s term for the top level of a design.

Variables found within a module foo within TOP would be defined with a similar module section, only that this section would be found within the TOP section.

 $scope module TOP $end
   ...
   $scope module foo $end
   ... (variables of the foo module within TOP would be defined here)
   $upscope $end
 $upscope $end

Variables themselves are declared on lines between $var and $end tags. Four tokens are used, between these two flags, to define any variable, as shown below:

$var vary_type size identifier_code reference $end

The first token, var_type specifies the type of variable. The standard allows many different variable types, although I’ve only ever used wire. Other types that might be useful include parameter, and reg, although the standard identifies many more types.

The second token, size specifies the number of bits this value will contain.

The third token is perhaps the most cryptic, although it need not be. This is the identifer_code assigned to this particular variable. This is a printable character, or string of printable characters, used to identify the variable during the data section of the file. We’ll come back to this in a moment.

The last part of the $var line is the reference. This is the variable name the user has given to the trace. If the variable had a width, it would then be followed by something like [MSB:LSB]. For example, a four bit trace i_button could have the reference of i_button[3:0].

One line that GTKWave understand’s that I haven’t found in the specification is a $timezero line. This line has three items in it, $timezero, the internal time where the zero occurs, and the $end tag. wbscope uses this tag to place the trigger at time zero.

Finally, the header section is ended by an $enddefinitions line:

$var vary_type size identifier_code reference $end

If you are confused about these values and terms, consider looking through a VCD file from a reader that works. The files just aren’t that hard to understand.

Data Section

From the end of the header to the end of the file is the data section. This section contains two types of lines: simulation time lines and value change lines.

Simulation time lines start with a # and a time value. That’s it. For example,

#295

specifies that the following changes happen at 295 time units. Exactly how much time this references depends upon the $timescale command in the header. Further, the simulation time is an unsigned number. Negative numbers are not allowed, and will really mess up your VCD file. (I know … I’ve tried.)

Value change lines contain the value the variable is taking on, followed by the identifier code for the variable that was assigned in the header. These lines are only necessary any time the value in question changes.

For single bit values, the value in a value change line consists of a 0, 1, x, or z followed by the identifier code that was assigned to this value in the header. For multibit values, a b precedes all of the bits. If not all of the bits are given, then the value is left-extended in an unsigned fashion.

As an example, if J is defined in the header to reference i_clk, then

0J

specifies that the clock is now set to zero. Likewise if # is assigned to the 8-bit data value i_data[7:0], then this value can be set with

b01000101#

Conclusion

While most FPGA programmers will not need to read or write VCD files, anyone wanting to build their own digital logic scope will find it valuable to know how to create VCD files.