mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Integrate the SPI-P2 shared-DMA ABI into the Tiny product: SPI0 at 0x1001D000 with IRQ25 and RCU target16, the four frozen Gen1 ALT routes, latched four-bit source qualification through the fabric and APB CDC, GPIO readiness/ownership guard, and the bounded rs_spi_* HAL with the TINY_SPI_P3_ACCEPTANCE ci_smoke image. Tiny DMA enables V2.2 requests 16/17 with exact fixed FIFO MMIO checks; Mini remains default-disabled and unchanged. Review closure: the DMA AXI4 master stages the origin channel with each burst so final and contended SPI alias bursts stay qualified, the RCU spare feedback bit is tied off for four-state simulators, and the clock-switch quiescence now includes the SPI session. Validation: full Pytest 2097 passed, 0 failed, 1 skipped (optional pypdf); both Tiny profiles pass the SPI-P3 Verilator acceptance (SIM_TEST_PASS cycles=1030088); R2-P7 Verilator acceptance, Mini DMA formal prove and cover, Mini/Tiny lint, Tiny Yosys synthesis, netsim-boot and OpenSTA pass; C format/policy/host tests and RTL format/style checks clean. Evidence and remaining obligations are recorded in docs/ip/spi-verification.md. Tiny Icarus behavioral remains blocked by the pre-existing observer/sv2v defect (routed to the R2 DV owners); Mini smoke synthesis host timeout is skipped per maintainer decision. Human phase acceptance and SPI-P4/P5 remain outstanding; phase_accepted stays false.
784 lines
29 KiB
Systemverilog
784 lines
29 KiB
Systemverilog
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
|
// retroSoC is licensed under Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
`include "axi4_define.svh"
|
|
|
|
module usb2_dma #(
|
|
parameter int AddrWidth = 32,
|
|
parameter int DataWidth = 32,
|
|
parameter int MaxDescriptors = 256
|
|
) (
|
|
// verilog_format: off -- preserve command, packet stream, and completion groups
|
|
input logic clk_i,
|
|
input logic rst_n_i,
|
|
input logic start_i,
|
|
input logic abort_i,
|
|
input logic memory_to_packet_i,
|
|
input logic allow_short_i,
|
|
input logic [AddrWidth-1:0] desc_base_i,
|
|
input logic [15:0] desc_limit_i,
|
|
input logic [31:0] transfer_bytes_i,
|
|
input logic packet_in_valid_i,
|
|
output logic packet_in_ready_o,
|
|
input logic [DataWidth-1:0] packet_in_data_i,
|
|
input logic [DataWidth/8-1:0] packet_in_strb_i,
|
|
input logic packet_in_last_i,
|
|
output logic packet_out_valid_o,
|
|
input logic packet_out_ready_i,
|
|
output logic [DataWidth-1:0] packet_out_data_o,
|
|
output logic [DataWidth/8-1:0] packet_out_strb_o,
|
|
output logic packet_out_last_o,
|
|
output logic busy_o,
|
|
output logic done_o,
|
|
output logic error_o,
|
|
output logic [7:0] error_code_o,
|
|
output logic [AddrWidth-1:0] current_desc_o,
|
|
output logic [AddrWidth-1:0] next_desc_o,
|
|
output logic [31:0] bytes_done_o,
|
|
output logic [31:0] frame_o,
|
|
output logic descriptor_irq_o,
|
|
output logic abort_done_o,
|
|
axi4_if.master dma_axi4
|
|
// verilog_format: on
|
|
);
|
|
localparam int BeatBytes = DataWidth / 8;
|
|
localparam int DescIndexWidth = $clog2(MaxDescriptors);
|
|
localparam logic [7:0] ErrorDescriptor = 8'd1;
|
|
localparam logic [7:0] ErrorLength = 8'd2;
|
|
localparam logic [7:0] ErrorAxiRead = 8'd3;
|
|
localparam logic [7:0] ErrorAxiWrite = 8'd4;
|
|
localparam logic [7:0] ErrorPacket = 8'd5;
|
|
localparam logic [7:0] ErrorAbort = 8'd6;
|
|
localparam logic [7:0] ErrorWriteback = 8'd7;
|
|
|
|
typedef enum logic [3:0] {
|
|
Idle,
|
|
FetchStart,
|
|
FetchWait,
|
|
Validate,
|
|
PayloadReadStart,
|
|
PayloadReadWait,
|
|
PayloadWriteStart,
|
|
PayloadWriteWait,
|
|
WritebackStart,
|
|
WritebackWait,
|
|
OwnershipStart,
|
|
OwnershipWait,
|
|
Complete,
|
|
Terminal
|
|
} dma_state_e;
|
|
|
|
dma_state_e s_state_d, s_state_q;
|
|
logic [3:0] s_state_bits_q;
|
|
logic [AddrWidth-1:0] s_desc_addr_d, s_desc_addr_q;
|
|
logic [DescIndexWidth-1:0] s_desc_index_d, s_desc_index_q;
|
|
logic [15:0] s_desc_limit_d, s_desc_limit_q;
|
|
logic s_memory_to_packet_d, s_memory_to_packet_q;
|
|
logic s_allow_short_d, s_allow_short_q;
|
|
logic s_unlimited_d, s_unlimited_q;
|
|
logic [31:0] s_remaining_d, s_remaining_q;
|
|
logic [31:0] s_total_done_d, s_total_done_q;
|
|
logic [7:0][31:0] s_desc_words_d, s_desc_words_q;
|
|
logic [3:0] s_fetch_index_d, s_fetch_index_q;
|
|
logic [31:0] s_selected_bytes_d, s_selected_bytes_q;
|
|
logic [31:0] s_desc_progress_d, s_desc_progress_q;
|
|
logic [4:0] s_burst_beats_d, s_burst_beats_q;
|
|
logic [4:0] s_burst_index_d, s_burst_index_q;
|
|
logic [2:0] s_writeback_index_d, s_writeback_index_q;
|
|
logic s_fault_d, s_fault_q;
|
|
logic [7:0] s_err_code_d, s_err_code_q;
|
|
logic s_abort_pending_d, s_abort_pending_q;
|
|
logic s_done_d, s_done_q;
|
|
logic s_err_d, s_err_q;
|
|
logic s_descriptor_irq_d, s_descriptor_irq_q;
|
|
logic s_abort_done_d, s_abort_done_q;
|
|
|
|
logic s_desc_own;
|
|
logic s_desc_chain;
|
|
logic s_desc_end;
|
|
logic s_desc_irq;
|
|
logic s_desc_short_ok;
|
|
logic s_desc_zero_packet;
|
|
logic s_desc_valid;
|
|
usb2_pkg::usb2_desc_error_e s_desc_err;
|
|
logic [ 31:0] s_desc_buffer;
|
|
logic [ 31:0] s_desc_len;
|
|
logic [ 31:0] s_desc_next;
|
|
logic [ 31:0] s_desc_control;
|
|
logic [ 31:0] s_desc_frame;
|
|
logic [ 31:0] s_payload_addr;
|
|
logic [ 31:0] s_payload_left;
|
|
logic [ 4:0] s_payload_beats;
|
|
logic [ 2:0] s_payload_beat_bytes;
|
|
logic [ 3:0] s_payload_expected_strb;
|
|
logic [ 31:0] s_progress_after_beat;
|
|
logic [ 31:0] s_remaining_after_desc;
|
|
logic s_transfer_last;
|
|
logic s_descriptor_short;
|
|
logic [ 31:0] s_completion_stat;
|
|
logic [ 31:0] s_control_released;
|
|
logic s_packet_input_valid;
|
|
logic s_packet_input_fault;
|
|
|
|
logic s_read_start_valid;
|
|
logic s_read_start_ready;
|
|
logic [ AddrWidth-1:0] s_read_addr;
|
|
logic [ 4:0] s_read_beats;
|
|
logic s_read_beat_valid;
|
|
logic s_read_beat_ready;
|
|
logic [ DataWidth-1:0] s_read_data;
|
|
logic [ 1:0] s_read_resp;
|
|
logic s_read_last;
|
|
logic s_read_expected_last;
|
|
logic s_read_id_err;
|
|
logic s_read_done;
|
|
logic s_read_busy;
|
|
logic s_write_start_valid;
|
|
logic s_write_start_ready;
|
|
logic [ AddrWidth-1:0] s_write_addr;
|
|
logic [ 4:0] s_write_beats;
|
|
logic s_write_data_valid;
|
|
logic s_write_data_ready;
|
|
logic [ DataWidth-1:0] s_write_data;
|
|
logic [DataWidth/8-1:0] s_write_strb;
|
|
logic s_write_done;
|
|
logic [ 1:0] s_write_resp;
|
|
logic s_write_id_err;
|
|
logic s_write_busy;
|
|
logic s_axi_read_fault;
|
|
logic s_axi_write_fault;
|
|
|
|
function automatic logic [4:0] choose_burst(input logic [11:0] addr_low_i,
|
|
input logic [31:0] bytes_i);
|
|
logic [31:0] beats_needed;
|
|
logic [31:0] boundary_beats;
|
|
logic [31:0] selected;
|
|
begin
|
|
beats_needed = (bytes_i + (BeatBytes - 1)) / BeatBytes;
|
|
boundary_beats = (32'd4096 - {20'd0, addr_low_i}) / BeatBytes;
|
|
selected = beats_needed;
|
|
if (selected > 32'd16) selected = 32'd16;
|
|
if (selected > boundary_beats) selected = boundary_beats;
|
|
if (selected == 32'd0) selected = 32'd1;
|
|
return selected[4:0];
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic [2:0] beat_bytes(input logic [31:0] bytes_i);
|
|
return (bytes_i >= BeatBytes) ? 3'(BeatBytes) : bytes_i[2:0];
|
|
endfunction
|
|
|
|
function automatic logic [3:0] byte_strobe(input logic [2:0] bytes_i);
|
|
unique case (bytes_i)
|
|
3'd0: return 4'b0000;
|
|
3'd1: return 4'b0001;
|
|
3'd2: return 4'b0011;
|
|
3'd3: return 4'b0111;
|
|
default: return 4'b1111;
|
|
endcase
|
|
endfunction
|
|
|
|
assign s_state_q = dma_state_e'(s_state_bits_q);
|
|
assign s_desc_buffer = s_desc_words_q[usb2_pkg::USB2_DESC_BUFFER_WORD];
|
|
assign s_desc_len = s_desc_words_q[usb2_pkg::USB2_DESC_LENGTH_WORD];
|
|
assign s_desc_next = s_desc_words_q[usb2_pkg::USB2_DESC_NEXT_WORD];
|
|
assign s_desc_control = s_desc_words_q[usb2_pkg::USB2_DESC_CONTROL_WORD];
|
|
assign s_desc_frame = s_desc_words_q[usb2_pkg::USB2_DESC_FRAME_WORD];
|
|
assign s_payload_addr = s_desc_buffer + s_desc_progress_q;
|
|
assign s_payload_left = s_selected_bytes_q - s_desc_progress_q;
|
|
assign s_payload_beats = choose_burst(s_payload_addr[11:0], s_payload_left);
|
|
assign s_payload_beat_bytes = beat_bytes(s_payload_left);
|
|
assign s_payload_expected_strb = byte_strobe(s_payload_beat_bytes);
|
|
assign s_progress_after_beat = s_desc_progress_q + {29'd0, s_payload_beat_bytes};
|
|
assign s_remaining_after_desc = s_remaining_q - s_selected_bytes_q;
|
|
assign s_transfer_last = s_unlimited_q ? s_desc_end : (s_remaining_q <= s_selected_bytes_q);
|
|
assign s_descriptor_short = s_selected_bytes_q < s_desc_len;
|
|
assign s_control_released = s_desc_control & 32'hFFFF_FFFE;
|
|
|
|
always_comb begin
|
|
s_completion_stat = 32'd0;
|
|
s_completion_stat[usb2_pkg::USB2_DESC_DONE] = 1'b1;
|
|
s_completion_stat[usb2_pkg::USB2_DESC_SHORT] = s_descriptor_short;
|
|
if (s_fault_q) begin
|
|
unique case (s_err_code_q)
|
|
ErrorAxiRead, ErrorAxiWrite, ErrorWriteback:
|
|
s_completion_stat[usb2_pkg::USB2_DESC_AXI_ERROR] = 1'b1;
|
|
ErrorAbort: s_completion_stat[usb2_pkg::USB2_DESC_ABORTED] = 1'b1;
|
|
default: s_completion_stat[usb2_pkg::USB2_DESC_PROTOCOL_ERROR] = 1'b1;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
usb2_dma_descriptor #(
|
|
.MaxDescriptors(MaxDescriptors)
|
|
) u_descriptor (
|
|
.buffer_addr_i (s_desc_buffer),
|
|
.byte_length_i (s_desc_len),
|
|
.next_addr_i (s_desc_next),
|
|
.control_i (s_desc_control),
|
|
.actual_length_i(s_desc_words_q[usb2_pkg::USB2_DESC_ACTUAL_WORD]),
|
|
.status_i (s_desc_words_q[usb2_pkg::USB2_DESC_STATUS_WORD]),
|
|
.frame_i (s_desc_frame),
|
|
.reserved_i (s_desc_words_q[usb2_pkg::USB2_DESC_RESERVED_WORD]),
|
|
.desc_index_i (s_desc_index_q),
|
|
.own_o (s_desc_own),
|
|
.chain_o (s_desc_chain),
|
|
.end_o (s_desc_end),
|
|
.irq_o (s_desc_irq),
|
|
.short_ok_o (s_desc_short_ok),
|
|
.zero_packet_o (s_desc_zero_packet),
|
|
.valid_o (s_desc_valid),
|
|
.error_o (s_desc_err)
|
|
);
|
|
|
|
assign s_read_start_valid = !abort_i &&
|
|
((s_state_q == FetchStart) || (s_state_q == PayloadReadStart));
|
|
assign s_read_addr = (s_state_q == FetchStart) ? s_desc_addr_q : s_payload_addr;
|
|
assign s_read_beats = (s_state_q == FetchStart) ? 5'd8 : s_payload_beats;
|
|
assign s_axi_read_fault = (s_read_resp != `AXI4_RESP_OKAY) || s_read_id_err ||
|
|
(s_read_last != s_read_expected_last);
|
|
assign s_read_beat_ready = (s_state_q == FetchWait) ||
|
|
((s_state_q == PayloadReadWait) &&
|
|
(s_fault_q || s_abort_pending_q || packet_out_ready_i));
|
|
assign packet_out_valid_o = (s_state_q == PayloadReadWait) && s_read_beat_valid &&
|
|
!s_fault_q && !s_abort_pending_q && !s_axi_read_fault;
|
|
assign packet_out_data_o = s_read_data;
|
|
assign packet_out_strb_o = s_payload_expected_strb;
|
|
assign packet_out_last_o = (s_progress_after_beat >= s_selected_bytes_q) && s_transfer_last;
|
|
|
|
assign s_write_start_valid = (s_state_q == PayloadWriteStart) ||
|
|
(s_state_q == WritebackStart) ||
|
|
(s_state_q == OwnershipStart);
|
|
assign s_write_addr = (s_state_q == PayloadWriteStart) ? s_payload_addr :
|
|
((s_state_q == WritebackStart) ? (s_desc_addr_q + 32'd16) :
|
|
(s_desc_addr_q + 32'd12));
|
|
assign s_write_beats = (s_state_q == WritebackStart) ? 5'd2 :
|
|
((s_state_q == OwnershipStart) ? 5'd1 : s_payload_beats);
|
|
assign s_packet_input_valid = (s_state_q == PayloadWriteWait) && packet_in_valid_i;
|
|
assign s_packet_input_fault = s_packet_input_valid &&
|
|
((packet_in_strb_i != s_payload_expected_strb) ||
|
|
(packet_in_last_i !=
|
|
((s_progress_after_beat >= s_selected_bytes_q) &&
|
|
s_transfer_last)));
|
|
assign packet_in_ready_o = (s_state_q == PayloadWriteWait) && !s_fault_q &&
|
|
!s_abort_pending_q && s_write_data_ready;
|
|
assign s_write_data_valid = (s_state_q == PayloadWriteWait) ?
|
|
(packet_in_valid_i || s_fault_q || s_abort_pending_q) :
|
|
((s_state_q == WritebackWait) || (s_state_q == OwnershipWait));
|
|
assign s_write_data = (s_state_q == PayloadWriteWait) ?
|
|
((s_fault_q || s_abort_pending_q) ? '0 : packet_in_data_i) :
|
|
((s_state_q == WritebackWait) ?
|
|
((s_writeback_index_q == 3'd0) ? s_desc_progress_q :
|
|
s_completion_stat) :
|
|
s_control_released);
|
|
assign s_write_strb = ((s_state_q == PayloadWriteWait) &&
|
|
(s_fault_q || s_abort_pending_q)) ? '0 :
|
|
((s_state_q == PayloadWriteWait) ? packet_in_strb_i : '1);
|
|
assign s_axi_write_fault = (s_write_resp != `AXI4_RESP_OKAY) || s_write_id_err;
|
|
|
|
assign busy_o = ((s_state_q != Idle) && (s_state_q != Terminal)) || s_read_busy || s_write_busy;
|
|
assign done_o = s_done_q;
|
|
assign error_o = s_err_q;
|
|
assign error_code_o = s_err_code_q;
|
|
assign current_desc_o = s_desc_addr_q;
|
|
assign next_desc_o = s_desc_next;
|
|
assign bytes_done_o = s_total_done_q;
|
|
assign frame_o = s_desc_frame;
|
|
assign descriptor_irq_o = s_descriptor_irq_q;
|
|
assign abort_done_o = s_abort_done_q;
|
|
|
|
dma_axi4_master #(
|
|
.AddrWidth (AddrWidth),
|
|
.DataWidth (DataWidth),
|
|
.MaxBurstBeats(16)
|
|
) u_axi4_master (
|
|
.clk_i (clk_i),
|
|
.rst_n_i (rst_n_i),
|
|
.clear_i (1'b0),
|
|
.read_start_valid_i (s_read_start_valid),
|
|
.read_start_ready_o (s_read_start_ready),
|
|
.read_addr_i (s_read_addr),
|
|
.read_beats_i (s_read_beats),
|
|
.read_fixed_i (1'b0),
|
|
.read_busy_o (s_read_busy),
|
|
.read_beat_valid_o (s_read_beat_valid),
|
|
.read_beat_ready_i (s_read_beat_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_err),
|
|
.read_done_o (s_read_done),
|
|
.write_start_valid_i (s_write_start_valid),
|
|
.write_start_ready_o (s_write_start_ready),
|
|
.write_addr_i (s_write_addr),
|
|
.write_beats_i (s_write_beats),
|
|
.write_fixed_i (1'b0),
|
|
.write_busy_o (s_write_busy),
|
|
.write_data_valid_i (s_write_data_valid),
|
|
.write_data_ready_o (s_write_data_ready),
|
|
.write_data_i (s_write_data),
|
|
.write_strb_i (s_write_strb),
|
|
.write_done_o (s_write_done),
|
|
.write_resp_o (s_write_resp),
|
|
.write_id_error_o (s_write_id_err),
|
|
.origin_enable_i (1'b0),
|
|
.read_origin_channel_i (3'd0),
|
|
.write_origin_channel_i(3'd0),
|
|
.axi4 (dma_axi4)
|
|
);
|
|
|
|
always_comb begin
|
|
s_state_d = s_state_q;
|
|
s_desc_addr_d = s_desc_addr_q;
|
|
s_desc_index_d = s_desc_index_q;
|
|
s_desc_limit_d = s_desc_limit_q;
|
|
s_memory_to_packet_d = s_memory_to_packet_q;
|
|
s_allow_short_d = s_allow_short_q;
|
|
s_unlimited_d = s_unlimited_q;
|
|
s_remaining_d = s_remaining_q;
|
|
s_total_done_d = s_total_done_q;
|
|
s_desc_words_d = s_desc_words_q;
|
|
s_fetch_index_d = s_fetch_index_q;
|
|
s_selected_bytes_d = s_selected_bytes_q;
|
|
s_desc_progress_d = s_desc_progress_q;
|
|
s_burst_beats_d = s_burst_beats_q;
|
|
s_burst_index_d = s_burst_index_q;
|
|
s_writeback_index_d = s_writeback_index_q;
|
|
s_fault_d = s_fault_q;
|
|
s_err_code_d = s_err_code_q;
|
|
s_abort_pending_d = s_abort_pending_q || abort_i;
|
|
s_done_d = 1'b0;
|
|
s_err_d = s_err_q;
|
|
s_descriptor_irq_d = 1'b0;
|
|
s_abort_done_d = 1'b0;
|
|
|
|
unique case (s_state_q)
|
|
Idle: begin
|
|
s_abort_pending_d = 1'b0;
|
|
if (start_i) begin
|
|
s_desc_addr_d = desc_base_i;
|
|
s_desc_index_d = '0;
|
|
s_desc_limit_d = desc_limit_i;
|
|
s_memory_to_packet_d = memory_to_packet_i;
|
|
s_allow_short_d = allow_short_i;
|
|
s_unlimited_d = transfer_bytes_i == 32'd0;
|
|
s_remaining_d = transfer_bytes_i;
|
|
s_total_done_d = 32'd0;
|
|
s_desc_words_d = '0;
|
|
s_fetch_index_d = '0;
|
|
s_desc_progress_d = 32'd0;
|
|
s_fault_d = 1'b0;
|
|
s_err_code_d = 8'd0;
|
|
s_err_d = 1'b0;
|
|
if ((desc_base_i[4:0] != 5'd0) || (desc_base_i[11:0] > 12'hFE0) ||
|
|
(desc_limit_i == 16'd0) || (desc_limit_i > 16'(MaxDescriptors))) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorDescriptor;
|
|
s_state_d = Terminal;
|
|
end else begin
|
|
s_state_d = FetchStart;
|
|
end
|
|
end
|
|
end
|
|
FetchStart: begin
|
|
if (abort_i) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorAbort;
|
|
s_state_d = Terminal;
|
|
end else if (s_read_start_ready) begin
|
|
s_fetch_index_d = '0;
|
|
s_state_d = FetchWait;
|
|
end
|
|
end
|
|
FetchWait: begin
|
|
if (s_read_beat_valid && s_read_beat_ready) begin
|
|
if (s_axi_read_fault) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorAxiRead;
|
|
end else begin
|
|
s_desc_words_d[s_fetch_index_q] = s_read_data;
|
|
end
|
|
s_fetch_index_d = s_fetch_index_q + 1'b1;
|
|
if (s_read_done) begin
|
|
if (s_fault_q || s_axi_read_fault || abort_i) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = abort_i ? ErrorAbort : ErrorAxiRead;
|
|
s_state_d = Terminal;
|
|
end else begin
|
|
s_state_d = Validate;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Validate: begin
|
|
s_desc_progress_d = 32'd0;
|
|
if (!s_desc_valid || (s_desc_err != usb2_pkg::Usb2DescOk) || !s_desc_own ||
|
|
(s_desc_zero_packet && !s_memory_to_packet_q)) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorDescriptor;
|
|
s_state_d = Terminal;
|
|
end else if (!s_unlimited_q && (s_remaining_q > s_desc_len) && s_desc_end) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorLength;
|
|
s_state_d = WritebackStart;
|
|
end else begin
|
|
if (!s_unlimited_q && (s_remaining_q < s_desc_len)) begin
|
|
s_selected_bytes_d = s_remaining_q;
|
|
end else begin
|
|
s_selected_bytes_d = s_desc_len;
|
|
end
|
|
if (!s_unlimited_q && (s_remaining_q < s_desc_len) &&
|
|
!(s_allow_short_q || s_desc_short_ok)) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorLength;
|
|
s_state_d = WritebackStart;
|
|
end else if (s_memory_to_packet_q) begin
|
|
s_state_d = PayloadReadStart;
|
|
end else begin
|
|
s_state_d = PayloadWriteStart;
|
|
end
|
|
end
|
|
end
|
|
PayloadReadStart: begin
|
|
if (s_read_start_ready) begin
|
|
s_burst_beats_d = s_payload_beats;
|
|
s_burst_index_d = '0;
|
|
s_state_d = PayloadReadWait;
|
|
end
|
|
end
|
|
PayloadReadWait: begin
|
|
if (s_read_beat_valid && s_read_beat_ready) begin
|
|
s_burst_index_d = s_burst_index_q + 1'b1;
|
|
if (s_axi_read_fault) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorAxiRead;
|
|
end else if (!s_fault_q && !s_abort_pending_q) begin
|
|
s_desc_progress_d = s_progress_after_beat;
|
|
s_total_done_d = s_total_done_q + {29'd0, s_payload_beat_bytes};
|
|
end
|
|
if (s_read_done) begin
|
|
if (s_fault_q || s_axi_read_fault || s_abort_pending_q || abort_i) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = (s_abort_pending_q || abort_i) ? ErrorAbort : ErrorAxiRead;
|
|
s_state_d = WritebackStart;
|
|
end else if (s_progress_after_beat >= s_selected_bytes_q) begin
|
|
s_state_d = WritebackStart;
|
|
end else begin
|
|
s_state_d = PayloadReadStart;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
PayloadWriteStart: begin
|
|
if (s_write_start_ready) begin
|
|
s_burst_beats_d = s_payload_beats;
|
|
s_burst_index_d = '0;
|
|
s_state_d = PayloadWriteWait;
|
|
end
|
|
end
|
|
PayloadWriteWait: begin
|
|
if (s_write_data_valid && s_write_data_ready) begin
|
|
s_burst_index_d = s_burst_index_q + 1'b1;
|
|
if (s_packet_input_fault && !s_fault_q) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorPacket;
|
|
end else if (!s_fault_q && !s_abort_pending_q) begin
|
|
s_desc_progress_d = s_progress_after_beat;
|
|
s_total_done_d = s_total_done_q + {29'd0, s_payload_beat_bytes};
|
|
end
|
|
end
|
|
if (s_write_done) begin
|
|
if (s_axi_write_fault || s_fault_q || s_packet_input_fault ||
|
|
s_abort_pending_q || abort_i) begin
|
|
s_fault_d = 1'b1;
|
|
if (s_abort_pending_q || abort_i) begin
|
|
s_err_code_d = ErrorAbort;
|
|
end else if (s_axi_write_fault) begin
|
|
s_err_code_d = ErrorAxiWrite;
|
|
end else begin
|
|
s_err_code_d = ErrorPacket;
|
|
end
|
|
s_state_d = WritebackStart;
|
|
end else if (s_desc_progress_q >= s_selected_bytes_q) begin
|
|
s_state_d = WritebackStart;
|
|
end else begin
|
|
s_state_d = PayloadWriteStart;
|
|
end
|
|
end
|
|
end
|
|
WritebackStart: begin
|
|
if (s_write_start_ready) begin
|
|
s_writeback_index_d = 3'd0;
|
|
s_state_d = WritebackWait;
|
|
end
|
|
end
|
|
WritebackWait: begin
|
|
if (s_write_data_valid && s_write_data_ready) begin
|
|
s_writeback_index_d = s_writeback_index_q + 1'b1;
|
|
end
|
|
if (s_write_done) begin
|
|
if (s_axi_write_fault) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorWriteback;
|
|
s_state_d = Terminal;
|
|
end else begin
|
|
s_state_d = OwnershipStart;
|
|
end
|
|
end
|
|
end
|
|
OwnershipStart: begin
|
|
if (s_write_start_ready) begin
|
|
s_state_d = OwnershipWait;
|
|
end
|
|
end
|
|
OwnershipWait: begin
|
|
if (s_write_done) begin
|
|
if (s_axi_write_fault) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorWriteback;
|
|
s_state_d = Terminal;
|
|
end else begin
|
|
s_descriptor_irq_d = s_desc_irq;
|
|
s_state_d = Complete;
|
|
end
|
|
end
|
|
end
|
|
Complete: begin
|
|
if (s_fault_q) begin
|
|
s_state_d = Terminal;
|
|
end else if ((!s_unlimited_q && (s_remaining_q <= s_selected_bytes_q)) ||
|
|
(s_unlimited_q && s_desc_end)) begin
|
|
s_state_d = Terminal;
|
|
end else if (!s_desc_chain || s_desc_end ||
|
|
(({8'd0, s_desc_index_q} + 1'b1) >= s_desc_limit_q)) begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorLength;
|
|
s_state_d = Terminal;
|
|
end else begin
|
|
s_desc_addr_d = s_desc_next;
|
|
s_desc_index_d = s_desc_index_q + 1'b1;
|
|
s_desc_words_d = '0;
|
|
s_desc_progress_d = 32'd0;
|
|
if (!s_unlimited_q) begin
|
|
s_remaining_d = s_remaining_after_desc;
|
|
end
|
|
s_state_d = FetchStart;
|
|
end
|
|
end
|
|
Terminal: begin
|
|
s_done_d = !s_fault_q;
|
|
s_err_d = s_fault_q;
|
|
s_abort_done_d = s_err_code_q == ErrorAbort;
|
|
s_state_d = Idle;
|
|
end
|
|
default: begin
|
|
s_fault_d = 1'b1;
|
|
s_err_code_d = ErrorPacket;
|
|
s_state_d = Terminal;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
dffr #(
|
|
.DATA_WIDTH(4)
|
|
) u_state_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_state_d),
|
|
.dat_o (s_state_bits_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(AddrWidth)
|
|
) u_desc_addr_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_desc_addr_d),
|
|
.dat_o (s_desc_addr_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(DescIndexWidth)
|
|
) u_desc_index_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_desc_index_d),
|
|
.dat_o (s_desc_index_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(16)
|
|
) u_desc_limit_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_desc_limit_d),
|
|
.dat_o (s_desc_limit_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_memory_to_packet_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_memory_to_packet_d),
|
|
.dat_o (s_memory_to_packet_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_allow_short_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_allow_short_d),
|
|
.dat_o (s_allow_short_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_unlimited_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_unlimited_d),
|
|
.dat_o (s_unlimited_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(32)
|
|
) u_remaining_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_remaining_d),
|
|
.dat_o (s_remaining_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(32)
|
|
) u_total_done_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_total_done_d),
|
|
.dat_o (s_total_done_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(256)
|
|
) u_desc_words_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_desc_words_d),
|
|
.dat_o (s_desc_words_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(4)
|
|
) u_fetch_index_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_fetch_index_d),
|
|
.dat_o (s_fetch_index_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(32)
|
|
) u_selected_bytes_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_selected_bytes_d),
|
|
.dat_o (s_selected_bytes_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(32)
|
|
) u_desc_progress_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_desc_progress_d),
|
|
.dat_o (s_desc_progress_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(5)
|
|
) u_burst_beats_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_burst_beats_d),
|
|
.dat_o (s_burst_beats_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(5)
|
|
) u_burst_index_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_burst_index_d),
|
|
.dat_o (s_burst_index_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(3)
|
|
) u_writeback_index_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_writeback_index_d),
|
|
.dat_o (s_writeback_index_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_fault_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_fault_d),
|
|
.dat_o (s_fault_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(8)
|
|
) u_error_code_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_err_code_d),
|
|
.dat_o (s_err_code_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_abort_pending_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_abort_pending_d),
|
|
.dat_o (s_abort_pending_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_done_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_done_d),
|
|
.dat_o (s_done_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_error_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_err_d),
|
|
.dat_o (s_err_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_descriptor_irq_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_descriptor_irq_d),
|
|
.dat_o (s_descriptor_irq_q)
|
|
);
|
|
dffr #(
|
|
.DATA_WIDTH(1)
|
|
) u_abort_done_dffr (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst_n_i),
|
|
.dat_i (s_abort_done_d),
|
|
.dat_o (s_abort_done_q)
|
|
);
|
|
|
|
`ifndef SYNTHESIS
|
|
initial begin
|
|
if ((AddrWidth != 32) || (DataWidth != 32) || (MaxDescriptors < 2) ||
|
|
(MaxDescriptors > 256)) begin
|
|
$fatal(1, "usb2_dma: delivered configuration requires 32-bit AXI and 2..256 descriptors");
|
|
end
|
|
end
|
|
`endif
|
|
endmodule
|