mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- Updated test files to change instances of ribp_if to apb4_if in various test cases, ensuring consistency with the new APB4 interface. - Modified assertions and checks in tests to reflect the new APB4 naming conventions, including changes in expected output and input files. - Adjusted the test descriptions to accurately represent the APB4 functionality being tested, particularly in timer, UART, and GPIO tests. - Ensured that all relevant paths and file names in the tests are updated to point to the new APB4 implementations, maintaining the integrity of the test suite.
319 lines
9.7 KiB
Systemverilog
319 lines
9.7 KiB
Systemverilog
`timescale 1ns / 1ps
|
|
|
|
`include "axi4_define.svh"
|
|
|
|
module axi4_word_bridge_tb;
|
|
logic clk_i = 1'b0;
|
|
logic rst_n_i = 1'b0;
|
|
logic [31:0] memory [0:63];
|
|
logic [31:0] expected;
|
|
integer target_accesses = 0;
|
|
integer error_beats;
|
|
integer cycle_count = 0;
|
|
integer burst_cycles;
|
|
integer single_cycles;
|
|
|
|
axi4_if #(
|
|
.ADDR_WIDTH(32),
|
|
.DATA_WIDTH(32),
|
|
.ID_WIDTH (1),
|
|
.USER_WIDTH(1)
|
|
) axi4 (
|
|
.aclk (clk_i),
|
|
.aresetn(rst_n_i)
|
|
);
|
|
logic req_valid;
|
|
logic req_ready;
|
|
logic [31:0] req_addr;
|
|
logic [31:0] req_wdata;
|
|
logic [ 3:0] req_wstrb;
|
|
logic [31:0] req_rdata;
|
|
logic req_resp_err;
|
|
|
|
always #5 clk_i = ~clk_i;
|
|
|
|
always @(posedge clk_i) cycle_count <= cycle_count + 1;
|
|
|
|
axi4_word_bridge u_dut (
|
|
.clk_i (clk_i),
|
|
.rst_n_i (rst_n_i),
|
|
.axi4 (axi4),
|
|
.req_valid_o (req_valid),
|
|
.req_ready_i (req_ready),
|
|
.req_addr_o (req_addr),
|
|
.req_wdata_o (req_wdata),
|
|
.req_wstrb_o (req_wstrb),
|
|
.req_rdata_i (req_rdata),
|
|
.req_resp_err_i(req_resp_err)
|
|
);
|
|
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
req_ready <= 1'b0;
|
|
req_rdata <= '0;
|
|
target_accesses <= 0;
|
|
end else begin
|
|
req_ready <= req_valid && !req_ready;
|
|
if (req_valid && !req_ready) begin
|
|
target_accesses <= target_accesses + 1;
|
|
req_rdata <= memory[req_addr[7:2]];
|
|
for (int lane = 0; lane < 4; lane++) begin
|
|
if (req_wstrb[lane]) begin
|
|
memory[req_addr[7:2]][lane*8+:8] <= req_wdata[lane*8+:8];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assign req_resp_err = 1'b0;
|
|
|
|
task automatic init_axi4;
|
|
begin
|
|
axi4.awid = '0;
|
|
axi4.awaddr = '0;
|
|
axi4.awlen = '0;
|
|
axi4.awsize = `AXI4_BURST_SIZE_4BYTES;
|
|
axi4.awburst = `AXI4_BURST_TYPE_INCR;
|
|
axi4.awlock = `AXI4_LOCK_NORM;
|
|
axi4.awcache = `AXI4_CACHE_NO_BUF;
|
|
axi4.awprot = `AXI4_PROT_DATA;
|
|
axi4.awqos = `AXI4_QOS_NORMAL;
|
|
axi4.awregion = `AXI4_REGION_NORMAL;
|
|
axi4.awuser = '0;
|
|
axi4.awvalid = 1'b0;
|
|
axi4.wdata = '0;
|
|
axi4.wstrb = '0;
|
|
axi4.wlast = 1'b0;
|
|
axi4.wuser = '0;
|
|
axi4.wvalid = 1'b0;
|
|
axi4.bready = 1'b0;
|
|
axi4.arid = '0;
|
|
axi4.araddr = '0;
|
|
axi4.arlen = '0;
|
|
axi4.arsize = `AXI4_BURST_SIZE_4BYTES;
|
|
axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
|
axi4.arlock = `AXI4_LOCK_NORM;
|
|
axi4.arcache = `AXI4_CACHE_NO_BUF;
|
|
axi4.arprot = `AXI4_PROT_DATA;
|
|
axi4.arqos = `AXI4_QOS_NORMAL;
|
|
axi4.arregion = `AXI4_REGION_NORMAL;
|
|
axi4.aruser = '0;
|
|
axi4.arvalid = 1'b0;
|
|
axi4.rready = 1'b0;
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_write4(input logic [31:0] address);
|
|
begin
|
|
@(negedge clk_i);
|
|
axi4.awaddr = address;
|
|
axi4.awlen = 8'd3;
|
|
axi4.awvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.awready);
|
|
@(negedge clk_i);
|
|
axi4.awvalid = 1'b0;
|
|
for (int beat = 0; beat < 4; beat++) begin
|
|
axi4.wdata = 32'hA500_0000 + beat;
|
|
axi4.wstrb = 4'hF;
|
|
axi4.wlast = beat == 3;
|
|
axi4.wvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.wready);
|
|
@(negedge clk_i);
|
|
axi4.wvalid = 1'b0;
|
|
end
|
|
repeat (2) @(posedge clk_i);
|
|
axi4.bready = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.bvalid);
|
|
if (axi4.bresp != `AXI4_RESP_OKAY) $fatal(1, "write burst returned an error");
|
|
@(negedge clk_i);
|
|
axi4.bready = 1'b0;
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_read4(input logic [31:0] address);
|
|
begin
|
|
@(negedge clk_i);
|
|
axi4.araddr = address;
|
|
axi4.arlen = 8'd3;
|
|
axi4.arvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.arready);
|
|
@(negedge clk_i);
|
|
axi4.arvalid = 1'b0;
|
|
for (int beat = 0; beat < 4; beat++) begin
|
|
if (beat == 1) begin
|
|
repeat (3) @(posedge clk_i);
|
|
@(negedge clk_i);
|
|
end
|
|
axi4.rready = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.rvalid);
|
|
expected = 32'hA500_0000 + beat;
|
|
if (axi4.rdata != expected || axi4.rresp != `AXI4_RESP_OKAY ||
|
|
axi4.rlast != (beat == 3)) begin
|
|
$fatal(1, "read beat %0d mismatch: data=%08x expected=%08x resp=%0d last=%0d", beat,
|
|
axi4.rdata, expected, axi4.rresp, axi4.rlast);
|
|
end
|
|
@(negedge clk_i);
|
|
axi4.rready = 1'b0;
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_illegal_read(input logic [31:0] address);
|
|
integer accesses_before;
|
|
begin
|
|
accesses_before = target_accesses;
|
|
@(negedge clk_i);
|
|
axi4.araddr = address;
|
|
axi4.arlen = 8'd16;
|
|
axi4.arvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.arready);
|
|
@(negedge clk_i);
|
|
axi4.arvalid = 1'b0;
|
|
axi4.rready = 1'b1;
|
|
error_beats = 0;
|
|
while (error_beats < 17) begin
|
|
@(posedge clk_i);
|
|
if (axi4.rvalid) begin
|
|
if (axi4.rresp != `AXI4_RESP_SLAVE_ERROR || axi4.rlast != (error_beats == 16)) begin
|
|
$fatal(1, "illegal burst response mismatch");
|
|
end
|
|
error_beats = error_beats + 1;
|
|
end
|
|
end
|
|
@(negedge clk_i);
|
|
axi4.rready = 1'b0;
|
|
if (target_accesses != accesses_before) begin
|
|
$fatal(1, "illegal burst reached the word target");
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_early_last_write(input logic [31:0] address);
|
|
integer accesses_before;
|
|
begin
|
|
accesses_before = target_accesses;
|
|
@(negedge clk_i);
|
|
axi4.awaddr = address;
|
|
axi4.awlen = 8'd3;
|
|
axi4.awvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.awready);
|
|
@(negedge clk_i);
|
|
axi4.awvalid = 1'b0;
|
|
axi4.wdata = 32'hDEAD_BEEF;
|
|
axi4.wstrb = 4'hF;
|
|
axi4.wlast = 1'b1;
|
|
axi4.wvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.wready);
|
|
@(negedge clk_i);
|
|
axi4.wvalid = 1'b0;
|
|
axi4.bready = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.bvalid);
|
|
if (axi4.bresp != `AXI4_RESP_SLAVE_ERROR) begin
|
|
$fatal(1, "early WLAST did not return SLVERR");
|
|
end
|
|
@(negedge clk_i);
|
|
axi4.bready = 1'b0;
|
|
axi4.wlast = 1'b0;
|
|
if (target_accesses != accesses_before + 1) begin
|
|
$fatal(1, "early WLAST must commit exactly one word beat");
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_read(input logic [31:0] address, input logic [7:0] length);
|
|
integer beat;
|
|
begin
|
|
@(negedge clk_i);
|
|
axi4.araddr = address;
|
|
axi4.arlen = length;
|
|
axi4.arvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.arready);
|
|
@(negedge clk_i);
|
|
axi4.arvalid = 1'b0;
|
|
axi4.rready = 1'b1;
|
|
beat = 0;
|
|
while (beat <= length) begin
|
|
@(posedge clk_i);
|
|
if (axi4.rvalid) begin
|
|
if (axi4.rresp != `AXI4_RESP_OKAY || axi4.rlast != (beat == length)) begin
|
|
$fatal(1, "performance read response mismatch");
|
|
end
|
|
beat = beat + 1;
|
|
end
|
|
end
|
|
@(negedge clk_i);
|
|
axi4.rready = 1'b0;
|
|
end
|
|
endtask
|
|
|
|
task automatic issue_address_pattern_read(input logic [31:0] address, input logic [7:0] length,
|
|
input logic [1:0] burst);
|
|
integer beat;
|
|
logic [31:0] expected_address;
|
|
logic [31:0] burst_bytes;
|
|
begin
|
|
burst_bytes = (length + 1) * 4;
|
|
@(negedge clk_i);
|
|
axi4.araddr = address;
|
|
axi4.arlen = length;
|
|
axi4.arburst = burst;
|
|
axi4.arvalid = 1'b1;
|
|
do @(posedge clk_i); while (!axi4.arready);
|
|
@(negedge clk_i);
|
|
axi4.arvalid = 1'b0;
|
|
axi4.rready = 1'b1;
|
|
beat = 0;
|
|
while (beat <= length) begin
|
|
@(posedge clk_i);
|
|
if (axi4.rvalid) begin
|
|
if (burst == `AXI4_BURST_TYPE_FIXED) begin
|
|
expected_address = address;
|
|
end else begin
|
|
expected_address = (address & ~(burst_bytes - 1)) |
|
|
((address + (beat * 4)) & (burst_bytes - 1));
|
|
end
|
|
if (axi4.rdata != memory[expected_address[7:2]] ||
|
|
axi4.rresp != `AXI4_RESP_OKAY || axi4.rlast != (beat == length)) begin
|
|
$fatal(1, "address pattern mismatch beat=%0d addr=%08x data=%08x", beat,
|
|
expected_address, axi4.rdata);
|
|
end
|
|
beat = beat + 1;
|
|
end
|
|
end
|
|
@(negedge clk_i);
|
|
axi4.rready = 1'b0;
|
|
axi4.arburst = `AXI4_BURST_TYPE_INCR;
|
|
end
|
|
endtask
|
|
|
|
initial begin
|
|
init_axi4();
|
|
for (int index = 0; index < 64; index++) memory[index] = '0;
|
|
repeat (4) @(posedge clk_i);
|
|
rst_n_i = 1'b1;
|
|
issue_write4(32'h0000_0040);
|
|
issue_read4(32'h0000_0040);
|
|
for (int index = 0; index < 64; index++) memory[index] = 32'h5000_0000 + index;
|
|
issue_address_pattern_read(32'h0000_0020, 8'd3, `AXI4_BURST_TYPE_FIXED);
|
|
issue_address_pattern_read(32'h0000_002C, 8'd3, `AXI4_BURST_TYPE_WRAP);
|
|
issue_illegal_read(32'h0000_0080);
|
|
issue_early_last_write(32'h0000_00C0);
|
|
burst_cycles = cycle_count;
|
|
issue_read(32'h0000_0000, 8'd15);
|
|
burst_cycles = cycle_count - burst_cycles;
|
|
single_cycles = cycle_count;
|
|
for (int index = 0; index < 16; index++) begin
|
|
issue_read(32'(index * 4), 8'd0);
|
|
end
|
|
single_cycles = cycle_count - single_cycles;
|
|
if ((burst_cycles * 5) > (single_cycles * 4)) begin
|
|
$fatal(1, "16-beat burst improvement is below 20%%: burst=%0d single=%0d", burst_cycles,
|
|
single_cycles);
|
|
end
|
|
if (target_accesses != 49) $fatal(1, "unexpected word access count");
|
|
$display("AXI4 word bridge test passed");
|
|
$finish;
|
|
end
|
|
endmodule
|