Verification
Verification Overview
iDMA uses a SystemVerilog testbench driven by job files that describe transfer sequences. The testbench compares hardware behavior against a byte-accurate golden model. The full job-file testbenches run under Questa or VCS; Verilator is wired for elaboration/lint only. Job files live in jobs/backend_<variant>/.
For documentation quality checks, see Docs Verification Plan.
Testbench Architecture
Key Components
| Component | Location | Description |
|---|---|---|
idma_test package | test/idma_test.sv | Shared test infrastructure: job class, golden model, drivers |
tb_idma_backend_* | test/tpl/tb_idma_backend.sv.tpl (generated) | Per-variant top-level testbench module |
tb_tasks.svh | test/include/tb_tasks.svh | Protocol-specific memory access tasks, job file parser |
Golden Model
The testbench uses a golden model - a software reference implementation that predicts the expected memory state after each DMA transfer. By comparing the hardware’s actual memory writes against the model’s predictions, the testbench detects functional bugs without requiring hand-written expected values.
The idma_model class in idma_test.sv is a byte-addressed memory model that simulates DMA transfers at the byte granularity. It replicates the legalizer’s burst splitting logic (page boundaries, maximum burst lengths) and error handling behavior (continue/abort semantics). After each transfer, the testbench compares the hardware memory state against the model’s expected state to detect mismatches. The golden model uses the max_src_len and max_dst_len fields from the job file to configure burst splitting - these must match the protocol’s actual limits for comparisons to pass. If max_src_len/max_dst_len don’t match the protocol’s actual burst limits, the golden model will split bursts differently than the hardware legalizer, causing false mismatches.
Job File Format
Each job file contains one or more transfer descriptions, concatenated back-to-back. Each transfer is described by the following fields, one per line.
The most important fields are length, src_addr, dst_addr, and the two max_*_len fields (which must match your protocol’s actual burst limits). For a basic AXI test, set both protocols to 0, both max lengths to 256, and both decoupling flags to 0.
Fields
| Line | Format | Field | Description |
|---|---|---|---|
| 1 | %d | length | Transfer length in bytes |
| 2 | 0x%x | src_addr | Source address (hex) |
| 3 | 0x%x | dst_addr | Destination address (hex) |
| 4 | %d | src_protocol | Source protocol enum (0=AXI, 1=OBI, …) |
| 5 | %d | dst_protocol | Destination protocol enum |
| 6 | %d | max_src_len | Max source burst length in beats |
| 7 | %d | max_dst_len | Max destination burst length in beats |
| 8 | %b | aw_decoupled | AW decoupling flag (0 or 1) |
| 9 | %b | rw_decoupled | RW decoupling flag (0 or 1) |
| 10 | %d | num_errors | Number of injected errors (0 for clean transfers) |
| 11+ | %c%c0x%h | error specs | One line per error: r/w + c/a + hex address |
Error spec format: first character is r (read) or w (write), second is c (continue) or a (abort), followed by the error address in hex.
Example
Annotated simple.txt (a single 2-byte transfer with no errors):
2 # length: 2 bytes0x0 # src_addr: 0x0000x3ff # dst_addr: 0x3ff0 # src_protocol: AXI (0)0 # dst_protocol: AXI (0)256 # max_src_len: 256 beats256 # max_dst_len: 256 beats0 # aw_decoupled: no0 # rw_decoupled: no0 # num_errors: 0Annotated error transfer (from error_simple.txt):
32 # length: 32 bytes0x0 # src_addr0x10000 # dst_addr0 # AXI read0 # AXI write256 # max 256 beats256 # max 256 beats0 # aw_decoupled: no0 # rw_decoupled: no3 # 3 injected errors:rc0x4 # read error at 0x4, action: continuerc0x8 # read error at 0x8, action: continuerc0xc # read error at 0xc, action: continueWriting Custom Job Files
To create a custom test, write a text file following the format above. Each field is on its own line, with transfers concatenated back-to-back (no blank lines between them). The max_src_len and max_dst_len fields control burst splitting in the golden model - set to 256 for AXI (matching the protocol maximum) or 1 for single-beat protocols (OBI, INIT, AXI Stream).
Here is an example with two back-to-back transfers in a single job file (64 bytes followed by 128 bytes, no errors):
640x00x1000002562560001280x1000x200000256256100Available Test Suites
Each backend variant has its own set of job files under jobs/backend_<variant>/:
Basic - start here for bring-up and sanity checks:
| Job File | Description |
|---|---|
simple.txt | Minimal transfer (2 bytes), basic sanity check |
small.txt | A few short transfers |
tiny.txt | Very small (sub-word) transfers |
Stress - increasing transfer sizes for throughput and boundary testing:
| Job File | Description |
|---|---|
medium.txt | Mixed lengths, some with AW decoupling |
large.txt | Longer transfers approaching page boundaries |
huge.txt | Large transfers spanning multiple pages |
Pattern - specific address and access patterns:
| Job File | Description |
|---|---|
linear.txt | Sequential address patterns |
mixed.txt | Varied source/destination alignments |
same_dst.txt | Multiple transfers to the same destination |
zero_transfer.txt | Zero-length transfer (tests RejectZeroTransfers) |
Error - error injection and handling:
| Job File | Description |
|---|---|
error_simple.txt | Transfers with injected read/write errors and continue/abort actions |
error_mixed.txt | Complex error scenarios with multiple error types |
For initial bring-up, start with simple.txt on the rw_axi variant - it’s the smallest test on the most common backend.
Compute Testbenches
The on-the-fly compute engines have dedicated self-checking testbenches, checked byte-exact against DPI-C golden models (they are separate from the job-file backend flow):
| Make target | DUT | Checks |
|---|---|---|
idma_sim_tb_idma_otf_transpose | idma_otf_transpose standalone | Transpose engine over StrbWidth and FullDuplex sweeps, golden idma_transpose_dpi.c |
idma_sim_tb_idma_transpose_nd | ND midend -> rw_axi -> axi_sim_mem | Multi-tile transpose end-to-end at DataWidth 32/64 |
idma_sim_tb_idma_transpose_b2b | ND midend -> rw_axi | Two back-to-back transposes to distinct destinations |
idma_sim_tb_idma_mxquant | rw_axi backend, COMPUTE_MXQUANT{,_FP16} | FP32/FP16 -> MXFP8 33 B blocks, golden idma_mxquant_dpi.c; one run crosses a 4K page |
These idma_sim_tb_* targets drive Questa directly (they source compile.tcl and run under vsim), so they are Questa-only; pass the tool binaries through the VSIM / VLOG / VLIB variables:
make idma_sim_tb_idma_mxquant VSIM="questa-2023.4 vsim" \ VLOG="questa-2023.4 vlog" VLIB="questa-2023.4 vlib"Running Simulations
idma.mk wires three simulators. Questa and VCS run the full job-file testbenches; Verilator is wired for elaboration/lint only (it Verilates a chosen top with --cc, not a full behavioral run). Each flow takes its tool binaries as make variables, whose defaults are the bare vsim / vcs / verilator names; the Questa examples below use the questa-2023.4 SEPP wrapper.
Prerequisites
Generate the RTL (and, for Questa/VCS, the compile scripts) first:
make idma_hw_all # Generate RTL into target/rtl/make idma_sim_all # Generate compile.tcl + start.tcl (Questa) and compile.sh (VCS)Compile and Run
Compile with the generated compile.tcl, then run a job file with the +job_file plusarg:
cd target/sim/vsimquesta-2023.4 vsim -c -do "source compile.tcl; quit"questa-2023.4 vsim -c -t 1ps -voptargs=+acc \ +job_file=../../../jobs/backend_rw_axi/simple.txt \ -logfile output.log \ tb_idma_backend_rw_axi \ -do "source start.tcl; run -all"Replace backend_rw_axi and tb_idma_backend_rw_axi with the desired variant. The +job_file plusarg points to the job file describing the test sequence.
Analyze the sources (this generates and runs target/sim/vcs/compile.sh), elaborate a specific testbench into a simv binary, then run it with the same +job_file plusarg:
make idma_vcs_compile VCS=vcs VLOGAN=vloganmake target/sim/vcs/bin/rw_axi.vcs IDMA_VCS_TB=tb_idma_backend_rw_axicd target/sim/vcs./bin/rw_axi.vcs +job_file=../../../jobs/backend_rw_axi/simple.txtIDMA_VCS_TB selects the testbench top; the bin/<name>.vcs output name is yours to choose.
Verilator is wired for elaboration / lint only (--cc), not a full job-file run. It pickles the design with -D VERILATOR and Verilates a chosen top, writing the result to target/sim/verilator/<top>_elab.log:
make target/sim/verilator/idma_backend_synth_rw_axi_elab.log \ IDMA_VLT_TOP=idma_backend_synth_rw_axi VERILATOR=verilatorUse this as a fast structural / lint check; run Questa or VCS for behavioral job-file simulation.
Debugging Failures
If a simulation fails, the testbench prints the first mismatching address and expected vs. actual values. To debug further, open the waveform in the Questa GUI by removing the -c flag from the vsim command.
Check the busy_o flags to identify which subunit stalled:
| Flag | Stuck means |
|---|---|
r_leg_busy | Legalizer can’t split - check transfer parameters |
r_dp_busy | Read channel not getting responses - check memory slave |
buffer_busy | Write channel not draining - check write backpressure |
w_dp_busy | Write channel blocked - check destination memory |
eh_fsm_busy | Error handler waiting for software action |
Key signals to trace in waveforms: idma_req_i/req_valid_i/req_ready_o (request handshake), idma_rsp_o/rsp_valid_o (response), busy_o (subunit status), and the AXI AR/AW/R/W/B channels on the bus interface.
Source Files
test/idma_test.sv- Test package (job class, golden model, drivers)test/tpl/tb_idma_backend.sv.tpl- Testbench template (generates per-variant TBs)test/include/tb_tasks.svh- Memory tasks, job parser (read_jobs)jobs/- Job files organized by backend variant