Lab 5.1 — Streaming DSP Interface and Testbench¶
Goal¶
Define a simple streaming interface for FPGA DSP blocks and write a testbench strategy that can validate latency, reset behaviour and sample alignment.
This lab prepares the interface style used later for FIR, mixer, decimator and full SDR processing chains.
Executable HDL package¶
| File | Purpose |
|---|---|
blocks/block_05_fpga_hdl_flow/rtl/iq_passthrough.v |
valid-only IQ streaming pass-through RTL block |
blocks/block_05_fpga_hdl_flow/tb/tb_iq_passthrough.v |
self-checking Verilog testbench |
blocks/block_05_fpga_hdl_flow/tb/iq_passthrough_vectors.txt |
documented input vector format |
blocks/block_05_fpga_hdl_flow/README_lab_5_1_hdl_sim.md |
local simulation instructions |
Run from the repository root:
iverilog -g2012 \
-o blocks/block_05_fpga_hdl_flow/tb/tb_iq_passthrough.out \
blocks/block_05_fpga_hdl_flow/rtl/iq_passthrough.v \
blocks/block_05_fpga_hdl_flow/tb/tb_iq_passthrough.v
vvp blocks/block_05_fpga_hdl_flow/tb/tb_iq_passthrough.out
Expected result:
PASS: iq_passthrough test completed without errors
The GitHub Actions workflow .github/workflows/block5_hdl.yml runs this simulation automatically when Block 5 HDL files change.
Engineering question¶
How do we wrap a fixed-point DSP algorithm into a deterministic clocked hardware block that can be tested sample-by-sample?
Minimal valid-only interface¶
For the first HDL labs, use a simple valid-only streaming interface:
module dsp_block #(
parameter integer W = 16
)(
input wire clk,
input wire rst,
input wire in_valid,
input wire signed [W-1:0] in_i,
input wire signed [W-1:0] in_q,
output reg out_valid,
output reg signed [W-1:0] out_i,
output reg signed [W-1:0] out_q
);
This is not full AXI-Stream yet, but it teaches the most important ideas:
- samples are accepted on clock edges;
in_validmarks meaningful input samples;- output latency must be documented;
out_validmust align with output samples;- reset behaviour must be deterministic.
Timing model¶
sequenceDiagram
participant TB as Testbench
participant DUT as DSP block
TB->>DUT: rst = 1
TB->>DUT: rst = 0
TB->>DUT: in_valid + input sample 0
TB->>DUT: in_valid + input sample 1
DUT-->>TB: out_valid + output sample after latency L
Example pass-through block¶
The executable implementation is stored in rtl/iq_passthrough.v.
module iq_passthrough #(
parameter integer W = 16
)(
input wire clk,
input wire rst,
input wire in_valid,
input wire signed [W-1:0] in_i,
input wire signed [W-1:0] in_q,
output reg out_valid,
output reg signed [W-1:0] out_i,
output reg signed [W-1:0] out_q
);
always @(posedge clk) begin
if (rst) begin
out_valid <= 1'b0;
out_i <= '0;
out_q <= '0;
end else begin
out_valid <= in_valid;
if (in_valid) begin
out_i <= in_i;
out_q <= in_q;
end
end
end
endmodule
Testbench checklist¶
The testbench should verify:
| Check | Expected behaviour |
|---|---|
| Reset | output valid is zero and outputs are known |
| Valid propagation | output valid appears after expected latency |
| Data alignment | output sample corresponds to correct input sample |
| Idle cycles | block does not generate fake valid samples |
| Signed values | negative I/Q values are preserved correctly |
| Saturation cases | clipping behaviour is deterministic when applicable |
Reference-vector approach¶
Use text files for deterministic test vectors:
input_vectors.txt
expected_vectors.txt
Suggested format:
valid i q
1 32767 0
1 0 32767
1 -32768 0
0 0 0
1 1234 -5678
Testbench structure¶
flowchart LR
VEC[Input vectors] --> TB[Testbench driver]
TB --> DUT[RTL block]
DUT --> MON[Output monitor]
EXP[Expected vectors] --> CMP[Comparator]
MON --> CMP
CMP --> PASS[Pass / fail]
What to expect¶
PASS: iq_passthrough test completed without errors
The bench drives 8 vectors, two of them with in_valid = 0, and includes the full-scale values +32767 and -32768. After reset it checks every clock: out_valid must equal in_valid delayed by one cycle, and out_i/out_q must equal the delayed input whenever the delayed valid is 1. Data is not compared on idle cycles: the contract says it is "don't care" there.
The course runner generates the vectors, compiles, simulates and turns any FAIL line or non-zero simulator exit into an error:
python tools/run_block5_hdl_smoke.py --test tb_iq_passthrough
Exercises¶
Each exercise below is a deliberate one-line RTL mutation. Make it, run the bench, read the messages, then restore the file (git checkout -- <file>). The quoted outputs were observed with Icarus Verilog 12.0.
- Break the valid contract: replace
out_valid <= in_valid;without_valid <= 1'b1;. The bench reportsERROR at 45000: out_valid=1 expected=0on every idle cycle and ends withFAIL: iq_passthrough test completed with 5 errors. - Remove the
if (in_valid)around the data registers soout_i/out_qload on every clock. The bench still passes. Explain why this is correct under the valid-only contract, and name one situation where holding the last valid sample is still worth the clock-enable (a downstream block that ignoresvalid, a waveform that is easier to read, or switching power). - Add a second register stage (latency 2). Before touching the bench, predict which check fires first; then change the reference model's delay so the bench passes again. Record the new latency in the report.
Answers (checked against the RTL and the bench)
- The observed output is in the exercise: a valid flag that is always high announces garbage as data, and only a check of
validon idle cycles catches it. - Under the valid-only contract a consumer looks at the data only when
validis high, so what the data registers hold in between is irrelevant and the bench rightly passes. The clock enable still earns its place when something downstream does not honourvalid(a DAC that samples every clock, an ILA trace read by a human), and it saves switching power because the registers do not toggle on idle cycles. - The first check to fire is the one on the output timing: with latency 2 the first valid output appears one clock later than the reference model expects, so the first comparison fails on valid: the run prints
ERROR at 55000: out_valid=0 expected=1, followed by the data one sample late (out=(0,0) expected=(32767,0)). Delaying the reference model by one more clock makes the bench pass again; the report records latency 2.
Minimal report¶
The lab report should contain:
- interface table;
- reset description;
- latency definition;
- timing diagram;
- test-vector format;
- pass/fail criteria;
- notes on how the interface will evolve into AXI-Stream.
Engineering conclusion template¶
The selected interface uses ____-bit signed I/Q samples and a valid-only protocol.
The measured latency is ____ clock cycles. The testbench verifies reset, valid
alignment and signed sample propagation. The next step is to replace the
pass-through datapath with FIR or mixer logic.