Files
mlkem-sync/.trellis/spec/rtl/xsim-tb-conventions.md
FallenSigh 52c625b3ef docs(spec): add XSIM testbench conventions to RTL spec
Document Vivado XSIM Verilog testbench conventions:
- File naming, directory structure, TB template
- Clock/reset patterns, valid/ready protocol
- Vector format for
- xsim_run.tcl conventions with -include_dirs requirement
- gen_vectors.py conventions (stdlib only, bit ordering)
- Common mistakes checklist
2026-06-25 20:48:44 +08:00

182 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vivado XSIM Verilog Testbench Conventions
## Purpose
Conventions for writing Verilog testbenches targeting Vivado XSIM simulator.
These testbenches co-exist with Verilator C++ testbenches (`.cpp`) — both go in the same `TB/` directory.
## Directory Structure
```
sync_rtl/<module>/
├── <module>.v # RTL source
├── TB/
│ ├── tb_<module>.cpp # Verilator C++ testbench (optional)
│ ├── tb_<module>_xsim.v # XSIM Verilog testbench
│ ├── gen_vectors.py # Python vector generator (stdlib only)
│ ├── xsim_run.tcl # Vivado compile+elaborate+simulate script
│ └── vectors/
│ └── <module>_input.hex # Test input vectors for $readmemh
```
## TB File Template
```verilog
// tb_<module>_xsim.v - Vivado XSIM testbench for <module>
// Usage:
// xvlog -sv <deps> <module>.v tb_<module>_xsim.v
// xelab tb_<module>_xsim -s <snapshot>
// xsim <snapshot> -R
`timescale 1ns / 1ps
module tb_<module>_xsim;
parameter VECTOR_FILE = "sync_rtl/<module>/TB/vectors/<module>_input.hex";
parameter TIMEOUT_CYCLES = 10000;
// DUT signals (reg for inputs, wire for outputs)
reg clk, rst_n;
// ... module-specific ports ...
// DUT instantiation
<module> u_dut (
.clk(clk), .rst_n(rst_n),
// ...
);
// Clock: 100 MHz (10 ns period)
initial clk = 1'b0;
always #5 clk = ~clk;
// Vector memory (width depends on module)
reg [W-1:0] vector_mem [0:MAX_VECTORS-1];
integer pass_count, fail_count;
initial begin
// Load vectors
$readmemh(VECTOR_FILE, vector_mem);
// Reset: rst_n low 3 cycles
rst_n <= 1'b0;
repeat (3) @(posedge clk);
rst_n <= 1'b1;
@(posedge clk);
// Process each vector: drive → wait valid_o → capture → verify
for (idx = 0; idx < vec_count; idx = idx + 1) begin
// Drive DUT inputs (use <= for reg drives)
// ...
// Wait for valid_o with timeout
cycle_count = 0;
while (!valid_o && cycle_count < TIMEOUT_CYCLES) begin
@(posedge clk);
cycle_count = cycle_count + 1;
end
if (cycle_count >= TIMEOUT_CYCLES) begin
$display("ERROR: Timeout on vector %0d", idx);
fail_count = fail_count + 1;
end else begin
// Capture output, verify, write result
pass_count = pass_count + 1;
end
end
$display("PASS: %0d FAIL: %0d", pass_count, fail_count);
$finish;
end
// Timeout watchdog
initial begin
#(TIMEOUT_CYCLES * 10 * 100);
$display("FATAL: Global timeout");
$finish;
end
endmodule
```
## Key Rules
### Clock
- Always: `initial clk = 1'b0; always #5 clk = ~clk;` (100 MHz, 10 ns period)
### Reset
- Active-low (`rst_n`), held low for 3 cycles minimum
- Pattern: `rst_n <= 1'b0; repeat (3) @(posedge clk); rst_n <= 1'b1; @(posedge clk);`
### DUT Drive Protocol
- Use **non-blocking assignment** (`<=`) for all DUT input drives in `initial` blocks
- Use **blocking assignment** (`=`) for local variable initialization only
- Follow valid/ready handshake: assert `valid_i`, wait `ready_o`, then de-assert
### Timeout Watchdog
- Every TB MUST have a global timeout watchdog initial block
- Pattern: `#(TIMEOUT_CYCLES * 10 * 100);` (gives TIMEOUT_CYCLES × 1000ns margin)
- Module-specific per-vector timeouts also required (using `cycle_count < TIMEOUT_CYCLES` loop)
### Pass/Fail Tracking
- Use `integer pass_count, fail_count;`
- Increment on each vector completion or timeout
- Print summary with `$display("PASS: %0d FAIL: %0d", pass_count, fail_count)` before `$finish`
## Vector Format (`$readmemh`)
Vectors are packed as single hex numbers per line:
```
// Simple module (e.g., mod_add, 24-bit vectors)
// Hex chars = ceil(W/4)
000000
0640c8
d00d00
```
```
// Complex module (e.g., ntt_core, 3076-bit vectors)
// Hex chars = 769
00000000...0000
10000000...0001
```
Width `W` must evenly contain all packed fields. Use padding bits to align to hex-char boundaries (multiples of 4 bits).
## xsim_run.tcl
```tcl
# Compile RTL dependencies (order matters for submodule hierarchy)
xvlog -sv -include_dirs . <rtl_dir>/<submodule>.v
xvlog -sv -include_dirs . <rtl_dir>/<dut>.v
# Compile testbench
xvlog -sv <tb_dir>/tb_<module>_xsim.v
# Elaborate
xelab tb_<module>_xsim -s <snapshot>
# Run
xsim <snapshot> -R
```
### CRITICAL: Include Directories
- If ANY compiled file uses `` `include "sync_rtl/common/defines.vh" `` (or any relative include), add `-include_dirs .` to ALL `xvlog` invocations in that TCL
- This ensures Vivado resolves paths relative to the project root
## gen_vectors.py
- **Stdlib only**: Use only Python standard library modules (`hashlib`, `os`, `sys`, `random`, `math`)
- **Output**: Write hex vectors to `vectors/<module>_input.hex`, one packed hex number per line
- **Self-contained**: Should be runnable standalone (`python3 gen_vectors.py`)
- **Bit ordering**: Match RTL FIPS 202 bit ordering — seed[0] = first bit into sponge. For Python hashlib, this means `bytes.fromhex(seed_hex)[::-1]` (reverse byte order)
## Common Mistakes
1. **Forgetting `-include_dirs .`**: DUTs using `` `include "sync_rtl/common/defines.vh" `` will fail compilation if the project root is not in the include path
2. **Using `=` for DUT drives**: All DUT input assignments in `initial` blocks must use `<=`, not `=`, to avoid race conditions
3. **Missing timeout watchdog**: If the DUT deadlocks, the simulation will hang forever without a watchdog
4. **Bit order mismatch**: Python hashlib and Verilog RTL may have different bit ordering for SHA3/SHAKE operations — verify with known test vectors
5. **rush_n polarity**: The reset is active-low (`rst_n`), not active-high — held to 0 for reset, 1 for normal operation