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:
maksyuki committed 2026-09-03 13:56:04 +08:00
1 parent a464cc26dc
commit 5c65fcca27
48 files changed
+7485 -469

No files matched your search

+516
View File
@@ -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
+81 -9
View File
@@ -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;
+83
View File
@@ -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
+312
View File
@@ -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
View File
@@ -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
+626
View File
@@ -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
+294
View File
@@ -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
+8 -1
View File
@@ -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
+31
View File
@@ -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
+532
View File
@@ -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