pluto_hdl_adi/library/axi_dmac/axi_dmac_response_manager.v

283 lines
8.9 KiB
Coq
Raw Normal View History

// ***************************************************************************
// ***************************************************************************
// Copyright (C) 2014-2023 Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
// 1. The GNU General Public License version 2 as published by the
// Free Software Foundation, which can be found in the top level directory
// of this repository (LICENSE_GPL2), and also online at:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/main/LICENSE_ADIBSD
// This will allow to generate bit files and not release the source code,
// as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************
`timescale 1ns/100ps
module axi_dmac_response_manager #(
parameter DMA_DATA_WIDTH_SRC = 64,
parameter DMA_DATA_WIDTH_DEST = 64,
parameter DMA_LENGTH_WIDTH = 24,
parameter BYTES_PER_BURST_WIDTH = 7,
parameter BYTES_PER_BEAT_WIDTH_SRC = $clog2(DMA_DATA_WIDTH_SRC/8),
parameter ASYNC_CLK_DEST_REQ = 1
) (
// Interface to destination side
input dest_clk,
input dest_resetn,
input dest_response_valid,
output dest_response_ready,
input [1:0] dest_response_resp,
input dest_response_partial,
input dest_response_resp_eot,
input [BYTES_PER_BURST_WIDTH-1:0] dest_response_data_burst_length,
// Interface to processor
input req_clk,
input req_resetn,
output response_eot,
output reg [BYTES_PER_BURST_WIDTH-1:0] measured_burst_length = 'h0,
output response_partial,
output reg response_valid = 1'b0,
input response_ready,
// Interface to requester side
input completion_req_valid,
output reg completion_req_ready = 1'b1,
input completion_req_last,
input [1:0] completion_transfer_id
);
localparam STATE_IDLE = 3'h0;
localparam STATE_ACC = 3'h1;
localparam STATE_WRITE_RESPR = 3'h2;
localparam STATE_ZERO_COMPL = 3'h3;
localparam STATE_WRITE_ZRCMPL = 3'h4;
reg [2:0] state = STATE_IDLE;
reg [2:0] nx_state;
localparam DEST_SRC_RATIO = DMA_DATA_WIDTH_DEST/DMA_DATA_WIDTH_SRC;
localparam DEST_SRC_RATIO_WIDTH = DEST_SRC_RATIO > 64 ? 7 :
DEST_SRC_RATIO > 32 ? 6 :
DEST_SRC_RATIO > 16 ? 5 :
DEST_SRC_RATIO > 8 ? 4 :
DEST_SRC_RATIO > 4 ? 3 :
DEST_SRC_RATIO > 2 ? 2 :
DEST_SRC_RATIO > 1 ? 1 : 0;
localparam BYTES_PER_BEAT_WIDTH = DEST_SRC_RATIO_WIDTH + BYTES_PER_BEAT_WIDTH_SRC;
localparam BURST_LEN_WIDTH = BYTES_PER_BURST_WIDTH - BYTES_PER_BEAT_WIDTH;
wire do_acc_st;
wire do_compl;
reg req_eot = 1'b0;
reg req_response_partial = 1'b0;
reg [BYTES_PER_BURST_WIDTH-1:0] req_response_dest_data_burst_length = 'h0;
wire response_dest_valid;
reg response_dest_ready = 1'b1;
wire [1:0] response_dest_resp;
wire response_dest_resp_eot;
wire [BYTES_PER_BURST_WIDTH-1:0] response_dest_data_burst_length;
wire completion_req;
reg [1:0] to_complete_count = 'h0;
reg [1:0] transfer_id = 'h0;
reg completion_req_last_found = 1'b0;
util_axis_fifo #(
.DATA_WIDTH(BYTES_PER_BURST_WIDTH+1+1),
.ADDRESS_WIDTH(0),
.ASYNC_CLK(ASYNC_CLK_DEST_REQ)
) i_dest_response_fifo (
.s_axis_aclk(dest_clk),
.s_axis_aresetn(dest_resetn),
.s_axis_valid(dest_response_valid),
.s_axis_ready(dest_response_ready),
.s_axis_full(),
.s_axis_data({dest_response_data_burst_length,
dest_response_partial,
dest_response_resp_eot}),
.s_axis_room(),
.m_axis_aclk(req_clk),
.m_axis_aresetn(req_resetn),
.m_axis_valid(response_dest_valid),
.m_axis_ready(response_dest_ready),
.m_axis_data({response_dest_data_burst_length,
response_dest_partial,
response_dest_resp_eot}),
.m_axis_level(),
.m_axis_empty());
always @(posedge req_clk)
begin
if (response_dest_valid & response_dest_ready) begin
req_eot <= response_dest_resp_eot;
req_response_partial <= response_dest_partial;
req_response_dest_data_burst_length <= response_dest_data_burst_length;
end
end
always @(posedge req_clk)
begin
if (req_resetn == 1'b0) begin
response_dest_ready <= 1'b1;
end else begin
response_dest_ready <= (nx_state == STATE_IDLE);
end
end
axi_dmac: Add support for DMA Scatter-Gather This commit introduces a different interface to submit transfers, using DMA descriptors. The structure of the DMA descriptor is as follows: struct dma_desc { u32 flags, u32 id, u64 dest_addr, u64 src_addr, u64 next_sg_addr, u32 y_len, u32 x_len, u32 src_stride, u32 dst_stride, }; The 'flags' field currently offers two control bits: - bit 0: if set, the transfer will complete after this last descriptor is processed, and the DMA core will go back to idle state; if cleared, the next DMA descriptor pointed to by 'next_sg_addr' will be loaded. - bit 1: if set, an end-of-transfer interrupt will be raised after the memory segment pointed to by this descriptor has been transferred. The 'id' field corresponds to an identifier of the descriptor. The 'dest_addr' and 'src_addr' contain the destination and source addresses to use for the transfer, respectively. The 'x_len' field contains the number of bytes to transfer, minus one. The 'y_len', 'src_stride' and 'dst_stride' fields are only useful for 2D transfers, and should be set to zero if 2D transfers are not required. To start a transfer, the address of the first DMA descriptor must be written to register 0x47c and the HWDESC bit of CONTROL register must be set. The Scatter-Gather transfer is queued similarly to the simple transfers, by writing 1 in TRANSFER_SUBMIT. The Scatter-Gather interface has a dedicated AXI-MM bus configured for read transfers, with its own dedicated clock, which can be asynchronous. The Scatter-Gather reset is generated by the reset manager to reset the logic after completing any pending transactions on the bus. When the Scatter-Gather is enabled during runtime, the legacy cyclic functionality of the DMA is disabled. Signed-off-by: Ionut Podgoreanu <ionut.podgoreanu@analog.com>
2023-08-10 10:10:24 +00:00
assign response_eot = (state == STATE_WRITE_RESPR) ? req_eot : 1'b0;
assign response_partial = (state == STATE_WRITE_RESPR) ? req_response_partial : 1'b0;
always @(posedge req_clk)
begin
if (req_resetn == 1'b0) begin
response_valid <= 1'b0;
end else begin
if (nx_state == STATE_WRITE_RESPR || nx_state == STATE_WRITE_ZRCMPL) begin
response_valid <= 1'b1;
end else if (response_ready == 1'b1) begin
response_valid <= 1'b0;
end
end
end
always @(posedge req_clk)
begin
if (state == STATE_ZERO_COMPL) begin
measured_burst_length <= {BYTES_PER_BURST_WIDTH{1'b1}};
end else if (state == STATE_ACC) begin
measured_burst_length <= req_response_dest_data_burst_length;
end
end
always @(*) begin
nx_state = state;
case (state)
STATE_IDLE: begin
if (response_dest_valid == 1'b1) begin
nx_state = STATE_ACC;
end else if (|to_complete_count) begin
if (transfer_id == completion_transfer_id)
nx_state = STATE_ZERO_COMPL;
end
end
STATE_ACC: begin
nx_state = STATE_WRITE_RESPR;
end
STATE_WRITE_RESPR: begin
if (response_ready == 1'b1) begin
if (|to_complete_count && transfer_id == completion_transfer_id) begin
nx_state = STATE_ZERO_COMPL;
end else begin
nx_state = STATE_IDLE;
end
end
end
STATE_ZERO_COMPL: begin
if (|to_complete_count) begin
nx_state = STATE_WRITE_ZRCMPL;
end else begin
if (completion_req_last_found == 1'b1) begin
nx_state = STATE_IDLE;
end
end
end
STATE_WRITE_ZRCMPL:begin
if (response_ready == 1'b1) begin
nx_state = STATE_ZERO_COMPL;
end
end
default: begin
nx_state = STATE_IDLE;
end
endcase
end
always @(posedge req_clk) begin
if (req_resetn == 1'b0) begin
state <= STATE_IDLE;
end else begin
state <= nx_state;
end
end
assign do_compl = (state == STATE_WRITE_ZRCMPL) && response_ready;
// Once the last completion request from request generator is received
// we can wait for completions from the destination side
always @(posedge req_clk) begin
if (req_resetn == 1'b0) begin
completion_req_last_found <= 1'b0;
end else if (completion_req) begin
completion_req_last_found <= completion_req_last;
end else if (state ==STATE_ZERO_COMPL && ~(|to_complete_count)) begin
completion_req_last_found <= 1'b0;
end
end
// Once the last completion is received wit until all completions are done
always @(posedge req_clk) begin
if (req_resetn == 1'b0) begin
completion_req_ready <= 1'b1;
end else if (completion_req_valid && completion_req_last) begin
completion_req_ready <= 1'b0;
end else if (to_complete_count == 0) begin
completion_req_ready <= 1'b1;
end
end
assign completion_req = completion_req_ready && completion_req_valid;
// Track transfers so we can tell when did the destination completed all its
// transfers
always @(posedge req_clk) begin
if (req_resetn == 1'b0) begin
transfer_id <= 'h0;
end else if ((state == STATE_ACC && req_eot) || do_compl) begin
transfer_id <= transfer_id + 1;
end
end
// Count how many transfers we need to complete
always @(posedge req_clk) begin
if (req_resetn == 1'b0) begin
to_complete_count <= 'h0;
end else if (completion_req & ~do_compl) begin
to_complete_count <= to_complete_count + 1;
end else if (~completion_req & do_compl) begin
to_complete_count <= to_complete_count - 1;
end
end
endmodule