mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: add formal verification properties for DMA design and corresponding testbench
- Introduced to define formal properties for the DMA design, ensuring correct behavior under various conditions. - Created as a testbench for the DMA register interface, implementing APB and AXI4 protocols to validate register operations. - Added to verify synchronization between RTL and SDK register constants, ensuring consistency across implementations.
This commit is contained in:
57 files changed
+5285
-1300
No files matched your search
+380
-211
@@ -1,43 +1,84 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module dma_error_tb;
|
||||
localparam logic [1:0] MODEL_IDLE = 2'd0;
|
||||
localparam logic [1:0] MODEL_WDATA = 2'd1;
|
||||
localparam logic [1:0] MODEL_RESP = 2'd2;
|
||||
localparam int NumChannels = 4;
|
||||
localparam logic [31:0] SourceBase = 32'h4000_0000;
|
||||
localparam logic [31:0] CopySource = 32'h4000_0FF0;
|
||||
localparam logic [31:0] CopyDestination = 32'h4000_2000;
|
||||
localparam logic [31:0] ErrorSource = 32'hDEAD_0000;
|
||||
localparam logic [31:0] StreamSource = 32'h4000_3000;
|
||||
localparam logic [31:0] StreamDestination = 32'h4000_4000;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic [ 3:0] mode_i = 4'd0;
|
||||
logic [31:0] srcaddr_i = 32'h3800_1000;
|
||||
logic srcincr_i = 1'b1;
|
||||
logic [31:0] dstaddr_i = 32'h4000_1000;
|
||||
logic dstincr_i = 1'b1;
|
||||
logic [31:0] xferlen_i = 32'd1;
|
||||
logic start_i = 1'b0;
|
||||
logic stop_i = 1'b0;
|
||||
logic reset_i = 1'b0;
|
||||
logic done_o;
|
||||
logic error_o;
|
||||
logic [ 2:0] error_code_o;
|
||||
logic [31:0] error_addr_o;
|
||||
logic [ 1:0] fsm_o;
|
||||
logic error_mode = 1'b0;
|
||||
integer write_count = 0;
|
||||
integer incr4_read_count = 0;
|
||||
integer incr4_write_count = 0;
|
||||
integer legacy_cycles;
|
||||
integer burst_cycles;
|
||||
integer stream_tx_count = 0;
|
||||
integer stream_rx_count = 0;
|
||||
logic stream_tx_ready = 1'b0;
|
||||
logic stream_rx_valid = 1'b0;
|
||||
logic [ 1:0] model_state_q = MODEL_IDLE;
|
||||
logic [ 1:0] model_len_q = '0;
|
||||
logic [ 1:0] model_beat_q = '0;
|
||||
logic model_write_q = 1'b0;
|
||||
logic model_error_q = 1'b0;
|
||||
dma_hw_trg_if hw_trg ();
|
||||
rib_if rib ();
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic [NumChannels*32-1:0] ch_cfg_i = '0;
|
||||
logic [NumChannels*32-1:0] src_addr_i = '0;
|
||||
logic [NumChannels*32-1:0] dst_addr_i = '0;
|
||||
logic [NumChannels*32-1:0] byte_count_i = '0;
|
||||
logic [NumChannels*32-1:0] request_sel_i = '0;
|
||||
logic [NumChannels*32-1:0] burst_cfg_i = '0;
|
||||
logic [ NumChannels-1:0] start_i = '0;
|
||||
logic [ NumChannels-1:0] suspend_i = '0;
|
||||
logic [ NumChannels-1:0] resume_i = '0;
|
||||
logic [ NumChannels-1:0] abort_i = '0;
|
||||
logic [ NumChannels-1:0] channel_reset_i = '0;
|
||||
logic [ NumChannels*3-1:0] event_clear_i = '0;
|
||||
logic [ NumChannels-1:0] busy_o;
|
||||
logic [ NumChannels-1:0] suspended_o;
|
||||
logic [ NumChannels-1:0] done_o;
|
||||
logic [ NumChannels-1:0] aborted_o;
|
||||
logic [ NumChannels-1:0] error_o;
|
||||
logic [ NumChannels-1:0] stream_last_o;
|
||||
logic [ NumChannels*3-1:0] event_status_o;
|
||||
logic [NumChannels*32-1:0] error_status_o;
|
||||
logic [NumChannels*32-1:0] error_addr_o;
|
||||
logic [NumChannels*32-1:0] current_src_o;
|
||||
logic [NumChannels*32-1:0] current_dst_o;
|
||||
logic [NumChannels*32-1:0] remaining_o;
|
||||
logic [NumChannels*32-1:0] bytes_done_o;
|
||||
logic [NumChannels*32-1:0] stall_cycles_lo_o;
|
||||
logic [NumChannels*32-1:0] stall_cycles_hi_o;
|
||||
logic first_error_valid_o;
|
||||
logic [ 1:0] first_error_channel_o;
|
||||
logic [ 31:0] first_error_status_o;
|
||||
logic [ 31:0] first_error_addr_o;
|
||||
logic [ 15:0] request_status_o;
|
||||
logic xpi_xfer_done_o;
|
||||
logic error_mode = 1'b0;
|
||||
logic [ 31:0] cycle_q = '0;
|
||||
|
||||
logic read_active_q = 1'b0;
|
||||
logic [ 31:0] read_addr_q = '0;
|
||||
logic [ 4:0] read_beats_q = '0;
|
||||
logic [ 4:0] read_index_q = '0;
|
||||
logic write_active_q = 1'b0;
|
||||
logic [ 31:0] write_addr_q = '0;
|
||||
logic [ 4:0] write_beats_q = '0;
|
||||
logic [ 4:0] write_index_q = '0;
|
||||
logic write_response_q = 1'b0;
|
||||
integer write_count = 0;
|
||||
integer read_burst_count = 0;
|
||||
integer write_burst_count = 0;
|
||||
integer max_read_beats = 0;
|
||||
integer max_write_beats = 0;
|
||||
integer stream_tx_count = 0;
|
||||
integer stream_rx_count = 0;
|
||||
logic [ 31:0] written_addr [0:127];
|
||||
logic [ 31:0] written_data [0:127];
|
||||
logic [ 31:0] stream_tx_data [ 0:7];
|
||||
logic stream_tx_last [ 0:7];
|
||||
logic rx_driver_valid_q = 1'b0;
|
||||
logic [ 31:0] rx_driver_data_q = 32'h5100_0000;
|
||||
logic [ 3:0] rx_driver_keep_q = 4'hF;
|
||||
logic rx_driver_last_q = 1'b0;
|
||||
logic stream_tx_ready_enable = 1'b1;
|
||||
logic write_accept_enable = 1'b1;
|
||||
|
||||
dma_req_if req ();
|
||||
axi4_if axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
axi4_stream_if i2s_tx_axis (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
@@ -53,223 +94,351 @@ module dma_error_tb;
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign rib.cmd_ready = model_state_q == MODEL_IDLE;
|
||||
assign rib.w_ready = model_state_q == MODEL_WDATA;
|
||||
assign rib.rsp_valid = model_state_q == MODEL_RESP;
|
||||
assign rib.rdata = 32'hA5A5_5A5A + model_beat_q;
|
||||
assign rib.resp_err = model_error_q;
|
||||
assign rib.resp_code = model_error_q ? `RIB_RESP_DECERR : `RIB_RESP_OK;
|
||||
assign rib.rsp_beat = model_beat_q;
|
||||
assign rib.rsp_last = model_write_q || model_error_q || (model_beat_q == model_len_q);
|
||||
assign i2s_tx_axis.tready = stream_tx_ready;
|
||||
assign i2s_rx_axis.tdata = 32'h5100_0000 + stream_rx_count;
|
||||
assign i2s_rx_axis.tkeep = '1;
|
||||
assign i2s_rx_axis.tstrb = '1;
|
||||
assign i2s_rx_axis.tlast = stream_rx_count == 1;
|
||||
assign i2s_rx_axis.tid = '0;
|
||||
assign i2s_rx_axis.tdest = '0;
|
||||
assign i2s_rx_axis.tuser = '0;
|
||||
assign i2s_rx_axis.tvalid = stream_rx_valid;
|
||||
assign dvp_rx_axis.tdata = '0;
|
||||
assign dvp_rx_axis.tkeep = '0;
|
||||
assign dvp_rx_axis.tstrb = '0;
|
||||
assign dvp_rx_axis.tlast = 1'b0;
|
||||
assign dvp_rx_axis.tid = '0;
|
||||
assign dvp_rx_axis.tdest = '0;
|
||||
assign dvp_rx_axis.tuser = '0;
|
||||
function automatic logic [31:0] model_read_data(input logic [31:0] address);
|
||||
model_read_data = 32'hA500_0000 + ((address - SourceBase) >> 2);
|
||||
endfunction
|
||||
|
||||
assign axi4.arready = !read_active_q && cycle_q[0];
|
||||
assign axi4.rvalid = read_active_q && cycle_q[1];
|
||||
assign axi4.rid = '0;
|
||||
assign axi4.rdata = model_read_data(read_addr_q + (read_index_q << 2));
|
||||
assign axi4.rresp =
|
||||
error_mode && (read_addr_q == ErrorSource) ? `AXI4_RESP_DECODE_ERROR : `AXI4_RESP_OKAY;
|
||||
assign axi4.rlast = (read_index_q + 1'b1) == read_beats_q;
|
||||
assign axi4.ruser = '0;
|
||||
|
||||
assign axi4.awready = write_accept_enable && !write_active_q && !write_response_q && cycle_q[0];
|
||||
assign axi4.wready = write_active_q && cycle_q[1];
|
||||
assign axi4.bid = '0;
|
||||
assign axi4.bresp = `AXI4_RESP_OKAY;
|
||||
assign axi4.buser = '0;
|
||||
assign axi4.bvalid = write_response_q && cycle_q[0];
|
||||
|
||||
assign i2s_tx_axis.tready = stream_tx_ready_enable && cycle_q[0];
|
||||
assign i2s_rx_axis.tdata = rx_driver_data_q;
|
||||
assign i2s_rx_axis.tkeep = rx_driver_keep_q;
|
||||
assign i2s_rx_axis.tstrb = 4'hF;
|
||||
assign i2s_rx_axis.tlast = rx_driver_last_q;
|
||||
assign i2s_rx_axis.tid = '0;
|
||||
assign i2s_rx_axis.tdest = '0;
|
||||
assign i2s_rx_axis.tuser = '0;
|
||||
assign i2s_rx_axis.tvalid = rx_driver_valid_q;
|
||||
assign dvp_rx_axis.tdata = '0;
|
||||
assign dvp_rx_axis.tkeep = '0;
|
||||
assign dvp_rx_axis.tstrb = '0;
|
||||
assign dvp_rx_axis.tlast = 1'b0;
|
||||
assign dvp_rx_axis.tid = '0;
|
||||
assign dvp_rx_axis.tdest = '0;
|
||||
assign dvp_rx_axis.tuser = '0;
|
||||
assign dvp_rx_axis.tvalid = 1'b0;
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (i2s_tx_axis.tvalid && i2s_tx_axis.tready) begin
|
||||
if (i2s_tx_axis.tdata !== 32'hA5A5_5A5A) begin
|
||||
$fatal(1, "I2S TX stream data mismatch");
|
||||
end
|
||||
stream_tx_count <= stream_tx_count + 1;
|
||||
end
|
||||
if (i2s_rx_axis.tvalid && i2s_rx_axis.tready) begin
|
||||
stream_rx_count <= stream_rx_count + 1;
|
||||
if (stream_rx_count == 1) stream_rx_valid <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk_i or negedge rst_n_i) begin
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
model_state_q <= MODEL_IDLE;
|
||||
model_len_q <= '0;
|
||||
model_beat_q <= '0;
|
||||
model_write_q <= 1'b0;
|
||||
model_error_q <= 1'b0;
|
||||
write_count <= 0;
|
||||
cycle_q <= '0;
|
||||
read_active_q <= 1'b0;
|
||||
read_addr_q <= '0;
|
||||
read_beats_q <= '0;
|
||||
read_index_q <= '0;
|
||||
write_active_q <= 1'b0;
|
||||
write_addr_q <= '0;
|
||||
write_beats_q <= '0;
|
||||
write_index_q <= '0;
|
||||
write_response_q <= 1'b0;
|
||||
write_count <= 0;
|
||||
read_burst_count <= 0;
|
||||
write_burst_count <= 0;
|
||||
max_read_beats <= 0;
|
||||
max_write_beats <= 0;
|
||||
end else begin
|
||||
unique case (model_state_q)
|
||||
MODEL_IDLE: begin
|
||||
if (rib.cmd_valid && rib.cmd_ready) begin
|
||||
model_len_q <= rib.cmd_len;
|
||||
model_beat_q <= '0;
|
||||
model_write_q <= rib.cmd_write;
|
||||
model_error_q <= error_mode && !rib.cmd_write;
|
||||
model_state_q <= rib.cmd_write ? MODEL_WDATA : MODEL_RESP;
|
||||
if (rib.cmd_len == `RIB_LEN_INCR4 && rib.cmd_write) begin
|
||||
incr4_write_count <= incr4_write_count + 1;
|
||||
end
|
||||
if (rib.cmd_len == `RIB_LEN_INCR4 && !rib.cmd_write) begin
|
||||
incr4_read_count <= incr4_read_count + 1;
|
||||
end
|
||||
end
|
||||
cycle_q <= cycle_q + 1'b1;
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
read_active_q <= 1'b1;
|
||||
read_addr_q <= axi4.araddr;
|
||||
read_beats_q <= axi4.arlen + 1'b1;
|
||||
read_index_q <= '0;
|
||||
read_burst_count <= read_burst_count + 1;
|
||||
if ((axi4.arlen + 1) > max_read_beats) begin
|
||||
max_read_beats <= axi4.arlen + 1;
|
||||
end
|
||||
MODEL_WDATA: begin
|
||||
if (rib.w_valid && rib.w_ready) begin
|
||||
write_count <= write_count + 1;
|
||||
if (rib.wlast) begin
|
||||
model_beat_q <= model_len_q;
|
||||
model_state_q <= MODEL_RESP;
|
||||
end else begin
|
||||
model_beat_q <= model_beat_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
if (axi4.rvalid && axi4.rready) begin
|
||||
if (axi4.rlast) begin
|
||||
read_active_q <= 1'b0;
|
||||
end else begin
|
||||
read_index_q <= read_index_q + 1'b1;
|
||||
end
|
||||
MODEL_RESP: begin
|
||||
if (rib.rsp_valid && rib.rsp_ready) begin
|
||||
if (rib.rsp_last) begin
|
||||
model_state_q <= MODEL_IDLE;
|
||||
end else begin
|
||||
model_beat_q <= model_beat_q + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
if (axi4.awvalid && axi4.awready) begin
|
||||
write_active_q <= 1'b1;
|
||||
write_addr_q <= axi4.awaddr;
|
||||
write_beats_q <= axi4.awlen + 1'b1;
|
||||
write_index_q <= '0;
|
||||
write_burst_count <= write_burst_count + 1;
|
||||
if ((axi4.awlen + 1) > max_write_beats) begin
|
||||
max_write_beats <= axi4.awlen + 1;
|
||||
end
|
||||
default: model_state_q <= MODEL_IDLE;
|
||||
endcase
|
||||
end
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
written_addr[write_count] <= write_addr_q + (write_index_q << 2);
|
||||
written_data[write_count] <= axi4.wdata;
|
||||
write_count <= write_count + 1;
|
||||
if (axi4.wlast) begin
|
||||
if ((write_index_q + 1'b1) != write_beats_q) begin
|
||||
$fatal(1, "AXI WLAST did not match the declared burst length");
|
||||
end
|
||||
write_active_q <= 1'b0;
|
||||
write_response_q <= 1'b1;
|
||||
end else begin
|
||||
write_index_q <= write_index_q + 1'b1;
|
||||
end
|
||||
end
|
||||
if (axi4.bvalid && axi4.bready) begin
|
||||
write_response_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dma_core u_dma_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.mode_i (mode_i),
|
||||
.srcaddr_i (srcaddr_i),
|
||||
.srcincr_i (srcincr_i),
|
||||
.dstaddr_i (dstaddr_i),
|
||||
.dstincr_i (dstincr_i),
|
||||
.xferlen_i (xferlen_i),
|
||||
.start_i (start_i),
|
||||
.stop_i (stop_i),
|
||||
.reset_i (reset_i),
|
||||
.done_o (done_o),
|
||||
.error_o (error_o),
|
||||
.error_code_o(error_code_o),
|
||||
.error_addr_o(error_addr_o),
|
||||
.fsm_o (fsm_o),
|
||||
.hw_trg (hw_trg),
|
||||
.rib (rib),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis)
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
stream_tx_count <= 0;
|
||||
stream_rx_count <= 0;
|
||||
rx_driver_valid_q <= 1'b0;
|
||||
rx_driver_data_q <= 32'h5100_0000;
|
||||
end else begin
|
||||
if (i2s_tx_axis.tvalid && i2s_tx_axis.tready) begin
|
||||
stream_tx_data[stream_tx_count] <= i2s_tx_axis.tdata;
|
||||
stream_tx_last[stream_tx_count] <= i2s_tx_axis.tlast;
|
||||
stream_tx_count <= stream_tx_count + 1;
|
||||
end
|
||||
if (i2s_rx_axis.tvalid && i2s_rx_axis.tready) begin
|
||||
stream_rx_count <= stream_rx_count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dma_core #(
|
||||
.NumChannels (NumChannels),
|
||||
.MaxBurstBeats(16),
|
||||
.FifoDepth (16)
|
||||
) u_dma_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.global_reset_i (1'b0),
|
||||
.global_error_clear_i (1'b0),
|
||||
.ch_cfg_i (ch_cfg_i),
|
||||
.src_addr_i (src_addr_i),
|
||||
.dst_addr_i (dst_addr_i),
|
||||
.byte_count_i (byte_count_i),
|
||||
.request_sel_i (request_sel_i),
|
||||
.burst_cfg_i (burst_cfg_i),
|
||||
.start_i (start_i),
|
||||
.suspend_i (suspend_i),
|
||||
.resume_i (resume_i),
|
||||
.abort_i (abort_i),
|
||||
.channel_reset_i (channel_reset_i),
|
||||
.event_clear_i (event_clear_i),
|
||||
.busy_o (busy_o),
|
||||
.suspended_o (suspended_o),
|
||||
.done_o (done_o),
|
||||
.aborted_o (aborted_o),
|
||||
.error_o (error_o),
|
||||
.stream_last_o (stream_last_o),
|
||||
.event_status_o (event_status_o),
|
||||
.error_status_o (error_status_o),
|
||||
.error_addr_o (error_addr_o),
|
||||
.current_src_o (current_src_o),
|
||||
.current_dst_o (current_dst_o),
|
||||
.remaining_o (remaining_o),
|
||||
.bytes_done_o (bytes_done_o),
|
||||
.stall_cycles_lo_o (stall_cycles_lo_o),
|
||||
.stall_cycles_hi_o (stall_cycles_hi_o),
|
||||
.first_error_valid_o (first_error_valid_o),
|
||||
.first_error_channel_o(first_error_channel_o),
|
||||
.first_error_status_o (first_error_status_o),
|
||||
.first_error_addr_o (first_error_addr_o),
|
||||
.request_status_o (request_status_o),
|
||||
.xpi_xfer_done_o (xpi_xfer_done_o),
|
||||
.req (req),
|
||||
.axi4 (axi4),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis)
|
||||
);
|
||||
|
||||
task automatic start_transfer;
|
||||
task automatic configure_channel(input integer channel, input logic [2:0] kind,
|
||||
input logic [3:0] request, input logic [31:0] source,
|
||||
input logic source_increment, input logic [31:0] destination,
|
||||
input logic destination_increment, input logic [31:0] byte_count,
|
||||
input logic [1:0] priority_i, input logic [4:0] burst_beats);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
ch_cfg_i[(channel*32)+:32] = {
|
||||
22'd0, priority_i, destination_increment, source_increment, 2'd2, 1'b0, kind
|
||||
};
|
||||
src_addr_i[(channel*32)+:32] = source;
|
||||
dst_addr_i[(channel*32)+:32] = destination;
|
||||
byte_count_i[(channel*32)+:32] = byte_count;
|
||||
request_sel_i[(channel*32)+:32] = {28'd0, request};
|
||||
burst_cfg_i[(channel*32)+:32] = {27'd0, burst_beats};
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic measure_transfer(output integer elapsed);
|
||||
task automatic start_channel(input integer channel);
|
||||
begin
|
||||
start_transfer();
|
||||
elapsed = 0;
|
||||
while (!done_o) begin
|
||||
@(negedge clk_i);
|
||||
start_i[channel] = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i[channel] = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_terminal(input integer channel);
|
||||
integer timeout;
|
||||
|
||||
begin
|
||||
timeout = 0;
|
||||
while (!done_o[channel] && !error_o[channel] && !aborted_o[channel]) begin
|
||||
@(posedge clk_i);
|
||||
elapsed = elapsed + 1;
|
||||
timeout = timeout + 1;
|
||||
if (timeout > 1000) begin
|
||||
$fatal(1, "DMA channel %0d timed out", channel);
|
||||
end
|
||||
end
|
||||
@(negedge clk_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic drive_rx_beat(input logic [31:0] data, input logic [3:0] keep, input logic last);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
rx_driver_data_q = data;
|
||||
rx_driver_keep_q = keep;
|
||||
rx_driver_last_q = last;
|
||||
rx_driver_valid_q = 1'b1;
|
||||
do begin
|
||||
@(posedge clk_i);
|
||||
end while (!i2s_rx_axis.tready);
|
||||
@(negedge clk_i);
|
||||
rx_driver_valid_q = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
hw_trg.i2s_tx_proc = 1'b0;
|
||||
hw_trg.i2s_rx_proc = 1'b0;
|
||||
hw_trg.qspi_tx_proc = 1'b0;
|
||||
hw_trg.qspi_rx_proc = 1'b0;
|
||||
hw_trg.uart_tx_proc = 1'b0;
|
||||
hw_trg.uart_rx_proc = 1'b0;
|
||||
hw_trg.i2c0_tx_proc = 1'b0;
|
||||
hw_trg.i2c0_rx_proc = 1'b0;
|
||||
hw_trg.i2c1_tx_proc = 1'b0;
|
||||
hw_trg.i2c1_rx_proc = 1'b0;
|
||||
repeat (2) @(posedge clk_i);
|
||||
integer write_before;
|
||||
logic [31:0] stalled_stream_data;
|
||||
logic stalled_stream_last;
|
||||
|
||||
req.i2s_tx_proc = 1'b1;
|
||||
req.i2s_rx_proc = 1'b1;
|
||||
req.qspi_tx_proc = 1'b1;
|
||||
req.qspi_rx_proc = 1'b1;
|
||||
req.uart_tx_proc = 1'b1;
|
||||
req.uart_rx_proc = 1'b1;
|
||||
req.i2c0_tx_proc = 1'b1;
|
||||
req.i2c0_rx_proc = 1'b1;
|
||||
req.i2c1_tx_proc = 1'b1;
|
||||
req.i2c1_rx_proc = 1'b1;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
start_transfer();
|
||||
wait (done_o);
|
||||
@(negedge clk_i);
|
||||
if (write_count != 1) begin
|
||||
$fatal(1, "one-word DMA transfer issued %0d writes", write_count);
|
||||
configure_channel(0, 3'd0, 4'd0, CopySource, 1'b1, CopyDestination, 1'b1, 32'd80, 2'd1, 5'd16);
|
||||
start_channel(0);
|
||||
wait_terminal(0);
|
||||
if (error_o[0] || (write_count != 20) || (max_read_beats != 16) ||
|
||||
(max_write_beats != 16) || (read_burst_count < 2) || (write_burst_count < 2) ||
|
||||
(bytes_done_o[0 +: 32] != 32'd80)) begin
|
||||
$fatal(1, "DMA copy count=%0d max-r=%0d max-w=%0d reads=%0d writes=%0d done=%0d error=%b",
|
||||
write_count, max_read_beats, max_write_beats, read_burst_count, write_burst_count,
|
||||
bytes_done_o[0+:32], error_o[0]);
|
||||
end
|
||||
for (integer index = 0; index < 20; index++) begin
|
||||
if ((written_addr[index] != (CopyDestination + (index * 4))) ||
|
||||
(written_data[index] != model_read_data(
|
||||
CopySource + (index * 4)
|
||||
))) begin
|
||||
$fatal(1, "DMA copy data mismatch at beat %0d", index);
|
||||
end
|
||||
end
|
||||
|
||||
error_mode = 1'b1;
|
||||
start_transfer();
|
||||
wait (error_o);
|
||||
@(negedge clk_i);
|
||||
if (error_code_o !== `RIB_RESP_DECERR || error_addr_o !== srcaddr_i) begin
|
||||
$fatal(1, "DMA error response was not propagated");
|
||||
configure_channel(1, 3'd0, 4'd0, ErrorSource, 1'b1, 32'h4000_5000, 1'b1, 32'd4, 2'd3, 5'd1);
|
||||
start_channel(1);
|
||||
wait_terminal(1);
|
||||
if (!error_o[1] || !first_error_valid_o || (first_error_channel_o != 2'd1) ||
|
||||
(write_count != 20)) begin
|
||||
$fatal(1, "DMA read error was not isolated to its channel");
|
||||
end
|
||||
if (write_count != 1) begin
|
||||
$fatal(1, "DMA issued a write after a read error");
|
||||
end
|
||||
|
||||
error_mode = 1'b0;
|
||||
xferlen_i = 32'd8;
|
||||
srcincr_i = 1'b0;
|
||||
dstincr_i = 1'b1;
|
||||
measure_transfer(legacy_cycles);
|
||||
|
||||
srcincr_i = 1'b1;
|
||||
measure_transfer(burst_cycles);
|
||||
if (incr4_read_count != 2 || incr4_write_count != 2) begin
|
||||
$fatal(1, "aligned eight-word transfer did not use two INCR4 chunks");
|
||||
end
|
||||
if ((burst_cycles * 3) > (legacy_cycles * 2)) begin
|
||||
$fatal(1, "INCR4 DMA speedup is below 1.5x: legacy=%0d burst=%0d", legacy_cycles,
|
||||
burst_cycles);
|
||||
configure_channel(2, 3'd1, 4'd1, StreamSource, 1'b1, 32'd0, 1'b0, 32'd8, 2'd2, 5'd16);
|
||||
start_channel(2);
|
||||
wait_terminal(2);
|
||||
if (error_o[2] || (stream_tx_count != 2) || !stream_tx_last[1] ||
|
||||
(stream_tx_data[0] != model_read_data(
|
||||
StreamSource
|
||||
)) || (stream_tx_data[1] != model_read_data(
|
||||
StreamSource + 32'd4
|
||||
))) begin
|
||||
$fatal(1, "DMA AXI4-Stream TX did not obey backpressure or TLAST");
|
||||
end
|
||||
|
||||
srcaddr_i = 32'h1000_0000;
|
||||
dstaddr_i = 32'h1000_1000;
|
||||
measure_transfer(legacy_cycles);
|
||||
if (incr4_read_count != 2 || incr4_write_count != 2) begin
|
||||
$fatal(1, "DMA did not fall back to INCR1 for a register window");
|
||||
configure_channel(3, 3'd2, 4'd2, 32'd0, 1'b0, StreamDestination, 1'b1, 32'd8, 2'd2, 5'd16);
|
||||
stream_rx_count = 0;
|
||||
start_channel(3);
|
||||
drive_rx_beat(32'h5100_0000, 4'hF, 1'b0);
|
||||
drive_rx_beat(32'h5100_0001, 4'hF, 1'b1);
|
||||
wait_terminal(3);
|
||||
if (error_o[3] || (stream_rx_count != 2) || !stream_last_o[3] ||
|
||||
(written_data[20] != 32'h5100_0000) || (written_data[21] != 32'h5100_0001)) begin
|
||||
$fatal(1, "DMA AXI4-Stream RX did not write the programmed byte count");
|
||||
end
|
||||
|
||||
mode_i = 4'd1;
|
||||
srcaddr_i = 32'h3800_2000;
|
||||
srcincr_i = 1'b1;
|
||||
xferlen_i = 32'd2;
|
||||
stream_tx_ready = 1'b0;
|
||||
start_transfer();
|
||||
repeat (3) @(posedge clk_i);
|
||||
stream_tx_ready_enable = 1'b0;
|
||||
configure_channel(2, 3'd1, 4'd1, StreamSource + 32'h100, 1'b1, 32'd0, 1'b0, 32'd8, 2'd2, 5'd16);
|
||||
start_channel(2);
|
||||
wait (i2s_tx_axis.tvalid);
|
||||
@(negedge clk_i);
|
||||
stream_tx_ready = 1'b1;
|
||||
wait (done_o);
|
||||
stalled_stream_data = i2s_tx_axis.tdata;
|
||||
stalled_stream_last = i2s_tx_axis.tlast;
|
||||
abort_i[2] = 1'b1;
|
||||
@(negedge clk_i);
|
||||
if (stream_tx_count != 2) begin
|
||||
$fatal(1, "I2S TX stream transferred %0d words", stream_tx_count);
|
||||
abort_i[2] = 1'b0;
|
||||
repeat (3) begin
|
||||
@(posedge clk_i);
|
||||
if (!i2s_tx_axis.tvalid || (i2s_tx_axis.tdata != stalled_stream_data) ||
|
||||
(i2s_tx_axis.tlast != stalled_stream_last)) begin
|
||||
$fatal(1, "DMA abort unstable: valid=%b data=%h/%h last=%b/%b busy=%b abort=%b stop=%b",
|
||||
i2s_tx_axis.tvalid, i2s_tx_axis.tdata, stalled_stream_data, i2s_tx_axis.tlast,
|
||||
stalled_stream_last, busy_o[2], u_dma_core.s_abort_q[2],
|
||||
u_dma_core.s_stream_tx_stop_q[2]);
|
||||
end
|
||||
end
|
||||
stream_tx_ready_enable = 1'b1;
|
||||
wait_terminal(2);
|
||||
if (!aborted_o[2] || done_o[2]) begin
|
||||
$fatal(1, "DMA stream abort did not terminate as aborted");
|
||||
end
|
||||
|
||||
mode_i = 4'd2;
|
||||
dstaddr_i = 32'h4000_2000;
|
||||
dstincr_i = 1'b1;
|
||||
xferlen_i = 32'd2;
|
||||
stream_rx_valid = 1'b1;
|
||||
start_transfer();
|
||||
wait (done_o);
|
||||
@(negedge clk_i);
|
||||
if (stream_rx_count != 2) begin
|
||||
$fatal(1, "I2S RX stream transferred %0d words", stream_rx_count);
|
||||
write_accept_enable = 1'b0;
|
||||
configure_channel(3, 3'd2, 4'd2, 32'd0, 1'b0, StreamDestination + 32'h100, 1'b1, 32'd8, 2'd2,
|
||||
5'd16);
|
||||
start_channel(3);
|
||||
drive_rx_beat(32'h6100_0000, 4'hF, 1'b0);
|
||||
drive_rx_beat(32'h6100_0001, 4'h3, 1'b1);
|
||||
wait (error_o[3]);
|
||||
wait (!busy_o[3]);
|
||||
write_accept_enable = 1'b1;
|
||||
write_before = write_count;
|
||||
configure_channel(3, 3'd2, 4'd2, 32'd0, 1'b0, StreamDestination + 32'h200, 1'b1, 32'd4, 2'd2,
|
||||
5'd1);
|
||||
start_channel(3);
|
||||
drive_rx_beat(32'h6200_0000, 4'hF, 1'b1);
|
||||
wait_terminal(3);
|
||||
if (error_o[3] || (write_count != (write_before + 1)) ||
|
||||
(written_data[write_before] != 32'h6200_0000)) begin
|
||||
$fatal(1, "DMA retained stale stream data after a TKEEP error");
|
||||
end
|
||||
|
||||
$display("dma error, burst performance, and AXI4-Stream test passed");
|
||||
$display("DMA native AXI4 burst, backpressure, stream, and error test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,229 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module dma_reg_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic irq_o;
|
||||
logic xpi_done_o;
|
||||
logic read_active_q = 1'b0;
|
||||
logic write_active_q = 1'b0;
|
||||
logic write_response_q = 1'b0;
|
||||
logic allow_write_response = 1'b0;
|
||||
|
||||
dma_req_if req ();
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
axi4_if axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
axi4_stream_if i2s_tx_axis (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
axi4_stream_if i2s_rx_axis (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
axi4_stream_if dvp_rx_axis (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
assign axi4.arready = !read_active_q;
|
||||
assign axi4.rid = '0;
|
||||
assign axi4.rdata = 32'hC0DE_0001;
|
||||
assign axi4.rresp = `AXI4_RESP_OKAY;
|
||||
assign axi4.rlast = 1'b1;
|
||||
assign axi4.ruser = '0;
|
||||
assign axi4.rvalid = read_active_q;
|
||||
assign axi4.awready = !write_active_q && !write_response_q;
|
||||
assign axi4.wready = write_active_q;
|
||||
assign axi4.bid = '0;
|
||||
assign axi4.bresp = `AXI4_RESP_OKAY;
|
||||
assign axi4.buser = '0;
|
||||
assign axi4.bvalid = write_response_q && allow_write_response;
|
||||
assign i2s_tx_axis.tready = 1'b0;
|
||||
assign i2s_rx_axis.tdata = '0;
|
||||
assign i2s_rx_axis.tkeep = '0;
|
||||
assign i2s_rx_axis.tstrb = '0;
|
||||
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;
|
||||
assign dvp_rx_axis.tdata = '0;
|
||||
assign dvp_rx_axis.tkeep = '0;
|
||||
assign dvp_rx_axis.tstrb = '0;
|
||||
assign dvp_rx_axis.tlast = 1'b0;
|
||||
assign dvp_rx_axis.tid = '0;
|
||||
assign dvp_rx_axis.tdest = '0;
|
||||
assign dvp_rx_axis.tuser = '0;
|
||||
assign dvp_rx_axis.tvalid = 1'b0;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
read_active_q <= 1'b0;
|
||||
write_active_q <= 1'b0;
|
||||
write_response_q <= 1'b0;
|
||||
end else begin
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
if (axi4.arlen != 8'd0) begin
|
||||
$fatal(1, "register-directed transfer unexpectedly burst");
|
||||
end
|
||||
read_active_q <= 1'b1;
|
||||
end
|
||||
if (axi4.rvalid && axi4.rready) begin
|
||||
read_active_q <= 1'b0;
|
||||
end
|
||||
if (axi4.awvalid && axi4.awready) begin
|
||||
if (axi4.awlen != 8'd0) begin
|
||||
$fatal(1, "register-directed transfer unexpectedly burst");
|
||||
end
|
||||
write_active_q <= 1'b1;
|
||||
end
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
if (!axi4.wlast || (axi4.wdata != 32'hC0DE_0001)) begin
|
||||
$fatal(1, "DMA register transfer write data mismatch");
|
||||
end
|
||||
write_active_q <= 1'b0;
|
||||
write_response_q <= 1'b1;
|
||||
end
|
||||
if (axi4.bvalid && axi4.bready) begin
|
||||
write_response_q <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
apb4_dma u_apb4_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.dma_xfer_done_o(xpi_done_o),
|
||||
.irq_o (irq_o),
|
||||
.hw_trg (req),
|
||||
.apb4 (apb4),
|
||||
.axi4 (axi4),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis)
|
||||
);
|
||||
|
||||
task automatic apb_write(input logic [31:0] address, input logic [31:0] data,
|
||||
input logic [3:0] strobe, input logic expected_error);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address;
|
||||
apb4.pwdata = data;
|
||||
apb4.pstrb = strobe;
|
||||
apb4.pwrite = 1'b1;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
#1;
|
||||
while (!apb4.pready) @(negedge clk_i);
|
||||
if (apb4.pslverr != expected_error) begin
|
||||
$fatal(1, "APB write %h PSLVERR=%b expected=%b", address, apb4.pslverr, expected_error);
|
||||
end
|
||||
@(negedge clk_i);
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [31:0] address, output logic [31:0] data);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
apb4.paddr = address;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.psel = 1'b1;
|
||||
apb4.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
apb4.penable = 1'b1;
|
||||
#1;
|
||||
while (!apb4.pready) @(negedge clk_i);
|
||||
if (apb4.pslverr) begin
|
||||
$fatal(1, "APB read %h unexpectedly failed", address);
|
||||
end
|
||||
data = apb4.prdata;
|
||||
@(negedge clk_i);
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
logic [31:0] value;
|
||||
|
||||
apb4.paddr = '0;
|
||||
apb4.pprot = '0;
|
||||
apb4.psel = 1'b0;
|
||||
apb4.penable = 1'b0;
|
||||
apb4.pwrite = 1'b0;
|
||||
apb4.pwdata = '0;
|
||||
apb4.pstrb = '0;
|
||||
req.i2s_tx_proc = 1'b1;
|
||||
req.i2s_rx_proc = 1'b1;
|
||||
req.qspi_tx_proc = 1'b1;
|
||||
req.qspi_rx_proc = 1'b1;
|
||||
req.uart_tx_proc = 1'b1;
|
||||
req.uart_rx_proc = 1'b1;
|
||||
req.i2c0_tx_proc = 1'b1;
|
||||
req.i2c0_rx_proc = 1'b1;
|
||||
req.i2c1_tx_proc = 1'b1;
|
||||
req.i2c1_rx_proc = 1'b1;
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
apb_read(32'h000, value);
|
||||
if (value != 32'h444D_4134) begin
|
||||
$fatal(1, "DMA IP identification mismatch");
|
||||
end
|
||||
apb_write(32'h02c, 32'h1, 4'hF, 1'b1);
|
||||
apb_write(32'h104, 32'h0000_0160, 4'h1, 1'b0);
|
||||
apb_write(32'h108, 32'h4000_0000, 4'hF, 1'b0);
|
||||
apb_write(32'h10c, 32'h4000_1000, 4'hF, 1'b0);
|
||||
apb_write(32'h110, 32'h0000_0004, 4'hF, 1'b0);
|
||||
apb_write(32'h114, 32'h0000_0000, 4'hF, 1'b0);
|
||||
apb_write(32'h118, 32'h0000_0001, 4'hF, 1'b0);
|
||||
apb_write(32'h11c, 32'h0000_0007, 4'hF, 1'b0);
|
||||
apb_write(32'h018, 32'h0000_0001, 4'hF, 1'b0);
|
||||
apb_write(32'h100, 32'h0000_0001, 4'h1, 1'b0);
|
||||
|
||||
wait (write_response_q);
|
||||
apb_write(32'h110, 32'h0000_0008, 4'hF, 1'b1);
|
||||
apb_write(32'h100, 32'h0000_0010, 4'h1, 1'b1);
|
||||
apb_write(32'h00c, 32'h0000_0001, 4'h1, 1'b1);
|
||||
apb_write(32'h100, 32'h0000_0001, 4'h2, 1'b1);
|
||||
allow_write_response = 1'b1;
|
||||
wait (!write_response_q);
|
||||
repeat (2) @(posedge clk_i);
|
||||
apb_read(32'h120, value);
|
||||
if ((value & 32'h0000_0005) != 32'h0000_0004) begin
|
||||
$fatal(1, "DMA status did not report a completed, idle transfer: %h", value);
|
||||
end
|
||||
apb_read(32'h124, value);
|
||||
if (((value & 32'h1) == 32'd0) || !irq_o) begin
|
||||
$fatal(1, "DMA done event did not assert the aggregate IRQ");
|
||||
end
|
||||
apb_write(32'h124, 32'h0000_0007, 4'h2, 1'b1);
|
||||
if (!irq_o) begin
|
||||
$fatal(1, "DMA event clear ignored unsupported PSTRB");
|
||||
end
|
||||
apb_write(32'h124, 32'h0000_0007, 4'h1, 1'b0);
|
||||
repeat (1) @(posedge clk_i);
|
||||
if (irq_o) begin
|
||||
$fatal(1, "DMA W1C event clear did not lower the aggregate IRQ");
|
||||
end
|
||||
apb_write(32'h00c, 32'h0000_0001, 4'h1, 1'b0);
|
||||
|
||||
$display("DMA APB register and aggregate IRQ test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -35,6 +35,7 @@ module soc_irq_topology_tb;
|
||||
s_apb4_periph_irq[bit_index] = 1'b1;
|
||||
case (bit_index)
|
||||
13: expect_irq(32'd1 << 15);
|
||||
14: expect_irq(32'd1 << 20);
|
||||
default: expect_irq(32'd1 << (bit_index + 7));
|
||||
endcase
|
||||
end
|
||||
@@ -58,8 +59,8 @@ module soc_irq_topology_tb;
|
||||
if (s_irq[10] !== 1'b0) begin
|
||||
$fatal(1, "removed core IRQ bit is not low: IRQ10=%b", s_irq[10]);
|
||||
end
|
||||
if (s_irq[31:18] !== '0) begin
|
||||
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq[31:18]);
|
||||
if ((s_irq[31:21] !== '0) || (s_irq[19:18] !== '0)) begin
|
||||
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
|
||||
end
|
||||
|
||||
$display("SoC topology IRQ routing test passed");
|
||||
|
||||
+207
-172
@@ -1,51 +1,60 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
`include "rib_defs.svh"
|
||||
`include "ws2812_define.svh"
|
||||
|
||||
module ws2812_dma_tb;
|
||||
localparam logic [31:0] SOURCE_BASE = 32'h4000_0000;
|
||||
localparam logic [31:0] WS2812_TXDATA = 32'h1000_8010;
|
||||
localparam int BIT_CYCLES = 4;
|
||||
localparam int T0H_CYCLES = 1;
|
||||
localparam int T1H_CYCLES = 3;
|
||||
localparam int RESET_CYCLES = 5;
|
||||
localparam logic [31:0] SourceBase = 32'h4000_0000;
|
||||
localparam logic [31:0] Ws2812Txdata = 32'h1000_8010;
|
||||
localparam int BitCycles = 4;
|
||||
localparam int T0hCycles = 1;
|
||||
localparam int T1hCycles = 3;
|
||||
localparam int ResetCycles = 5;
|
||||
|
||||
typedef enum logic [1:0] {
|
||||
BUS_IDLE,
|
||||
BUS_READ_RESP,
|
||||
BUS_WRITE_DATA,
|
||||
BUS_WRITE_RESP
|
||||
} bus_state_t;
|
||||
typedef enum logic [2:0] {
|
||||
AxiIdle,
|
||||
AxiReadResponse,
|
||||
AxiWriteWait,
|
||||
AxiWriteSetup,
|
||||
AxiWriteAccess,
|
||||
AxiWriteResponse
|
||||
} axi_state_e;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic dma_start = 1'b0;
|
||||
logic dma_done;
|
||||
logic dma_error;
|
||||
logic [ 2:0] dma_error_code;
|
||||
logic [31:0] dma_error_addr;
|
||||
logic [ 1:0] dma_fsm;
|
||||
logic host_active = 1'b1;
|
||||
logic host_psel = 1'b0;
|
||||
logic host_penable = 1'b0;
|
||||
logic [31:0] host_addr = '0;
|
||||
logic [31:0] host_wdata = '0;
|
||||
logic [ 3:0] host_wstrb = '0;
|
||||
logic dma_penable = 1'b0;
|
||||
logic dma_ws_valid;
|
||||
bus_state_t bus_state_q = BUS_IDLE;
|
||||
logic [31:0] bus_addr_q = '0;
|
||||
integer dma_write_count = 0;
|
||||
integer dma_backpressure_cycles = 0;
|
||||
logic [23:0] pixels [0:5];
|
||||
integer observed_pixel = 0;
|
||||
integer observed_bit = 0;
|
||||
integer observed_cycle = 0;
|
||||
integer reset_low_cycles = 0;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic dma_start = 1'b0;
|
||||
logic [ 31:0] dma_cfg = '0;
|
||||
logic [ 31:0] dma_src_addr = '0;
|
||||
logic [ 31:0] dma_dst_addr = '0;
|
||||
logic [ 31:0] dma_byte_count = '0;
|
||||
logic [ 31:0] dma_request = '0;
|
||||
logic [ 31:0] dma_burst = '0;
|
||||
logic dma_done;
|
||||
logic dma_error;
|
||||
logic [ 3:0] dma_done_vector;
|
||||
logic [ 3:0] dma_error_vector;
|
||||
logic [127:0] dma_error_status_vector;
|
||||
logic [127:0] dma_error_addr_vector;
|
||||
logic host_active = 1'b1;
|
||||
logic host_psel = 1'b0;
|
||||
logic host_penable = 1'b0;
|
||||
logic [ 31:0] host_addr = '0;
|
||||
logic [ 31:0] host_wdata = '0;
|
||||
logic [ 3:0] host_wstrb = '0;
|
||||
axi_state_e axi_state_q = AxiIdle;
|
||||
logic [ 31:0] axi_read_addr_q = '0;
|
||||
integer dma_write_count = 0;
|
||||
integer dma_backpressure_cycles = 0;
|
||||
logic [ 23:0] pixels [0:5];
|
||||
integer observed_pixel = 0;
|
||||
integer observed_bit = 0;
|
||||
integer observed_cycle = 0;
|
||||
integer reset_low_cycles = 0;
|
||||
|
||||
dma_hw_trg_if hw_trg ();
|
||||
rib_if dma_rib ();
|
||||
dma_req_if req ();
|
||||
axi4_if axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
apb4_if apb4 (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
@@ -71,132 +80,155 @@ module ws2812_dma_tb;
|
||||
$fatal(1, "WS2812 DMA integration test timeout");
|
||||
end
|
||||
|
||||
assign apb4.psel = host_active ? host_psel : dma_ws_valid;
|
||||
assign apb4.penable = host_active ? host_penable : dma_penable;
|
||||
assign apb4.pwrite = 1'b1;
|
||||
assign apb4.paddr = host_active ? host_addr : bus_addr_q;
|
||||
assign apb4.pwdata = host_active ? host_wdata : dma_rib.wdata;
|
||||
assign apb4.pstrb = host_active ? host_wstrb : dma_rib.wstrb;
|
||||
assign apb4.pprot = '0;
|
||||
assign apb4.psel = host_active ? host_psel :
|
||||
((axi_state_q == AxiWriteSetup) || (axi_state_q == AxiWriteAccess));
|
||||
assign apb4.penable = host_active ? host_penable : (axi_state_q == AxiWriteAccess);
|
||||
assign apb4.pwrite = 1'b1;
|
||||
assign apb4.paddr = host_active ? host_addr : Ws2812Txdata;
|
||||
assign apb4.pwdata = host_active ? host_wdata : axi4.wdata;
|
||||
assign apb4.pstrb = host_active ? host_wstrb : axi4.wstrb;
|
||||
assign apb4.pprot = '0;
|
||||
|
||||
assign dma_rib.cmd_ready = bus_state_q == BUS_IDLE;
|
||||
assign dma_rib.w_ready = (bus_state_q == BUS_WRITE_DATA) && dma_penable && apb4.pready;
|
||||
assign dma_rib.rsp_valid = (bus_state_q == BUS_READ_RESP) || (bus_state_q == BUS_WRITE_RESP);
|
||||
assign dma_rib.rdata = pixels[bus_addr_q[4:2]];
|
||||
assign dma_rib.resp_err = 1'b0;
|
||||
assign dma_rib.resp_code = `RIB_RESP_OK;
|
||||
assign dma_rib.rsp_beat = 2'd0;
|
||||
assign dma_rib.rsp_last = 1'b1;
|
||||
assign dma_ws_valid = (bus_state_q == BUS_WRITE_DATA) && dma_rib.w_valid;
|
||||
assign axi4.arready = axi_state_q == AxiIdle;
|
||||
assign axi4.rid = '0;
|
||||
assign axi4.rdata = pixels[axi_read_addr_q[4:2]];
|
||||
assign axi4.rresp = `AXI4_RESP_OKAY;
|
||||
assign axi4.rlast = 1'b1;
|
||||
assign axi4.ruser = '0;
|
||||
assign axi4.rvalid = axi_state_q == AxiReadResponse;
|
||||
assign axi4.awready = (axi_state_q == AxiIdle) && !axi4.arvalid;
|
||||
assign axi4.wready = (axi_state_q == AxiWriteAccess) && apb4.pready;
|
||||
assign axi4.bid = '0;
|
||||
assign axi4.bresp = `AXI4_RESP_OKAY;
|
||||
assign axi4.buser = '0;
|
||||
assign axi4.bvalid = axi_state_q == AxiWriteResponse;
|
||||
assign i2s_tx_axis.tready = 1'b0;
|
||||
assign i2s_rx_axis.tdata = '0;
|
||||
assign i2s_rx_axis.tkeep = '0;
|
||||
assign i2s_rx_axis.tstrb = '0;
|
||||
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.tdata = '0;
|
||||
assign i2s_rx_axis.tkeep = '0;
|
||||
assign i2s_rx_axis.tstrb = '0;
|
||||
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;
|
||||
assign dvp_rx_axis.tdata = '0;
|
||||
assign dvp_rx_axis.tkeep = '0;
|
||||
assign dvp_rx_axis.tstrb = '0;
|
||||
assign dvp_rx_axis.tlast = 1'b0;
|
||||
assign dvp_rx_axis.tid = '0;
|
||||
assign dvp_rx_axis.tdest = '0;
|
||||
assign dvp_rx_axis.tuser = '0;
|
||||
assign dvp_rx_axis.tdata = '0;
|
||||
assign dvp_rx_axis.tkeep = '0;
|
||||
assign dvp_rx_axis.tstrb = '0;
|
||||
assign dvp_rx_axis.tlast = 1'b0;
|
||||
assign dvp_rx_axis.tid = '0;
|
||||
assign dvp_rx_axis.tdest = '0;
|
||||
assign dvp_rx_axis.tuser = '0;
|
||||
assign dvp_rx_axis.tvalid = 1'b0;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
dma_penable <= 1'b0;
|
||||
end else if (!dma_ws_valid) begin
|
||||
dma_penable <= 1'b0;
|
||||
end else if (!dma_penable) begin
|
||||
dma_penable <= 1'b1;
|
||||
end else if (apb4.pready) begin
|
||||
dma_penable <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
bus_state_q <= BUS_IDLE;
|
||||
bus_addr_q <= '0;
|
||||
axi_state_q <= AxiIdle;
|
||||
axi_read_addr_q <= '0;
|
||||
dma_write_count <= 0;
|
||||
dma_backpressure_cycles <= 0;
|
||||
end else begin
|
||||
if ((bus_state_q == BUS_WRITE_DATA) && dma_rib.w_valid && !dma_rib.w_ready) begin
|
||||
if (axi4.wvalid && !axi4.wready) begin
|
||||
dma_backpressure_cycles <= dma_backpressure_cycles + 1;
|
||||
end
|
||||
unique case (bus_state_q)
|
||||
BUS_IDLE: begin
|
||||
if (dma_rib.cmd_valid && dma_rib.cmd_ready) begin
|
||||
bus_addr_q <= dma_rib.cmd_addr;
|
||||
if (dma_rib.cmd_len != `RIB_LEN_INCR1) begin
|
||||
$fatal(1, "fixed-destination WS2812 DMA transfer used a burst");
|
||||
unique case (axi_state_q)
|
||||
AxiIdle: begin
|
||||
if (axi4.arvalid && axi4.arready) begin
|
||||
if ((axi4.arlen != 8'd0) || (axi4.araddr < (SourceBase + 32'd4)) ||
|
||||
(axi4.araddr > (SourceBase + 32'd20))) begin
|
||||
$fatal(1, "DMA issued an invalid WS2812 source read");
|
||||
end
|
||||
if (dma_rib.cmd_write) begin
|
||||
if (dma_rib.cmd_addr != WS2812_TXDATA) begin
|
||||
$fatal(1, "DMA wrote unexpected destination %h", dma_rib.cmd_addr);
|
||||
end
|
||||
bus_state_q <= BUS_WRITE_DATA;
|
||||
end else begin
|
||||
if ((dma_rib.cmd_addr < (SOURCE_BASE + 32'd4)) ||
|
||||
(dma_rib.cmd_addr > (SOURCE_BASE + 32'd20))) begin
|
||||
$fatal(1, "DMA read unexpected source %h", dma_rib.cmd_addr);
|
||||
end
|
||||
bus_state_q <= BUS_READ_RESP;
|
||||
axi_read_addr_q <= axi4.araddr;
|
||||
axi_state_q <= AxiReadResponse;
|
||||
end else if (axi4.awvalid && axi4.awready) begin
|
||||
if ((axi4.awlen != 8'd0) || (axi4.awaddr != Ws2812Txdata) ||
|
||||
(axi4.awburst != `AXI4_BURST_TYPE_FIXED)) begin
|
||||
$fatal(1, "fixed WS2812 destination was not a single AXI4 FIXED beat");
|
||||
end
|
||||
axi_state_q <= AxiWriteWait;
|
||||
end
|
||||
end
|
||||
BUS_READ_RESP: begin
|
||||
if (dma_rib.rsp_valid && dma_rib.rsp_ready) begin
|
||||
bus_state_q <= BUS_IDLE;
|
||||
AxiReadResponse: begin
|
||||
if (axi4.rvalid && axi4.rready) begin
|
||||
axi_state_q <= AxiIdle;
|
||||
end
|
||||
end
|
||||
BUS_WRITE_DATA: begin
|
||||
if (dma_rib.w_valid && dma_rib.w_ready) begin
|
||||
if (!dma_rib.wlast) begin
|
||||
$fatal(1, "INCR1 WS2812 write did not assert wlast");
|
||||
AxiWriteWait: begin
|
||||
if (axi4.wvalid) begin
|
||||
axi_state_q <= AxiWriteSetup;
|
||||
end
|
||||
end
|
||||
AxiWriteSetup: axi_state_q <= AxiWriteAccess;
|
||||
AxiWriteAccess: begin
|
||||
if (axi4.wvalid && axi4.wready) begin
|
||||
if (!axi4.wlast) begin
|
||||
$fatal(1, "single-beat WS2812 write did not assert WLAST");
|
||||
end
|
||||
dma_write_count <= dma_write_count + 1;
|
||||
bus_state_q <= BUS_WRITE_RESP;
|
||||
axi_state_q <= AxiWriteResponse;
|
||||
end
|
||||
end
|
||||
BUS_WRITE_RESP: begin
|
||||
if (dma_rib.rsp_valid && dma_rib.rsp_ready) begin
|
||||
bus_state_q <= BUS_IDLE;
|
||||
AxiWriteResponse: begin
|
||||
if (axi4.bvalid && axi4.bready) begin
|
||||
axi_state_q <= AxiIdle;
|
||||
end
|
||||
end
|
||||
default: bus_state_q <= BUS_IDLE;
|
||||
default: axi_state_q <= AxiIdle;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
dma_core u_dma_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.mode_i (4'd0),
|
||||
.srcaddr_i (SOURCE_BASE + 32'd4),
|
||||
.srcincr_i (1'b1),
|
||||
.dstaddr_i (WS2812_TXDATA),
|
||||
.dstincr_i (1'b0),
|
||||
.xferlen_i (32'd5),
|
||||
.start_i (dma_start),
|
||||
.stop_i (1'b0),
|
||||
.reset_i (1'b0),
|
||||
.done_o (dma_done),
|
||||
.error_o (dma_error),
|
||||
.error_code_o(dma_error_code),
|
||||
.error_addr_o(dma_error_addr),
|
||||
.fsm_o (dma_fsm),
|
||||
.hw_trg (hw_trg),
|
||||
.rib (dma_rib),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis)
|
||||
dma_core #(
|
||||
.NumChannels (4),
|
||||
.MaxBurstBeats(16),
|
||||
.FifoDepth (16)
|
||||
) u_dma_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.global_reset_i (1'b0),
|
||||
.global_error_clear_i (1'b0),
|
||||
.ch_cfg_i ({96'd0, dma_cfg}),
|
||||
.src_addr_i ({96'd0, dma_src_addr}),
|
||||
.dst_addr_i ({96'd0, dma_dst_addr}),
|
||||
.byte_count_i ({96'd0, dma_byte_count}),
|
||||
.request_sel_i ({96'd0, dma_request}),
|
||||
.burst_cfg_i ({96'd0, dma_burst}),
|
||||
.start_i ({3'd0, dma_start}),
|
||||
.suspend_i ('0),
|
||||
.resume_i ('0),
|
||||
.abort_i ('0),
|
||||
.channel_reset_i ('0),
|
||||
.event_clear_i ('0),
|
||||
.busy_o (),
|
||||
.suspended_o (),
|
||||
.done_o (dma_done_vector),
|
||||
.aborted_o (),
|
||||
.error_o (dma_error_vector),
|
||||
.stream_last_o (),
|
||||
.event_status_o (),
|
||||
.error_status_o (dma_error_status_vector),
|
||||
.error_addr_o (dma_error_addr_vector),
|
||||
.current_src_o (),
|
||||
.current_dst_o (),
|
||||
.remaining_o (),
|
||||
.bytes_done_o (),
|
||||
.stall_cycles_lo_o (),
|
||||
.stall_cycles_hi_o (),
|
||||
.first_error_valid_o (),
|
||||
.first_error_channel_o(),
|
||||
.first_error_status_o (),
|
||||
.first_error_addr_o (),
|
||||
.request_status_o (),
|
||||
.xpi_xfer_done_o (),
|
||||
.req (req),
|
||||
.axi4 (axi4),
|
||||
.i2s_tx_axis (i2s_tx_axis),
|
||||
.i2s_rx_axis (i2s_rx_axis),
|
||||
.dvp_rx_axis (dvp_rx_axis)
|
||||
);
|
||||
|
||||
assign dma_done = dma_done_vector[0];
|
||||
assign dma_error = dma_error_vector[0];
|
||||
|
||||
apb4_ws2812 #(
|
||||
.TxFifoDepth(4)
|
||||
) u_ws2812 (
|
||||
@@ -217,7 +249,7 @@ module ws2812_dma_tb;
|
||||
@(negedge clk_i);
|
||||
host_penable = 1'b1;
|
||||
while (!apb4.pready) @(negedge clk_i);
|
||||
if (apb4.pslverr) $fatal(1, "WS2812 write %h unexpected error", address);
|
||||
if (apb4.pslverr) $fatal(1, "WS2812 host write %h failed", address);
|
||||
host_psel = 1'b0;
|
||||
host_penable = 1'b0;
|
||||
end
|
||||
@@ -228,14 +260,15 @@ module ws2812_dma_tb;
|
||||
if (rst_n_i && u_ws2812.s_busy && !u_ws2812.s_reset_active) begin
|
||||
integer high_cycles;
|
||||
logic expected_data;
|
||||
high_cycles = pixels[observed_pixel][23-observed_bit] ? T1H_CYCLES : T0H_CYCLES;
|
||||
|
||||
high_cycles = pixels[observed_pixel][23-observed_bit] ? T1hCycles : T0hCycles;
|
||||
expected_data = observed_cycle < high_cycles;
|
||||
if (ws2812.dat_o !== expected_data) begin
|
||||
$fatal(1, "DMA waveform mismatch pixel=%0d bit=%0d cycle=%0d", observed_pixel,
|
||||
observed_bit, observed_cycle);
|
||||
end
|
||||
observed_cycle = observed_cycle + 1;
|
||||
if (observed_cycle == BIT_CYCLES) begin
|
||||
if (observed_cycle == BitCycles) begin
|
||||
observed_cycle = 0;
|
||||
observed_bit = observed_bit + 1;
|
||||
if (observed_bit == 24) begin
|
||||
@@ -251,62 +284,64 @@ module ws2812_dma_tb;
|
||||
reset_low_cycles = reset_low_cycles + 1;
|
||||
end
|
||||
if (dma_error) begin
|
||||
$fatal(1, "DMA failed code=%0d address=%h", dma_error_code, dma_error_addr);
|
||||
$fatal(1, "DMA failed during WS2812 transfer status=%h address=%h",
|
||||
dma_error_status_vector[31:0], dma_error_addr_vector[31:0]);
|
||||
end
|
||||
end
|
||||
|
||||
initial begin
|
||||
pixels[0] = 24'h800001;
|
||||
pixels[1] = 24'h010203;
|
||||
pixels[2] = 24'hA5A5A5;
|
||||
pixels[3] = 24'h5A5A5A;
|
||||
pixels[4] = 24'hFFFFFF;
|
||||
pixels[5] = 24'h000000;
|
||||
hw_trg.i2s_tx_proc = 1'b0;
|
||||
hw_trg.i2s_rx_proc = 1'b0;
|
||||
hw_trg.qspi_tx_proc = 1'b0;
|
||||
hw_trg.qspi_rx_proc = 1'b0;
|
||||
hw_trg.uart_tx_proc = 1'b0;
|
||||
hw_trg.uart_rx_proc = 1'b0;
|
||||
hw_trg.i2c0_tx_proc = 1'b0;
|
||||
hw_trg.i2c0_rx_proc = 1'b0;
|
||||
hw_trg.i2c1_tx_proc = 1'b0;
|
||||
hw_trg.i2c1_rx_proc = 1'b0;
|
||||
pixels[0] = 24'h800001;
|
||||
pixels[1] = 24'h010203;
|
||||
pixels[2] = 24'hA5A5A5;
|
||||
pixels[3] = 24'h5A5A5A;
|
||||
pixels[4] = 24'hFFFFFF;
|
||||
pixels[5] = 24'h000000;
|
||||
req.i2s_tx_proc = 1'b1;
|
||||
req.i2s_rx_proc = 1'b1;
|
||||
req.qspi_tx_proc = 1'b1;
|
||||
req.qspi_rx_proc = 1'b1;
|
||||
req.uart_tx_proc = 1'b1;
|
||||
req.uart_rx_proc = 1'b1;
|
||||
req.i2c0_tx_proc = 1'b1;
|
||||
req.i2c0_rx_proc = 1'b1;
|
||||
req.i2c1_tx_proc = 1'b1;
|
||||
req.i2c1_rx_proc = 1'b1;
|
||||
|
||||
repeat (3) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
host_write(`APB4_WS2812_BIT_CYCLES, BIT_CYCLES);
|
||||
host_write(`APB4_WS2812_T0H_CYCLES, T0H_CYCLES);
|
||||
host_write(`APB4_WS2812_T1H_CYCLES, T1H_CYCLES);
|
||||
host_write(`APB4_WS2812_RESET_CYCLES, RESET_CYCLES);
|
||||
host_write(`APB4_WS2812_BIT_CYCLES, BitCycles);
|
||||
host_write(`APB4_WS2812_T0H_CYCLES, T0hCycles);
|
||||
host_write(`APB4_WS2812_T1H_CYCLES, T1hCycles);
|
||||
host_write(`APB4_WS2812_RESET_CYCLES, ResetCycles);
|
||||
host_write(`APB4_WS2812_FIFO_WATERMARK, 3);
|
||||
host_write(`APB4_WS2812_INTR_ENABLE, 1);
|
||||
host_write(`APB4_WS2812_TXDATA, pixels[0]);
|
||||
host_write(`APB4_WS2812_FRAME_WORDS, 6);
|
||||
host_write(`APB4_WS2812_CTRL, 1);
|
||||
|
||||
host_active = 1'b0;
|
||||
host_active = 1'b0;
|
||||
dma_cfg = {22'd0, 2'd1, 1'b0, 1'b1, 2'd2, 1'b0, 3'd0};
|
||||
dma_src_addr = SourceBase + 32'd4;
|
||||
dma_dst_addr = Ws2812Txdata;
|
||||
dma_byte_count = 32'd20;
|
||||
dma_request = 32'd0;
|
||||
dma_burst = 32'd16;
|
||||
@(negedge clk_i);
|
||||
dma_start = 1'b1;
|
||||
@(negedge clk_i);
|
||||
dma_start = 1'b0;
|
||||
|
||||
wait (dma_done);
|
||||
wait (!u_ws2812.s_busy);
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
if (dma_write_count != 5) begin
|
||||
$fatal(1, "DMA completed %0d WS2812 writes, expected 5", dma_write_count);
|
||||
end
|
||||
if (dma_backpressure_cycles < 40) begin
|
||||
$fatal(1, "DMA did not observe full-FIFO backpressure: %0d cycles", dma_backpressure_cycles);
|
||||
end
|
||||
if ((observed_pixel != 6) || (reset_low_cycles < RESET_CYCLES) || !ws2812.irq_o) begin
|
||||
$fatal(1, "DMA frame did not complete pixels=%0d reset=%0d irq=%b", observed_pixel,
|
||||
reset_low_cycles, ws2812.irq_o);
|
||||
if ((dma_write_count != 5) || (dma_backpressure_cycles < 40) ||
|
||||
(observed_pixel != 6) || (reset_low_cycles < ResetCycles) || !ws2812.irq_o) begin
|
||||
$fatal(1, "DMA WS2812 result writes=%0d stalls=%0d pixels=%0d reset=%0d irq=%b",
|
||||
dma_write_count, dma_backpressure_cycles, observed_pixel, reset_low_cycles,
|
||||
ws2812.irq_o);
|
||||
end
|
||||
|
||||
$display("WS2812 DMA backpressure integration test passed");
|
||||
$display("WS2812 native AXI4 DMA backpressure integration test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in new issue
Block a user