Files
retroSoC/tests/rtl/axi4_interconnect_tb.sv
T
maksyuki 52f619b9dc feat: add APU-P1 support and enhance test coverage
- Introduced new task  in  for handling high-performance APU writes with access fault checks.
- Updated  to support an expanded resource count, including ownership and IRQ routing for the new APU resource.
- Enhanced  to validate IRQ expectations for the newly added APU resource.
- Created  for directed verification of APU-P1 APB4 register shell and integrated IRQ ownership topology.
- Added  to ensure synchronization between RTL and SDK register ABI.
- Modified  to parameterize tests for product modes, improving flexibility in testing.
- Updated  to reflect changes in APU memory mapping and ensure correct assertions.
- Enhanced  to validate the complete IRQ and resource allocations for the APU.
2026-09-02 22:00:45 +08:00

400 lines
14 KiB
Systemverilog

`timescale 1ns / 1ps
`include "axi4_define.svh"
`include "rib_defs.svh"
module axi4_interconnect_tb;
localparam int NumMasters = 8;
localparam int NumTargets = 9;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic fault_valid_o;
logic [31:0] fault_addr_o;
logic [ 3:0] fault_wstrb_o;
logic fault_reserved_o;
logic fault_access_o;
logic [ 2:0] fault_master_o;
logic [ 2:0] fault_code_o;
logic user_bus_idle_o;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) masters[NumMasters] (
.aclk (clk_i),
.aresetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) targets[NumTargets] (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
axi4_interconnect #(
.NumMasters(NumMasters),
.NumTargets(NumTargets)
) u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.masters (masters),
.targets (targets),
.user_bus_enable_i (1'b1),
.user_bus_idle_o (user_bus_idle_o),
.perf_enable_i (1'b0),
.perf_clear_i (1'b0),
.fault_valid_o (fault_valid_o),
.fault_addr_o (fault_addr_o),
.fault_wstrb_o (fault_wstrb_o),
.fault_reserved_o (fault_reserved_o),
.fault_access_o (fault_access_o),
.fault_master_o (fault_master_o),
.fault_code_o (fault_code_o),
.perf_mgmt_wait_o (),
.perf_user_wait_o (),
.perf_dma_wait_o (),
.perf_sdio0_wait_o (),
.perf_sdio1_wait_o (),
.perf_usb2_wait_o (),
.perf_apb4_periph_wait_o(),
.perf_apb4_system_wait_o(),
.perf_sdram_wait_o (),
.perf_psram_wait_o (),
.perf_flash_wait_o (),
.perf_opipsram_wait_o ()
);
for (genvar target = 0; target < NumTargets; target++) begin : GEN_TARGETS
localparam logic [1:0] Response =
(target == 7) ? `AXI4_RESP_DECODE_ERROR :
(target == 8) ? `AXI4_RESP_SLAVE_ERROR : `AXI4_RESP_OKAY;
axi4_error_slave #(
.Response(Response)
) u_target (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (targets[target])
);
end
`define INIT_MASTER(index) \
masters[index].awid = '0; \
masters[index].awaddr = '0; \
masters[index].awlen = '0; \
masters[index].awsize = `AXI4_BURST_SIZE_4BYTES; \
masters[index].awburst = `AXI4_BURST_TYPE_INCR; \
masters[index].awlock = `AXI4_LOCK_NORM; \
masters[index].awcache = `AXI4_CACHE_NO_BUF; \
masters[index].awprot = `AXI4_PROT_DATA; \
masters[index].awqos = `AXI4_QOS_NORMAL; \
masters[index].awregion = `AXI4_REGION_NORMAL; \
masters[index].awuser = '0; \
masters[index].awvalid = 1'b0; \
masters[index].wdata = '0; \
masters[index].wstrb = '0; \
masters[index].wlast = 1'b0; \
masters[index].wuser = '0; \
masters[index].wvalid = 1'b0; \
masters[index].bready = 1'b0; \
masters[index].arid = '0; \
masters[index].araddr = '0; \
masters[index].arlen = '0; \
masters[index].arsize = `AXI4_BURST_SIZE_4BYTES; \
masters[index].arburst = `AXI4_BURST_TYPE_INCR; \
masters[index].arlock = `AXI4_LOCK_NORM; \
masters[index].arcache = `AXI4_CACHE_NO_BUF; \
masters[index].arprot = `AXI4_PROT_DATA; \
masters[index].arqos = `AXI4_QOS_NORMAL; \
masters[index].arregion = `AXI4_REGION_NORMAL; \
masters[index].aruser = '0; \
masters[index].arvalid = 1'b0; \
masters[index].rready = 1'b0;
task automatic issue_mgmt_error_read(input logic [31:0] address, input logic [7:0] length,
input logic [1:0] response, input logic [2:0] fault_code);
integer beat;
begin
@(negedge clk_i);
masters[0].araddr = address;
masters[0].arlen = length;
masters[0].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[0].arready);
@(negedge clk_i);
masters[0].arvalid = 1'b0;
masters[0].rready = 1'b1;
beat = 0;
while (beat <= length) begin
@(negedge clk_i);
if (masters[0].rvalid) begin
if ((masters[0].rresp != response) || (masters[0].rlast != (beat == int'(length)))) begin
$fatal(1, "management error response mismatch on beat %0d", beat);
end
if ((beat == int'(length)) &&
(!fault_valid_o || (fault_addr_o != address) || fault_access_o ||
(fault_master_o != 3'd0) || (fault_code_o != fault_code))) begin
$fatal(1, "management fault classification mismatch");
end
beat = beat + 1;
end
end
@(negedge clk_i);
masters[0].rready = 1'b0;
end
endtask
task automatic issue_denied_user_write(input logic [31:0] address);
begin
@(negedge clk_i);
masters[1].awaddr = address;
masters[1].awlen = 8'd0;
masters[1].awvalid = 1'b1;
do @(posedge clk_i); while (!masters[1].awready);
@(negedge clk_i);
masters[1].awvalid = 1'b0;
masters[1].wdata = 32'hA5A5_5A5A;
masters[1].wstrb = 4'hF;
masters[1].wlast = 1'b1;
masters[1].wvalid = 1'b1;
do @(posedge clk_i); while (!masters[1].wready);
@(negedge clk_i);
masters[1].wvalid = 1'b0;
while (!masters[1].bvalid) @(negedge clk_i);
masters[1].bready = 1'b1;
#1;
if ((masters[1].bresp != `AXI4_RESP_SLAVE_ERROR) || !fault_valid_o ||
!fault_access_o || (fault_addr_o != address) || (fault_wstrb_o != 4'hF) ||
(fault_master_o != 3'd1) || (fault_code_o != `RIB_RESP_PROTERR)) begin
$fatal(1, "user access fault classification mismatch");
end
@(negedge clk_i);
masters[1].bready = 1'b0;
end
endtask
task automatic issue_hp_apu_write(input logic [31:0] address, input logic [1:0] expected_response,
input logic expected_access_fault);
begin
@(negedge clk_i);
masters[6].awaddr = address;
masters[6].awlen = 8'd0;
masters[6].awvalid = 1'b1;
do @(posedge clk_i); while (!masters[6].awready);
@(negedge clk_i);
masters[6].awvalid = 1'b0;
masters[6].wdata = 32'h1234_5678;
masters[6].wstrb = 4'hf;
masters[6].wlast = 1'b1;
masters[6].wvalid = 1'b1;
do @(posedge clk_i); while (!masters[6].wready);
@(negedge clk_i);
masters[6].wvalid = 1'b0;
masters[6].bready = 1'b1;
while (!masters[6].bvalid) @(negedge clk_i);
#1;
if ((masters[6].bresp != expected_response) ||
(fault_valid_o != expected_access_fault) ||
(expected_access_fault &&
(!fault_access_o || (fault_addr_o != address) || (fault_master_o != 3'd6) ||
(fault_code_o != `RIB_RESP_PROTERR)))) begin
$fatal(
1,
"HP APU access policy mismatch at %h: resp=%0d fault=%0d access=%0d master=%0d code=%0d",
address, masters[6].bresp, fault_valid_o, fault_access_o, fault_master_o, fault_code_o);
end
@(negedge clk_i);
masters[6].bready = 1'b0;
end
endtask
task automatic issue_management_with_idle_sdio;
integer cycles;
begin
@(negedge clk_i);
masters[0].araddr = 32'h0000_0000;
masters[0].arlen = 8'd0;
masters[0].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[0].arready);
@(negedge clk_i);
masters[0].arvalid = 1'b0;
masters[0].rready = 1'b1;
cycles = 0;
while (!masters[0].rvalid && cycles < 100) begin
if (masters[3].arvalid || masters[3].awvalid || masters[3].wvalid ||
masters[4].arvalid || masters[4].awvalid || masters[4].wvalid ||
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid ||
masters[6].arvalid || masters[6].awvalid || masters[6].wvalid) begin
$fatal(1, "inactive storage master drove a request during management boot traffic");
end
@(negedge clk_i);
cycles = cycles + 1;
end
if (cycles >= 100) $fatal(1, "idle SDIO masters blocked management boot traffic");
if (masters[0].rresp != `AXI4_RESP_OKAY) begin
$fatal(1, "management boot traffic received an unexpected response");
end
@(posedge clk_i);
@(negedge clk_i);
masters[0].rready = 1'b0;
end
endtask
task automatic issue_contending_sdio_faults;
integer cycles;
begin
@(negedge clk_i);
masters[3].araddr = 32'hA000_1000;
masters[3].arlen = 8'd0;
masters[3].arvalid = 1'b1;
masters[4].araddr = 32'hA000_2000;
masters[4].arlen = 8'd0;
masters[4].arvalid = 1'b1;
do @(posedge clk_i); while (!(masters[3].arready && masters[4].arready));
@(negedge clk_i);
masters[3].arvalid = 1'b0;
masters[4].arvalid = 1'b0;
cycles = 0;
while (!masters[3].rvalid && !masters[4].rvalid && cycles < 100) begin
@(negedge clk_i);
cycles = cycles + 1;
end
if (cycles >= 100) $fatal(1, "SDIO master contention did not produce a response");
if (masters[3].rvalid) begin
masters[3].rready = 1'b1;
#1;
if ((masters[3].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd3) || (fault_addr_o != 32'hA000_1000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "SDIO0 fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[3].rready = 1'b0;
end else begin
masters[4].rready = 1'b1;
#1;
if ((masters[4].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd4) || (fault_addr_o != 32'hA000_2000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "SDIO1 fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[4].rready = 1'b0;
end
cycles = 0;
while (!masters[3].rvalid && !masters[4].rvalid && cycles < 100) begin
@(negedge clk_i);
cycles = cycles + 1;
end
if (cycles >= 100) $fatal(1, "second SDIO master response was lost");
if (masters[3].rvalid) begin
masters[3].rready = 1'b1;
#1;
if ((masters[3].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd3) || (fault_addr_o != 32'hA000_1000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "second SDIO0 fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[3].rready = 1'b0;
end else begin
masters[4].rready = 1'b1;
#1;
if ((masters[4].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd4) || (fault_addr_o != 32'hA000_2000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "second SDIO1 fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[4].rready = 1'b0;
end
end
endtask
task automatic issue_hp_accesses;
integer beat;
begin
@(negedge clk_i);
masters[7].araddr = 32'h3800_0000;
masters[7].arlen = 8'd15;
masters[7].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[7].arready);
@(negedge clk_i);
masters[7].arvalid = 1'b0;
masters[7].rready = 1'b1;
beat = 0;
while (beat < 16) begin
@(negedge clk_i);
if (masters[7].rvalid) begin
if ((masters[7].rresp != `AXI4_RESP_OKAY) ||
(masters[7].rlast != (beat == 15)) || fault_valid_o) begin
$fatal(1, "HP SDRAM burst was rejected or misclassified");
end
beat = beat + 1;
end
end
@(negedge clk_i);
masters[7].rready = 1'b0;
masters[7].araddr = 32'hA000_7000;
masters[7].arlen = 8'd0;
masters[7].arvalid = 1'b1;
do @(posedge clk_i); while (!masters[7].arready);
@(negedge clk_i);
masters[7].arvalid = 1'b0;
masters[7].rready = 1'b1;
while (!masters[7].rvalid) @(negedge clk_i);
#1;
if ((masters[7].rresp != `AXI4_RESP_DECODE_ERROR) || !fault_valid_o ||
(fault_master_o != 3'd7) || (fault_addr_o != 32'hA000_7000) ||
(fault_code_o != `RIB_RESP_DECERR)) begin
$fatal(1, "HP decode fault attribution mismatch");
end
@(posedge clk_i);
@(negedge clk_i);
masters[7].rready = 1'b0;
end
endtask
initial begin
`INIT_MASTER(0)
`INIT_MASTER(1)
`INIT_MASTER(2)
`INIT_MASTER(3)
`INIT_MASTER(4)
`INIT_MASTER(5)
`INIT_MASTER(6)
`INIT_MASTER(7)
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
issue_management_with_idle_sdio();
issue_mgmt_error_read(32'hA000_0000, 8'd0, `AXI4_RESP_DECODE_ERROR, `RIB_RESP_DECERR);
issue_mgmt_error_read(32'h3000_0000, 8'd16, `AXI4_RESP_SLAVE_ERROR, `RIB_RESP_BURSTERR);
`ifdef MINI_PRODUCT
issue_hp_apu_write(32'h1001_301C, `AXI4_RESP_OKAY, 1'b0);
issue_hp_apu_write(32'h1001_3040, `AXI4_RESP_SLAVE_ERROR, 1'b1);
`else
issue_denied_user_write(32'h1000_B000);
`endif
issue_contending_sdio_faults();
issue_hp_accesses();
$display("AXI4 interconnect fault classification test passed");
$finish;
end
`undef INIT_MASTER
endmodule