Test Data Files (Python ↔ Vitis)
To validate a generated kernel, the Vitis C-simulation testbench needs the same test vectors the Python
model uses, and its outputs must be comparable to the Python golden. Waveflow bridges the two with a shared
on-disk format: a little-endian uint32 binary file.
A test-data file is just a schema (or array) serialized into 32-bit words — the exact packing from
Serialization at word_bw = 32, written as little-endian bytes. Because the Python
writer and the C++ reader use that one packing rule, a value round-trips bit-for-bit: Python generates
the inputs, the Vitis testbench reads them, runs the kernel, writes its responses, and Python reads those
back to compare against the golden.
Python side
A schema instance carries the file methods directly:
hdr = PolyCmdHdr(); hdr.tx_id = 42; hdr.nsamp = 1024
hdr.write_uint32_file("data/cmd_hdr.bin") # serialize -> LE uint32 words on disk
rx = PolyCmdHdr().read_uint32_file("data/cmd_hdr.bin") # read back; rx == hdr, bit-for-bit
inst.write_uint32_file(file_path) -> Path— serializeinstto 32-bit words and write them.inst.read_uint32_file(file_path) -> <Schema>— read the words back and deserialize (returns the instance).
A raw array uses the free functions in waveflow.hw.arrayutils — you supply the element type:
from waveflow.hw.arrayutils import write_uint32_file, read_uint32_file
write_uint32_file(samples, elem_type=Float32, file_path="data/samp_in.bin", nwrite=1024)
samp = read_uint32_file("data/samp_in.bin", elem_type=Float32, shape=1024)
write_uint32_file(arr, elem_type, file_path, write_slice=None, nwrite=None) -> Path— packarr(optionally just the firstnwriteentries, or a NumPy-stylewrite_slice) and write it.read_uint32_file(file_path, elem_type, shape) -> array— read the words and unpack toshape.
Vitis testbench side
The C++ testbench reads and writes the same files through streamutils helpers in the generated
streamutils_tb.h (emitted by StreamUtilsStep; see Code Generation). These are
testbench-only — host file I/O, not synthesizable:
#include "include/poly_cmd_hdr.h"
#include "include/streamutils_tb.h"
PolyCmdHdr hdr;
streamutils::read_uint32_file(hdr, "data/cmd_hdr.bin"); // file -> schema (calls hdr.read_array<32>)
// ... run the kernel ...
streamutils::write_uint32_file(resp, "results/resp.bin"); // schema -> file
streamutils::read_uint32_file(value, file_path)/write_uint32_file(value, file_path)— a fixed-size schema or array.streamutils::read_uint32_file_len(value, file_path, n0)/write_uint32_file_len(value, file_path, n0)— for a schema whose array length is a runtimen0.
For a raw array element type, the generated <elem>_array_utils_tb.h (from ArrayUtilsStep) carries the
array-level pair:
#include "include/float32_array_utils_tb.h"
namespace au = float32_array_utils;
au::value_type samp[1024];
au::read_uint32_file_array(samp, "data/samp_in.bin", 1024); // file -> raw array
au::write_uint32_file_array(out, "results/out.bin", 1024); // raw array -> file
The round-trip in a build flow
The two sides meet in a build: a Python step writes the input vectors (so the same bytes feed both the SimPy model and Vitis), the C-sim testbench consumes them and emits responses, and a verify step reads both back and asserts they match the golden bit-for-bit. The poly accelerator’s Python simulation step is the worked example of the Python side.
See also
- Serialization — the 32-bit-word packing these files store.
- Code Generation —
StreamUtilsStep(emitsstreamutils_tb.h) and the_tb.hcompanions. - Python Simulation Pattern —
write_uint32_file/read_uint32_filein a realBuildStep.