aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi/axi_dma_master.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/axi/axi_dma_master.v')
-rw-r--r--fpga/usrp3/lib/axi/axi_dma_master.v548
1 files changed, 548 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/axi/axi_dma_master.v b/fpga/usrp3/lib/axi/axi_dma_master.v
new file mode 100644
index 000000000..59e2e97a7
--- /dev/null
+++ b/fpga/usrp3/lib/axi/axi_dma_master.v
@@ -0,0 +1,548 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+`include "axi_defs.v"
+
+`define DEBUG if (0)
+
+module axi_dma_master #(
+ parameter AWIDTH = 32,
+ parameter DWIDTH = 64
+) (
+ input aclk, // Global AXI clock
+ input areset, // Global AXI reset
+ //
+ // AXI Write address channel
+ //
+ output [0 : 0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
+ output reg [AWIDTH-1 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
+ output reg [7 : 0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
+ output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output [1 : 0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
+ output [0 : 0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
+ output [3 : 0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
+ output [2 : 0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
+ output [3 : 0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
+ output [3 : 0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
+ output [0 : 0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
+ output reg m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
+ input m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
+ //
+ // AXI Write data channel.
+ //
+ output [DWIDTH-1 : 0] m_axi_wdata, // Write data
+ output [DWIDTH/8-1 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
+ output reg m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
+ output m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
+ output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
+ input m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
+ //
+ // AXI Write response channel signals
+ //
+ input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
+ input [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
+ input [0 : 0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
+ input m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
+ output reg m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
+ //
+ // AXI Read address channel
+ //
+ output [0 : 0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
+ output reg [AWIDTH-1 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
+ output reg [7 : 0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
+ output [2 : 0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output [1 : 0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
+ output [0 : 0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
+ output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
+ output [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
+ output [3 : 0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
+ output [3 : 0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
+ output [0 : 0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
+ output reg m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
+ input m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
+ //
+ // AXI Read data channel
+ //
+ input [0 : 0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
+ input [DWIDTH-1 : 0] m_axi_rdata, // Read data.
+ input [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
+ input m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
+ input [0 : 0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
+ input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
+ output m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
+ //
+ // DMA interface for Write transaction
+ //
+ input [AWIDTH-1:0] write_addr, // Byte address for start of write transaction (should be 64bit alligned)
+ input [7:0] write_count, // Count of 64bit words to write. (minus one)
+ input write_ctrl_valid,
+ output reg write_ctrl_ready,
+ input [DWIDTH-1:0] write_data,
+ input write_data_valid,
+ output write_data_ready,
+ //
+ // DMA interface for Read
+ //
+ input [AWIDTH-1:0] read_addr, // Byte address for start of read transaction (should be 64bit alligned)
+ input [7:0] read_count, // Count of 64bit words to read.
+ input read_ctrl_valid,
+ output reg read_ctrl_ready,
+ output [DWIDTH-1:0] read_data,
+ output read_data_valid,
+ input read_data_ready,
+ //
+ // Debug Bus
+ //
+ output [31:0] debug
+
+ );
+
+
+ localparam AW_IDLE = 0;
+ localparam WAIT_AWREADY = 1;
+ localparam WAIT_BVALID = 2;
+ localparam AW_ERROR = 3;
+
+ reg [1:0] write_addr_state;
+ reg [7:0] write_data_count; // Count write transfers.
+ reg enable_data_write;
+
+ localparam DW_IDLE = 0;
+ localparam DW_RUN = 1;
+ localparam DW_LAST = 2;
+
+ reg [1:0] write_data_state;
+
+ localparam AR_IDLE = 0;
+ localparam WAIT_ARREADY = 1;
+ localparam WAIT_READ_DONE = 2;
+ localparam AR_ERROR = 3;
+
+ reg [1:0] read_addr_state;
+
+ localparam DR_IDLE = 0;
+ localparam DR_RUN = 1;
+ localparam DR_WAIT_ERROR = 2;
+ localparam DR_ERROR = 3;
+
+ reg [1:0] read_data_state;
+ reg [7:0] read_data_count;
+ reg enable_data_read;
+
+ ///////////////////////////
+ // DEBUG
+ ///////////////////////////
+ assign debug= {24'h0,write_addr_state[1:0],write_data_state[1:0],read_addr_state[1:0],read_data_state[1:0]};
+
+
+ //
+ //
+ //
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Write address channel
+ //
+ /////////////////////////////////////////////////////////////////////////////////
+ assign m_axi_awid = 1'b0;
+ assign m_axi_awsize = $clog2(DWIDTH/8);
+ assign m_axi_awburst = `AXI4_BURST_INCR;
+ assign m_axi_awlock = `AXI4_LOCK_NORMAL;
+ assign m_axi_awcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
+ assign m_axi_awprot = `AXI4_PROT_NON_SECURE;
+ assign m_axi_awqos = 4'h0;
+ assign m_axi_awregion = 4'h0;
+ assign m_axi_awuser = 1'b0;
+
+
+ //
+ // AXI Write address state machine
+ //
+ always @(posedge aclk)
+ if (areset) begin
+ write_ctrl_ready <= 1'b0;
+ write_addr_state <= AW_IDLE;
+ m_axi_awaddr <= {AWIDTH{1'b0}};
+ m_axi_awlen[7:0] <= 8'h0;
+ m_axi_awvalid <= 1'b0;
+ m_axi_bready <= 1'b0;
+ end else
+ case (write_addr_state)
+ //
+ // AW_IDLE
+ // We are ready to accept a new write transaction.
+ //
+ AW_IDLE: begin
+ // Premptively accept new write transaction since we are idle.
+ write_ctrl_ready <= 1'b1;
+ // No need to be waiting for a response while idle.
+ m_axi_bready <= 1'b0;
+ // If we are offered a new transaction then.....
+ if (write_ctrl_valid) begin
+ // Drive all the relevent AXI4 write address channel signals next cycle.
+ m_axi_awaddr <= write_addr;
+ m_axi_awlen[7:0] <= {write_count};
+ m_axi_awvalid <= 1'b1;
+ // If the AXI4 write channel is pre-emptively accepting the transaction...
+ if (m_axi_awready == 1'b1) begin
+ // ...go straight to looking for a transaction response...
+ `DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",write_addr,write_count,$time);
+ write_addr_state <= WAIT_BVALID;
+ m_axi_bready <= 1'b1;
+ end else begin
+ // ...otherwise wait to get the transaction accepted.
+ write_addr_state <= WAIT_AWREADY;
+ end
+ end
+ end
+ //
+ // WAIT_AWREADY
+ // Waiting for AXI4 slave to accept new write transaction.
+ //
+ WAIT_AWREADY: begin
+ write_ctrl_ready <= 1'b0;
+ // If the AXI4 write channel is accepting the transaction...
+ if (m_axi_awready == 1'b1) begin
+ // ...go to looking for a transaction response...
+ write_addr_state <= WAIT_BVALID;
+ m_axi_awvalid <= 1'b0;
+ m_axi_bready <= 1'b1;
+ `DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_awaddr,m_axi_awlen[7:0],$time);
+ end else begin
+ // ...otherwise wait to get the trasaction accepted.
+ write_addr_state <= WAIT_AWREADY;
+ end
+ end // case: WAIT_AWREADY
+ //
+ // WAIT_BVALID
+ // Write transaction has been accepted, now waiting for a response to signal it's sucsesful.
+ // Ignoring ID tag for the moment
+ //
+ WAIT_BVALID: begin
+ write_ctrl_ready <= 1'b0;
+ m_axi_awvalid <= 1'b0;
+ // Wait for response channel to signal how write transaction went down....
+ if (m_axi_bvalid == 1'b1) begin
+ if ((m_axi_bresp == `AXI4_RESP_OKAY) || (m_axi_bresp == `AXI4_RESP_EXOKAY)) begin
+ // ....it went well, we are ready to start something new.
+ write_addr_state <= AW_IDLE;
+ m_axi_bready <= 1'b0;
+ write_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
+ end else if ((m_axi_bresp == `AXI4_RESP_SLVERR) || (m_axi_bresp == `AXI4_RESP_DECERR)) begin
+ // ....things got ugly, retreat to an error stat and wait for intervention.
+ write_addr_state <= AW_ERROR;
+ m_axi_bready <= 1'b0;
+ end
+ end else begin
+ write_addr_state <= WAIT_BVALID;
+ m_axi_bready <= 1'b1;
+ end
+ end // case: WAIT_BVALID
+ //
+ // AW_ERROR
+ // Something bad happened, going to need external intervention to restore a safe state.
+ //
+ AW_ERROR: begin
+ write_ctrl_ready <= 1'b0;
+ write_addr_state <= AW_ERROR;
+ m_axi_awaddr <= {AWIDTH{1'b0}};
+ m_axi_awlen[7:0] <= 8'h0;
+ m_axi_awvalid <= 1'b0;
+ m_axi_bready <= 1'b0;
+ end
+ endcase // case(write_addr_state)
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Write data channel
+ //
+ /////////////////////////////////////////////////////////////////////////////////
+ assign m_axi_wstrb = {DWIDTH/8{1'b1}};
+ assign m_axi_wuser = 1'b0;
+
+ //
+ // AXI Write data state machine
+ //
+ always @(posedge aclk)
+ if (areset) begin
+ write_data_state <= AW_IDLE;
+ write_data_count <= 1;
+ enable_data_write <= 1'b0;
+ m_axi_wlast <= 1'b0;
+
+ end else
+ case (write_data_state)
+ //
+ // DW_IDLE
+ // Sit in this state until presented with the control details of a new write transaction.
+ //
+ DW_IDLE: begin
+ write_data_count <= 1;
+ m_axi_wlast <= 1'b0;
+
+ if (write_ctrl_valid && write_ctrl_ready) begin
+ enable_data_write <= 1'b1;
+ if (write_count[7:0] == 8'h0) begin
+ // Single transfer transaction
+ write_data_state <= DW_LAST;
+ m_axi_wlast <= 1'b1;
+ end else begin
+ write_data_state <= DW_RUN;
+ end
+ end else begin
+ write_data_state <= DW_IDLE;
+ end
+ end
+ //
+ // DW_RUN
+ //
+ DW_RUN : begin
+ enable_data_write <= 1'b1;
+ m_axi_wlast <= 1'b0;
+
+ if (write_data_valid && m_axi_wready) begin
+ // Single write transfer
+ write_data_count <= write_data_count + 1;
+
+ if (write_data_count == m_axi_awlen[7:0]) begin
+ write_data_state <= DW_LAST;
+ m_axi_wlast <= 1'b1;
+ end else begin
+ write_data_state <= DW_RUN;
+ end
+ end else begin
+ write_data_state <= DW_RUN;
+ end
+ end
+ //
+ // DW_LAST
+ //
+ DW_LAST: begin
+ if (write_data_valid && m_axi_wready) begin
+ enable_data_write <= 1'b0;
+ write_data_state <= DW_IDLE;
+ m_axi_wlast <= 1'b0;
+ end else begin
+ enable_data_write <= 1'b1;
+ write_data_state <= DW_LAST;
+ m_axi_wlast <= 1'b1;
+ end
+ end // case: DW_LAST
+ //
+ default:
+ write_data_state <= DW_IDLE;
+
+ endcase // case(write_data_state)
+
+
+ assign m_axi_wdata = write_data;
+ assign m_axi_wvalid = enable_data_write && write_data_valid;
+ assign write_data_ready = enable_data_write && m_axi_wready;
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Read address channel
+ //
+ /////////////////////////////////////////////////////////////////////////////////
+ assign m_axi_arid = 1'b0;
+ assign m_axi_arsize = $clog2(DWIDTH/8);
+ assign m_axi_arburst = `AXI4_BURST_INCR;
+ assign m_axi_arlock = `AXI4_LOCK_NORMAL;
+ assign m_axi_arcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
+ assign m_axi_arprot = `AXI4_PROT_NON_SECURE;
+ assign m_axi_arqos = 4'h0;
+ assign m_axi_arregion = 4'h0;
+ assign m_axi_aruser = 1'b0;
+
+
+ //
+ // AXI Read address state machine
+ //
+ always @(posedge aclk)
+ if (areset) begin
+ read_ctrl_ready <= 1'b0;
+ read_addr_state <= AR_IDLE;
+ m_axi_araddr <= {AWIDTH{1'b0}};
+ m_axi_arlen[7:0] <= 8'h0;
+ m_axi_arvalid <= 1'b0;
+ end else
+ case (read_addr_state)
+ //
+ // AR_IDLE
+ // We are ready to accept a new read transaction.
+ //
+ AR_IDLE: begin
+ // Premptively accept new read transaction since we are idle.
+ read_ctrl_ready <= 1'b1;
+ // If we are offered a new transaction then.....
+ if (read_ctrl_valid) begin
+ // Drive all the relevent AXI4 read address channel signals next cycle.
+ m_axi_araddr <= read_addr;
+ m_axi_arlen[7:0] <= {read_count};
+ m_axi_arvalid <= 1'b1;
+ // If the AXI4 read channel is pre-emptively accepting the transaction...
+ if (m_axi_arready == 1'b1) begin
+ // ...go straight to looking for the transaction to complete
+ `DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",read_addr,read_count,$time);
+ read_addr_state <= WAIT_READ_DONE;
+ end else begin
+ // ...otherwise wait to get the transaction accepted.
+ read_addr_state <= WAIT_ARREADY;
+ end
+ end
+ end
+ //
+ // WAIT_ARREADY
+ // Waiting for AXI4 slave to accept new read transaction.
+ //
+ WAIT_ARREADY: begin
+ read_ctrl_ready <= 1'b0;
+ // If the AXI4 read channel is accepting the transaction...
+ if (m_axi_arready == 1'b1) begin
+ // ...go to looking for the transaction to complete...
+ read_addr_state <= WAIT_READ_DONE;
+ m_axi_arvalid <= 1'b0;
+ `DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_araddr,m_axi_arlen[7:0],$time);
+ end else begin
+ // ...otherwise wait to get the trasaction accepted.
+ read_addr_state <= WAIT_ARREADY;
+ end
+ end // case: WAIT_ARREADY
+ //
+ // WAIT_READ_DONE
+ // Read transaction has been accepted, now waiting for the data transfer to complete
+ // Ignoring ID tag for the moment
+ //
+ WAIT_READ_DONE: begin
+ read_ctrl_ready <= 1'b0;
+ m_axi_arvalid <= 1'b0;
+ // Wait for read transaction to complete
+ if (read_data_state == DR_IDLE) begin
+ // ....it went well, we are ready to start something new.
+ read_addr_state <= AR_IDLE;
+ read_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
+ end else if (read_data_state == DR_ERROR) begin
+ // ....things got ugly, retreat to an error stat and wait for intervention.
+ read_addr_state <= AR_ERROR;
+ end else begin
+ read_addr_state <= WAIT_READ_DONE;
+ end
+ end // case: WAIT_BVALID
+ //
+ // AR_ERROR
+ // Something bad happened, going to need external intervention to restore a safe state.
+ //
+ AR_ERROR: begin
+ read_ctrl_ready <= 1'b0;
+ read_addr_state <= AR_ERROR;
+ m_axi_araddr <= {AWIDTH{1'b0}};
+ m_axi_arlen[7:0] <= 8'h0;
+ m_axi_arvalid <= 1'b0;
+ end
+ endcase // case(read_addr_state)
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Read data channel
+ //
+ /////////////////////////////////////////////////////////////////////////////////
+
+
+ //
+ // AXI Read data state machine
+ //
+ always @(posedge aclk)
+ if (areset) begin
+ read_data_state <= AR_IDLE;
+ read_data_count <= 0;
+ enable_data_read <= 1'b0;
+
+ end else
+ case (read_data_state)
+ //
+ // DR_IDLE
+ // Sit in this state until presented with the control details of a new read transaction.
+ //
+ DR_IDLE: begin
+ read_data_count <= 0;
+
+ if (read_ctrl_valid && read_ctrl_ready) begin
+ enable_data_read <= 1'b1;
+ read_data_state <= DR_RUN;
+ end else begin
+ read_data_state <= DR_IDLE;
+ end
+ end
+ //
+ // DR_RUN
+ // Sit here counting read transfers. If any have error's shift to error state.
+ //
+ DR_RUN : begin
+ enable_data_read <= 1'b1;
+
+ if (read_data_ready && m_axi_rvalid) begin
+ // Single read transfer
+ read_data_count <= read_data_count + 1;
+ if ((m_axi_rresp == `AXI4_RESP_SLVERR) || (m_axi_rresp == `AXI4_RESP_DECERR)) begin
+ if (m_axi_rlast) begin
+ read_data_state <= DR_ERROR;
+ end else begin
+ read_data_state <= DR_WAIT_ERROR;
+ end
+ end else if (m_axi_rlast) begin // Implicitly good response signalled this transfer.
+ if (read_data_count == m_axi_arlen[7:0]) begin
+ read_data_state <= DR_IDLE;
+ end else begin
+ read_data_state <= DR_ERROR;
+ end
+ end else begin
+ read_data_state <= DR_RUN;
+ end
+ end else begin
+ read_data_state <= DR_RUN;
+ end
+ end
+ //
+ // DR_WAIT_ERROR
+ // Something bad happened, wait for last signalled in this burst
+ //
+ DR_WAIT_ERROR: begin
+ if (read_data_ready && m_axi_rvalid && m_axi_rlast) begin
+ enable_data_read <= 1'b0;
+ read_data_state <= DR_ERROR;
+ end else begin
+ enable_data_read <= 1'b1;
+ read_data_state <= DR_WAIT_ERROR;
+ end
+ end // case: DR_WAIT_ERROR
+ //
+ // DR_ERROR
+ // Something bad happened, going to need external intervention to restore a safe state.
+ //
+ DR_ERROR: begin
+ enable_data_read <= 1'b0;
+ read_data_state <= DR_ERROR;
+ end // case: DR_ERROR
+
+
+ endcase // case(read_data_state)
+
+
+ assign read_data = m_axi_rdata;
+ assign m_axi_rready = enable_data_read && read_data_ready;
+ assign read_data_valid = enable_data_read && m_axi_rvalid;
+
+endmodule // axi_dma_master
+
+
+
+
+
+ \ No newline at end of file