wdbcvt: reading Vivado’s undocumented .wdb waveform files
wdbcvt is an open-source converter that reads Vivado’s .wdb waveform
files and converts them to the open-source FST format.
Vivado’s xsim simulator writes simulation waveforms to a proprietary binary
format that external tools cannot open.
wdbcvt parses these .wdb files and produces standard FST waveforms that open
directly in GTKWave and Surfer.
The tool converts real simulations, including a dual-core RISC-V SoC with
5,696 signals and 18,875,466 value changes in 71 seconds.
Below are downloads, usage instructions, and sample waveform images.
Author’s note. What you see here is a successful and completely automated discovery of the internals of the Vivado xsim WDB format. As a result, there’s a bunch of documentation, but also a small program which can read WDB and output FST that you can then feed into Surfer, or GTKWave. I build binaries for several popular OS environments, but you can download the source and build it for whatever you need.
Normally there would have been a published reference implementation from AMD, but that’s not a world we live in. Well, OK.
While it has been worked out in full by AI, it actually started from some work I started manually on this quite a while ago. In retrospect, I was right to call it too much work for a single person to do. With AI help, however, and fairly rigid guardrails I already had in place, it was easy to just tell the machine to go figure out the format. I do not think that it would have been successful otherwise. It was way too much work for a single human, who needs to eat, sleep, and also has a life.
As a result, here you can download a small program, which converts all the WDBs I threw at it into the FST format used by open source tooling for waveform displays.
The example set contains more than 1000 (yes, a thousand!) sample simulations, plus 4 from actual and somewhat complex open source projects. So the code is not something I just threw over the wall. It is an actual software package, with tests run on each change and a well-defined release process, all powered by a well-defined build system. And it can handle realistic workloads. I hope that sets the standard for others’ future work too.
The rest of the text was generated automatically because I thought it was important to present the result. It is also very weirdly worded in what has since become known as “Claudish” language. It’s not overly dense in this case and it’s readable to humans, but be forewarned. I tried to apply some known instructions to improve that, but it didn’t seem to have an effect.
Over to you, Claude.
This is an automated account. An AI agent decoded the file format, implemented the tool, ran the measurements, and wrote this document and the report linked below under human supervision. Nothing here serves as an official specification. All reported numbers come directly from the project test suite. Read the report and
docs/provenance.mdin the repository for verification details and usage limits.
Why it exists
A Vivado simulation run can write two waveform outputs: sim.wdb and
sim.vcd.
sim.wdb stores the complete simulation history recorded by the simulator.
In contrast, sim.vcd drops significant parts of the design.
Measurements across a corpus of small VHDL designs show the extent of this
data loss.
Vivado’s VCD export omits eight of the fifteen VHDL types in the test corpus.
It generates no $var declaration and records no value changes for boolean,
integer, real, time, character, enumerations, records, and arrays.
Only std_ulogic, bit, and bit-vector types appear in the VCD file.
Converting through VCD therefore discards non-vector signals silently because
standard VCD lacks representations for them.
The .wdb file preserves those omitted signals.
wdbcvt decodes the binary .wdb format and emits FST waveforms.
FST natively supports integers, reals, times, and strings, and it compresses
waveform data efficiently.
On one test design, the converted FST file is 45 times smaller than the
corresponding VCD file while retaining all signals.
On another test design, the FST file is 25 times smaller.
Because AMD does not publish a format specification, the decoder rules were
verified against 1,061 test cases.
Each test case pairs a simulation output with a ground-truth reference derived
from HDL source files.
An automated agent performed the format analysis under human supervision, as
documented in the project README, docs/provenance.md, and the CLI help.
These decoded structures represent empirical findings rather than a formal
specification.
For mission-critical verification where a conversion error would cause costly
delays, inspect the original waveform in Vivado.
Downloads
These artifacts come from nightly build commit 990b104 (2026-09-05).
The binary runs on Linux x86-64 as a standalone static executable.
- wdbcvt-linux-amd64 (3.8 MB): the standalone converter binary.
- wdbcvt-source.tar.gz (1.0 MB): the source archive, including corpus
generators, test cases, and format documentation under
docs/. You can also inspect the source on the GitHub mirror. - wdbcvt-report.pdf (573 kB): a seven-page technical report describing
the reverse-engineering methodology, format discoveries, test results, and
verification bounds.
The report is compiled from
//docs/latexand attached to each release. - counter-sim.wdb (14 kB): a sample simulation database for testing.
- counter-sim.vcd (5.6 kB): the partial VCD file generated by Vivado from the same counter simulation run.
- counter-sim.fst (1.6 kB): the converted FST file produced by
wdbcvt from
counter-sim.wdb. - SHA256SUMS: checksums for all release files.
Each download link redirects through an access counter before serving the file.
Example usage
The tool provides three operating modes. Invoked without flags, wdbcvt inspects the input file and displays header metadata, block entropy, and ASCII strings:
chmod +x wdbcvt-linux-amd64
./wdbcvt-linux-amd64 -in counter-sim.wdb
file: counter-sim.wdb
size: 14439 bytes
mean entropy: 5.206 bits/byte
header:
00000000 58 69 6c 69 6e 78 20 57 41 56 45 20 44 41 54 41 |Xilinx WAVE DATA|
00000010 42 41 53 45 20 30 31 00 58 69 6c 69 6e 78 20 53 |BASE 01.Xilinx S|
The -dump flag decodes all identified database structures to text.
This output includes the type table, the scope hierarchy, signal declarations,
and timestamped signal value transitions:
./wdbcvt-linux-amd64 -dump -in counter-sim.wdb
scopes (9):
[1] tb parent 0 children 3+3 first object 0 unit 1 file 2 line 22
[2] counter_types parent 0 children -1+0 first object 5 unit 2 file 9 line 22
[3] tb.dut parent 1 children 6+3 first object 6 unit 3 file 2 line 41
...
[0] signal ctl : [0] ctl_t, 8 bytes, file 2 line 29, class 0
[1] signal stat : [2] stat_t, 16 bytes, file 2 line 34, class 0 (7 downto 0)
[2] signal running : [5] BOOLEAN, 1 bytes, file 2 line 36, class 0
...
t=180000 tb.ctl = (clk => 0, reset => 0, enable => 1)
t=185000 tb.ctl = (clk => 1, reset => 0, enable => 1)
t=190000 tb.ctl = (clk => 0, reset => 0, enable => 1)
Timestamps use the time unit specified in the database file (picoseconds in
this example).
The dumper formats records with their named member fields.
Vivado’s VCD export for this simulation contains neither running nor
tb.ctl.
The -fst flag converts the database directly into an FST waveform file:
./wdbcvt-linux-amd64 -in counter-sim.wdb -fst counter.fst
surfer counter.fst # or: gtkwave counter.fst
The pre-converted counter-sim.fst file in the downloads section was generated
from counter-sim.wdb using this command.
What it looks like
Here is the counter design converted from .wdb and opened in Surfer:

The waveform display illustrates signals preserved by the converter.
full_scale is an integer constant, period is a time value with physical
units, and running is a boolean signal.
Vivado’s VCD output omits all three signals.
Below those signals, the VHDL record tb.ctl appears split into individual
member fields: tb.ctl.clk, tb.ctl.enable, and tb.ctl.reset.
FST does not define a composite record type, so flattening records into
individual signals allows waveform viewers to display and search each field.
The converter handles large simulation databases with the same fidelity. The second example shows NEORV32, a dual-core RISC-V processor. wdbcvt converted this 24 MB database (containing 18,875,466 value changes across 5,696 signals in 4,832 scopes) into a 690 kB FST file. The screenshot displays the core 0 register file during instruction execution:

Designs it is tested on
Besides the 1,061 synthetic test cases, the test suite verifies conversion on seven open-source designs. The test harness compares the converted signals against Vivado’s VCD output for every run. Table I of the technical report summarizes the test suite designs:
| Design | Language | Objects | Value changes |
|---|---|---|---|
counter (in the repository) |
VHDL | 14 | 2,388 |
uart (in the repository) |
VHDL | 59 | 16,718 |
| SERV | Verilog | 968 | 9,233,899 |
| Potato | VHDL | 557 | 7,441 |
| PicoRV32 | Verilog | 280 | 40,589 |
| NEORV32 | VHDL | 5,696 | 18,875,466 |
| Ibex | SystemVerilog | 3,287 | 73,835 |
Testing against these complex designs uncovered several edge cases in the binary format. The SERV simulation revealed how Vivado encodes bit offsets for Verilog ports connected to sub-bus slices. The Potato processor simulation revealed a second value-chunking threshold used for large memories, resolving an addressing error in 32,768-byte memory arrays. The NEORV32 simulation established that value chunk maps attach to the signal handle rather than the scope entry, a pattern present on 39 of that design’s 5,696 objects. The Ibex simulation clarified enumeration widths: the format stores the bit width in the range field following the literal table, rather than in the base type record. Finally, simulating PicoRV32 confirmed that Verilog designs conformed to previously observed format rules without requiring parser adjustments.