pluto_hdl_adi/library/axi_dmac/axi_dmac_regmap.v

302 lines
8.9 KiB
Coq
Raw Normal View History

// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2018 (c) 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/master/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_regmap #(
parameter ID = 0,
parameter DISABLE_DEBUG_REGISTERS = 0,
parameter BYTES_PER_BEAT_WIDTH_DEST = 1,
parameter BYTES_PER_BEAT_WIDTH_SRC = 1,
parameter BYTES_PER_BURST_WIDTH = 7,
parameter DMA_TYPE_DEST = 0,
parameter DMA_TYPE_SRC = 2,
parameter DMA_AXI_ADDR_WIDTH = 32,
parameter DMA_LENGTH_WIDTH = 24,
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
parameter DMA_LENGTH_ALIGN = 3,
parameter DMA_CYCLIC = 0,
parameter HAS_DEST_ADDR = 1,
parameter HAS_SRC_ADDR = 1,
parameter DMA_2D_TRANSFER = 0,
parameter SYNC_TRANSFER_START = 0
) (
// Slave AXI interface
input s_axi_aclk,
input s_axi_aresetn,
input s_axi_awvalid,
output s_axi_awready,
input [10:0] s_axi_awaddr,
input [2:0] s_axi_awprot,
input s_axi_wvalid,
output s_axi_wready,
input [31:0] s_axi_wdata,
input [3:0] s_axi_wstrb,
output s_axi_bvalid,
input s_axi_bready,
output [1:0] s_axi_bresp,
input s_axi_arvalid,
output s_axi_arready,
input [10:0] s_axi_araddr,
input [2:0] s_axi_arprot,
output s_axi_rvalid,
input s_axi_rready,
output [1:0] s_axi_rresp,
output [31:0] s_axi_rdata,
// Interrupt
output reg irq,
// Control interface
output reg ctrl_enable = 1'b0,
output reg ctrl_pause = 1'b0,
// DMA request interface
output request_valid,
input request_ready,
output [DMA_AXI_ADDR_WIDTH-1:BYTES_PER_BEAT_WIDTH_DEST] request_dest_address,
output [DMA_AXI_ADDR_WIDTH-1:BYTES_PER_BEAT_WIDTH_SRC] request_src_address,
output [DMA_LENGTH_WIDTH-1:0] request_x_length,
output [DMA_LENGTH_WIDTH-1:0] request_y_length,
output [DMA_LENGTH_WIDTH-1:0] request_dest_stride,
output [DMA_LENGTH_WIDTH-1:0] request_src_stride,
output request_sync_transfer_start,
output request_last,
// DMA response interface
input response_eot,
input [BYTES_PER_BURST_WIDTH-1:0] response_measured_burst_length,
input response_partial,
input response_valid,
output response_ready,
// Debug interface
input [DMA_AXI_ADDR_WIDTH-1:0] dbg_src_addr,
input [DMA_AXI_ADDR_WIDTH-1:0] dbg_dest_addr,
axi_dmac: Rework transfer shutdown The DMAC allows a transfer to be aborted. When a transfer is aborted the DMAC shuts down as fast as possible while still completing any pending transactions as required by the protocol specifications of the port. E.g. for AXI-MM this means to complete all outstanding bursts. Once the DMAC has entered an idle state a special synchronization signal is send to all modules. This synchronization signal instructs them to flush the pipeline and remove any stale data and metadata associated with the aborted transfer. Once all data has been flushed the DMAC enters the shutdown state and is ready for the next transfer. In addition each module has a reset that resets the modules state and is used at system startup to bring them into a consistent state. Re-work the shutdown process to instead of flushing the pipeline re-use the startup reset signal also for shutdown. To manage the reset signal generation introduce the reset manager module. It contains a state machine that will assert the reset signals in the correct order and for the appropriate duration in case of a transfer shutdown. The reset signal is asserted in all domains until it has been asserted for at least 4 clock cycles in the slowest domain. This ensures that the reset signal is not de-asserted in the faster domains before the slower domains have had a chance to process the reset signal. In addition the reset signal is de-asserted in the opposite direction of the data flow. This ensures that the data sink is ready to receive data before the data source can start sending data. This simplifies the internal handshaking. This approach has multiple advantages. * Issuing a reset and removing all state takes less time than explicitly flushing one sample per clock cycle at a time. * It simplifies the logic in the faster clock domains at the expense of more complicated logic in the slower control clock domain. This allows for higher fMax on the data paths. * Less signals to synchronize from the control domain to the data domains The implementation of the pause mode has also slightly changed. Pause is now a simple disable of the data domains. When the transfer is resumed after a pause the data domains are re-enabled and continue at their previous state. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 14:02:44 +00:00
input [11:0] dbg_status,
input [31:0] dbg_ids0,
input [31:0] dbg_ids1
);
localparam PCORE_VERSION = 'h00040361;
// Register interface signals
reg [31:0] up_rdata = 32'h00;
reg up_wack = 1'b0;
reg up_rack = 1'b0;
wire up_wreq;
wire up_rreq;
wire [31:0] up_wdata;
wire [8:0] up_waddr;
wire [8:0] up_raddr;
wire [31:0] up_rdata_request;
// Scratch register
reg [31:0] up_scratch = 32'h00;
// Start and end of transfer
wire up_eot; // Asserted for one cycle when a transfer has been completed
wire up_sot; // Asserted for one cycle when a transfer has been queued
// Interupt handling
reg [1:0] up_irq_mask = 2'h3;
reg [1:0] up_irq_source = 2'h0;
wire [1:0] up_irq_pending;
wire [1:0] up_irq_trigger;
wire [1:0] up_irq_source_clear;
// IRQ handling
assign up_irq_pending = ~up_irq_mask & up_irq_source;
assign up_irq_trigger = {up_eot, up_sot};
assign up_irq_source_clear = (up_wreq == 1'b1 && up_waddr == 9'h021) ? up_wdata[1:0] : 2'b00;
always @(posedge s_axi_aclk) begin
if (s_axi_aresetn == 1'b0) begin
irq <= 1'b0;
end else begin
irq <= |up_irq_pending;
end
end
always @(posedge s_axi_aclk) begin
if (s_axi_aresetn == 1'b0) begin
up_irq_source <= 2'b00;
end else begin
up_irq_source <= up_irq_trigger | (up_irq_source & ~up_irq_source_clear);
end
end
// Register Interface
always @(posedge s_axi_aclk) begin
if (s_axi_aresetn == 1'b0) begin
ctrl_enable <= 1'b0;
ctrl_pause <= 1'b0;
up_irq_mask <= 2'b11;
up_scratch <= 32'h00;
up_wack <= 1'b0;
end else begin
up_wack <= up_wreq;
if (up_wreq == 1'b1) begin
case (up_waddr)
9'h002: up_scratch <= up_wdata;
9'h020: up_irq_mask <= up_wdata[1:0];
9'h100: {ctrl_pause, ctrl_enable} <= up_wdata[1:0];
endcase
end
end
end
always @(posedge s_axi_aclk) begin
if (s_axi_aresetn == 1'b0) begin
up_rack <= 'd0;
end else begin
up_rack <= up_rreq;
end
end
always @(posedge s_axi_aclk) begin
if (up_rreq == 1'b1) begin
case (up_raddr)
9'h000: up_rdata <= PCORE_VERSION;
9'h001: up_rdata <= ID;
9'h002: up_rdata <= up_scratch;
9'h003: up_rdata <= 32'h444d4143; // "DMAC"
9'h004: up_rdata <= {8'b0,
4'b0,BYTES_PER_BURST_WIDTH[3:0],
2'b0,DMA_TYPE_SRC[1:0],BYTES_PER_BEAT_WIDTH_SRC[3:0],
2'b0,DMA_TYPE_DEST[1:0],BYTES_PER_BEAT_WIDTH_DEST[3:0]};
9'h020: up_rdata <= up_irq_mask;
9'h021: up_rdata <= up_irq_pending;
9'h022: up_rdata <= up_irq_source;
9'h100: up_rdata <= {ctrl_pause, ctrl_enable};
9'h10d: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_dest_addr;
9'h10e: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_src_addr;
9'h10f: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_status;
9'h110: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_ids0;
9'h111: up_rdata <= DISABLE_DEBUG_REGISTERS ? 32'h00 : dbg_ids1;
default: up_rdata <= up_rdata_request;
endcase
end
end
axi_dmac_regmap_request #(
.DISABLE_DEBUG_REGISTERS(DISABLE_DEBUG_REGISTERS),
.BYTES_PER_BEAT_WIDTH_DEST(BYTES_PER_BEAT_WIDTH_DEST),
.BYTES_PER_BEAT_WIDTH_SRC(BYTES_PER_BEAT_WIDTH_SRC),
.BYTES_PER_BURST_WIDTH(BYTES_PER_BURST_WIDTH),
.DMA_AXI_ADDR_WIDTH(DMA_AXI_ADDR_WIDTH),
.DMA_LENGTH_WIDTH(DMA_LENGTH_WIDTH),
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
.DMA_LENGTH_ALIGN(DMA_LENGTH_ALIGN),
.DMA_CYCLIC(DMA_CYCLIC),
.HAS_DEST_ADDR(HAS_DEST_ADDR),
.HAS_SRC_ADDR(HAS_SRC_ADDR),
.DMA_2D_TRANSFER(DMA_2D_TRANSFER),
.SYNC_TRANSFER_START(SYNC_TRANSFER_START)
) i_regmap_request (
.clk(s_axi_aclk),
.reset(~s_axi_aresetn),
.up_sot(up_sot),
.up_eot(up_eot),
.up_wreq(up_wreq),
.up_rreq(up_rreq),
.up_waddr(up_waddr),
.up_wdata(up_wdata),
.up_raddr(up_raddr),
.up_rdata(up_rdata_request),
.ctrl_enable(ctrl_enable),
.request_valid(request_valid),
.request_ready(request_ready),
.request_dest_address(request_dest_address),
.request_src_address(request_src_address),
.request_x_length(request_x_length),
.request_y_length(request_y_length),
.request_dest_stride(request_dest_stride),
.request_src_stride(request_src_stride),
.request_sync_transfer_start(request_sync_transfer_start),
.request_last(request_last),
.response_eot(response_eot),
.response_measured_burst_length(response_measured_burst_length),
.response_partial(response_partial),
.response_valid(response_valid),
.response_ready(response_ready)
);
up_axi #(
.AXI_ADDRESS_WIDTH (11)
) i_up_axi (
.up_rstn(s_axi_aresetn),
.up_clk(s_axi_aclk),
.up_axi_awvalid(s_axi_awvalid),
.up_axi_awaddr(s_axi_awaddr),
.up_axi_awready(s_axi_awready),
.up_axi_wvalid(s_axi_wvalid),
.up_axi_wdata(s_axi_wdata),
.up_axi_wstrb(s_axi_wstrb),
.up_axi_wready(s_axi_wready),
.up_axi_bvalid(s_axi_bvalid),
.up_axi_bresp(s_axi_bresp),
.up_axi_bready(s_axi_bready),
.up_axi_arvalid(s_axi_arvalid),
.up_axi_araddr(s_axi_araddr),
.up_axi_arready(s_axi_arready),
.up_axi_rvalid(s_axi_rvalid),
.up_axi_rresp(s_axi_rresp),
.up_axi_rdata(s_axi_rdata),
.up_axi_rready(s_axi_rready),
.up_wreq(up_wreq),
.up_waddr(up_waddr),
.up_wdata(up_wdata),
.up_wack(up_wack),
.up_rreq(up_rreq),
.up_raddr(up_raddr),
.up_rdata(up_rdata),
.up_rack(up_rack)
);
endmodule