2018-08-10 14:47:21 +00:00
|
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
2023-07-06 13:54:40 +00:00
|
|
|
// Copyright (C) 2014-2023 Analog Devices, Inc. All rights reserved.
|
2018-08-10 14:47:21 +00:00
|
|
|
//
|
|
|
|
// 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:
|
2023-12-13 16:03:34 +00:00
|
|
|
// https://github.com/analogdevicesinc/hdl/blob/main/LICENSE_ADIBSD
|
2018-08-10 14:47:21 +00:00
|
|
|
// This will allow to generate bit files and not release the source code,
|
|
|
|
// as long as it attaches to an ADI device.
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
|
|
|
2018-08-27 07:14:54 +00:00
|
|
|
`timescale 1ns/100ps
|
|
|
|
|
2018-08-10 14:47:21 +00:00
|
|
|
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
|
2022-04-08 10:21:52 +00:00
|
|
|
) (
|
|
|
|
|
2018-08-10 14:47:21 +00:00
|
|
|
// 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,
|
2018-08-25 04:57:20 +00:00
|
|
|
input response_ready,
|
2018-08-10 14:47:21 +00:00
|
|
|
|
|
|
|
// Interface to requester side
|
2018-08-25 04:57:20 +00:00
|
|
|
input completion_req_valid,
|
2018-11-14 10:41:04 +00:00
|
|
|
output reg completion_req_ready = 1'b1,
|
2018-08-25 04:57:20 +00:00
|
|
|
input completion_req_last,
|
|
|
|
input [1:0] completion_transfer_id
|
2018-08-10 14:47:21 +00:00
|
|
|
);
|
|
|
|
|
2022-04-08 10:21:52 +00:00
|
|
|
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(),
|
2018-08-10 14:47:21 +00:00
|
|
|
|
2022-04-08 10:21:52 +00:00
|
|
|
.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
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
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
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
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;
|
2022-04-08 10:21:52 +00:00
|
|
|
assign response_partial = (state == STATE_WRITE_RESPR) ? req_response_partial : 1'b0;
|
|
|
|
|
|
|
|
always @(posedge req_clk)
|
|
|
|
begin
|
|
|
|
if (req_resetn == 1'b0) begin
|
2018-08-10 14:47:21 +00:00
|
|
|
response_valid <= 1'b0;
|
2022-04-08 10:21:52 +00:00
|
|
|
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
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
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
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
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
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
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;
|
2018-11-14 10:41:04 +00:00
|
|
|
end else begin
|
2022-04-08 10:21:52 +00:00
|
|
|
if (completion_req_last_found == 1'b1) begin
|
|
|
|
nx_state = STATE_IDLE;
|
|
|
|
end
|
2018-11-14 10:41:04 +00:00
|
|
|
end
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
STATE_WRITE_ZRCMPL:begin
|
|
|
|
if (response_ready == 1'b1) begin
|
|
|
|
nx_state = STATE_ZERO_COMPL;
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
default: begin
|
|
|
|
nx_state = STATE_IDLE;
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
endcase
|
|
|
|
end
|
|
|
|
|
|
|
|
always @(posedge req_clk) begin
|
|
|
|
if (req_resetn == 1'b0) begin
|
|
|
|
state <= STATE_IDLE;
|
|
|
|
end else begin
|
|
|
|
state <= nx_state;
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
2018-08-10 14:47:21 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
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
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
// 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
|
2018-11-14 10:41:04 +00:00
|
|
|
end
|
|
|
|
|
2022-04-08 10:21:52 +00:00
|
|
|
assign completion_req = completion_req_ready && completion_req_valid;
|
2018-11-14 10:41:04 +00:00
|
|
|
|
2022-04-08 10:21:52 +00:00
|
|
|
// 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
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
2022-04-08 10:21:52 +00:00
|
|
|
|
|
|
|
// 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
|
2018-08-25 04:57:20 +00:00
|
|
|
end
|
|
|
|
|
2018-08-10 14:47:21 +00:00
|
|
|
endmodule
|