Перейти к содержанию

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_valid marks meaningful input samples;
  • output latency must be documented;
  • out_valid must 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.

  1. Break the valid contract: replace out_valid <= in_valid; with out_valid <= 1'b1;. The bench reports ERROR at 45000: out_valid=1 expected=0 on every idle cycle and ends with FAIL: iq_passthrough test completed with 5 errors.
  2. Remove the if (in_valid) around the data registers so out_i/out_q load 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 ignores valid, a waveform that is easier to read, or switching power).
  3. 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)
  1. The observed output is in the exercise: a valid flag that is always high announces garbage as data, and only a check of valid on idle cycles catches it.
  2. Under the valid-only contract a consumer looks at the data only when valid is 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 honour valid (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.
  3. 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.