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 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:
The first line identifies the tool that created the
file and what it’s version is. This line begins with
$version, and ends
The second line identifies when this file was created. This field begins
$date and ends with
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
$scope) defines the name of the scope the variables are found within.
For example, variables at the top level of a
file will have a top-level scope of TOP.
$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,
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
Variables themselves are declared on lines between
Four tokens are used, between these two flags, to define any variable,
as shown below:
The first token, var_type specifies the type of variable. The standard
allows many different variable types, although I’ve only ever used
Other types that might be useful include
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
i_button could have the reference of
One line that
understand’s that I haven’t found in the specification is a
line. This line has three items in it,
$timezero, the internal time
where the zero occurs, and the
wbscope uses this tag to place the
trigger at time zero.
Finally, the header section is ended by an
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.
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.
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
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
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
As an example, if
J is defined in the header to reference
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
And the vision of all is become unto you as the words of a book that is sealed, which men deliver to one that is learned, saying, Read this, I pray thee: and he saith, I cannot; for it is sealed (Is 29:11)