mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- 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.
411 lines
16 KiB
Systemverilog
411 lines
16 KiB
Systemverilog
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
|
// SPDX-License-Identifier: MulanPSL-2.0
|
|
|
|
`include "apu_define.svh"
|
|
`include "axi4_define.svh"
|
|
|
|
module apu_dma (
|
|
// verilog_format: off -- preserve command, result, and bus columns
|
|
input logic clk_i,
|
|
input logic rst_n_i,
|
|
input logic abort_i,
|
|
input logic quiesce_i,
|
|
input logic [7:0] bridge_epoch_i,
|
|
input logic perf_enable_i,
|
|
input logic counter_clear_i,
|
|
input logic request_valid_i,
|
|
output logic request_ready_o,
|
|
input logic request_write_i,
|
|
input logic [31:0] request_addr_i,
|
|
input logic [31:0] request_bytes_i,
|
|
input logic [31:0] read_base_i,
|
|
input logic [31:0] read_limit_i,
|
|
input logic [31:0] write_base_i,
|
|
input logic [31:0] write_limit_i,
|
|
input logic [31:0] timeout_i,
|
|
axi4_stream_if.source read_axis,
|
|
axi4_stream_if.sink write_axis,
|
|
output logic busy_o,
|
|
output logic done_o,
|
|
output logic error_o,
|
|
output logic aborted_o,
|
|
output logic aborting_o,
|
|
output logic [5:0] error_code_o,
|
|
output logic [3:0] error_stage_o,
|
|
output logic [1:0] error_resp_o,
|
|
output logic [31:0] error_addr_o,
|
|
output logic input_pending_o,
|
|
output logic output_pending_o,
|
|
output logic [63:0] read_bytes_o,
|
|
output logic [63:0] write_bytes_o,
|
|
output logic [63:0] read_stalls_o,
|
|
output logic [63:0] write_stalls_o,
|
|
axi4_if.master axi4
|
|
// verilog_format: on
|
|
);
|
|
typedef enum logic [2:0] {
|
|
Idle,
|
|
ReadStart,
|
|
ReadData,
|
|
WriteStart,
|
|
WriteData,
|
|
WriteResponse
|
|
} state_e;
|
|
|
|
state_e s_state_q;
|
|
logic [31:0] s_addr_q;
|
|
logic [31:0] s_remaining_q;
|
|
logic [ 4:0] s_burst_beats_q;
|
|
logic [31:0] s_timeout_q;
|
|
logic s_drain_q;
|
|
logic s_err_q;
|
|
logic s_aborted_q;
|
|
logic [ 5:0] s_err_code_q;
|
|
logic [ 3:0] s_err_stage_q;
|
|
logic [ 1:0] s_err_resp_q;
|
|
logic [31:0] s_err_addr_q;
|
|
logic s_done_q;
|
|
logic [63:0] s_read_bytes_q;
|
|
logic [63:0] s_write_bytes_q;
|
|
logic [63:0] s_read_stalls_q;
|
|
logic [63:0] s_write_stalls_q;
|
|
logic [ 4:0] s_sel_beats;
|
|
logic [31:0] s_beat_bytes;
|
|
logic [ 3:0] s_expected_keep;
|
|
logic s_read_start_ready;
|
|
logic s_read_busy;
|
|
logic s_read_valid;
|
|
logic s_read_ready;
|
|
logic [31:0] s_read_data;
|
|
logic [ 1:0] s_read_resp;
|
|
logic s_read_last;
|
|
logic s_read_expected_last;
|
|
logic s_read_id_error;
|
|
logic s_read_done;
|
|
logic s_write_start_ready;
|
|
logic s_write_busy;
|
|
logic s_write_ready;
|
|
logic s_write_done;
|
|
logic [ 1:0] s_write_resp;
|
|
logic s_write_id_error;
|
|
logic s_progress;
|
|
logic s_timeout_expire;
|
|
logic s_read_fault;
|
|
logic s_write_fault;
|
|
logic s_payload_fault;
|
|
logic s_timeout_active;
|
|
logic s_bridge_flush;
|
|
logic s_read_missing_last;
|
|
logic s_req_range_valid;
|
|
logic [32:0] s_req_last;
|
|
logic [ 7:0] s_bridge_epoch_q;
|
|
|
|
function automatic logic [4:0] choose_burst(input logic [11:0] addr_i,
|
|
input logic [31:0] bytes_i);
|
|
logic [31:0] s_needed;
|
|
logic [31:0] s_boundary;
|
|
logic [31:0] s_sel;
|
|
begin
|
|
s_needed = (bytes_i + 32'd3) >> 2;
|
|
s_boundary = (32'd4096 - {20'd0, addr_i}) >> 2;
|
|
s_sel = s_needed;
|
|
if (s_sel > 32'd16) s_sel = 32'd16;
|
|
if (s_sel > s_boundary) s_sel = s_boundary;
|
|
if (s_sel == 32'd0) s_sel = 32'd1;
|
|
return s_sel[4:0];
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic [3:0] byte_mask(input logic [31:0] bytes_i);
|
|
case (bytes_i)
|
|
32'd1: byte_mask = 4'b0001;
|
|
32'd2: byte_mask = 4'b0011;
|
|
32'd3: byte_mask = 4'b0111;
|
|
default: byte_mask = 4'b1111;
|
|
endcase
|
|
endfunction
|
|
|
|
function automatic logic [63:0] saturating_add(input logic [63:0] value_i,
|
|
input logic [31:0] increment_i);
|
|
logic [64:0] s_sum;
|
|
begin
|
|
s_sum = {1'b0, value_i} + {33'd0, increment_i};
|
|
return s_sum[64] ? 64'hffff_ffff_ffff_ffff : s_sum[63:0];
|
|
end
|
|
endfunction
|
|
|
|
assign s_sel_beats = choose_burst(s_addr_q[11:0], s_remaining_q);
|
|
assign s_beat_bytes = (s_remaining_q < 32'd4) ? s_remaining_q : 32'd4;
|
|
assign s_expected_keep = byte_mask(s_beat_bytes);
|
|
assign s_req_last = {1'b0, request_addr_i} + {1'b0, request_bytes_i} - 1'b1;
|
|
assign s_req_range_valid = (request_bytes_i != 32'd0) && !s_req_last[32] &&
|
|
(request_addr_i[1:0] == 2'd0) &&
|
|
(request_write_i ? ((request_addr_i >= write_base_i) &&
|
|
(s_req_last[31:0] <= write_limit_i)) :
|
|
((request_addr_i >= read_base_i) &&
|
|
(s_req_last[31:0] <= read_limit_i)));
|
|
assign request_ready_o = (s_state_q == Idle) && !quiesce_i && !abort_i && !s_bridge_flush;
|
|
assign busy_o = s_state_q != Idle;
|
|
assign done_o = s_done_q;
|
|
assign error_o = s_err_q;
|
|
assign aborted_o = s_aborted_q;
|
|
assign aborting_o = busy_o && s_drain_q && s_aborted_q;
|
|
assign error_code_o = s_err_code_q;
|
|
assign error_stage_o = s_err_stage_q;
|
|
assign error_resp_o = s_err_resp_q;
|
|
assign error_addr_o = s_err_addr_q;
|
|
assign input_pending_o = (s_state_q == ReadStart) || (s_state_q == ReadData);
|
|
assign output_pending_o = (s_state_q == WriteStart) || (s_state_q == WriteData) ||
|
|
(s_state_q == WriteResponse);
|
|
assign read_bytes_o = s_read_bytes_q;
|
|
assign write_bytes_o = s_write_bytes_q;
|
|
assign read_stalls_o = s_read_stalls_q;
|
|
assign write_stalls_o = s_write_stalls_q;
|
|
|
|
assign read_axis.tdata = s_read_data;
|
|
assign read_axis.tkeep = s_expected_keep;
|
|
assign read_axis.tstrb = s_expected_keep;
|
|
assign read_axis.tlast = s_remaining_q <= 32'd4;
|
|
assign read_axis.tid = '0;
|
|
assign read_axis.tdest = '0;
|
|
assign read_axis.tuser = '0;
|
|
assign read_axis.tvalid = (s_state_q == ReadData) && s_read_valid && !s_drain_q && !s_read_fault;
|
|
assign s_read_ready = (s_state_q == ReadData) && (s_drain_q || s_read_fault || read_axis.tready);
|
|
assign s_read_fault = s_read_valid &&
|
|
((s_read_resp != `AXI4_RESP_OKAY) || s_read_id_error ||
|
|
(s_read_last != s_read_expected_last));
|
|
assign s_read_missing_last = (s_state_q == ReadData) && s_read_valid && s_read_ready &&
|
|
s_read_expected_last && !s_read_last;
|
|
|
|
assign s_payload_fault = write_axis.tvalid &&
|
|
((write_axis.tkeep != s_expected_keep) || (write_axis.tstrb != s_expected_keep) ||
|
|
(write_axis.tlast != (s_remaining_q <= 32'd4)));
|
|
assign write_axis.tready = (s_state_q == WriteData) && s_write_ready && !s_drain_q &&
|
|
!s_payload_fault;
|
|
assign s_write_fault = s_write_done && ((s_write_resp != `AXI4_RESP_OKAY) || s_write_id_error);
|
|
assign s_progress = (axi4.awvalid && axi4.awready) || (axi4.wvalid && axi4.wready) ||
|
|
(axi4.bvalid && axi4.bready) || (axi4.arvalid && axi4.arready) ||
|
|
(axi4.rvalid && axi4.rready);
|
|
assign s_timeout_active = s_read_busy || s_write_busy;
|
|
assign s_timeout_expire = s_timeout_active && !s_progress && (s_timeout_q == 32'd1);
|
|
assign s_bridge_flush = bridge_epoch_i != s_bridge_epoch_q;
|
|
|
|
dma_axi4_master #(
|
|
.AddrWidth (32),
|
|
.DataWidth (32),
|
|
.MaxBurstBeats(16)
|
|
) u_axi4_master (
|
|
.clk_i(clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.clear_i(s_bridge_flush || s_read_missing_last),
|
|
.read_start_valid_i((s_state_q == ReadStart) && !s_drain_q && !abort_i && !s_timeout_expire),
|
|
.read_start_ready_o(s_read_start_ready),
|
|
.read_addr_i(s_addr_q),
|
|
.read_beats_i(s_sel_beats),
|
|
.read_fixed_i(1'b0),
|
|
.read_busy_o(s_read_busy),
|
|
.read_beat_valid_o(s_read_valid),
|
|
.read_beat_ready_i(s_read_ready),
|
|
.read_data_o(s_read_data),
|
|
.read_resp_o(s_read_resp),
|
|
.read_last_o(s_read_last),
|
|
.read_expected_last_o(s_read_expected_last),
|
|
.read_id_error_o(s_read_id_error),
|
|
.read_done_o(s_read_done),
|
|
.write_start_valid_i((s_state_q == WriteStart) && !s_drain_q && !abort_i &&
|
|
!s_timeout_expire),
|
|
.write_start_ready_o(s_write_start_ready),
|
|
.write_addr_i(s_addr_q),
|
|
.write_beats_i(s_sel_beats),
|
|
.write_fixed_i(1'b0),
|
|
.write_busy_o(s_write_busy),
|
|
.write_data_valid_i ((s_state_q == WriteData) &&
|
|
(s_drain_q || s_payload_fault || write_axis.tvalid)),
|
|
.write_data_ready_o(s_write_ready),
|
|
.write_data_i((s_drain_q || s_payload_fault) ? 32'd0 : write_axis.tdata),
|
|
.write_strb_i((s_drain_q || s_payload_fault) ? 4'd0 : write_axis.tkeep),
|
|
.write_done_o(s_write_done),
|
|
.write_resp_o(s_write_resp),
|
|
.write_id_error_o(s_write_id_error),
|
|
.axi4(axi4)
|
|
);
|
|
|
|
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
s_state_q <= Idle;
|
|
s_addr_q <= 32'd0;
|
|
s_remaining_q <= 32'd0;
|
|
s_burst_beats_q <= 5'd0;
|
|
s_timeout_q <= 32'd0;
|
|
s_drain_q <= 1'b0;
|
|
s_err_q <= 1'b0;
|
|
s_aborted_q <= 1'b0;
|
|
s_err_code_q <= 6'd0;
|
|
s_err_stage_q <= 4'd0;
|
|
s_err_resp_q <= 2'd0;
|
|
s_err_addr_q <= 32'd0;
|
|
s_done_q <= 1'b0;
|
|
s_read_bytes_q <= 64'd0;
|
|
s_write_bytes_q <= 64'd0;
|
|
s_read_stalls_q <= 64'd0;
|
|
s_write_stalls_q <= 64'd0;
|
|
s_bridge_epoch_q <= 8'd0;
|
|
end else begin
|
|
s_done_q <= 1'b0;
|
|
s_bridge_epoch_q <= bridge_epoch_i;
|
|
if (counter_clear_i) begin
|
|
s_read_bytes_q <= 64'd0;
|
|
s_write_bytes_q <= 64'd0;
|
|
s_read_stalls_q <= 64'd0;
|
|
s_write_stalls_q <= 64'd0;
|
|
end
|
|
if (s_bridge_flush) begin
|
|
s_timeout_q <= 32'd0;
|
|
s_drain_q <= 1'b0;
|
|
if (s_state_q != Idle) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_err_q <= 1'b1;
|
|
s_aborted_q <= s_aborted_q || abort_i;
|
|
s_err_code_q <= output_pending_o ? 6'd16 : 6'd15;
|
|
s_err_stage_q <= output_pending_o ? 4'd8 : 4'd3;
|
|
s_err_resp_q <= `AXI4_RESP_DECODE_ERROR;
|
|
s_err_addr_q <= s_addr_q;
|
|
end
|
|
end else if (request_valid_i && request_ready_o) begin
|
|
s_drain_q <= 1'b0;
|
|
s_err_q <= 1'b0;
|
|
s_aborted_q <= 1'b0;
|
|
s_err_code_q <= 6'd0;
|
|
s_err_stage_q <= 4'd0;
|
|
s_err_resp_q <= 2'd0;
|
|
s_err_addr_q <= 32'd0;
|
|
s_addr_q <= request_addr_i;
|
|
s_remaining_q <= request_bytes_i;
|
|
s_timeout_q <= timeout_i;
|
|
if (!s_req_range_valid) begin
|
|
s_err_q <= 1'b1;
|
|
s_err_code_q <= request_write_i ? 6'd16 : 6'd15;
|
|
s_err_stage_q <= request_write_i ? 4'd8 : 4'd3;
|
|
s_err_resp_q <= `AXI4_RESP_SLAVE_ERROR;
|
|
s_err_addr_q <= request_addr_i;
|
|
s_done_q <= 1'b1;
|
|
end else begin
|
|
s_state_q <= request_write_i ? WriteStart : ReadStart;
|
|
end
|
|
end else if (s_state_q != Idle) begin
|
|
if (s_progress) s_timeout_q <= timeout_i;
|
|
else if (s_timeout_active && (s_timeout_q != 32'd0)) s_timeout_q <= s_timeout_q - 1'b1;
|
|
if (perf_enable_i && !counter_clear_i && input_pending_o && !s_progress)
|
|
s_read_stalls_q <= saturating_add(s_read_stalls_q, 1);
|
|
if (perf_enable_i && !counter_clear_i && output_pending_o && !s_progress)
|
|
s_write_stalls_q <= saturating_add(s_write_stalls_q, 1);
|
|
if ((abort_i || s_timeout_expire) && !s_drain_q) begin
|
|
s_drain_q <= 1'b1;
|
|
s_timeout_q <= 32'd0;
|
|
s_err_q <= s_timeout_expire;
|
|
s_aborted_q <= abort_i;
|
|
s_err_code_q <= s_timeout_expire ? 6'd17 : 6'd20;
|
|
s_err_stage_q <= output_pending_o ? 4'd8 : 4'd3;
|
|
s_err_addr_q <= s_addr_q;
|
|
end
|
|
case (s_state_q)
|
|
ReadStart: begin
|
|
if (s_drain_q) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_drain_q <= 1'b0;
|
|
s_timeout_q <= 32'd0;
|
|
end else if (s_read_start_ready && !abort_i && !s_timeout_expire) begin
|
|
s_state_q <= ReadData;
|
|
end
|
|
end
|
|
ReadData: begin
|
|
if (s_read_valid && s_read_ready) begin
|
|
if (s_read_fault) begin
|
|
s_err_q <= 1'b1;
|
|
s_err_code_q <= 6'd15;
|
|
s_err_stage_q <= 4'd3;
|
|
s_err_resp_q <= s_read_resp;
|
|
s_err_addr_q <= s_addr_q;
|
|
s_drain_q <= 1'b1;
|
|
end else if (perf_enable_i && !s_drain_q && !counter_clear_i) begin
|
|
s_read_bytes_q <= saturating_add(s_read_bytes_q, s_beat_bytes);
|
|
end
|
|
s_addr_q <= s_addr_q + s_beat_bytes;
|
|
s_remaining_q <= s_remaining_q - s_beat_bytes;
|
|
if (s_read_missing_last) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_drain_q <= 1'b0;
|
|
s_timeout_q <= 32'd0;
|
|
end else if (s_read_last) begin
|
|
if (s_drain_q || s_read_fault || (s_remaining_q <= 32'd4)) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_drain_q <= 1'b0;
|
|
s_timeout_q <= 32'd0;
|
|
end else begin
|
|
s_state_q <= ReadStart;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
WriteStart: begin
|
|
if (s_drain_q) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_drain_q <= 1'b0;
|
|
s_timeout_q <= 32'd0;
|
|
end else if (s_write_start_ready && !abort_i && !s_timeout_expire) begin
|
|
s_burst_beats_q <= s_sel_beats;
|
|
s_state_q <= WriteData;
|
|
end
|
|
end
|
|
WriteData: begin
|
|
if (s_write_ready && (s_drain_q || s_payload_fault || write_axis.tvalid)) begin
|
|
if (s_payload_fault && !s_err_q && !s_drain_q) begin
|
|
s_err_q <= 1'b1;
|
|
s_err_code_q <= 6'd16;
|
|
s_err_stage_q <= 4'd8;
|
|
s_err_resp_q <= `AXI4_RESP_SLAVE_ERROR;
|
|
s_err_addr_q <= s_addr_q;
|
|
s_drain_q <= 1'b1;
|
|
end else if (perf_enable_i && !s_drain_q && !counter_clear_i) begin
|
|
s_write_bytes_q <= saturating_add(s_write_bytes_q, s_beat_bytes);
|
|
end
|
|
s_addr_q <= s_addr_q + s_beat_bytes;
|
|
s_remaining_q <= s_remaining_q - s_beat_bytes;
|
|
s_burst_beats_q <= s_burst_beats_q - 1'b1;
|
|
if (s_burst_beats_q == 5'd1) s_state_q <= WriteResponse;
|
|
end
|
|
end
|
|
WriteResponse: begin
|
|
if (s_write_done) begin
|
|
if (s_write_fault) begin
|
|
s_err_q <= 1'b1;
|
|
s_err_code_q <= 6'd16;
|
|
s_err_stage_q <= 4'd8;
|
|
s_err_resp_q <= s_write_resp;
|
|
s_err_addr_q <= s_addr_q;
|
|
end
|
|
if (s_drain_q || s_write_fault || (s_remaining_q == 32'd0)) begin
|
|
s_state_q <= Idle;
|
|
s_done_q <= 1'b1;
|
|
s_drain_q <= 1'b0;
|
|
s_timeout_q <= 32'd0;
|
|
end else begin
|
|
s_state_q <= WriteStart;
|
|
end
|
|
end
|
|
end
|
|
default: s_state_q <= Idle;
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
|
|
logic s_unused_master_status;
|
|
assign s_unused_master_status = s_read_busy ^ s_read_done ^ s_write_busy;
|
|
endmodule
|