Skip to content

File-driven testbenches

File-driven verification separates test data from VHDL. It is useful when vectors come from Python, MATLAB, a spreadsheet export, a protocol trace, or a golden algorithm.

Vector file

# a b cin expected
0  0  0  0
3  4  1  8
7  8  1  16
15 15 1  31

Keep the first format simple: whitespace-separated decimal integers with comment lines.

Reading with TextIO

library std;
use std.textio.all;

stimulus : process
  file vectors       : text open read_mode is VECTOR_FILE;
  variable row       : line;
  variable a_value   : natural;
  variable b_value   : natural;
  variable cin_value : natural;
  variable expected  : natural;
begin
  while not endfile(vectors) loop
    readline(vectors, row);
    if row.all'length > 0 and row.all(row.all'left) /= '#' then
      read(row, a_value);
      read(row, b_value);
      read(row, cin_value);
      read(row, expected);

      -- Drive, wait, calculate actual, assert.
    end if;
  end loop;
  finish;
end process;

Use a string generic for the path:

entity tb_file is
  generic (
    VECTOR_FILE : string := "vectors.txt"
  );
end entity;

The automation script can pass an absolute file path, making the test independent of the simulator working directory.

Robust file-format rules

Document:

  • Radix: decimal, hexadecimal, or binary.
  • Signedness.
  • Field order.
  • Units.
  • Whether values are input or expected output.
  • How comments and blank lines work.
  • Version of the vector schema.
  • Expected number of rows.

Fail when a required row cannot be parsed. Silently skipping malformed vectors can create false passes.

Generate vectors with Python

Example generator for the adder:

from pathlib import Path

output = Path("tb/vectors-generated.txt")

with output.open("w", encoding="utf-8") as stream:
    stream.write("# a b cin expected\n")
    for a in range(16):
        for b in range(16):
            for cin in (0, 1):
                stream.write(f"{a} {b} {cin} {a + b + cin}\n")

The expected value comes from Python integer arithmetic, independently of the VHDL implementation.

Floating-point scientific models

An FPGA design often uses fixed-point while a Python reference uses floating-point. Define the conversion exactly:

  1. Scale: integer = round(real × 2^fraction_bits).
  2. Rounding mode: nearest, floor, truncate, or convergent.
  3. Overflow behavior: wrap or saturate.
  4. Signed representation: two's complement.
  5. Acceptable error tolerance in least significant bits.

Compare using a tolerance when the specification permits:

error_value := abs(actual_integer - expected_integer);
assert error_value <= MAX_ERROR_LSB
  report "Result exceeds allowed error"
  severity failure;

Binary files

Text is easiest to inspect and version-control. Binary files are useful for large datasets but require explicit endianness, packing, and tool-portability rules. Start with text unless performance becomes a measured problem.

Golden vectors and source control

Store small, meaningful vector sets in Git. For large generated datasets, store:

  • Generator script.
  • Seed.
  • Model version.
  • Configuration.
  • Checksum if reproducibility matters.

Avoid checking in huge generated files when they can be recreated deterministically.

Common pitfalls

  • Relative path interpreted from the simulator build directory.
  • Windows backslashes misread by another tool.
  • Reading signed data into natural.
  • Comparing before signal updates settle.
  • Forgetting to count executed vectors.
  • Passing because the file was empty.

At the end, assert the number of tests:

assert tests > 0
  report "No vectors were executed"
  severity failure;