mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: enhance APU and AXI4 functionality with new tests and updates
- Updated axi4_async_bridge_tb.sv to include unilateral reset handling in the test. - Enhanced axi4_target_guard_tb.sv with a new task for read beat normalization and additional read tests. - Introduced hp_axi4_mux3_rr_tb.sv to test round-robin arbitration for multiple AXI4 interfaces. - Expanded test_apu.py to include tests for APU stream router, DMA, ring scheduler, and integrated DMA/ring/backend functionality. - Added new test cases for APU-P2 Gateway A round-robin fairness. - Updated test_clock_reset_domains.py to include APU data crossing. - Enhanced test_script_tools.py to scope formal filelists for APU and Gateway A.
This commit is contained in:
48 files changed
+7485
-469
No files matched your search
@@ -0,0 +1,516 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "axi4_define.svh"
|
||||
|
||||
module apu_dma_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic abort_i = 1'b0;
|
||||
logic quiesce_i = 1'b0;
|
||||
logic counter_clear_i = 1'b0;
|
||||
logic [ 7:0] bridge_epoch_i = 8'd0;
|
||||
logic request_valid_i = 1'b0;
|
||||
logic request_ready_o;
|
||||
logic request_write_i;
|
||||
logic [31:0] request_addr_i;
|
||||
logic [31:0] request_bytes_i;
|
||||
logic [31:0] timeout_i = 32'd32;
|
||||
logic [31:0] read_acl_base_i = 32'd0;
|
||||
logic [31:0] read_acl_limit_i = 32'h0000_ffff;
|
||||
logic [31:0] write_acl_base_i = 32'd0;
|
||||
logic [31:0] write_acl_limit_i = 32'h0000_ffff;
|
||||
logic busy_o, done_o, error_o, aborted_o, aborting_o;
|
||||
logic [ 5:0] error_code_o;
|
||||
logic [ 3:0] error_stage_o;
|
||||
logic [ 1:0] error_resp_o;
|
||||
logic [31:0] error_addr_o;
|
||||
logic [63:0] read_bytes_o, write_bytes_o;
|
||||
logic [31:0] memory [0:32767];
|
||||
logic [31:0] buffer [ 0:31];
|
||||
logic [ 3:0] buffer_keep[ 0:31];
|
||||
logic read_active, write_active, write_source_active;
|
||||
logic [31:0] read_base, write_base;
|
||||
logic [7:0] read_len, write_len, read_index, write_index, source_index;
|
||||
logic bvalid;
|
||||
logic allow_addresses = 1'b1;
|
||||
logic read_sink_ready = 1'b1;
|
||||
logic [1:0] read_response = `AXI4_RESP_OKAY;
|
||||
logic [1:0] write_response = `AXI4_RESP_OKAY;
|
||||
logic read_id = 1'b0;
|
||||
logic write_id = 1'b0;
|
||||
logic early_read_last = 1'b0;
|
||||
logic suppress_read_last = 1'b0;
|
||||
logic allow_write_response = 1'b1;
|
||||
logic [7:0] source_last_index = 8'd0;
|
||||
int unsigned read_bursts, write_bursts;
|
||||
string s_phase;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) axi4 (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) read_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) write_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign read_axis.tready = read_sink_ready;
|
||||
assign write_axis.tdata = buffer[source_index];
|
||||
assign write_axis.tkeep = buffer_keep[source_index];
|
||||
assign write_axis.tstrb = buffer_keep[source_index];
|
||||
assign write_axis.tlast = source_index == source_last_index;
|
||||
assign write_axis.tid = '0;
|
||||
assign write_axis.tdest = '0;
|
||||
assign write_axis.tuser = '0;
|
||||
assign write_axis.tvalid = write_source_active;
|
||||
|
||||
assign axi4.arready = allow_addresses && !read_active;
|
||||
assign axi4.rid = read_id;
|
||||
assign axi4.rdata = memory[(read_base>>2)+read_index];
|
||||
assign axi4.rresp = read_response;
|
||||
assign axi4.rlast = !suppress_read_last &&
|
||||
(early_read_last ? (read_index == 8'd0) : (read_index == read_len));
|
||||
assign axi4.ruser = 1'b0;
|
||||
assign axi4.rvalid = read_active;
|
||||
assign axi4.awready = allow_addresses && !write_active && !bvalid;
|
||||
assign axi4.wready = write_active;
|
||||
assign axi4.bid = write_id;
|
||||
assign axi4.bresp = write_response;
|
||||
assign axi4.buser = 1'b0;
|
||||
assign axi4.bvalid = bvalid && allow_write_response;
|
||||
|
||||
apu_dma u_dut (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.abort_i,
|
||||
.quiesce_i,
|
||||
.bridge_epoch_i,
|
||||
.perf_enable_i (1'b1),
|
||||
.counter_clear_i,
|
||||
.request_valid_i,
|
||||
.request_ready_o,
|
||||
.request_write_i,
|
||||
.request_addr_i,
|
||||
.request_bytes_i,
|
||||
.read_base_i (read_acl_base_i),
|
||||
.read_limit_i (read_acl_limit_i),
|
||||
.write_base_i (write_acl_base_i),
|
||||
.write_limit_i (write_acl_limit_i),
|
||||
.timeout_i,
|
||||
.read_axis,
|
||||
.write_axis,
|
||||
.busy_o,
|
||||
.done_o,
|
||||
.error_o,
|
||||
.aborted_o,
|
||||
.aborting_o,
|
||||
.error_code_o,
|
||||
.error_stage_o,
|
||||
.error_resp_o,
|
||||
.error_addr_o,
|
||||
.input_pending_o (),
|
||||
.output_pending_o(),
|
||||
.read_bytes_o,
|
||||
.write_bytes_o,
|
||||
.read_stalls_o (),
|
||||
.write_stalls_o (),
|
||||
.axi4
|
||||
);
|
||||
|
||||
task automatic issue_request(input logic write_i, input logic [31:0] addr_i,
|
||||
input logic [31:0] bytes_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
request_write_i = write_i;
|
||||
request_addr_i = addr_i;
|
||||
request_bytes_i = bytes_i;
|
||||
if (write_i) source_last_index = ((bytes_i + 32'd3) >> 2) - 1'b1;
|
||||
request_valid_i = 1'b1;
|
||||
while (!request_ready_o) @(negedge clk_i);
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
request_valid_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
always @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
read_active <= 1'b0;
|
||||
write_active <= 1'b0;
|
||||
bvalid <= 1'b0;
|
||||
read_index <= 8'd0;
|
||||
write_index <= 8'd0;
|
||||
source_index <= 8'd0;
|
||||
read_bursts <= 0;
|
||||
write_bursts <= 0;
|
||||
end else begin
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
read_active <= 1'b1;
|
||||
read_base <= axi4.araddr;
|
||||
read_len <= axi4.arlen;
|
||||
read_index <= 8'd0;
|
||||
read_bursts <= read_bursts + 1;
|
||||
end else if (axi4.rvalid && axi4.rready) begin
|
||||
if (axi4.rlast) read_active <= 1'b0;
|
||||
else read_index <= read_index + 1'b1;
|
||||
end
|
||||
if (read_axis.tvalid && read_axis.tready) begin
|
||||
buffer[source_index] <= read_axis.tdata;
|
||||
buffer_keep[source_index] <= read_axis.tkeep;
|
||||
source_index <= source_index + 1'b1;
|
||||
end
|
||||
if (axi4.awvalid && axi4.awready) begin
|
||||
write_active <= 1'b1;
|
||||
write_base <= axi4.awaddr;
|
||||
write_len <= axi4.awlen;
|
||||
write_index <= 8'd0;
|
||||
write_bursts <= write_bursts + 1;
|
||||
end
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
for (int byte_index = 0; byte_index < 4; byte_index++) begin
|
||||
if (axi4.wstrb[byte_index]) begin
|
||||
memory[(write_base>>2)+write_index][byte_index*8+:8] <= axi4.wdata[byte_index*8+:8];
|
||||
end
|
||||
end
|
||||
if (axi4.wlast) begin
|
||||
write_active <= 1'b0;
|
||||
bvalid <= 1'b1;
|
||||
end else begin
|
||||
write_index <= write_index + 1'b1;
|
||||
end
|
||||
end
|
||||
if (write_axis.tvalid && write_axis.tready) source_index <= source_index + 1'b1;
|
||||
if (axi4.bvalid && axi4.bready) bvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
request_write_i = 1'b0;
|
||||
request_addr_i = 32'd0;
|
||||
request_bytes_i = 32'd0;
|
||||
write_source_active = 1'b0;
|
||||
for (int index = 0; index < 32768; index++) memory[index] = 32'hdead_beef;
|
||||
for (int index = 0; index < 17; index++) memory[(32'h0ff0>>2)+index] = 32'h1000 + index;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
source_index = 8'd0;
|
||||
s_phase = "read";
|
||||
issue_request(1'b0, 32'h0000_0ff0, 32'd67);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || (read_bytes_o != 64'd67) || (read_bursts < 2)) begin
|
||||
$fatal(1, "APU DMA read/split failed");
|
||||
end
|
||||
source_index = 8'd0;
|
||||
s_phase = "write";
|
||||
write_source_active = 1'b1;
|
||||
issue_request(1'b1, 32'h0000_2000, 32'd67);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
if (error_o || (write_bytes_o != 64'd67) || (write_bursts < 2)) begin
|
||||
$fatal(1, "APU DMA write/split failed");
|
||||
end
|
||||
for (int index = 0; index < 16; index++) begin
|
||||
if (memory[(32'h2000>>2)+index] != 32'h1000 + index) $fatal(1, "DMA copy mismatch");
|
||||
end
|
||||
if ((memory[(32'h2040>>2)][23:0] != 24'h001010) || (memory[(32'h2040>>2)][31:24] != 8'hde))
|
||||
$fatal(1, "DMA tail strobe mismatch");
|
||||
|
||||
s_phase = "acl";
|
||||
issue_request(1'b0, 32'h0002_0000, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA ACL denial failed");
|
||||
read_acl_limit_i = 32'hffff_ffff;
|
||||
issue_request(1'b0, 32'hffff_fffc, 32'd8);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd15) || busy_o) begin
|
||||
$fatal(1, "DMA ACL arithmetic overflow was accepted");
|
||||
end
|
||||
read_acl_limit_i = 32'h0000_ffff;
|
||||
|
||||
s_phase = "read response faults";
|
||||
read_response = `AXI4_RESP_SLAVE_ERROR;
|
||||
issue_request(1'b0, 32'h0000_4000, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd15) || (error_resp_o != `AXI4_RESP_SLAVE_ERROR))
|
||||
$fatal(1, "DMA read SLVERR failed");
|
||||
read_response = `AXI4_RESP_DECODE_ERROR;
|
||||
issue_request(1'b0, 32'h0000_4000, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_resp_o != `AXI4_RESP_DECODE_ERROR)) begin
|
||||
$fatal(1, "DMA read DECERR failed");
|
||||
end
|
||||
read_response = `AXI4_RESP_OKAY;
|
||||
read_id = 1'b1;
|
||||
issue_request(1'b0, 32'h0000_4000, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA read ID failure missed");
|
||||
read_id = 1'b0;
|
||||
early_read_last = 1'b1;
|
||||
issue_request(1'b0, 32'h0000_4000, 32'd8);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd15)) $fatal(1, "DMA malformed RLAST missed");
|
||||
early_read_last = 1'b0;
|
||||
suppress_read_last = 1'b1;
|
||||
issue_request(1'b0, 32'h0000_4000, 32'd8);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || busy_o || (error_code_o != 6'd15)) begin
|
||||
$fatal(1, "DMA missing terminal RLAST did not terminate boundedly");
|
||||
end
|
||||
suppress_read_last = 1'b0;
|
||||
read_active = 1'b0;
|
||||
|
||||
s_phase = "epoch/request coincidence";
|
||||
@(negedge clk_i);
|
||||
request_write_i = 1'b0;
|
||||
request_addr_i = 32'h0000_4000;
|
||||
request_bytes_i = 32'd4;
|
||||
request_valid_i = 1'b1;
|
||||
bridge_epoch_i = bridge_epoch_i + 1'b1;
|
||||
#1;
|
||||
if (request_ready_o) $fatal(1, "DMA accepted a request during epoch recovery");
|
||||
@(posedge clk_i);
|
||||
do @(posedge clk_i); while (!request_ready_o);
|
||||
@(negedge clk_i);
|
||||
request_valid_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || (read_bytes_o != 64'd75)) begin
|
||||
$fatal(1, "DMA lost the request coincident with epoch recovery");
|
||||
end
|
||||
|
||||
s_phase = "write response faults";
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
write_response = `AXI4_RESP_SLAVE_ERROR;
|
||||
issue_request(1'b1, 32'h0000_5000, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd16) || (error_resp_o != `AXI4_RESP_SLAVE_ERROR))
|
||||
$fatal(1, "DMA write SLVERR failed");
|
||||
source_index = 8'd0;
|
||||
write_response = `AXI4_RESP_DECODE_ERROR;
|
||||
issue_request(1'b1, 32'h0000_5004, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_resp_o != `AXI4_RESP_DECODE_ERROR)) begin
|
||||
$fatal(1, "DMA write DECERR failed");
|
||||
end
|
||||
source_index = 8'd0;
|
||||
write_response = `AXI4_RESP_OKAY;
|
||||
write_id = 1'b1;
|
||||
issue_request(1'b1, 32'h0000_5008, 32'd4);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (!error_o || (error_code_o != 6'd16)) $fatal(1, "DMA write ID failure missed");
|
||||
write_id = 1'b0;
|
||||
write_source_active = 1'b0;
|
||||
|
||||
s_phase = "read-start abort";
|
||||
issue_request(1'b0, 32'h0000_6000, 32'd4);
|
||||
if (u_dut.s_state_q != 3'd1) $fatal(1, "DMA did not hold ReadStart for abort injection");
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || !aborted_o || busy_o) $fatal(1, "DMA ReadStart abort failed");
|
||||
|
||||
s_phase = "write-start abort";
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
issue_request(1'b1, 32'h0000_5010, 32'd4);
|
||||
if (u_dut.s_state_q != 3'd3) $fatal(1, "DMA did not hold WriteStart for abort injection");
|
||||
abort_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!aborted_o || !aborting_o) begin
|
||||
$fatal(1, "DMA did not latch the WriteStart abort state=%0d drain=%0d", u_dut.s_state_q,
|
||||
u_dut.s_drain_q);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
if (error_o || !aborted_o || busy_o) begin
|
||||
$fatal(1, "DMA WriteStart abort failed error=%0d aborted=%0d busy=%0d code=%0d state=%0d",
|
||||
error_o, aborted_o, busy_o, error_code_o, u_dut.s_state_q);
|
||||
end
|
||||
|
||||
s_phase = "write-data abort";
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b0;
|
||||
issue_request(1'b1, 32'h0000_5020, 32'd16);
|
||||
wait (write_active && (u_dut.s_state_q == 3'd4));
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || !aborted_o || busy_o) $fatal(1, "DMA WriteData abort failed");
|
||||
|
||||
s_phase = "write response abort";
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
allow_write_response = 1'b0;
|
||||
issue_request(1'b1, 32'h0000_5010, 32'd4);
|
||||
while (!bvalid) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
if (!aborting_o) $fatal(1, "DMA did not report aborting while waiting for B");
|
||||
allow_write_response = 1'b1;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
if (error_o || !aborted_o || (error_code_o != 6'd20)) begin
|
||||
$fatal(1, "DMA write-response abort did not drain");
|
||||
end
|
||||
|
||||
s_phase = "write error over abort";
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
write_response = `AXI4_RESP_SLAVE_ERROR;
|
||||
allow_write_response = 1'b0;
|
||||
issue_request(1'b1, 32'h0000_5014, 32'd4);
|
||||
while (!bvalid) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
allow_write_response = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
write_response = `AXI4_RESP_OKAY;
|
||||
if (!error_o || !aborted_o || (error_code_o != 6'd16) ||
|
||||
(error_resp_o != `AXI4_RESP_SLAVE_ERROR)) begin
|
||||
$fatal(1, "DMA write error did not outrank simultaneous abort");
|
||||
end
|
||||
|
||||
s_phase = "accepted abort drain";
|
||||
read_sink_ready = 1'b0;
|
||||
issue_request(1'b0, 32'h0000_6000, 32'd16);
|
||||
while (!read_active) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
if (!aborting_o) $fatal(1, "DMA live aborting status was not asserted");
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
read_sink_ready = 1'b1;
|
||||
if (error_o || !aborted_o || (error_code_o != 6'd20)) begin
|
||||
$fatal(1, "DMA accepted-read abort drain failed");
|
||||
end
|
||||
if (aborting_o) $fatal(1, "DMA live aborting status remained set after drain");
|
||||
|
||||
s_phase = "read error over abort";
|
||||
read_sink_ready = 1'b0;
|
||||
issue_request(1'b0, 32'h0000_6000, 32'd16);
|
||||
while (!read_active) @(posedge clk_i);
|
||||
read_response = `AXI4_RESP_SLAVE_ERROR;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
read_response = `AXI4_RESP_OKAY;
|
||||
read_sink_ready = 1'b1;
|
||||
if (!error_o || !aborted_o || (error_code_o != 6'd15)) begin
|
||||
$fatal(1, "DMA read error did not outrank abort");
|
||||
end
|
||||
|
||||
s_phase = "unaccepted abort cancellation";
|
||||
allow_addresses = 1'b0;
|
||||
issue_request(1'b0, 32'h0000_6000, 32'd4);
|
||||
repeat (2) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
while (!aborted_o) @(posedge clk_i);
|
||||
if (!axi4.arvalid || !busy_o) $fatal(1, "aborted AR was withdrawn before handshake");
|
||||
allow_addresses = 1'b1;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || !aborted_o || (error_code_o != 6'd20)) begin
|
||||
$fatal(1, "DMA unaccepted-address abort drain failed");
|
||||
end
|
||||
|
||||
s_phase = "deterministic random lengths";
|
||||
for (int unsigned transfer = 0; transfer < 12; transfer++) begin
|
||||
automatic int unsigned length = ((transfer * 29) % 95) + 1;
|
||||
automatic int unsigned source_addr = 32'h0000_7000 + (transfer * 32'h100);
|
||||
automatic int unsigned dest_addr = 32'h0000_9000 + (transfer * 32'h100);
|
||||
automatic int unsigned beats = (length + 3) / 4;
|
||||
automatic logic [63:0] read_before = read_bytes_o;
|
||||
automatic logic [63:0] write_before = write_bytes_o;
|
||||
for (int unsigned word_index = 0; word_index < beats; word_index++) begin
|
||||
memory[(source_addr>>2)+word_index] = 32'h600d_0000 ^ (transfer << 8) ^ word_index;
|
||||
memory[(dest_addr>>2)+word_index] = 32'hdead_beef;
|
||||
end
|
||||
source_index = 8'd0;
|
||||
issue_request(1'b0, source_addr, length);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
if (error_o || (read_bytes_o != read_before + length)) begin
|
||||
$fatal(1, "DMA randomized read failed");
|
||||
end
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
issue_request(1'b1, dest_addr, length);
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
if (error_o || (write_bytes_o != write_before + length)) begin
|
||||
$fatal(1, "DMA randomized write failed");
|
||||
end
|
||||
for (int unsigned byte_index = 0; byte_index < length; byte_index++) begin
|
||||
if (memory[(dest_addr >> 2) + (byte_index / 4)][(byte_index%4)*8+:8] !==
|
||||
memory[(source_addr >> 2) + (byte_index / 4)][(byte_index%4)*8+:8]) begin
|
||||
$fatal(1, "DMA randomized copy mismatch");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
allow_addresses = 1'b0;
|
||||
s_phase = "timeout";
|
||||
timeout_i = 32'd1;
|
||||
source_index = 8'd0;
|
||||
write_source_active = 1'b1;
|
||||
issue_request(1'b1, 32'h0000_3000, 32'd4);
|
||||
while (!error_o) @(posedge clk_i);
|
||||
if (!axi4.awvalid || !busy_o || (error_code_o != 6'd17)) begin
|
||||
$fatal(1, "timed-out AW was withdrawn before handshake");
|
||||
end
|
||||
allow_addresses = 1'b1;
|
||||
while (!done_o) @(posedge clk_i);
|
||||
write_source_active = 1'b0;
|
||||
if (!error_o || (error_code_o != 6'd17) || aborted_o) $fatal(1, "DMA timeout failed");
|
||||
|
||||
@(negedge clk_i);
|
||||
counter_clear_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
counter_clear_i = 1'b0;
|
||||
#1;
|
||||
if ((read_bytes_o != 64'd0) || (write_bytes_o != 64'd0) ||
|
||||
(u_dut.s_read_stalls_q != 64'd0) || (u_dut.s_write_stalls_q != 64'd0)) begin
|
||||
$fatal(1, "DMA counter clear failed");
|
||||
end
|
||||
|
||||
$display("APU-P2 DMA tests passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (3000) @(posedge clk_i);
|
||||
$fatal(1, "APU-P2 DMA test timed out in %s state=%0d remaining=%0d", s_phase, u_dut.s_state_q,
|
||||
u_dut.s_remaining_q);
|
||||
end
|
||||
endmodule
|
||||
@@ -45,6 +45,39 @@ module apu_irq_topology_tb;
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) u_apu_axi4_if (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_dma_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_dma_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_i2s_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_i2s_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
@@ -62,15 +95,23 @@ module apu_irq_topology_tb;
|
||||
`include "soc_irq_wiring.svh"
|
||||
|
||||
apb4_apu u_apu (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.owner_i (s_resource_owner[7]),
|
||||
.owner_lock_i (s_resource_owner_lock[7]),
|
||||
.quiesce_i (s_resource_quiesce[7]),
|
||||
.resource_reset_i(s_resource_reset[7]),
|
||||
.apb4 (u_apu_apb4_if),
|
||||
.idle_o (s_apu_idle),
|
||||
.irq_o (s_apu_irq_raw)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.owner_i (s_resource_owner[7]),
|
||||
.owner_lock_i (s_resource_owner_lock[7]),
|
||||
.quiesce_i (s_resource_quiesce[7]),
|
||||
.resource_reset_i (s_resource_reset[7]),
|
||||
.bridge_epoch_i (8'd0),
|
||||
.i2s_tx_underrun_i(1'b0),
|
||||
.i2s_rx_overrun_i (1'b0),
|
||||
.apb4 (u_apu_apb4_if),
|
||||
.axi4 (u_apu_axi4_if),
|
||||
.dma_tx_axis (u_dma_tx_axis),
|
||||
.dma_rx_axis (u_dma_rx_axis),
|
||||
.i2s_tx_axis (u_i2s_tx_axis),
|
||||
.i2s_rx_axis (u_i2s_rx_axis),
|
||||
.idle_o (s_apu_idle),
|
||||
.irq_o (s_apu_irq_raw)
|
||||
);
|
||||
|
||||
resource_controller #(
|
||||
@@ -214,6 +255,37 @@ apb4_apu u_apu (
|
||||
|
||||
initial begin
|
||||
drive_apb_idle();
|
||||
u_apu_axi4_if.awready = 1'b0;
|
||||
u_apu_axi4_if.wready = 1'b0;
|
||||
u_apu_axi4_if.bid = '0;
|
||||
u_apu_axi4_if.bresp = '0;
|
||||
u_apu_axi4_if.buser = '0;
|
||||
u_apu_axi4_if.bvalid = 1'b0;
|
||||
u_apu_axi4_if.arready = 1'b0;
|
||||
u_apu_axi4_if.rid = '0;
|
||||
u_apu_axi4_if.rdata = '0;
|
||||
u_apu_axi4_if.rresp = '0;
|
||||
u_apu_axi4_if.rlast = 1'b0;
|
||||
u_apu_axi4_if.ruser = '0;
|
||||
u_apu_axi4_if.rvalid = 1'b0;
|
||||
u_dma_tx_axis.tdata = '0;
|
||||
u_dma_tx_axis.tkeep = '1;
|
||||
u_dma_tx_axis.tstrb = '1;
|
||||
u_dma_tx_axis.tlast = 1'b0;
|
||||
u_dma_tx_axis.tid = '0;
|
||||
u_dma_tx_axis.tdest = '0;
|
||||
u_dma_tx_axis.tuser = '0;
|
||||
u_dma_tx_axis.tvalid = 1'b0;
|
||||
u_dma_rx_axis.tready = 1'b1;
|
||||
u_i2s_tx_axis.tready = 1'b1;
|
||||
u_i2s_rx_axis.tdata = '0;
|
||||
u_i2s_rx_axis.tkeep = '1;
|
||||
u_i2s_rx_axis.tstrb = '1;
|
||||
u_i2s_rx_axis.tlast = 1'b0;
|
||||
u_i2s_rx_axis.tid = '0;
|
||||
u_i2s_rx_axis.tdest = '0;
|
||||
u_i2s_rx_axis.tuser = '0;
|
||||
u_i2s_rx_axis.tvalid = 1'b0;
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
// Verification-only deterministic endpoint for the production P2 ring scheduler.
|
||||
module apu_p2_backend (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic abort_i,
|
||||
input logic hold_result_i,
|
||||
input logic result_error_i,
|
||||
input logic [ 5:0] result_code_i,
|
||||
input logic [ 3:0] result_stage_i,
|
||||
input logic [ 1:0] result_resp_i,
|
||||
input logic job_valid_i,
|
||||
output logic job_ready_o,
|
||||
input logic [1023:0] descriptor_i,
|
||||
input logic [ 7:0] index_i,
|
||||
output logic result_valid_o,
|
||||
input logic result_ready_i,
|
||||
output logic result_error_o,
|
||||
output logic [ 5:0] result_code_o,
|
||||
output logic [ 3:0] result_stage_o,
|
||||
output logic [ 1:0] result_resp_o,
|
||||
output logic [ 31:0] input_used_o,
|
||||
output logic [ 31:0] output_bytes_o,
|
||||
output logic [ 31:0] frames_o,
|
||||
output logic [ 31:0] source_info_o,
|
||||
output logic [ 31:0] cycles_o,
|
||||
output logic [ 31:0] detail_o,
|
||||
output logic [ 31:0] accepted_jobs_o
|
||||
);
|
||||
logic s_pending_q;
|
||||
logic s_error_q;
|
||||
logic [ 5:0] s_code_q;
|
||||
logic [ 3:0] s_stage_q;
|
||||
logic [ 1:0] s_resp_q;
|
||||
logic [ 7:0] s_index_q;
|
||||
logic [31:0] s_input_length_q;
|
||||
logic [31:0] s_output_capacity_q;
|
||||
logic [31:0] s_accepted_jobs_q;
|
||||
|
||||
assign job_ready_o = !s_pending_q && !abort_i;
|
||||
assign result_valid_o = s_pending_q && !hold_result_i;
|
||||
assign result_error_o = s_error_q;
|
||||
assign result_code_o = s_code_q;
|
||||
assign result_stage_o = s_stage_q;
|
||||
assign result_resp_o = s_resp_q;
|
||||
assign input_used_o = s_input_length_q;
|
||||
assign output_bytes_o = s_output_capacity_q;
|
||||
assign frames_o = s_input_length_q >> 2;
|
||||
assign source_info_o = 32'h0004_0000 | {24'd0, s_index_q};
|
||||
assign cycles_o = 32'd64 + {24'd0, s_index_q};
|
||||
assign detail_o = 32'h5032_0000 | {24'd0, s_index_q};
|
||||
assign accepted_jobs_o = s_accepted_jobs_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_pending_q <= 1'b0;
|
||||
s_error_q <= 1'b0;
|
||||
s_code_q <= 6'd0;
|
||||
s_stage_q <= 4'd0;
|
||||
s_resp_q <= 2'd0;
|
||||
s_index_q <= 8'd0;
|
||||
s_input_length_q <= 32'd0;
|
||||
s_output_capacity_q <= 32'd0;
|
||||
s_accepted_jobs_q <= 32'd0;
|
||||
end else begin
|
||||
if (abort_i) s_pending_q <= 1'b0;
|
||||
if (job_valid_i && job_ready_o) begin
|
||||
s_pending_q <= 1'b1;
|
||||
s_error_q <= result_error_i;
|
||||
s_code_q <= result_code_i;
|
||||
s_stage_q <= result_stage_i;
|
||||
s_resp_q <= result_resp_i;
|
||||
s_index_q <= index_i;
|
||||
s_input_length_q <= descriptor_i[(3*32)+:32];
|
||||
s_output_capacity_q <= descriptor_i[(5*32)+:32];
|
||||
s_accepted_jobs_q <= s_accepted_jobs_q + 1'b1;
|
||||
end
|
||||
if (result_valid_o && result_ready_i) s_pending_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// SPDX-License-Identifier: MulanPSL-2.0
|
||||
|
||||
// Verification-only direct/ring client for the production APU DMA.
|
||||
module apu_p2_transport_backend (
|
||||
// verilog_format: off -- preserve job, request, stream, and result columns
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic abort_i,
|
||||
input logic direct_start_i,
|
||||
input logic [31:0] direct_source_i,
|
||||
input logic [31:0] direct_destination_i,
|
||||
input logic [31:0] direct_bytes_i,
|
||||
input logic job_valid_i,
|
||||
output logic job_ready_o,
|
||||
input logic [1023:0] descriptor_i,
|
||||
input logic [7:0] index_i,
|
||||
input logic hold_result_i,
|
||||
input logic inject_result_error_i,
|
||||
input logic [5:0] inject_result_code_i,
|
||||
input logic [3:0] inject_result_stage_i,
|
||||
input logic [1:0] inject_result_resp_i,
|
||||
output logic result_valid_o,
|
||||
input logic result_ready_i,
|
||||
output logic result_error_o,
|
||||
output logic [5:0] result_code_o,
|
||||
output logic [3:0] result_stage_o,
|
||||
output logic [1:0] result_resp_o,
|
||||
output logic [31:0] input_used_o,
|
||||
output logic [31:0] output_bytes_o,
|
||||
output logic [31:0] frames_o,
|
||||
output logic [31:0] source_info_o,
|
||||
output logic [31:0] cycles_o,
|
||||
output logic [31:0] detail_o,
|
||||
output logic [31:0] accepted_jobs_o,
|
||||
output logic request_valid_o,
|
||||
input logic request_ready_i,
|
||||
output logic request_write_o,
|
||||
output logic [31:0] request_addr_o,
|
||||
output logic [31:0] request_bytes_o,
|
||||
axi4_stream_if.sink read_axis,
|
||||
axi4_stream_if.source write_axis,
|
||||
input logic dma_done_i,
|
||||
input logic dma_error_i,
|
||||
input logic dma_aborted_i,
|
||||
input logic [5:0] dma_error_code_i,
|
||||
input logic [3:0] dma_error_stage_i,
|
||||
input logic [1:0] dma_error_resp_i,
|
||||
output logic busy_o,
|
||||
output logic direct_done_o,
|
||||
output logic direct_error_o
|
||||
// verilog_format: on
|
||||
);
|
||||
typedef enum logic [3:0] {
|
||||
Idle,
|
||||
ReadRequest,
|
||||
ReadData,
|
||||
ReadWait,
|
||||
WriteRequest,
|
||||
WriteData,
|
||||
WriteWait,
|
||||
Result
|
||||
} state_e;
|
||||
|
||||
state_e s_state_q;
|
||||
logic s_ring_q;
|
||||
logic [ 7:0] s_index_q;
|
||||
logic [31:0] s_source_addr_q;
|
||||
logic [31:0] s_destination_addr_q;
|
||||
logic [31:0] s_bytes_q;
|
||||
logic [ 5:0] s_read_index_q;
|
||||
logic [ 5:0] s_write_index_q;
|
||||
logic [31:0] s_buffer_q [32];
|
||||
logic [ 3:0] s_keep_q [32];
|
||||
logic s_result_error_q;
|
||||
logic [ 5:0] s_result_code_q;
|
||||
logic [ 3:0] s_result_stage_q;
|
||||
logic [ 1:0] s_result_resp_q;
|
||||
logic [31:0] s_accepted_jobs_q;
|
||||
logic s_direct_done_q;
|
||||
logic s_direct_error_q;
|
||||
logic s_abort_pending_q;
|
||||
|
||||
function automatic logic [31:0] bounded_bytes(input logic [31:0] length_i,
|
||||
input logic [31:0] capacity_i);
|
||||
return (length_i < capacity_i) ? length_i : capacity_i;
|
||||
endfunction
|
||||
|
||||
assign job_ready_o = (s_state_q == Idle) && !direct_start_i && !abort_i;
|
||||
assign result_valid_o = (s_state_q == Result) && !hold_result_i;
|
||||
assign result_error_o = s_result_error_q;
|
||||
assign result_code_o = s_result_code_q;
|
||||
assign result_stage_o = s_result_stage_q;
|
||||
assign result_resp_o = s_result_resp_q;
|
||||
assign input_used_o = s_bytes_q;
|
||||
assign output_bytes_o = s_bytes_q;
|
||||
assign frames_o = s_bytes_q >> 2;
|
||||
assign source_info_o = 32'h0004_0000 | {24'd0, s_index_q};
|
||||
assign cycles_o = 32'd64 + {24'd0, s_index_q};
|
||||
assign detail_o = 32'h5032_0000 | {24'd0, s_index_q};
|
||||
assign accepted_jobs_o = s_accepted_jobs_q;
|
||||
assign request_valid_o = !abort_i && ((s_state_q == ReadRequest) || (s_state_q == WriteRequest));
|
||||
assign request_write_o = s_state_q == WriteRequest;
|
||||
assign request_addr_o = request_write_o ? s_destination_addr_q : s_source_addr_q;
|
||||
assign request_bytes_o = s_bytes_q;
|
||||
assign read_axis.tready = s_state_q == ReadData;
|
||||
assign write_axis.tdata = s_buffer_q[s_write_index_q[4:0]];
|
||||
assign write_axis.tkeep = s_keep_q[s_write_index_q[4:0]];
|
||||
assign write_axis.tstrb = s_keep_q[s_write_index_q[4:0]];
|
||||
assign write_axis.tlast = s_write_index_q == s_read_index_q;
|
||||
assign write_axis.tid = '0;
|
||||
assign write_axis.tdest = '0;
|
||||
assign write_axis.tuser = '0;
|
||||
assign write_axis.tvalid = s_state_q == WriteData;
|
||||
assign busy_o = s_state_q != Idle;
|
||||
assign direct_done_o = s_direct_done_q;
|
||||
assign direct_error_o = s_direct_error_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_ring_q <= 1'b0;
|
||||
s_index_q <= 8'd0;
|
||||
s_source_addr_q <= 32'd0;
|
||||
s_destination_addr_q <= 32'd0;
|
||||
s_bytes_q <= 32'd0;
|
||||
s_read_index_q <= 6'd0;
|
||||
s_write_index_q <= 6'd0;
|
||||
s_result_error_q <= 1'b0;
|
||||
s_result_code_q <= 6'd0;
|
||||
s_result_stage_q <= 4'd0;
|
||||
s_result_resp_q <= 2'd0;
|
||||
s_accepted_jobs_q <= 32'd0;
|
||||
s_direct_done_q <= 1'b0;
|
||||
s_direct_error_q <= 1'b0;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
end else begin
|
||||
s_direct_done_q <= 1'b0;
|
||||
if (abort_i && (s_state_q != Idle)) s_abort_pending_q <= 1'b1;
|
||||
unique case (s_state_q)
|
||||
Idle: begin
|
||||
if (job_valid_i && job_ready_o) begin
|
||||
s_ring_q <= 1'b1;
|
||||
s_index_q <= index_i;
|
||||
s_source_addr_q <= descriptor_i[(2*32)+:32];
|
||||
s_destination_addr_q <= descriptor_i[(4*32)+:32];
|
||||
s_bytes_q <= bounded_bytes(descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32]);
|
||||
s_read_index_q <= 6'd0;
|
||||
s_write_index_q <= 6'd0;
|
||||
s_result_error_q <= 1'b0;
|
||||
s_result_code_q <= 6'd0;
|
||||
s_result_stage_q <= 4'd0;
|
||||
s_result_resp_q <= 2'd0;
|
||||
s_accepted_jobs_q <= s_accepted_jobs_q + 1'b1;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
s_state_q <= ReadRequest;
|
||||
end else if (direct_start_i && !abort_i) begin
|
||||
s_ring_q <= 1'b0;
|
||||
s_index_q <= 8'd0;
|
||||
s_source_addr_q <= direct_source_i;
|
||||
s_destination_addr_q <= direct_destination_i;
|
||||
s_bytes_q <= direct_bytes_i;
|
||||
s_read_index_q <= 6'd0;
|
||||
s_write_index_q <= 6'd0;
|
||||
s_direct_error_q <= 1'b0;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
s_state_q <= ReadRequest;
|
||||
end
|
||||
end
|
||||
ReadRequest: begin
|
||||
if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (request_ready_i) begin
|
||||
s_state_q <= ReadData;
|
||||
end
|
||||
end
|
||||
ReadData: begin
|
||||
if (read_axis.tvalid && read_axis.tready) begin
|
||||
s_buffer_q[s_read_index_q[4:0]] <= read_axis.tdata;
|
||||
s_keep_q[s_read_index_q[4:0]] <= read_axis.tkeep;
|
||||
if (read_axis.tlast) s_state_q <= ReadWait;
|
||||
else s_read_index_q <= s_read_index_q + 1'b1;
|
||||
end
|
||||
if (dma_done_i && (s_abort_pending_q || dma_aborted_i)) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (dma_done_i && dma_error_i) begin
|
||||
s_result_error_q <= 1'b1;
|
||||
s_result_code_q <= dma_error_code_i;
|
||||
s_result_stage_q <= dma_error_stage_i;
|
||||
s_result_resp_q <= dma_error_resp_i;
|
||||
s_state_q <= s_ring_q ? Result : Idle;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
ReadWait: begin
|
||||
if (dma_done_i) begin
|
||||
if (s_abort_pending_q || dma_aborted_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (dma_error_i) begin
|
||||
s_result_error_q <= 1'b1;
|
||||
s_result_code_q <= dma_error_code_i;
|
||||
s_result_stage_q <= dma_error_stage_i;
|
||||
s_result_resp_q <= dma_error_resp_i;
|
||||
s_state_q <= s_ring_q ? Result : Idle;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_write_index_q <= 6'd0;
|
||||
s_state_q <= WriteRequest;
|
||||
end
|
||||
end
|
||||
end
|
||||
WriteRequest: begin
|
||||
if (abort_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (request_ready_i) begin
|
||||
s_state_q <= WriteData;
|
||||
end
|
||||
end
|
||||
WriteData: begin
|
||||
if (dma_done_i && (s_abort_pending_q || dma_aborted_i)) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (dma_done_i && dma_error_i) begin
|
||||
s_result_error_q <= 1'b1;
|
||||
s_result_code_q <= dma_error_code_i;
|
||||
s_result_stage_q <= dma_error_stage_i;
|
||||
s_result_resp_q <= dma_error_resp_i;
|
||||
s_state_q <= s_ring_q ? Result : Idle;
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= 1'b1;
|
||||
end
|
||||
end else if (write_axis.tvalid && write_axis.tready) begin
|
||||
if (write_axis.tlast) s_state_q <= WriteWait;
|
||||
else s_write_index_q <= s_write_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
WriteWait: begin
|
||||
if (dma_done_i) begin
|
||||
s_result_error_q <= dma_error_i || inject_result_error_i;
|
||||
s_result_code_q <= dma_error_i ? dma_error_code_i : inject_result_code_i;
|
||||
s_result_stage_q <= dma_error_i ? dma_error_stage_i : inject_result_stage_i;
|
||||
s_result_resp_q <= dma_error_i ? dma_error_resp_i : inject_result_resp_i;
|
||||
if (s_abort_pending_q || dma_aborted_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
end else begin
|
||||
s_state_q <= s_ring_q ? Result : Idle;
|
||||
end
|
||||
if (!s_ring_q) begin
|
||||
s_direct_done_q <= 1'b1;
|
||||
s_direct_error_q <= dma_error_i || dma_aborted_i || s_abort_pending_q;
|
||||
end
|
||||
end
|
||||
end
|
||||
Result: begin
|
||||
if (result_valid_o && result_ready_i) begin
|
||||
s_state_q <= Idle;
|
||||
s_abort_pending_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
default: s_state_q <= Idle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
`ifndef SYNTHESIS
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (rst_n_i && job_valid_i && job_ready_o && ((bounded_bytes(
|
||||
descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32]
|
||||
) == 32'd0) || (bounded_bytes(
|
||||
descriptor_i[(3*32)+:32], descriptor_i[(5*32)+:32]
|
||||
) > 32'd128))) begin
|
||||
$fatal(1, "apu_p2_transport_backend: ring transfer bytes must be in [1, 128]");
|
||||
end
|
||||
if (rst_n_i && direct_start_i &&
|
||||
((direct_bytes_i == 32'd0) || (direct_bytes_i > 32'd128))) begin
|
||||
$fatal(1, "apu_p2_transport_backend: direct transfer bytes must be in [1, 128]");
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
+146
-17
@@ -4,7 +4,7 @@
|
||||
|
||||
module apu_reg_tb;
|
||||
localparam logic [31:0] ApuBase = 32'h1001_3000;
|
||||
localparam int unsigned RegisterCount = 95;
|
||||
localparam int unsigned RegisterCount = 96;
|
||||
localparam logic [1:0] AccessRo = 2'd0;
|
||||
localparam logic [1:0] AccessWo = 2'd1;
|
||||
localparam logic [1:0] AccessRw = 2'd2;
|
||||
@@ -23,24 +23,98 @@ module apu_reg_tb;
|
||||
logic [31:0] s_reg_reset [RegisterCount];
|
||||
logic s_reg_full_strobe [RegisterCount];
|
||||
int unsigned s_reg_count;
|
||||
string s_phase;
|
||||
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) axi4 (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) i2s_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) i2s_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
assign axi4.awready = 1'b0;
|
||||
assign axi4.wready = 1'b0;
|
||||
assign axi4.bid = '0;
|
||||
assign axi4.bresp = '0;
|
||||
assign axi4.buser = '0;
|
||||
assign axi4.bvalid = 1'b0;
|
||||
assign axi4.arready = 1'b0;
|
||||
assign axi4.rid = '0;
|
||||
assign axi4.rdata = '0;
|
||||
assign axi4.rresp = '0;
|
||||
assign axi4.rlast = 1'b0;
|
||||
assign axi4.ruser = '0;
|
||||
assign axi4.rvalid = 1'b0;
|
||||
assign dma_tx_axis.tdata = '0;
|
||||
assign dma_tx_axis.tkeep = '1;
|
||||
assign dma_tx_axis.tstrb = '1;
|
||||
assign dma_tx_axis.tlast = 1'b0;
|
||||
assign dma_tx_axis.tid = '0;
|
||||
assign dma_tx_axis.tdest = '0;
|
||||
assign dma_tx_axis.tuser = '0;
|
||||
assign dma_tx_axis.tvalid = 1'b0;
|
||||
assign dma_rx_axis.tready = 1'b1;
|
||||
assign i2s_tx_axis.tready = 1'b1;
|
||||
assign i2s_rx_axis.tdata = '0;
|
||||
assign i2s_rx_axis.tkeep = '1;
|
||||
assign i2s_rx_axis.tstrb = '1;
|
||||
assign i2s_rx_axis.tlast = 1'b0;
|
||||
assign i2s_rx_axis.tid = '0;
|
||||
assign i2s_rx_axis.tdest = '0;
|
||||
assign i2s_rx_axis.tuser = '0;
|
||||
assign i2s_rx_axis.tvalid = 1'b0;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
apb4_apu u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.owner_i (owner_i),
|
||||
.owner_lock_i (owner_lock_i),
|
||||
.quiesce_i (quiesce_i),
|
||||
.resource_reset_i(resource_reset_i),
|
||||
.apb4 (apb4),
|
||||
.idle_o (idle_o),
|
||||
.irq_o (irq_o)
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.owner_i (owner_i),
|
||||
.owner_lock_i (owner_lock_i),
|
||||
.quiesce_i (quiesce_i),
|
||||
.resource_reset_i (resource_reset_i),
|
||||
.bridge_epoch_i (8'd0),
|
||||
.i2s_tx_underrun_i(1'b0),
|
||||
.i2s_rx_overrun_i (1'b0),
|
||||
.apb4 (apb4),
|
||||
.axi4 (axi4),
|
||||
.dma_tx_axis (dma_tx_axis),
|
||||
.dma_rx_axis (dma_rx_axis),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.idle_o (idle_o),
|
||||
.irq_o (irq_o)
|
||||
);
|
||||
|
||||
function automatic logic [31:0] legal_write_value(input logic [11:0] offset_i);
|
||||
@@ -50,6 +124,7 @@ module apu_reg_tb;
|
||||
`APB4_APU__ERROR_STATUS: legal_write_value = 32'h0000_0001;
|
||||
`APB4_APU__SEQUENCER_TIMEOUT: legal_write_value = 32'h1234_5678;
|
||||
`APB4_APU__STREAM_ROUTE: legal_write_value = 32'd0;
|
||||
`APB4_APU__STREAM_WATERMARK: legal_write_value = 32'h0000_2020;
|
||||
`APB4_APU__READ_BASE: legal_write_value = 32'h3000_0000;
|
||||
`APB4_APU__READ_LIMIT: legal_write_value = 32'h3000_ffff;
|
||||
`APB4_APU__WRITE_BASE: legal_write_value = 32'h3001_0000;
|
||||
@@ -116,7 +191,7 @@ module apu_reg_tb;
|
||||
s_reg_count = 0;
|
||||
add_register(`APB4_APU__IP_ID, AccessRo, 32'h4150_5530, 1'b0);
|
||||
add_register(`APB4_APU__IP_VERSION, AccessRo, 32'h0001_0000, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY0, AccessRo, 32'h0000_0018, 1'b0);
|
||||
add_register(`APB4_APU__CAPABILITY1, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__COMMAND, AccessWo, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__STATUS, AccessRo, 32'h0000_0100, 1'b0);
|
||||
@@ -128,7 +203,7 @@ module apu_reg_tb;
|
||||
add_register(`APB4_APU__ERROR_DETAIL, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__SEQUENCER_TIMEOUT, AccessRw, 32'h0000_ffff, 1'b0);
|
||||
add_register(`APB4_APU__STREAM_ROUTE, AccessRw, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__STREAM_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__STREAM_STATUS, AccessRo, 32'h0000_0014, 1'b0);
|
||||
add_register(`APB4_APU__OWNER_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__READ_BASE, AccessRw, 32'hffff_ffff, 1'b1);
|
||||
add_register(`APB4_APU__READ_LIMIT, AccessRw, 32'd0, 1'b1);
|
||||
@@ -138,6 +213,7 @@ module apu_reg_tb;
|
||||
add_register(`APB4_APU__ABI_DIGEST, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__SEQUENCER_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__SEQUENCER_RETIRED, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__STREAM_WATERMARK, AccessRw, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__MC_IMAGE_ADDRESS, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__MC_IMAGE_SIZE, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__MC_EXPECTED_CRC, AccessRw, 32'd0, 1'b1);
|
||||
@@ -168,7 +244,7 @@ module apu_reg_tb;
|
||||
add_register(`APB4_APU__RING_HEAD, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__RING_TAIL, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__RING_CONTROL, AccessRw, 32'd0, 1'b1);
|
||||
add_register(`APB4_APU__RING_STATUS, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__RING_STATUS, AccessRo, 32'h0000_0004, 1'b0);
|
||||
add_register(`APB4_APU__RING_COMPLETED, AccessRo, 32'd0, 1'b0);
|
||||
add_register(`APB4_APU__RING_COALESCE, AccessRw, 32'h0001_0001, 1'b1);
|
||||
add_register(`APB4_APU__RING_DOORBELL, AccessWo, 32'd0, 1'b1);
|
||||
@@ -269,7 +345,9 @@ module apu_reg_tb;
|
||||
if (s_wait_cycles != 1) begin
|
||||
$fatal(1, "APU write wait count mismatch at %h: %0d", offset_i, s_wait_cycles);
|
||||
end
|
||||
if (apb4.pslverr != expected_err_i) $fatal(1, "APU write response mismatch at %h", offset_i);
|
||||
if (apb4.pslverr != expected_err_i) begin
|
||||
$fatal(1, "APU write response mismatch at %h in %s", offset_i, s_phase);
|
||||
end
|
||||
s_response_err = apb4.pslverr;
|
||||
#2;
|
||||
if (!apb4.pready || (apb4.pslverr != s_response_err)) begin
|
||||
@@ -307,7 +385,9 @@ module apu_reg_tb;
|
||||
if (s_wait_cycles != 1) begin
|
||||
$fatal(1, "APU read wait count mismatch at %h: %0d", offset_i, s_wait_cycles);
|
||||
end
|
||||
if (apb4.pslverr != expected_err_i) $fatal(1, "APU read response mismatch at %h", offset_i);
|
||||
if (apb4.pslverr != expected_err_i) begin
|
||||
$fatal(1, "APU read response mismatch at %h during %s", offset_i, s_phase);
|
||||
end
|
||||
s_response_data = apb4.prdata;
|
||||
s_response_err = apb4.pslverr;
|
||||
#2;
|
||||
@@ -383,10 +463,12 @@ module apu_reg_tb;
|
||||
|
||||
initial begin
|
||||
initialize_register_table();
|
||||
s_phase = "initial reset matrix";
|
||||
drive_apb_idle();
|
||||
hard_reset();
|
||||
if (!idle_o || irq_o) $fatal(1, "APU reset lifecycle outputs mismatch");
|
||||
|
||||
s_phase = "access matrix";
|
||||
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
|
||||
hard_reset();
|
||||
if (s_reg_access[register_index] == AccessWo) begin
|
||||
@@ -429,6 +511,7 @@ module apu_reg_tb;
|
||||
endcase
|
||||
end
|
||||
|
||||
s_phase = "valid byte strobes";
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_00aa, 4'h1, 1'b0);
|
||||
expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'h0000_ffaa);
|
||||
@@ -439,6 +522,7 @@ module apu_reg_tb;
|
||||
apb_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'hdd00_0000, 4'h8, 1'b0);
|
||||
expect_read(`APB4_APU__SEQUENCER_TIMEOUT, 32'hddcc_bbaa);
|
||||
|
||||
s_phase = "invalid full strobes";
|
||||
for (int unsigned register_index = 0; register_index < RegisterCount; register_index++) begin
|
||||
if (s_reg_full_strobe[register_index]) begin
|
||||
hard_reset();
|
||||
@@ -452,9 +536,12 @@ module apu_reg_tb;
|
||||
end
|
||||
end
|
||||
|
||||
s_phase = "rejected values";
|
||||
check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0080, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__COMMAND, 32'h0000_0002, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
for (int bit_index = 0; bit_index < 6; bit_index++) begin
|
||||
if ((bit_index != `APB4_APU__COMMAND_SOFT_RESET) &&
|
||||
(bit_index != `APB4_APU__COMMAND_ABORT) &&
|
||||
(bit_index != `APB4_APU__COMMAND_CLEAR_COUNTERS)) begin
|
||||
check_rejected_write(`APB4_APU__COMMAND, 32'd1 << bit_index,
|
||||
`APB4_APU__ERROR_CODE_UNSUPPORTED);
|
||||
@@ -469,6 +556,10 @@ module apu_reg_tb;
|
||||
check_rejected_write(`APB4_APU__SEQUENCER_TIMEOUT, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__STREAM_ROUTE, 32'h0000_0001, `APB4_APU__ERROR_CODE_UNSUPPORTED);
|
||||
check_rejected_write(`APB4_APU__STREAM_ROUTE, 32'h0000_0004, `APB4_APU__ERROR_CODE_UNSUPPORTED);
|
||||
check_rejected_write(`APB4_APU__STREAM_WATERMARK, 32'h0000_0041,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__STREAM_WATERMARK, 32'h0001_0000,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__DMA_TIMEOUT, 32'd0, `APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
check_rejected_write(`APB4_APU__MC_IMAGE_ADDRESS, 32'h3002_0001,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
@@ -500,6 +591,7 @@ module apu_reg_tb;
|
||||
check_rejected_write(`APB4_APU__PERF_CONTROL, 32'h0000_0008,
|
||||
`APB4_APU__ERROR_CODE_INVALID_CONFIG);
|
||||
|
||||
s_phase = "set over clear";
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__IRQ_TEST, 32'h0000_0001, 4'hf, 1'b0);
|
||||
force u_dut.u_apu_reg.s_irq_set = 11'h001;
|
||||
@@ -509,34 +601,71 @@ module apu_reg_tb;
|
||||
apb_write(`APB4_APU__IRQ_STATE, 32'h0000_0001, 4'h1, 1'b0);
|
||||
expect_read(`APB4_APU__IRQ_STATE, 32'd0);
|
||||
|
||||
s_phase = "soft reset";
|
||||
hard_reset();
|
||||
program_rw_registers();
|
||||
apb_write(`APB4_APU__COMMAND, 32'h0000_0004, 4'hf, 1'b0);
|
||||
check_reset_state(1'b1);
|
||||
|
||||
s_phase = "resource reset";
|
||||
hard_reset();
|
||||
program_rw_registers();
|
||||
force u_dut.s_dma_busy = 1'b1;
|
||||
@(negedge clk_i);
|
||||
resource_reset_i = 1'b1;
|
||||
repeat (3) @(posedge clk_i);
|
||||
if (u_dut.s_resource_reset_apply_q || (u_dut.u_apu_reg.s_timeout_q[0] != 32'h1234_5678)) begin
|
||||
$fatal(1, "resource reset applied before transport drain");
|
||||
end
|
||||
force u_dut.s_dma_busy = 1'b0;
|
||||
wait (u_dut.s_resource_reset_apply_q);
|
||||
release u_dut.s_dma_busy;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
resource_reset_i = 1'b0;
|
||||
check_reset_state(1'b1);
|
||||
|
||||
s_phase = "performance enable";
|
||||
hard_reset();
|
||||
force u_dut.s_dma_busy = 1'b1;
|
||||
repeat (5) @(posedge clk_i);
|
||||
if (u_dut.s_active_cycles_q != 64'd0) begin
|
||||
$fatal(1, "disabled performance counter advanced");
|
||||
end
|
||||
force u_dut.s_dma_busy = 1'b0;
|
||||
release u_dut.s_dma_busy;
|
||||
hard_reset();
|
||||
apb_write(`APB4_APU__PERF_CONTROL, 32'h0000_0001, 4'hf, 1'b0);
|
||||
force u_dut.s_dma_busy = 1'b1;
|
||||
repeat (5) @(posedge clk_i);
|
||||
if (u_dut.s_active_cycles_q == 64'd0) begin
|
||||
$fatal(1, "enabled performance counter did not advance");
|
||||
end
|
||||
force u_dut.s_dma_busy = 1'b0;
|
||||
release u_dut.s_dma_busy;
|
||||
|
||||
hard_reset();
|
||||
owner_i = 2'd1;
|
||||
owner_lock_i = 1'b1;
|
||||
quiesce_i = 1'b1;
|
||||
expect_read(`APB4_APU__OWNER_STATUS, 32'h0000_0301);
|
||||
apb_read(12'h060, s_value, 1'b1);
|
||||
apb_read(12'h064, s_value, 1'b1);
|
||||
apb_read(12'h001, s_value, 1'b1);
|
||||
|
||||
s_phase = "command state predicates";
|
||||
hard_reset();
|
||||
force u_dut.u_apu_reg.core_busy_i = 1'b1;
|
||||
force u_dut.u_apu_reg.core_idle_i = 1'b0;
|
||||
apb_write(`APB4_APU__COMMAND, 32'h0000_0002, 4'hf, 1'b0);
|
||||
apb_write(`APB4_APU__COMMAND, 32'h0000_0040, 4'hf, 1'b1);
|
||||
expect_read(`APB4_APU__ERROR_STATUS, 32'd1 | (32'(`APB4_APU__ERROR_CODE_INVALID_CONFIG) << 1));
|
||||
|
||||
$display("APU-P1 complete APB register matrix passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (20000) @(posedge clk_i);
|
||||
$fatal(1, "APU-P1 APB register matrix timed out");
|
||||
$fatal(1, "APU-P1 APB register matrix timed out in %s", s_phase);
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,626 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "apu_define.svh"
|
||||
|
||||
module apu_ring_scheduler_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic soft_reset_i = 1'b0;
|
||||
logic counter_clear_i = 1'b0;
|
||||
logic abort_i = 1'b0;
|
||||
logic quiesce_i = 1'b0;
|
||||
logic start_i = 1'b0;
|
||||
logic [31:0] ring_base_i = 32'h0000_1000;
|
||||
logic [ 8:0] ring_size_i = 9'd2;
|
||||
logic [ 7:0] ring_tail_i = 8'd0;
|
||||
logic ring_enable_i = 1'b0;
|
||||
logic stop_on_error_i = 1'b0;
|
||||
logic [ 7:0] coalesce_count_i = 8'd3;
|
||||
logic [15:0] coalesce_timeout_i = 16'd4;
|
||||
logic dma_request_valid_o, dma_request_ready_i, dma_request_write_o;
|
||||
logic [31:0] dma_request_addr_o, dma_request_bytes_o;
|
||||
logic dma_done_i = 1'b0;
|
||||
logic dma_error_i = 1'b0;
|
||||
logic [ 5:0] dma_error_code_i = 6'd0;
|
||||
logic [ 3:0] dma_error_stage_i = 4'd0;
|
||||
logic [ 1:0] dma_error_resp_i = 2'd0;
|
||||
logic [31:0] dma_error_addr_i = 32'd0;
|
||||
logic backend_job_valid, backend_job_ready;
|
||||
logic [1023:0] backend_descriptor;
|
||||
logic [ 7:0] backend_index;
|
||||
logic backend_result_valid, backend_result_ready;
|
||||
logic backend_result_error = 1'b0;
|
||||
logic [5:0] backend_result_code = 6'd0;
|
||||
logic [3:0] backend_result_stage = 4'd0;
|
||||
logic [1:0] backend_result_resp = 2'd0;
|
||||
logic backend_latched_error;
|
||||
logic [5:0] backend_latched_code;
|
||||
logic [3:0] backend_latched_stage;
|
||||
logic [1:0] backend_latched_resp;
|
||||
logic [31:0] backend_input_used, backend_output_bytes, backend_frames;
|
||||
logic [31:0] backend_source_info, backend_cycles, backend_detail;
|
||||
logic backend_hold = 1'b0;
|
||||
logic hold_dma_request = 1'b0;
|
||||
logic hold_dma_read = 1'b0;
|
||||
logic hold_dma_write = 1'b0;
|
||||
logic [31:0] backend_accepted;
|
||||
logic [31:0] job_status_o, ring_status_o;
|
||||
logic [ 7:0] ring_head_o;
|
||||
logic [31:0] ring_completed_o;
|
||||
logic ring_event_o, error_event_o, abort_done_o, aborting_o, idle_o;
|
||||
logic [ 5:0] error_code_o;
|
||||
logic [ 3:0] error_stage_o;
|
||||
logic [ 1:0] error_resp_o;
|
||||
logic [ 7:0] error_index_o;
|
||||
logic [31:0] error_addr_o;
|
||||
logic [31:0] memory [0:63];
|
||||
logic dma_active_q, dma_write_q;
|
||||
logic [5:0] dma_word_q, dma_last_word_q;
|
||||
logic inject_dma_error_q;
|
||||
int unsigned ring_events, error_events, abort_events;
|
||||
int unsigned events_before_invalid, errors_before_invalid;
|
||||
string s_phase;
|
||||
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_read_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_write_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign dma_request_ready_i = !dma_active_q && !hold_dma_request;
|
||||
assign dma_read_axis.tdata = memory[((dma_request_addr_o-ring_base_i)>>2)+dma_word_q];
|
||||
assign dma_read_axis.tkeep = 4'hf;
|
||||
assign dma_read_axis.tstrb = 4'hf;
|
||||
assign dma_read_axis.tlast = dma_word_q == dma_last_word_q;
|
||||
assign dma_read_axis.tid = '0;
|
||||
assign dma_read_axis.tdest = '0;
|
||||
assign dma_read_axis.tuser = '0;
|
||||
assign dma_read_axis.tvalid = dma_active_q && !dma_write_q && !hold_dma_read;
|
||||
assign dma_write_axis.tready = dma_active_q && dma_write_q && !hold_dma_write;
|
||||
|
||||
apu_ring_scheduler u_dut (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.soft_reset_i,
|
||||
.counter_clear_i,
|
||||
.abort_i,
|
||||
.quiesce_i,
|
||||
.start_i,
|
||||
.ring_base_i,
|
||||
.ring_size_i,
|
||||
.ring_tail_i,
|
||||
.ring_enable_i,
|
||||
.stop_on_error_i,
|
||||
.coalesce_count_i,
|
||||
.coalesce_timeout_i,
|
||||
.dma_request_valid_o,
|
||||
.dma_request_ready_i,
|
||||
.dma_request_write_o,
|
||||
.dma_request_addr_o,
|
||||
.dma_request_bytes_o,
|
||||
.dma_read_axis,
|
||||
.dma_write_axis,
|
||||
.dma_done_i,
|
||||
.dma_error_i,
|
||||
.dma_error_code_i,
|
||||
.dma_error_stage_i,
|
||||
.dma_error_resp_i,
|
||||
.dma_error_addr_i,
|
||||
.backend_job_valid_o (backend_job_valid),
|
||||
.backend_job_ready_i (backend_job_ready),
|
||||
.backend_descriptor_o (backend_descriptor),
|
||||
.backend_index_o (backend_index),
|
||||
.backend_result_valid_i(backend_result_valid),
|
||||
.backend_result_ready_o(backend_result_ready),
|
||||
.backend_result_error_i(backend_latched_error),
|
||||
.backend_result_code_i (backend_latched_code),
|
||||
.backend_result_stage_i(backend_latched_stage),
|
||||
.backend_result_resp_i (backend_latched_resp),
|
||||
.backend_input_used_i (backend_input_used),
|
||||
.backend_output_bytes_i(backend_output_bytes),
|
||||
.backend_frames_i (backend_frames),
|
||||
.backend_source_info_i (backend_source_info),
|
||||
.backend_cycles_i (backend_cycles),
|
||||
.backend_detail_i (backend_detail),
|
||||
.job_status_o,
|
||||
.ring_status_o,
|
||||
.ring_head_o,
|
||||
.ring_completed_o,
|
||||
.ring_event_o,
|
||||
.error_event_o,
|
||||
.error_code_o,
|
||||
.error_stage_o,
|
||||
.error_resp_o,
|
||||
.error_index_o,
|
||||
.error_addr_o,
|
||||
.abort_done_o,
|
||||
.aborting_o,
|
||||
.idle_o
|
||||
);
|
||||
|
||||
apu_p2_backend u_backend (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.abort_i,
|
||||
.hold_result_i (backend_hold),
|
||||
.result_error_i (backend_result_error),
|
||||
.result_code_i (backend_result_code),
|
||||
.result_stage_i (backend_result_stage),
|
||||
.result_resp_i (backend_result_resp),
|
||||
.job_valid_i (backend_job_valid),
|
||||
.job_ready_o (backend_job_ready),
|
||||
.descriptor_i (backend_descriptor),
|
||||
.index_i (backend_index),
|
||||
.result_valid_o (backend_result_valid),
|
||||
.result_ready_i (backend_result_ready),
|
||||
.result_error_o (backend_latched_error),
|
||||
.result_code_o (backend_latched_code),
|
||||
.result_stage_o (backend_latched_stage),
|
||||
.result_resp_o (backend_latched_resp),
|
||||
.input_used_o (backend_input_used),
|
||||
.output_bytes_o (backend_output_bytes),
|
||||
.frames_o (backend_frames),
|
||||
.source_info_o (backend_source_info),
|
||||
.cycles_o (backend_cycles),
|
||||
.detail_o (backend_detail),
|
||||
.accepted_jobs_o(backend_accepted)
|
||||
);
|
||||
|
||||
task automatic pulse_start;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic pulse_abort;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic hard_reset;
|
||||
begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_completed(input logic [31:0] expected_i);
|
||||
int unsigned timeout;
|
||||
begin
|
||||
timeout = 0;
|
||||
while ((ring_completed_o != expected_i) && (timeout < 1000)) begin
|
||||
@(posedge clk_i);
|
||||
timeout++;
|
||||
end
|
||||
if (ring_completed_o != expected_i) $fatal(1, "ring completion timeout in %s", s_phase);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic clear_descriptor(input int unsigned descriptor_i);
|
||||
for (int unsigned word_index = 0; word_index < 32; word_index++) begin
|
||||
memory[(descriptor_i*32)+word_index] = 32'd0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic make_valid_descriptor(input int unsigned descriptor_i, input logic ioc_i);
|
||||
begin
|
||||
clear_descriptor(descriptor_i);
|
||||
memory[descriptor_i*32] = (32'd1 << `APB4_APU__DESCRIPTOR_CONTROL_OWN) |
|
||||
(ioc_i ? (32'd1 << `APB4_APU__DESCRIPTOR_CONTROL_IOC) : 32'd0);
|
||||
memory[(descriptor_i*32)+2] = 32'h0000_4000 + (descriptor_i * 32'h100);
|
||||
memory[(descriptor_i*32)+3] = 32'd256 + descriptor_i;
|
||||
memory[(descriptor_i*32)+4] = 32'h0000_8000 + (descriptor_i * 32'h100);
|
||||
memory[(descriptor_i*32)+5] = 32'd512 + descriptor_i;
|
||||
memory[(descriptor_i*32)+16] = 32'hc001_0000 + descriptor_i;
|
||||
memory[(descriptor_i*32)+17] = 32'hc002_0000 + descriptor_i;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic abort_writeback_state(input logic [3:0] state_i);
|
||||
begin
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_size_i = 9'd2;
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
backend_hold = 1'b1;
|
||||
hold_dma_request = 1'b0;
|
||||
hold_dma_write = 1'b0;
|
||||
pulse_start();
|
||||
wait (u_dut.s_state_q == 4'd5);
|
||||
@(negedge clk_i);
|
||||
if (state_i == 4'd6) hold_dma_request = 1'b1;
|
||||
if (state_i == 4'd7) hold_dma_write = 1'b1;
|
||||
backend_hold = 1'b0;
|
||||
if (state_i == 4'd9) begin
|
||||
wait (u_dut.s_state_q == 4'd8);
|
||||
@(negedge clk_i);
|
||||
hold_dma_request = 1'b1;
|
||||
end
|
||||
if (state_i == 4'd10) begin
|
||||
wait (u_dut.s_state_q == 4'd9);
|
||||
@(negedge clk_i);
|
||||
hold_dma_write = 1'b1;
|
||||
end
|
||||
wait (u_dut.s_state_q == state_i);
|
||||
pulse_abort();
|
||||
hold_dma_request = 1'b0;
|
||||
hold_dma_write = 1'b0;
|
||||
wait (abort_done_o);
|
||||
wait (idle_o);
|
||||
if (memory[0][31] || (ring_completed_o != 32'd1)) begin
|
||||
$fatal(1, "abort did not retire ordered writeback state %0d", state_i);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
dma_active_q <= 1'b0;
|
||||
dma_write_q <= 1'b0;
|
||||
dma_word_q <= 6'd0;
|
||||
dma_last_word_q <= 6'd0;
|
||||
dma_done_i <= 1'b0;
|
||||
dma_error_i <= 1'b0;
|
||||
inject_dma_error_q <= 1'b0;
|
||||
ring_events <= 0;
|
||||
error_events <= 0;
|
||||
abort_events <= 0;
|
||||
end else begin
|
||||
dma_done_i <= 1'b0;
|
||||
dma_error_i <= 1'b0;
|
||||
if (dma_request_valid_o && dma_request_ready_i) begin
|
||||
if ((!dma_request_write_o &&
|
||||
((dma_request_bytes_o != 32'd128) || (dma_request_addr_o[6:0] != 7'd0))) ||
|
||||
(dma_request_write_o && (dma_request_bytes_o == 32'd124) &&
|
||||
(dma_request_addr_o[6:0] != 7'd4)) ||
|
||||
(dma_request_write_o && (dma_request_bytes_o == 32'd4) &&
|
||||
(dma_request_addr_o[6:0] != 7'd0)) ||
|
||||
(dma_request_write_o && (dma_request_bytes_o != 32'd124) &&
|
||||
(dma_request_bytes_o != 32'd4))) begin
|
||||
$fatal(1, "malformed descriptor DMA request");
|
||||
end
|
||||
if (dma_request_write_o && (dma_request_bytes_o == 32'd124) &&
|
||||
!memory[((dma_request_addr_o-ring_base_i)>>2)-1][31]) begin
|
||||
$fatal(1, "descriptor OWN cleared before result writeback");
|
||||
end
|
||||
if (dma_request_write_o && (dma_request_bytes_o == 32'd4) &&
|
||||
(!memory[((dma_request_addr_o-ring_base_i)>>2)][31] ||
|
||||
(memory[((dma_request_addr_o-ring_base_i)>>2)+1] == 32'd0))) begin
|
||||
$fatal(1, "descriptor OWN publication preceded result retirement");
|
||||
end
|
||||
dma_active_q <= 1'b1;
|
||||
dma_write_q <= dma_request_write_o;
|
||||
dma_word_q <= 6'd0;
|
||||
dma_last_word_q <= 6'(((dma_request_bytes_o + 32'd3) >> 2) - 1'b1);
|
||||
end
|
||||
if (dma_read_axis.tvalid && dma_read_axis.tready) begin
|
||||
if (dma_word_q == dma_last_word_q) begin
|
||||
dma_active_q <= 1'b0;
|
||||
dma_done_i <= 1'b1;
|
||||
dma_error_i <= inject_dma_error_q;
|
||||
dma_error_code_i <= inject_dma_error_q ? 6'd15 : 6'd0;
|
||||
dma_error_stage_i <= inject_dma_error_q ? 4'd3 : 4'd0;
|
||||
dma_error_resp_i <= inject_dma_error_q ? 2'd2 : 2'd0;
|
||||
dma_error_addr_i <= dma_request_addr_o;
|
||||
inject_dma_error_q <= 1'b0;
|
||||
end else begin
|
||||
dma_word_q <= dma_word_q + 1'b1;
|
||||
end
|
||||
end
|
||||
if (dma_write_axis.tvalid && dma_write_axis.tready) begin
|
||||
memory[((dma_request_addr_o-ring_base_i)>>2)+dma_word_q] <= dma_write_axis.tdata;
|
||||
if (dma_word_q == dma_last_word_q) begin
|
||||
dma_active_q <= 1'b0;
|
||||
dma_done_i <= 1'b1;
|
||||
end else begin
|
||||
dma_word_q <= dma_word_q + 1'b1;
|
||||
end
|
||||
end
|
||||
if (ring_event_o) ring_events <= ring_events + 1;
|
||||
if (error_event_o) error_events <= error_events + 1;
|
||||
if (abort_done_o) abort_events <= abort_events + 1;
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
for (int unsigned index = 0; index < 64; index++) memory[index] = 32'd0;
|
||||
s_phase = "reset";
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
if (ring_status_o != 32'h0000_0004 || job_status_o != 32'd0) begin
|
||||
$fatal(1, "ring/job reset status mismatch");
|
||||
end
|
||||
ring_size_i = 9'd256;
|
||||
force u_dut.s_head_q = 8'd254;
|
||||
#1;
|
||||
if (u_dut.s_next_head != 8'd255) $fatal(1, "256-entry ring truncated before index 255");
|
||||
force u_dut.s_head_q = 8'd255;
|
||||
#1;
|
||||
if (u_dut.s_next_head != 8'd0) $fatal(1, "256-entry ring did not wrap after index 255");
|
||||
release u_dut.s_head_q;
|
||||
|
||||
s_phase = "stale head validation";
|
||||
ring_size_i = 9'd2;
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
force u_dut.s_head_q = 8'd3;
|
||||
pulse_start();
|
||||
#1;
|
||||
if (!error_event_o || (error_code_o != 6'd2) || dma_active_q) begin
|
||||
$fatal(1, "stale ring head outside the configured extent was accepted");
|
||||
end
|
||||
release u_dut.s_head_q;
|
||||
|
||||
soft_reset_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
soft_reset_i = 1'b0;
|
||||
ring_size_i = 9'd2;
|
||||
|
||||
s_phase = "backend request abort exclusion";
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait (u_dut.s_state_q == 4'd4);
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b1;
|
||||
#1;
|
||||
if (backend_job_valid || backend_job_ready) begin
|
||||
$fatal(1, "backend handshake remained eligible during abort");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
wait_completed(32'd1);
|
||||
if ((backend_accepted != 32'd0) || !memory[1][2]) begin
|
||||
$fatal(1, "backend request abort was accepted or not published");
|
||||
end
|
||||
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
s_phase = "simultaneous backend error and abort";
|
||||
backend_hold = 1'b1;
|
||||
backend_result_error = 1'b1;
|
||||
backend_result_code = 6'd7;
|
||||
backend_result_stage = 4'd4;
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait (u_dut.s_state_q == 4'd5);
|
||||
@(negedge clk_i);
|
||||
backend_hold = 1'b0;
|
||||
abort_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
abort_i = 1'b0;
|
||||
wait_completed(32'd1);
|
||||
@(posedge clk_i);
|
||||
if (!memory[1][1] || !memory[1][2] || (memory[1][8:3] != 6'd7) ||
|
||||
(error_events != 1) || (abort_events != 1)) begin
|
||||
$fatal(1, "simultaneous backend error did not outrank and retain abort");
|
||||
end
|
||||
|
||||
backend_result_error = 1'b0;
|
||||
backend_result_code = 6'd0;
|
||||
backend_result_stage = 4'd0;
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
|
||||
s_phase = "idle-active abort";
|
||||
ring_tail_i = 8'd0;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait (ring_status_o[`APB4_APU__RING_STATUS_ACTIVE] && idle_o);
|
||||
pulse_abort();
|
||||
wait (abort_done_o);
|
||||
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
s_phase = "fetch-request abort";
|
||||
hold_dma_request = 1'b1;
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait (u_dut.s_state_q == 4'd1);
|
||||
pulse_abort();
|
||||
hold_dma_request = 1'b0;
|
||||
wait (abort_done_o);
|
||||
if (!memory[0][31]) $fatal(1, "FetchRequest abort mutated descriptor ownership");
|
||||
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
s_phase = "stalled-unowned abort";
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait (u_dut.s_state_q == 4'd12);
|
||||
pulse_abort();
|
||||
wait (abort_done_o);
|
||||
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
|
||||
for (int unsigned writeback_state = 6; writeback_state <= 11; writeback_state++) begin
|
||||
s_phase = "writeback abort matrix";
|
||||
abort_writeback_state(4'(writeback_state));
|
||||
end
|
||||
hard_reset();
|
||||
clear_descriptor(0);
|
||||
|
||||
s_phase = "timeout coalescing";
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_enable_i = 1'b1;
|
||||
ring_tail_i = 8'd1;
|
||||
pulse_start();
|
||||
wait_completed(32'd1);
|
||||
if (memory[0][31] || !memory[1][0] || (ring_head_o != 8'd1) || !job_status_o[1] ||
|
||||
(memory[16] != 32'hc001_0000) || (memory[17] != 32'hc002_0000) ||
|
||||
({memory[21], memory[20]} < {memory[19], memory[18]}) ||
|
||||
(memory[22] != 32'd0) || (memory[23] != 32'd0)) begin
|
||||
$fatal(1, "successful descriptor writeback mismatch");
|
||||
end
|
||||
while (ring_events == 0) @(posedge clk_i);
|
||||
if (ring_status_o[5] || (ring_status_o[15:8] != 8'd0)) begin
|
||||
$fatal(1, "coalescing timeout did not clear pending state");
|
||||
end
|
||||
|
||||
s_phase = "unowned and IOC wrap";
|
||||
clear_descriptor(1);
|
||||
ring_tail_i = 8'd0;
|
||||
repeat (40) @(posedge clk_i);
|
||||
if (!ring_status_o[1] || (ring_head_o != 8'd1) || (ring_completed_o != 32'd1)) begin
|
||||
$fatal(1, "unowned descriptor was not stalled");
|
||||
end
|
||||
make_valid_descriptor(1, 1'b1);
|
||||
pulse_start();
|
||||
wait_completed(32'd2);
|
||||
@(posedge clk_i);
|
||||
if ((ring_head_o != 8'd0) || !ring_status_o[4] || (ring_events != 2)) begin
|
||||
$fatal(1, "IOC/wrap behavior mismatch events=%0d", ring_events);
|
||||
end
|
||||
|
||||
s_phase = "count coalescing";
|
||||
coalesce_count_i = 8'd2;
|
||||
coalesce_timeout_i = 16'd100;
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_tail_i = 8'd1;
|
||||
pulse_start();
|
||||
wait_completed(32'd3);
|
||||
if ((ring_events != 2) || !ring_status_o[5] || (ring_status_o[15:8] != 8'd1)) begin
|
||||
$fatal(1, "coalescing count epoch did not retain first completion");
|
||||
end
|
||||
make_valid_descriptor(1, 1'b0);
|
||||
ring_tail_i = 8'd0;
|
||||
pulse_start();
|
||||
wait_completed(32'd4);
|
||||
@(posedge clk_i);
|
||||
if ((ring_events != 3) || ring_status_o[5] || (ring_status_o[15:8] != 8'd0)) begin
|
||||
$fatal(1, "coalescing count threshold mismatch");
|
||||
end
|
||||
|
||||
s_phase = "deterministic randomized descriptors";
|
||||
coalesce_count_i = 8'd4;
|
||||
coalesce_timeout_i = 16'd7;
|
||||
for (int unsigned job = 0; job < 8; job++) begin
|
||||
automatic int unsigned descriptor = ring_head_o;
|
||||
automatic logic expected_error = ((job * 5 + 1) % 7) == 2;
|
||||
make_valid_descriptor(descriptor, ((job * 3 + 1) % 5) == 0);
|
||||
memory[(descriptor*32)+3] = 32'd33 + ((job * 29) % 511);
|
||||
memory[(descriptor*32)+5] = 32'd65 + ((job * 17) % 1023);
|
||||
backend_result_error = expected_error;
|
||||
backend_result_code = expected_error ? 6'd7 : 6'd0;
|
||||
backend_result_stage = expected_error ? 4'd4 : 4'd0;
|
||||
ring_tail_i = descriptor == 0 ? 8'd1 : 8'd0;
|
||||
pulse_start();
|
||||
wait_completed(32'(5 + job));
|
||||
@(posedge clk_i);
|
||||
if (memory[descriptor*32][31] ||
|
||||
(memory[(descriptor*32)+1][1] != expected_error) ||
|
||||
(memory[(descriptor*32)+1][0] == expected_error)) begin
|
||||
$fatal(1, "randomized descriptor result mismatch job=%0d", job);
|
||||
end
|
||||
end
|
||||
backend_result_error = 1'b0;
|
||||
backend_result_code = 6'd0;
|
||||
backend_result_stage = 4'd0;
|
||||
|
||||
s_phase = "invalid stop on error";
|
||||
events_before_invalid = ring_events;
|
||||
errors_before_invalid = error_events;
|
||||
ring_enable_i = 1'b0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
stop_on_error_i = 1'b1;
|
||||
make_valid_descriptor(0, 1'b1);
|
||||
memory[0][12] = 1'b1;
|
||||
memory[10] = 32'hdead_beef;
|
||||
ring_tail_i = 8'd1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
wait_completed(32'd13);
|
||||
@(posedge clk_i);
|
||||
if (memory[0][31] || !memory[1][1] || (memory[1][8:3] != 6'd2) ||
|
||||
(memory[10] != 32'd0) ||
|
||||
!ring_status_o[3] || !ring_status_o[7] || ring_status_o[0] ||
|
||||
(ring_events != events_before_invalid + 1) ||
|
||||
(error_events != errors_before_invalid + 1)) begin
|
||||
$fatal(1, "invalid-owned stop-on-error behavior mismatch");
|
||||
end
|
||||
|
||||
s_phase = "abort writeback";
|
||||
ring_enable_i = 1'b0;
|
||||
stop_on_error_i = 1'b0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
make_valid_descriptor(1, 1'b0);
|
||||
ring_tail_i = 8'd0;
|
||||
backend_hold = 1'b1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
while (!backend_job_valid && !backend_result_ready) @(posedge clk_i);
|
||||
repeat (2) @(posedge clk_i);
|
||||
pulse_abort();
|
||||
if (!aborting_o) $fatal(1, "scheduler live aborting status was not asserted");
|
||||
backend_hold = 1'b0;
|
||||
wait_completed(32'd14);
|
||||
@(posedge clk_i);
|
||||
if (memory[32][31] || !memory[33][2] || !job_status_o[3] ||
|
||||
(abort_events != 1) || ring_status_o[0]) begin
|
||||
$fatal(1, "abort terminal writeback mismatch");
|
||||
end
|
||||
if (aborting_o) $fatal(1, "scheduler live aborting status remained set after writeback");
|
||||
|
||||
s_phase = "descriptor DMA error";
|
||||
ring_enable_i = 1'b0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
make_valid_descriptor(0, 1'b0);
|
||||
ring_tail_i = 8'd1;
|
||||
inject_dma_error_q = 1'b1;
|
||||
ring_enable_i = 1'b1;
|
||||
pulse_start();
|
||||
while (!error_event_o) @(posedge clk_i);
|
||||
repeat (2) @(posedge clk_i);
|
||||
if ((ring_completed_o != 32'd14) || !ring_status_o[3] || ring_status_o[0] ||
|
||||
(error_code_o != 6'd15) || (error_stage_o != 4'd3) ||
|
||||
(error_resp_o != 2'd2) || (error_index_o != 8'd0) ||
|
||||
(error_addr_o != ring_base_i)) begin
|
||||
$fatal(1, "descriptor DMA error attribution mismatch");
|
||||
end
|
||||
|
||||
if (backend_accepted != 32'd13) $fatal(1, "unexpected backend job count");
|
||||
@(negedge clk_i);
|
||||
counter_clear_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
counter_clear_i = 1'b0;
|
||||
#1;
|
||||
if (ring_completed_o != 32'd0) $fatal(1, "ring completion counter clear failed");
|
||||
$display("APU-P2 ring scheduler/backend tests passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (10000) @(posedge clk_i);
|
||||
$fatal(1, "APU-P2 ring scheduler test timed out in %s state=%0d", s_phase, u_dut.s_state_q);
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,294 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module apu_stream_router_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic flush_i = 1'b0;
|
||||
logic counter_clear_i = 1'b0;
|
||||
logic xrun_clear_i = 1'b0;
|
||||
logic tx_route_apu_i = 1'b0;
|
||||
logic rx_route_apu_i = 1'b0;
|
||||
logic tx_session_active_i = 1'b0;
|
||||
logic rx_session_active_i = 1'b0;
|
||||
logic [ 7:0] rx_high_watermark_i = 8'd0;
|
||||
logic [ 7:0] tx_low_watermark_i = 8'd0;
|
||||
logic tx_underrun_i = 1'b0;
|
||||
logic rx_overrun_i = 1'b0;
|
||||
logic [31:0] status_o;
|
||||
logic input_watermark_evt_o;
|
||||
logic output_watermark_evt_o;
|
||||
logic stream_xrun_evt_o;
|
||||
logic idle_o;
|
||||
logic input_watermark_seen;
|
||||
logic output_watermark_seen;
|
||||
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) dma_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) i2s_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) i2s_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) apu_tx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_stream_if #(
|
||||
.DATA_WIDTH(32)
|
||||
) apu_rx_axis (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
always @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
input_watermark_seen <= 1'b0;
|
||||
output_watermark_seen <= 1'b0;
|
||||
end else begin
|
||||
if (input_watermark_evt_o) input_watermark_seen <= 1'b1;
|
||||
if (output_watermark_evt_o) output_watermark_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
apu_stream_router u_dut (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.flush_i,
|
||||
.counter_clear_i,
|
||||
.xrun_clear_i,
|
||||
.tx_route_apu_i,
|
||||
.rx_route_apu_i,
|
||||
.tx_session_active_i,
|
||||
.rx_session_active_i,
|
||||
.rx_high_watermark_i,
|
||||
.tx_low_watermark_i,
|
||||
.tx_underrun_i,
|
||||
.rx_overrun_i,
|
||||
.dma_tx_axis,
|
||||
.dma_rx_axis,
|
||||
.i2s_tx_axis,
|
||||
.i2s_rx_axis,
|
||||
.apu_tx_axis,
|
||||
.apu_rx_axis,
|
||||
.status_o,
|
||||
.input_watermark_evt_o,
|
||||
.output_watermark_evt_o,
|
||||
.stream_xrun_evt_o,
|
||||
.idle_o
|
||||
);
|
||||
|
||||
task automatic push_apu_tx(input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apu_tx_axis.tdata = data_i;
|
||||
apu_tx_axis.tvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!apu_tx_axis.tready);
|
||||
@(negedge clk_i);
|
||||
apu_tx_axis.tvalid = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic push_i2s_rx(input logic [31:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
i2s_rx_axis.tdata = data_i;
|
||||
i2s_rx_axis.tvalid = 1'b1;
|
||||
do @(posedge clk_i); while (!i2s_rx_axis.tready);
|
||||
@(negedge clk_i);
|
||||
i2s_rx_axis.tvalid = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
task automatic pop_i2s_tx(input logic [31:0] expected_i, input int unsigned stall_cycles_i);
|
||||
logic [31:0] held_data;
|
||||
begin
|
||||
i2s_tx_axis.tready = 1'b0;
|
||||
held_data = i2s_tx_axis.tdata;
|
||||
repeat (stall_cycles_i) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != held_data)) begin
|
||||
$fatal(1, "TX payload changed under backpressure");
|
||||
end
|
||||
end
|
||||
if (held_data != expected_i) $fatal(1, "TX FIFO ordering mismatch");
|
||||
@(negedge clk_i);
|
||||
i2s_tx_axis.tready = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
i2s_tx_axis.tready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic pop_apu_rx(input logic [31:0] expected_i, input int unsigned stall_cycles_i);
|
||||
logic [31:0] held_data;
|
||||
begin
|
||||
apu_rx_axis.tready = 1'b0;
|
||||
held_data = apu_rx_axis.tdata;
|
||||
repeat (stall_cycles_i) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!apu_rx_axis.tvalid || (apu_rx_axis.tdata != held_data)) begin
|
||||
$fatal(1, "RX payload changed under backpressure");
|
||||
end
|
||||
end
|
||||
if (held_data != expected_i) $fatal(1, "RX FIFO ordering mismatch");
|
||||
@(negedge clk_i);
|
||||
apu_rx_axis.tready = 1'b1;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
apu_rx_axis.tready = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
dma_tx_axis.tdata = 32'h1122_3344;
|
||||
dma_tx_axis.tkeep = 4'hf;
|
||||
dma_tx_axis.tstrb = 4'hf;
|
||||
dma_tx_axis.tlast = 1'b1;
|
||||
dma_tx_axis.tid = '0;
|
||||
dma_tx_axis.tdest = '0;
|
||||
dma_tx_axis.tuser = '0;
|
||||
dma_tx_axis.tvalid = 1'b0;
|
||||
dma_rx_axis.tready = 1'b1;
|
||||
i2s_tx_axis.tready = 1'b1;
|
||||
i2s_rx_axis.tdata = 32'd0;
|
||||
i2s_rx_axis.tkeep = 4'hf;
|
||||
i2s_rx_axis.tstrb = 4'hf;
|
||||
i2s_rx_axis.tlast = 1'b0;
|
||||
i2s_rx_axis.tid = '0;
|
||||
i2s_rx_axis.tdest = '0;
|
||||
i2s_rx_axis.tuser = '0;
|
||||
i2s_rx_axis.tvalid = 1'b0;
|
||||
apu_tx_axis.tdata = 32'd0;
|
||||
apu_tx_axis.tkeep = 4'hf;
|
||||
apu_tx_axis.tstrb = 4'hf;
|
||||
apu_tx_axis.tlast = 1'b0;
|
||||
apu_tx_axis.tid = '0;
|
||||
apu_tx_axis.tdest = '0;
|
||||
apu_tx_axis.tuser = '0;
|
||||
apu_tx_axis.tvalid = 1'b0;
|
||||
apu_rx_axis.tready = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
#1;
|
||||
if ((status_o != 32'h0000_0014) || !idle_o) $fatal(1, "stream reset status mismatch");
|
||||
|
||||
dma_tx_axis.tvalid = 1'b1;
|
||||
#1;
|
||||
if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != 32'h1122_3344) || !dma_tx_axis.tready)
|
||||
$fatal(1, "central DMA TX bypass failed");
|
||||
dma_tx_axis.tvalid = 1'b0;
|
||||
i2s_rx_axis.tdata = 32'haabb_ccdd;
|
||||
i2s_rx_axis.tvalid = 1'b1;
|
||||
#1;
|
||||
if (!dma_rx_axis.tvalid || (dma_rx_axis.tdata != 32'haabb_ccdd) || !i2s_rx_axis.tready)
|
||||
$fatal(1, "central DMA RX bypass failed");
|
||||
i2s_rx_axis.tvalid = 1'b0;
|
||||
|
||||
tx_route_apu_i = 1'b1;
|
||||
rx_route_apu_i = 1'b1;
|
||||
tx_session_active_i = 1'b1;
|
||||
rx_session_active_i = 1'b1;
|
||||
tx_low_watermark_i = 8'd2;
|
||||
rx_high_watermark_i = 8'd2;
|
||||
i2s_tx_axis.tready = 1'b0;
|
||||
push_apu_tx(32'd1);
|
||||
push_apu_tx(32'd2);
|
||||
push_apu_tx(32'd3);
|
||||
if (!status_o[0]) $fatal(1, "APU TX route did not become active");
|
||||
i2s_tx_axis.tready = 1'b1;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!output_watermark_seen) $fatal(1, "TX low-watermark crossing was not detected");
|
||||
|
||||
push_i2s_rx(32'h1010_1010);
|
||||
push_i2s_rx(32'h2020_2020);
|
||||
if (!input_watermark_seen) $fatal(1, "RX high-watermark crossing was not detected");
|
||||
apu_rx_axis.tready = 1'b1;
|
||||
repeat (2) @(posedge clk_i);
|
||||
|
||||
@(negedge clk_i);
|
||||
flush_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
i2s_tx_axis.tready = 1'b0;
|
||||
apu_rx_axis.tready = 1'b0;
|
||||
for (int unsigned word_index = 0; word_index < 64; word_index++) begin
|
||||
push_apu_tx(32'h7100_0000 + word_index);
|
||||
end
|
||||
#1;
|
||||
if (!status_o[3] || apu_tx_axis.tready) $fatal(1, "TX FIFO did not reach depth 64");
|
||||
for (int unsigned word_index = 0; word_index < 64; word_index++) begin
|
||||
pop_i2s_tx(32'h7100_0000 + word_index, (word_index * 5 + 1) % 4);
|
||||
end
|
||||
#1;
|
||||
if (!status_o[2] || (status_o[15:8] != 8'd64)) begin
|
||||
$fatal(1, "TX FIFO empty/count status mismatch");
|
||||
end
|
||||
|
||||
for (int unsigned word_index = 0; word_index < 64; word_index++) begin
|
||||
push_i2s_rx(32'h7200_0000 + word_index);
|
||||
end
|
||||
#1;
|
||||
if (!status_o[5] || i2s_rx_axis.tready) $fatal(1, "RX FIFO did not reach depth 64");
|
||||
for (int unsigned word_index = 0; word_index < 64; word_index++) begin
|
||||
pop_apu_rx(32'h7200_0000 + word_index, (word_index * 7 + 2) % 5);
|
||||
end
|
||||
#1;
|
||||
if (!status_o[4] || (status_o[23:16] != 8'd64)) begin
|
||||
$fatal(1, "RX FIFO empty/count status mismatch");
|
||||
end
|
||||
counter_clear_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
counter_clear_i = 1'b0;
|
||||
#1;
|
||||
if (status_o[23:8] != 16'd0) $fatal(1, "stream counter clear failed");
|
||||
|
||||
tx_underrun_i = 1'b1;
|
||||
rx_overrun_i = 1'b1;
|
||||
#1;
|
||||
if (!stream_xrun_evt_o) $fatal(1, "xrun event pulse failed");
|
||||
@(posedge clk_i);
|
||||
tx_underrun_i = 1'b0;
|
||||
rx_overrun_i = 1'b0;
|
||||
#1;
|
||||
if (status_o[7:6] != 2'b11) $fatal(1, "xrun capture failed");
|
||||
xrun_clear_i = 1'b1;
|
||||
tx_underrun_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
xrun_clear_i = 1'b0;
|
||||
tx_underrun_i = 1'b0;
|
||||
#1;
|
||||
if (!status_o[6]) $fatal(1, "hardware xrun did not win same-cycle clear");
|
||||
|
||||
$display("APU-P2 stream router tests passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (5000) @(posedge clk_i);
|
||||
$fatal(1, "APU-P2 stream router timed out");
|
||||
end
|
||||
endmodule
|
||||
@@ -118,7 +118,14 @@ module axi4_async_bridge_tb;
|
||||
wait (!clear_busy_o);
|
||||
if (epoch_o != 8'd1) $fatal(1, "async bridge epoch did not advance");
|
||||
|
||||
$display("AXI4 async bridge transfer and warm flush test passed");
|
||||
@(negedge dst_clk_i);
|
||||
dst_rst_n_i = 1'b0;
|
||||
repeat (3) @(posedge src_clk_i);
|
||||
@(negedge dst_clk_i);
|
||||
dst_rst_n_i = 1'b1;
|
||||
wait (epoch_o == 8'd2);
|
||||
|
||||
$display("AXI4 async bridge transfer and warm flush test passed; unilateral reset passed");
|
||||
$finish;
|
||||
end
|
||||
|
||||
|
||||
@@ -74,6 +74,28 @@ module axi4_target_guard_tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic return_read_beat(input logic [5:0] id, input logic last,
|
||||
input logic expected_last, input logic [1:0] expected_resp);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
sink.rid = id;
|
||||
sink.rdata = 64'h0123_4567_89ab_cdef;
|
||||
sink.rresp = 2'b00;
|
||||
sink.rlast = last;
|
||||
sink.rvalid = 1'b1;
|
||||
wait (source.rvalid);
|
||||
#1;
|
||||
if ((source.rid != id) || (source.rresp != expected_resp) ||
|
||||
(source.rlast != expected_last)) begin
|
||||
$fatal(1, "target guard read normalization mismatch");
|
||||
end
|
||||
do @(posedge clk_i); while (!sink.rready);
|
||||
@(negedge clk_i);
|
||||
sink.rvalid = 1'b0;
|
||||
sink.rlast = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
source.awid = '0;
|
||||
source.awaddr = '0;
|
||||
@@ -124,6 +146,15 @@ module axi4_target_guard_tb;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
issue_read(6'h07, 8'd1, 32'h3800_0020);
|
||||
do @(posedge clk_i); while (!sink.arvalid);
|
||||
return_read_beat(6'h07, 1'b0, 1'b0, 2'b00);
|
||||
return_read_beat(6'h07, 1'b0, 1'b1, 2'b10);
|
||||
|
||||
issue_read(6'h08, 8'd0, 32'h3800_0030);
|
||||
do @(posedge clk_i); while (!sink.arvalid);
|
||||
return_read_beat(6'h08, 1'b1, 1'b1, 2'b00);
|
||||
|
||||
issue_read(6'h09, 8'd1, 32'h3800_0040);
|
||||
wait (timeout_valid_o);
|
||||
if (timeout_write_o || (timeout_id_o != 6'h09) || (timeout_addr_o != 32'h3800_0040)) begin
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module hp_axi4_mux3_rr_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic [ 7:0] epoch_i = 8'd0;
|
||||
logic [ 2:0] read_request = 3'd0;
|
||||
logic [ 2:0] write_request = 3'd0;
|
||||
logic [ 2:0] write_data_valid = 3'b111;
|
||||
logic [ 2:0] read_ready = 3'b111;
|
||||
logic [ 2:0] write_response_ready = 3'b111;
|
||||
logic response_valid_q;
|
||||
logic [31:0] response_data_q;
|
||||
logic write_active_q;
|
||||
logic write_response_valid_q;
|
||||
logic [31:0] write_address_q;
|
||||
logic mixed_mode_q;
|
||||
logic recovery_response_q = 1'b0;
|
||||
logic allow_read_address = 1'b1;
|
||||
logic allow_write_address = 1'b1;
|
||||
int unsigned response_count [0:2];
|
||||
int unsigned write_response_count [0:2];
|
||||
int unsigned mixed_response_count [0:2];
|
||||
int unsigned total_responses;
|
||||
int unsigned total_write_responses;
|
||||
int unsigned total_mixed_responses;
|
||||
logic [15:0] cycle_q;
|
||||
string s_phase;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) icache (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dcache (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) mmio (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) axi4 (
|
||||
clk_i,
|
||||
rst_n_i
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign icache.arid = 1'b0;
|
||||
assign icache.araddr = 32'h0000_1000;
|
||||
assign dcache.arid = 1'b0;
|
||||
assign dcache.araddr = 32'h0000_2000;
|
||||
assign mmio.arid = 1'b0;
|
||||
assign mmio.araddr = 32'h0000_3000;
|
||||
assign icache.arvalid = read_request[0];
|
||||
assign dcache.arvalid = read_request[1];
|
||||
assign mmio.arvalid = read_request[2];
|
||||
|
||||
assign icache.arlen = 8'd0;
|
||||
assign dcache.arlen = 8'd0;
|
||||
assign mmio.arlen = 8'd0;
|
||||
assign icache.arsize = 3'd2;
|
||||
assign dcache.arsize = 3'd2;
|
||||
assign mmio.arsize = 3'd2;
|
||||
assign icache.arburst = 2'd1;
|
||||
assign dcache.arburst = 2'd1;
|
||||
assign mmio.arburst = 2'd1;
|
||||
assign icache.arlock = 1'b0;
|
||||
assign dcache.arlock = 1'b0;
|
||||
assign mmio.arlock = 1'b0;
|
||||
assign icache.arcache = 4'd0;
|
||||
assign dcache.arcache = 4'd0;
|
||||
assign mmio.arcache = 4'd0;
|
||||
assign icache.arprot = 3'd0;
|
||||
assign dcache.arprot = 3'd0;
|
||||
assign mmio.arprot = 3'd0;
|
||||
assign icache.arqos = 4'd0;
|
||||
assign dcache.arqos = 4'd0;
|
||||
assign mmio.arqos = 4'd0;
|
||||
assign icache.arregion = 4'd0;
|
||||
assign dcache.arregion = 4'd0;
|
||||
assign mmio.arregion = 4'd0;
|
||||
assign icache.aruser = 1'b0;
|
||||
assign dcache.aruser = 1'b0;
|
||||
assign mmio.aruser = 1'b0;
|
||||
assign icache.rready = read_ready[0];
|
||||
assign dcache.rready = read_ready[1];
|
||||
assign mmio.rready = read_ready[2];
|
||||
|
||||
assign icache.awid = 1'b0;
|
||||
assign dcache.awid = 1'b0;
|
||||
assign mmio.awid = 1'b0;
|
||||
assign icache.awaddr = 32'h0000_4000;
|
||||
assign dcache.awaddr = 32'h0000_5000;
|
||||
assign mmio.awaddr = 32'h0000_6000;
|
||||
assign icache.awlen = 8'd0;
|
||||
assign dcache.awlen = 8'd0;
|
||||
assign mmio.awlen = 8'd0;
|
||||
assign icache.awsize = 3'd2;
|
||||
assign dcache.awsize = 3'd2;
|
||||
assign mmio.awsize = 3'd2;
|
||||
assign icache.awburst = 2'd1;
|
||||
assign dcache.awburst = 2'd1;
|
||||
assign mmio.awburst = 2'd1;
|
||||
assign icache.awlock = 1'b0;
|
||||
assign dcache.awlock = 1'b0;
|
||||
assign mmio.awlock = 1'b0;
|
||||
assign icache.awcache = 4'd0;
|
||||
assign dcache.awcache = 4'd0;
|
||||
assign mmio.awcache = 4'd0;
|
||||
assign icache.awprot = 3'd0;
|
||||
assign dcache.awprot = 3'd0;
|
||||
assign mmio.awprot = 3'd0;
|
||||
assign icache.awqos = 4'd0;
|
||||
assign dcache.awqos = 4'd0;
|
||||
assign mmio.awqos = 4'd0;
|
||||
assign icache.awregion = 4'd0;
|
||||
assign dcache.awregion = 4'd0;
|
||||
assign mmio.awregion = 4'd0;
|
||||
assign icache.awuser = 1'b0;
|
||||
assign dcache.awuser = 1'b0;
|
||||
assign mmio.awuser = 1'b0;
|
||||
assign icache.awvalid = write_request[0];
|
||||
assign dcache.awvalid = write_request[1];
|
||||
assign mmio.awvalid = write_request[2];
|
||||
assign icache.wdata = 32'ha000_0000;
|
||||
assign dcache.wdata = 32'hb000_0000;
|
||||
assign mmio.wdata = 32'hc000_0000;
|
||||
assign icache.wstrb = 4'hf;
|
||||
assign dcache.wstrb = 4'hf;
|
||||
assign mmio.wstrb = 4'hf;
|
||||
assign icache.wlast = 1'b1;
|
||||
assign dcache.wlast = 1'b1;
|
||||
assign mmio.wlast = 1'b1;
|
||||
assign icache.wuser = 1'b0;
|
||||
assign dcache.wuser = 1'b0;
|
||||
assign mmio.wuser = 1'b0;
|
||||
assign icache.wvalid = write_request[0] && write_data_valid[0];
|
||||
assign dcache.wvalid = write_request[1] && write_data_valid[1];
|
||||
assign mmio.wvalid = write_request[2] && write_data_valid[2];
|
||||
assign icache.bready = write_response_ready[0];
|
||||
assign dcache.bready = write_response_ready[1];
|
||||
assign mmio.bready = write_response_ready[2];
|
||||
|
||||
assign axi4.awready = allow_write_address && !write_active_q &&
|
||||
!write_response_valid_q && cycle_q[0];
|
||||
assign axi4.wready = write_active_q && cycle_q[1];
|
||||
assign axi4.bid = 1'b0;
|
||||
assign axi4.bresp = 2'd0;
|
||||
assign axi4.buser = 1'b0;
|
||||
assign axi4.bvalid = write_response_valid_q;
|
||||
assign axi4.arready = allow_read_address && !response_valid_q && cycle_q[0];
|
||||
assign axi4.rid = 1'b0;
|
||||
assign axi4.rdata = response_data_q;
|
||||
assign axi4.rresp = 2'd0;
|
||||
assign axi4.rlast = 1'b1;
|
||||
assign axi4.ruser = 1'b0;
|
||||
assign axi4.rvalid = response_valid_q;
|
||||
|
||||
hp_axi4_mux3 #(
|
||||
.RoundRobin (1'b1),
|
||||
.Client0EpochAware(1'b1)
|
||||
) u_dut (
|
||||
.clk_i,
|
||||
.rst_n_i,
|
||||
.epoch_i,
|
||||
.icache,
|
||||
.dcache,
|
||||
.mmio,
|
||||
.axi4
|
||||
);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
response_valid_q <= 1'b0;
|
||||
response_data_q <= 32'd0;
|
||||
response_count[0] <= 0;
|
||||
response_count[1] <= 0;
|
||||
response_count[2] <= 0;
|
||||
write_response_count[0] <= 0;
|
||||
write_response_count[1] <= 0;
|
||||
write_response_count[2] <= 0;
|
||||
mixed_response_count[0] <= 0;
|
||||
mixed_response_count[1] <= 0;
|
||||
mixed_response_count[2] <= 0;
|
||||
total_responses <= 0;
|
||||
total_write_responses <= 0;
|
||||
total_mixed_responses <= 0;
|
||||
write_active_q <= 1'b0;
|
||||
write_response_valid_q <= 1'b0;
|
||||
write_address_q <= 32'd0;
|
||||
cycle_q <= 16'd0;
|
||||
end else if (epoch_i != u_dut.s_epoch_q) begin
|
||||
response_valid_q <= 1'b0;
|
||||
write_active_q <= 1'b0;
|
||||
write_response_valid_q <= 1'b0;
|
||||
end else begin
|
||||
cycle_q <= cycle_q + 1'b1;
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
response_valid_q <= 1'b1;
|
||||
response_data_q <= axi4.araddr;
|
||||
end
|
||||
if (axi4.rvalid && axi4.rready) response_valid_q <= 1'b0;
|
||||
if (axi4.awvalid && axi4.awready) begin
|
||||
write_active_q <= 1'b1;
|
||||
write_address_q <= axi4.awaddr;
|
||||
end
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
unique case (write_address_q)
|
||||
32'h0000_4000: if (axi4.wdata != 32'ha000_0000) $fatal(1, "APU write data mismatch");
|
||||
32'h0000_5000: if (axi4.wdata != 32'hb000_0000) $fatal(1, "SDIO0 write data mismatch");
|
||||
32'h0000_6000: if (axi4.wdata != 32'hc000_0000) $fatal(1, "USB2 write data mismatch");
|
||||
default: $fatal(1, "Gateway A write owner address changed");
|
||||
endcase
|
||||
write_active_q <= 1'b0;
|
||||
write_response_valid_q <= 1'b1;
|
||||
end
|
||||
if (axi4.bvalid && axi4.bready) write_response_valid_q <= 1'b0;
|
||||
if (icache.rvalid && icache.rready) begin
|
||||
if (recovery_response_q) begin
|
||||
if ((icache.rresp != 2'b11) || !icache.rlast) begin
|
||||
$fatal(1, "Gateway A malformed synthetic APU read response");
|
||||
end
|
||||
end else if ((!mixed_mode_q && ((total_responses % 3) != 0)) ||
|
||||
(icache.rdata != 32'h0000_1000)) begin
|
||||
$fatal(1, "Gateway A round-robin order/ownership failure for APU");
|
||||
end
|
||||
if (mixed_mode_q) begin
|
||||
mixed_response_count[0] <= mixed_response_count[0] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
response_count[0] <= response_count[0] + 1;
|
||||
total_responses <= total_responses + 1;
|
||||
end
|
||||
end
|
||||
if (dcache.rvalid && dcache.rready) begin
|
||||
if (recovery_response_q) begin
|
||||
if ((dcache.rresp != 2'b11) || !dcache.rlast) begin
|
||||
$fatal(1, "Gateway A malformed synthetic SDIO0 read response");
|
||||
end
|
||||
end else if ((!mixed_mode_q && ((total_responses % 3) != 1)) ||
|
||||
(dcache.rdata != 32'h0000_2000)) begin
|
||||
$fatal(1, "Gateway A round-robin order/ownership failure for SDIO0");
|
||||
end
|
||||
if (mixed_mode_q) begin
|
||||
mixed_response_count[1] <= mixed_response_count[1] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
response_count[1] <= response_count[1] + 1;
|
||||
total_responses <= total_responses + 1;
|
||||
end
|
||||
end
|
||||
if (mmio.rvalid && mmio.rready) begin
|
||||
if (recovery_response_q) begin
|
||||
if ((mmio.rresp != 2'b11) || !mmio.rlast) begin
|
||||
$fatal(1, "Gateway A malformed synthetic USB2 read response");
|
||||
end
|
||||
end else if ((!mixed_mode_q && ((total_responses % 3) != 2)) ||
|
||||
(mmio.rdata != 32'h0000_3000)) begin
|
||||
$fatal(1, "Gateway A round-robin order/ownership failure for USB2");
|
||||
end
|
||||
if (mixed_mode_q) begin
|
||||
mixed_response_count[2] <= mixed_response_count[2] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
response_count[2] <= response_count[2] + 1;
|
||||
total_responses <= total_responses + 1;
|
||||
end
|
||||
end
|
||||
if (icache.bvalid && icache.bready) begin
|
||||
if (recovery_response_q) begin
|
||||
if (icache.bresp != 2'b11) $fatal(1, "Gateway A malformed synthetic APU B response");
|
||||
end else if (mixed_mode_q) begin
|
||||
mixed_response_count[0] <= mixed_response_count[0] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
write_response_count[0] <= write_response_count[0] + 1;
|
||||
total_write_responses <= total_write_responses + 1;
|
||||
end
|
||||
end
|
||||
if (dcache.bvalid && dcache.bready) begin
|
||||
if (recovery_response_q) begin
|
||||
if (dcache.bresp != 2'b11) begin
|
||||
$fatal(1, "Gateway A malformed synthetic SDIO0 B response");
|
||||
end
|
||||
end else if (mixed_mode_q) begin
|
||||
mixed_response_count[1] <= mixed_response_count[1] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
write_response_count[1] <= write_response_count[1] + 1;
|
||||
total_write_responses <= total_write_responses + 1;
|
||||
end
|
||||
end
|
||||
if (mmio.bvalid && mmio.bready) begin
|
||||
if (recovery_response_q) begin
|
||||
if (mmio.bresp != 2'b11) $fatal(1, "Gateway A malformed synthetic USB2 B response");
|
||||
end else if (mixed_mode_q) begin
|
||||
mixed_response_count[2] <= mixed_response_count[2] + 1;
|
||||
total_mixed_responses <= total_mixed_responses + 1;
|
||||
end else begin
|
||||
write_response_count[2] <= write_response_count[2] + 1;
|
||||
total_write_responses <= total_write_responses + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
s_phase = "read fairness";
|
||||
mixed_mode_q = 1'b0;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
read_request = 3'b111;
|
||||
read_ready = 3'd0;
|
||||
wait (axi4.rvalid);
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
unique case (response_data_q)
|
||||
32'h0000_1000:
|
||||
if (!icache.rvalid || dcache.rvalid || mmio.rvalid || !axi4.rlast)
|
||||
$fatal(1, "Gateway A did not retain the APU R owner/RLAST");
|
||||
32'h0000_2000:
|
||||
if (icache.rvalid || !dcache.rvalid || mmio.rvalid || !axi4.rlast)
|
||||
$fatal(1, "Gateway A did not retain the SDIO0 R owner/RLAST");
|
||||
32'h0000_3000:
|
||||
if (icache.rvalid || dcache.rvalid || !mmio.rvalid || !axi4.rlast)
|
||||
$fatal(1, "Gateway A did not retain the USB2 R owner/RLAST");
|
||||
default: $fatal(1, "Gateway A retained an unknown R owner");
|
||||
endcase
|
||||
end
|
||||
read_ready = 3'b111;
|
||||
wait (total_responses >= 18);
|
||||
@(negedge clk_i);
|
||||
read_request = 3'd0;
|
||||
if ((response_count[0] != 6) || (response_count[1] != 6) || (response_count[2] != 6)) begin
|
||||
$fatal(1, "Gateway A client starvation detected");
|
||||
end
|
||||
|
||||
s_phase = "write fairness";
|
||||
write_response_ready = 3'd0;
|
||||
write_request = 3'b111;
|
||||
wait (axi4.bvalid);
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
unique case (write_address_q)
|
||||
32'h0000_4000:
|
||||
if (!icache.bvalid || dcache.bvalid || mmio.bvalid || !axi4.bvalid)
|
||||
$fatal(1, "Gateway A did not retain the APU B owner");
|
||||
32'h0000_5000:
|
||||
if (icache.bvalid || !dcache.bvalid || mmio.bvalid || !axi4.bvalid)
|
||||
$fatal(1, "Gateway A did not retain the SDIO0 B owner");
|
||||
32'h0000_6000:
|
||||
if (icache.bvalid || dcache.bvalid || !mmio.bvalid || !axi4.bvalid)
|
||||
$fatal(1, "Gateway A did not retain the USB2 B owner");
|
||||
default: $fatal(1, "Gateway A retained an unknown B owner");
|
||||
endcase
|
||||
end
|
||||
write_response_ready = 3'b111;
|
||||
wait (total_write_responses >= 18);
|
||||
@(negedge clk_i);
|
||||
write_request = 3'd0;
|
||||
if ((write_response_count[0] != 6) || (write_response_count[1] != 6) ||
|
||||
(write_response_count[2] != 6)) begin
|
||||
$fatal(1, "Gateway A write client starvation detected");
|
||||
end
|
||||
|
||||
s_phase = "mixed fairness";
|
||||
wait (!write_active_q && !write_response_valid_q);
|
||||
mixed_mode_q = 1'b1;
|
||||
read_request = 3'b101;
|
||||
write_request = 3'b010;
|
||||
wait (total_mixed_responses >= 9);
|
||||
@(negedge clk_i);
|
||||
read_request = 3'd0;
|
||||
write_request = 3'd0;
|
||||
if ((mixed_response_count[0] != 3) || (mixed_response_count[1] != 3) ||
|
||||
(mixed_response_count[2] != 3)) begin
|
||||
$fatal(1, "Gateway A mixed-traffic fairness mismatch");
|
||||
end
|
||||
|
||||
s_phase = "dynamic read address hold";
|
||||
wait (!response_valid_q && !write_active_q && !write_response_valid_q);
|
||||
mixed_mode_q = 1'b1;
|
||||
allow_read_address = 1'b0;
|
||||
read_request = 3'b010;
|
||||
wait (axi4.arvalid);
|
||||
repeat (2) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
read_request = 3'b011;
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!axi4.arvalid || (axi4.araddr != 32'h0000_2000)) begin
|
||||
$fatal(1, "Gateway A changed a stalled read address owner");
|
||||
end
|
||||
end
|
||||
allow_read_address = 1'b1;
|
||||
do @(posedge clk_i); while (!dcache.arready);
|
||||
@(negedge clk_i);
|
||||
read_request = 3'd0;
|
||||
wait (u_dut.s_state_q == 3'd0);
|
||||
|
||||
s_phase = "dynamic write address hold";
|
||||
allow_write_address = 1'b0;
|
||||
write_request = 3'b100;
|
||||
wait (axi4.awvalid);
|
||||
repeat (2) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
write_request = 3'b101;
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!axi4.awvalid || (axi4.awaddr != 32'h0000_6000)) begin
|
||||
$fatal(1, "Gateway A changed a stalled write address owner");
|
||||
end
|
||||
end
|
||||
allow_write_address = 1'b1;
|
||||
do @(posedge clk_i); while (!mmio.awready);
|
||||
@(negedge clk_i);
|
||||
write_request = 3'b100;
|
||||
wait (u_dut.s_state_q == 3'd0);
|
||||
write_request = 3'd0;
|
||||
|
||||
s_phase = "active response flush recovery";
|
||||
read_ready = 3'd0;
|
||||
read_request = 3'b001;
|
||||
wait (icache.rvalid);
|
||||
@(negedge clk_i);
|
||||
epoch_i = epoch_i + 1'b1;
|
||||
@(negedge clk_i);
|
||||
read_request = 3'b010;
|
||||
read_ready = 3'b111;
|
||||
do @(posedge clk_i); while (!dcache.arready);
|
||||
@(negedge clk_i);
|
||||
read_request = 3'd0;
|
||||
wait (u_dut.s_state_q == 3'd0);
|
||||
|
||||
@(negedge clk_i);
|
||||
s_phase = "non-aware read recovery";
|
||||
read_ready = 3'd0;
|
||||
read_request = 3'b010;
|
||||
s_phase = "non-aware read accepted";
|
||||
do @(posedge clk_i); while (!dcache.rvalid);
|
||||
@(negedge clk_i);
|
||||
epoch_i = epoch_i + 1'b1;
|
||||
read_request = 3'd0;
|
||||
s_phase = "non-aware read recovery state";
|
||||
wait (u_dut.s_state_q == 3'd3);
|
||||
s_phase = "non-aware read retained response";
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!dcache.rvalid || icache.rvalid || mmio.rvalid || (dcache.rresp != 2'b11) ||
|
||||
!dcache.rlast || axi4.arvalid || axi4.awvalid) begin
|
||||
$fatal(1, "Gateway A did not retain synthetic read recovery response");
|
||||
end
|
||||
end
|
||||
@(negedge clk_i);
|
||||
recovery_response_q = 1'b1;
|
||||
read_ready = 3'b111;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
recovery_response_q = 1'b0;
|
||||
s_phase = "non-aware read completion";
|
||||
wait (u_dut.s_state_q == 3'd0);
|
||||
|
||||
s_phase = "non-aware write recovery";
|
||||
write_response_ready = 3'd0;
|
||||
write_data_valid = 3'b011;
|
||||
write_request = 3'b100;
|
||||
wait (u_dut.s_state_q == 3'd2);
|
||||
@(negedge clk_i);
|
||||
epoch_i = epoch_i + 1'b1;
|
||||
wait (u_dut.s_state_q == 3'd4);
|
||||
if (axi4.awvalid || axi4.arvalid) $fatal(1, "Gateway A issued an address during recovery");
|
||||
repeat (2) @(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
write_data_valid = 3'b111;
|
||||
wait (u_dut.s_state_q == 3'd5);
|
||||
write_request = 3'd0;
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (!mmio.bvalid || icache.bvalid || dcache.bvalid || (mmio.bresp != 2'b11) ||
|
||||
axi4.awvalid || axi4.arvalid) begin
|
||||
$fatal(1, "Gateway A did not retain synthetic write recovery response");
|
||||
end
|
||||
end
|
||||
@(negedge clk_i);
|
||||
recovery_response_q = 1'b1;
|
||||
write_response_ready = 3'b111;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
recovery_response_q = 1'b0;
|
||||
wait (u_dut.s_state_q == 3'd0);
|
||||
$display("APU-P2 Gateway A round-robin fairness passed with read/write/mixed retention");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
repeat (1000) @(posedge clk_i);
|
||||
$fatal(
|
||||
1,
|
||||
"Gateway A round-robin test timed out in %s state=%0d rr=%0d read=%b write=%b rv=%0d bv=%0d",
|
||||
s_phase, u_dut.s_state_q, u_dut.s_rr_selected, read_request, write_request,
|
||||
response_valid_q, write_response_valid_q);
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in new issue
Block a user