Files
retroSoC/tests/rtl/dma_error_tb.sv
T
maksyuki de32aa2979 feat: add AXI4 interconnect testbench and enhance existing tests
- Introduced a new testbench for the AXI4 interconnect () to validate fault classification for decode and access errors.
- Updated  and  to reflect changes in bus naming conventions.
- Enhanced  to include AXI4-Stream interface checks and validate stream data transfers.
- Modified  to integrate AXI4-Stream interfaces for improved DMA testing.
- Added new Python tests () to verify AXI4 to RIBP burst bridge functionality and interconnect fault classification.
- Updated existing tests to include AXI4 interfaces and ensure proper error handling and response validation.
- Adjusted memory map generation and test setups to accommodate new AXI4 interface requirements.
2026-08-12 12:30:12 +08:00

276 lines
8.2 KiB
Systemverilog

`timescale 1ns / 1ps
module dma_error_tb;
localparam logic [1:0] MODEL_IDLE = 2'd0;
localparam logic [1:0] MODEL_WDATA = 2'd1;
localparam logic [1:0] MODEL_RESP = 2'd2;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic [ 3:0] mode_i = 4'd0;
logic [31:0] srcaddr_i = 32'h3800_1000;
logic srcincr_i = 1'b1;
logic [31:0] dstaddr_i = 32'h4000_1000;
logic dstincr_i = 1'b1;
logic [31:0] xferlen_i = 32'd1;
logic start_i = 1'b0;
logic stop_i = 1'b0;
logic reset_i = 1'b0;
logic done_o;
logic error_o;
logic [ 2:0] error_code_o;
logic [31:0] error_addr_o;
logic [ 1:0] fsm_o;
logic error_mode = 1'b0;
integer write_count = 0;
integer incr4_read_count = 0;
integer incr4_write_count = 0;
integer legacy_cycles;
integer burst_cycles;
integer stream_tx_count = 0;
integer stream_rx_count = 0;
logic stream_tx_ready = 1'b0;
logic stream_rx_valid = 1'b0;
logic [ 1:0] model_state_q = MODEL_IDLE;
logic [ 1:0] model_len_q = '0;
logic [ 1:0] model_beat_q = '0;
logic model_write_q = 1'b0;
logic model_error_q = 1'b0;
dma_hw_trg_if hw_trg ();
rib_if rib ();
axi4_stream_if i2s_tx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if i2s_rx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_stream_if dvp_rx_axis (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
assign rib.cmd_ready = model_state_q == MODEL_IDLE;
assign rib.w_ready = model_state_q == MODEL_WDATA;
assign rib.rsp_valid = model_state_q == MODEL_RESP;
assign rib.rdata = 32'hA5A5_5A5A + model_beat_q;
assign rib.resp_err = model_error_q;
assign rib.resp_code = model_error_q ? `RIB_RESP_DECERR : `RIB_RESP_OK;
assign rib.rsp_beat = model_beat_q;
assign rib.rsp_last = model_write_q || model_error_q || (model_beat_q == model_len_q);
assign i2s_tx_axis.tready = stream_tx_ready;
assign i2s_rx_axis.tdata = 32'h5100_0000 + stream_rx_count;
assign i2s_rx_axis.tkeep = '1;
assign i2s_rx_axis.tstrb = '1;
assign i2s_rx_axis.tlast = stream_rx_count == 1;
assign i2s_rx_axis.tid = '0;
assign i2s_rx_axis.tdest = '0;
assign i2s_rx_axis.tuser = '0;
assign i2s_rx_axis.tvalid = stream_rx_valid;
assign dvp_rx_axis.tdata = '0;
assign dvp_rx_axis.tkeep = '0;
assign dvp_rx_axis.tstrb = '0;
assign dvp_rx_axis.tlast = 1'b0;
assign dvp_rx_axis.tid = '0;
assign dvp_rx_axis.tdest = '0;
assign dvp_rx_axis.tuser = '0;
assign dvp_rx_axis.tvalid = 1'b0;
always @(posedge clk_i) begin
if (i2s_tx_axis.tvalid && i2s_tx_axis.tready) begin
if (i2s_tx_axis.tdata !== 32'hA5A5_5A5A) begin
$fatal(1, "I2S TX stream data mismatch");
end
stream_tx_count <= stream_tx_count + 1;
end
if (i2s_rx_axis.tvalid && i2s_rx_axis.tready) begin
stream_rx_count <= stream_rx_count + 1;
if (stream_rx_count == 1) stream_rx_valid <= 1'b0;
end
end
always @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
model_state_q <= MODEL_IDLE;
model_len_q <= '0;
model_beat_q <= '0;
model_write_q <= 1'b0;
model_error_q <= 1'b0;
write_count <= 0;
end else begin
unique case (model_state_q)
MODEL_IDLE: begin
if (rib.cmd_valid && rib.cmd_ready) begin
model_len_q <= rib.cmd_len;
model_beat_q <= '0;
model_write_q <= rib.cmd_write;
model_error_q <= error_mode && !rib.cmd_write;
model_state_q <= rib.cmd_write ? MODEL_WDATA : MODEL_RESP;
if (rib.cmd_len == `RIB_LEN_INCR4 && rib.cmd_write) begin
incr4_write_count <= incr4_write_count + 1;
end
if (rib.cmd_len == `RIB_LEN_INCR4 && !rib.cmd_write) begin
incr4_read_count <= incr4_read_count + 1;
end
end
end
MODEL_WDATA: begin
if (rib.w_valid && rib.w_ready) begin
write_count <= write_count + 1;
if (rib.wlast) begin
model_beat_q <= model_len_q;
model_state_q <= MODEL_RESP;
end else begin
model_beat_q <= model_beat_q + 1'b1;
end
end
end
MODEL_RESP: begin
if (rib.rsp_valid && rib.rsp_ready) begin
if (rib.rsp_last) begin
model_state_q <= MODEL_IDLE;
end else begin
model_beat_q <= model_beat_q + 1'b1;
end
end
end
default: model_state_q <= MODEL_IDLE;
endcase
end
end
dma_core u_dma_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.mode_i (mode_i),
.srcaddr_i (srcaddr_i),
.srcincr_i (srcincr_i),
.dstaddr_i (dstaddr_i),
.dstincr_i (dstincr_i),
.xferlen_i (xferlen_i),
.start_i (start_i),
.stop_i (stop_i),
.reset_i (reset_i),
.done_o (done_o),
.error_o (error_o),
.error_code_o(error_code_o),
.error_addr_o(error_addr_o),
.fsm_o (fsm_o),
.hw_trg (hw_trg),
.rib (rib),
.i2s_tx_axis (i2s_tx_axis),
.i2s_rx_axis (i2s_rx_axis),
.dvp_rx_axis (dvp_rx_axis)
);
task automatic start_transfer;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic measure_transfer(output integer elapsed);
begin
start_transfer();
elapsed = 0;
while (!done_o) begin
@(posedge clk_i);
elapsed = elapsed + 1;
end
@(negedge clk_i);
end
endtask
initial begin
hw_trg.i2s_tx_proc = 1'b0;
hw_trg.i2s_rx_proc = 1'b0;
hw_trg.qspi_tx_proc = 1'b0;
hw_trg.qspi_rx_proc = 1'b0;
hw_trg.uart_tx_proc = 1'b0;
hw_trg.uart_rx_proc = 1'b0;
hw_trg.i2c0_tx_proc = 1'b0;
hw_trg.i2c0_rx_proc = 1'b0;
hw_trg.i2c1_tx_proc = 1'b0;
hw_trg.i2c1_rx_proc = 1'b0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
start_transfer();
wait (done_o);
@(negedge clk_i);
if (write_count != 1) begin
$fatal(1, "one-word DMA transfer issued %0d writes", write_count);
end
error_mode = 1'b1;
start_transfer();
wait (error_o);
@(negedge clk_i);
if (error_code_o !== `RIB_RESP_DECERR || error_addr_o !== srcaddr_i) begin
$fatal(1, "DMA error response was not propagated");
end
if (write_count != 1) begin
$fatal(1, "DMA issued a write after a read error");
end
error_mode = 1'b0;
xferlen_i = 32'd8;
srcincr_i = 1'b0;
dstincr_i = 1'b1;
measure_transfer(legacy_cycles);
srcincr_i = 1'b1;
measure_transfer(burst_cycles);
if (incr4_read_count != 2 || incr4_write_count != 2) begin
$fatal(1, "aligned eight-word transfer did not use two INCR4 chunks");
end
if ((burst_cycles * 3) > (legacy_cycles * 2)) begin
$fatal(1, "INCR4 DMA speedup is below 1.5x: legacy=%0d burst=%0d", legacy_cycles,
burst_cycles);
end
srcaddr_i = 32'h1000_0000;
dstaddr_i = 32'h1000_1000;
measure_transfer(legacy_cycles);
if (incr4_read_count != 2 || incr4_write_count != 2) begin
$fatal(1, "DMA did not fall back to INCR1 for a register window");
end
mode_i = 4'd1;
srcaddr_i = 32'h3800_2000;
srcincr_i = 1'b1;
xferlen_i = 32'd2;
stream_tx_ready = 1'b0;
start_transfer();
repeat (3) @(posedge clk_i);
@(negedge clk_i);
stream_tx_ready = 1'b1;
wait (done_o);
@(negedge clk_i);
if (stream_tx_count != 2) begin
$fatal(1, "I2S TX stream transferred %0d words", stream_tx_count);
end
mode_i = 4'd2;
dstaddr_i = 32'h4000_2000;
dstincr_i = 1'b1;
xferlen_i = 32'd2;
stream_rx_valid = 1'b1;
start_transfer();
wait (done_o);
@(negedge clk_i);
if (stream_rx_count != 2) begin
$fatal(1, "I2S RX stream transferred %0d words", stream_rx_count);
end
$display("dma error, burst performance, and AXI4-Stream test passed");
$finish;
end
endmodule