| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Filename: axisgdma.v |
| // {{{ |
| // Project: WB2AXIPSP: bus bridges and other odds and ends |
| // |
| // Purpose: Scripts an AXI DMA via in-memory tables: reads from the tables, |
| // commands the DMA. |
| // |
| // Registers: |
| // |
| // 0. Control |
| // 8b KEY |
| // 3'b PROT |
| // 4'b QOS |
| // 1b Abort: Either aborting or aborted |
| // 1b Err: Ended on an error |
| // 1b Busy |
| // 1b Interrupt Enable |
| // 1b Interrupt Set |
| // 1b Start |
| // 1. Reserved |
| // 2-3. First table entry address |
| // (Current) table entry address on read, if in progress |
| // (Optional) |
| // 4-5. Current read address |
| // 6-7. Current write address |
| // 1. Remaining amount to be written (this entry) |
| // |
| // Table entries (must be 32-bit aligned): |
| // If (address_width > 30) |
| // 64b: { 2'bflags, 62'b SOURCE ADDRESS (bytes) } |
| // 00: Continue after this to next |
| // 01: Skip this address |
| // 10: Jump to new address |
| // 11: Last item in chain |
| // 64b: { int_en, 1'b0, DESTINATION ADDRESS (bytes) } |
| // 32b LENGTH (in bytes) |
| // else |
| // 32b: { 2'bflags, 30'b SOURCE ADDRESS (bytes) } |
| // 32b: { int_en, 1'b0, 30'b DESTINATION ADDRESS (bytes) } |
| // 32b LENGTH (in bytes) |
| // |
| // |
| // Creator: Dan Gisselquist, Ph.D. |
| // Gisselquist Technology, LLC |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| // }}} |
| // Copyright (C) 2020, Gisselquist Technology, LLC |
| // {{{ |
| // This file is part of the WB2AXIP project. |
| // |
| // The WB2AXIP project contains free software and gateware, licensed under the |
| // Apache License, Version 2.0 (the "License"). You may not use this project, |
| // or this file, except in compliance with the License. You may obtain a copy |
| // of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations |
| // under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // |
| `default_nettype none |
| // `define AXI3 |
| // }}} |
| module axisgdma #( |
| // {{{ |
| parameter C_AXI_ID_WIDTH = 1, |
| parameter C_AXI_ADDR_WIDTH = 32, |
| parameter C_AXI_DATA_WIDTH = 64, |
| // |
| localparam C_AXIL_ADDR_WIDTH = 4, |
| localparam C_AXIL_DATA_WIDTH = 32, |
| // |
| // OPT_UNALIGNED turns on support for unaligned addresses, |
| // whether source, destination, or length parameters. |
| parameter [0:0] OPT_UNALIGNED = 1'b1, |
| // |
| // OPT_WRAPMEM controls what happens if the transfer runs off |
| // of the end of memory. If set, the transfer will continue |
| // again from the beginning of memory. If clear, the transfer |
| // will be aborted with an error if either read or write |
| // address ever get this far. |
| parameter [0:0] OPT_WRAPMEM = 1'b1, |
| // |
| // LGMAXBURST controls the size of the maximum burst produced |
| // by this core. Specifically, its the log (based 2) of that |
| // maximum size. Hence, for AXI4, this size must be 8 |
| // (i.e. 2^8 or 256 beats) or less. For AXI3, the size must |
| // be 4 or less. Tests have verified performance for |
| // LGMAXBURST as low as 2. While I expect it to fail at |
| // LGMAXBURST=0, I haven't verified at what value this burst |
| // parameter is too small. |
| `ifdef AXI3 |
| parameter LGMAXBURST=4, // 16 beats max |
| `else |
| parameter LGMAXBURST=8, // 256 beats |
| `endif |
| // The number of beats in this maximum burst size is |
| // automatically determined from LGMAXBURST, and so its |
| // forced to be a power of two this way. |
| localparam MAXBURST=(1<<LGMAXBURST), |
| // |
| // LGFIFO: This is the (log-based-2) size of the internal FIFO. |
| // Hence if LGFIFO=8, the internal FIFO will have 256 elements |
| // (words) in it. High throughput transfers are accomplished |
| // by first storing data into a FIFO, then once a full burst |
| // size is available bursting that data over the bus. In |
| // order to be able to keep receiving data while bursting it |
| // out, the FIFO size must be at least twice the size of the |
| // maximum burst size. Larger sizes are possible as well. |
| parameter LGFIFO = LGMAXBURST+1, // 512 element FIFO |
| // |
| // LGLEN: specifies the number of bits in the transfer length |
| // register. If a transfer cannot be specified in LGLEN bits, |
| // it won't happen. LGLEN must be less than or equal to the |
| // address width. |
| parameter LGLEN = C_AXI_ADDR_WIDTH, |
| // |
| // AXI uses ID's to transfer information. This core rather |
| // ignores them. Instead, it uses a constant ID for all |
| // transfers. The following two parameters control that ID. |
| parameter [C_AXI_ID_WIDTH-1:0] DMA_READ_ID = 0, |
| parameter [C_AXI_ID_WIDTH-1:0] DMA_WRITE_ID = 0, |
| parameter [C_AXI_ID_WIDTH-1:0] PF_READ_ID = DMA_READ_ID+1, |
| // |
| // The "ABORT_KEY" is a byte that, if written to the control |
| // word while the core is running, will cause the data transfer |
| // to be aborted. |
| parameter [7:0] ABORT_KEY = 8'h6d, |
| // |
| // OPT_LOWPOWER |
| parameter [0:0] OPT_LOWPOWER = 1'b0, |
| // |
| localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3, |
| localparam AXILLSB= $clog2(C_AXIL_DATA_WIDTH)-3, |
| localparam LGLENW= LGLEN-ADDRLSB |
| // }}} |
| ) ( |
| // {{{ |
| input wire S_AXI_ACLK, |
| input wire S_AXI_ARESETN, |
| // |
| // The AXI4-lite control interface |
| input wire S_AXIL_AWVALID, |
| output wire S_AXIL_AWREADY, |
| input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR, |
| input wire [2:0] S_AXIL_AWPROT, |
| // |
| input wire S_AXIL_WVALID, |
| output wire S_AXIL_WREADY, |
| input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA, |
| input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB, |
| // |
| output reg S_AXIL_BVALID, |
| input wire S_AXIL_BREADY, |
| output reg [1:0] S_AXIL_BRESP, |
| // |
| input wire S_AXIL_ARVALID, |
| output wire S_AXIL_ARREADY, |
| input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR, |
| input wire [2:0] S_AXIL_ARPROT, |
| // |
| output reg S_AXIL_RVALID, |
| input wire S_AXIL_RREADY, |
| output reg [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA, |
| output reg [1:0] S_AXIL_RRESP, |
| // |
| // |
| // The AXI Master (DMA) interface |
| output wire M_AXI_AWVALID, |
| input wire M_AXI_AWREADY, |
| output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID, |
| output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR, |
| `ifdef AXI3 |
| output wire [3:0] M_AXI_AWLEN, |
| `else |
| output wire [7:0] M_AXI_AWLEN, |
| `endif |
| output wire [2:0] M_AXI_AWSIZE, |
| output wire [1:0] M_AXI_AWBURST, |
| output wire M_AXI_AWLOCK, |
| output wire [3:0] M_AXI_AWCACHE, |
| output wire [2:0] M_AXI_AWPROT, |
| output wire [3:0] M_AXI_AWQOS, |
| // |
| // |
| output wire M_AXI_WVALID, |
| input wire M_AXI_WREADY, |
| `ifdef AXI3 |
| output wire [C_AXI_ID_WIDTH-1:0] M_AXI_WID, |
| `endif |
| output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA, |
| output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB, |
| output wire M_AXI_WLAST, |
| // |
| // |
| input wire M_AXI_BVALID, |
| output reg M_AXI_BREADY, |
| input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID, |
| input wire [1:0] M_AXI_BRESP, |
| // |
| // |
| output wire M_AXI_ARVALID, |
| input wire M_AXI_ARREADY, |
| output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID, |
| output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR, |
| `ifdef AXI3 |
| output wire [3:0] M_AXI_ARLEN, |
| `else |
| output wire [7:0] M_AXI_ARLEN, |
| `endif |
| output wire [2:0] M_AXI_ARSIZE, |
| output wire [1:0] M_AXI_ARBURST, |
| output wire M_AXI_ARLOCK, |
| output wire [3:0] M_AXI_ARCACHE, |
| output wire [2:0] M_AXI_ARPROT, |
| output wire [3:0] M_AXI_ARQOS, |
| // |
| input wire M_AXI_RVALID, |
| output reg M_AXI_RREADY, |
| input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID, |
| input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA, |
| input wire M_AXI_RLAST, |
| input wire [1:0] M_AXI_RRESP, |
| // |
| output reg o_int |
| // }}} |
| ); |
| |
| // Local parameter definitions |
| // {{{ |
| localparam [1:0] CTRL_ADDR = 2'b00, |
| UNUSED_ADDR = 2'b01, |
| TBLLO_ADDR = 2'b10, |
| TBLHI_ADDR = 2'b11; |
| localparam CTRL_START_BIT = 0, |
| CTRL_BUSY_BIT = 0, |
| CTRL_INT_BIT = 1, |
| CTRL_INTEN_BIT = 2, |
| CTRL_ABORT_BIT = 3, |
| CTRL_ERR_BIT = 4, |
| CTRL_INTERIM_BIT= 5; |
| localparam [1:0] AXI_INCR = 2'b01, AXI_OKAY = 2'b00; |
| |
| `ifdef AXI3 |
| localparam LENWIDTH = 4; |
| `else |
| localparam LENWIDTH = 8; |
| `endif |
| |
| // DMA device internal addresses |
| // {{{ |
| localparam [4:0] DMA_CONTROL= 5'b00000; |
| // }}} |
| |
| localparam [C_AXI_ADDR_WIDTH-1:0] TBL_SIZE |
| = (C_AXI_ADDR_WIDTH < 30) ? (4*5) : (4*7); |
| |
| // }}} |
| |
| // Register/net declarations |
| // {{{ |
| reg axil_write_ready, axil_read_ready; |
| reg [2*C_AXIL_DATA_WIDTH-1:0] wide_tbl, new_widetbl; |
| reg [C_AXI_ADDR_WIDTH-1:0] tbl_addr, r_tbl_addr; |
| reg r_int_enable, r_int, r_err, r_abort; |
| wire w_int, fsm_err; |
| |
| reg [3:0] r_qos; |
| reg [2:0] r_prot; |
| reg r_start; |
| wire r_done, r_busy; |
| |
| wire awskd_valid; |
| wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr; |
| wire wskd_valid; |
| wire [C_AXIL_DATA_WIDTH-1:0] wskd_data; |
| wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb; |
| |
| wire arskd_valid; |
| wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr; |
| |
| // Prefetch interface registers |
| // {{{ |
| wire new_pc, pf_ready, pf_clear_cache; |
| wire [C_AXI_ADDR_WIDTH-1:0] ipc; |
| wire [31:0] pf_insn; |
| wire pf_valid, pf_illegal; |
| wire pf_axi_arvalid; |
| reg pf_axi_arready; |
| wire [C_AXI_ADDR_WIDTH-1:0] pf_axi_araddr, pf_pc; |
| wire [2:0] pf_axi_arprot; |
| wire pf_axi_rready_ignored; |
| wire [C_AXI_ID_WIDTH-1:0] pf_axi_arid; |
| wire [7:0] pf_axi_arlen; |
| wire [2:0] pf_axi_arsize; |
| wire [1:0] pf_axi_arburst; |
| wire [3:0] pf_axi_arcache; |
| wire [2:0] pf_axi_arprot; |
| wire [3:0] pf_axi_arqos; |
| // }}} |
| |
| // DMA control registers/AXI-lite interface |
| // {{{ |
| wire dmac_awready_ignored; |
| reg [4:0] dmac_waddr; |
| // |
| reg dmac_wvalid; |
| wire dmac_wready; |
| reg [31:0] dmac_wdata; |
| reg [3:0] dmac_wstrb; |
| // |
| wire dmac_bvalid; |
| wire [1:0] dmac_bresp; |
| // |
| wire dmac_arready; |
| wire dmac_rvalid; |
| wire [31:0] dmac_rdata; |
| wire [1:0] dmac_rresp; |
| // }}} |
| |
| // DMA AXI nets |
| // {{{ |
| wire sdma_arvalid; |
| wire [C_AXI_ID_WIDTH-1:0] sdma_arid; |
| wire [C_AXI_ADDR_WIDTH-1:0] sdma_araddr; |
| wire [7:0] sdma_arlen; |
| wire [2:0] sdma_arsize; |
| wire [1:0] sdma_arburst; |
| wire [0:0] sdma_arlock; |
| wire [3:0] sdma_arcache; |
| wire [2:0] sdma_arprot; |
| wire [3:0] sdma_arqos; |
| reg sdma_arready; |
| wire sdma_rready_ignored; |
| wire dma_complete; |
| // }}} |
| |
| // Combined AXI nets |
| // {{{ |
| reg m_axi_arvalid; |
| reg [C_AXI_ID_WIDTH-1:0] m_axi_arid; |
| reg [C_AXI_ADDR_WIDTH-1:0] m_axi_araddr; |
| reg [7:0] m_axi_arlen; |
| reg [2:0] m_axi_arsize; |
| reg [1:0] m_axi_arburst; |
| reg [3:0] m_axi_arcache; |
| reg [2:0] m_axi_arprot; |
| reg [3:0] m_axi_arqos; |
| // }}} |
| |
| reg pf_wins_arbitration; |
| wire m_axi_arready; |
| |
| // }}} |
| |
| //////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // AXI-Lite control interface |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Write control logic |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // axil AW skid buffer |
| // {{{ |
| skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)) |
| axilawskid(// |
| .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), |
| .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY), |
| .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]), |
| .o_valid(awskd_valid), .i_ready(axil_write_ready), |
| .o_data(awskd_addr)); |
| // }}} |
| |
| // axil W skid buffer |
| // {{{ |
| skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8)) |
| axilwskid(// |
| .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), |
| .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY), |
| .i_data({ S_AXIL_WSTRB, S_AXIL_WDATA }), |
| .o_valid(wskd_valid), .i_ready(axil_write_ready), |
| .o_data({ wskd_strb, wskd_data })); |
| // }}} |
| |
| // axil_write_ready |
| // {{{ |
| always @(*) |
| begin |
| axil_write_ready = !S_AXIL_BVALID || S_AXIL_BREADY;; |
| if (!awskd_valid || !wskd_valid) |
| axil_write_ready = 0; |
| end |
| // }}} |
| |
| // S_AXIL_BVALID |
| // {{{ |
| initial S_AXIL_BVALID = 1'b0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| S_AXIL_BVALID <= 1'b0; |
| else if (!S_AXIL_BVALID || S_AXIL_BREADY) |
| S_AXIL_BVALID <= axil_write_ready; |
| // }}} |
| |
| // S_AXIL_BRESP |
| // {{{ |
| always @(*) |
| S_AXIL_BRESP = AXI_OKAY; |
| // }}} |
| |
| // r_start |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| r_start <= 1'b0; |
| else begin |
| r_start <= !r_busy && axil_write_ready && wskd_strb[0] |
| && wskd_data[CTRL_START_BIT] |
| && (awskd_addr == CTRL_ADDR); |
| if (r_err && !wskd_data[CTRL_ERR_BIT]) |
| r_start <= 0; |
| if (r_abort && !wskd_data[CTRL_ABORT_BIT]) |
| r_start <= 0; |
| end |
| // }}} |
| |
| // r_err |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| r_err <= 1'b0; |
| else if (!r_busy) |
| begin |
| if (axil_write_ready) |
| r_err <= (r_err) && (!wskd_strb[0] |
| || !wskd_data[CTRL_ERR_BIT]); |
| end else begin |
| r_err <= r_err || fsm_err; |
| end |
| // }}} |
| |
| // o_int |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN || !r_int_enable || !r_busy) |
| o_int <= 0; |
| else if (w_int) |
| o_int <= 1'b1; |
| // }}} |
| |
| // r_int |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| r_int <= 0; |
| else if (!r_busy) |
| begin |
| if (axil_write_ready && awskd_addr == CTRL_ADDR |
| && wskd_strb[0]) |
| begin |
| if (wskd_data[CTRL_START_BIT]) |
| r_int <= 0; |
| else if (wskd_data[CTRL_INT_BIT]) |
| r_int <= 0; |
| end |
| end else if (w_int) |
| r_int <= 1'b1; |
| // }}} |
| |
| // r_abort |
| // {{{ |
| initial r_abort = 0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| r_abort <= 1'b0; |
| else if (!r_busy) |
| begin |
| if (axil_write_ready && awskd_addr == CTRL_ADDR && wskd_strb[0]) |
| begin |
| if(wskd_data[CTRL_START_BIT] |
| ||wskd_data[CTRL_ABORT_BIT] |
| ||wskd_data[CTRL_ERR_BIT]) |
| r_abort <= 0; |
| end |
| end else if (!r_abort) |
| r_abort <= (axil_write_ready && awskd_addr == CTRL_ADDR) |
| &&(wskd_strb[3] && wskd_data[31:24] == ABORT_KEY); |
| // }}} |
| |
| // wide_tbl, new_widetbl |
| // {{{ |
| always @(*) |
| begin |
| wide_tbl = 0; |
| wide_tbl[C_AXI_ADDR_WIDTH-1:0] = r_tbl_addr; |
| |
| new_widetbl = wide_tbl; |
| if (awskd_addr == TBLLO_ADDR) |
| new_widetbl[31:0] = apply_wstrb(wide_tbl[31:0], |
| wskd_data, wskd_strb); |
| if (awskd_addr == TBLHI_ADDR) |
| new_widetbl[63:32] = apply_wstrb(wide_tbl[63:32], |
| wskd_data, wskd_strb); |
| end |
| // }}} |
| |
| // r_prot, r_qos, r_int_enable, r_tbl_addr |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| begin |
| r_prot <= 0; |
| r_qos <= 0; |
| r_int_enable <= 0; |
| end else if (!r_busy && axil_write_ready) |
| begin |
| case(awskd_addr) |
| CTRL_ADDR: begin |
| if (wskd_strb[2]) |
| begin |
| r_prot <= wskd_data[22:20]; |
| r_qos <= wskd_data[19:16]; |
| end |
| if (wskd_strb[0]) |
| begin |
| r_int_enable <= wskd_data[CTRL_INTEN_BIT]; |
| end end |
| TBLLO_ADDR: begin |
| r_tbl_addr <= new_widetbl[C_AXI_ADDR_WIDTH-1:0]; |
| end |
| TBLHI_ADDR: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH) begin |
| r_tbl_addr <= new_widetbl[C_AXI_ADDR_WIDTH-1:0]; |
| end |
| default: begin end |
| endcase |
| end else if (r_busy) |
| begin |
| r_tbl_addr <= tbl_addr; |
| end |
| // }}} |
| |
| // apply_wstrb function |
| // {{{ |
| function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb; |
| input [C_AXIL_DATA_WIDTH-1:0] prior_data; |
| input [C_AXIL_DATA_WIDTH-1:0] new_data; |
| input [C_AXIL_DATA_WIDTH/8-1:0] wstrb; |
| |
| integer k; |
| for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1) |
| begin |
| apply_wstrb[k*8 +: 8] = wstrb[k] ? new_data[k*8 +: 8] |
| : prior_data[k*8 +: 8]; |
| end |
| endfunction |
| // }}} |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // AXI-lite control read interface |
| // {{{ |
| |
| // AXI-lite AR skid buffer |
| // {{{ |
| skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)) |
| axilarskid(// |
| .i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), |
| .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY), |
| .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]), |
| .o_valid(arskd_valid), .i_ready(axil_read_ready), |
| .o_data(arskd_addr)); |
| // }}} |
| |
| // axil_read_ready |
| // {{{ |
| always @(*) |
| begin |
| axil_read_ready = !S_AXIL_RVALID || S_AXIL_RREADY; |
| if (!arskd_valid) |
| axil_read_ready = 1'b0; |
| end |
| // }}} |
| |
| // S_AXIL_RVALID |
| // {{{ |
| initial S_AXIL_RVALID = 1'b0; |
| always @(posedge S_AXI_ACLK) |
| if (!S_AXI_ARESETN) |
| S_AXIL_RVALID <= 1'b0; |
| else if (!S_AXIL_RVALID || S_AXIL_RREADY) |
| S_AXIL_RVALID <= axil_read_ready; |
| // }}} |
| |
| // S_AXIL_RDATA |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (OPT_LOWPOWER && !S_AXI_ARESETN) |
| S_AXIL_RDATA <= 0; |
| else if (!S_AXIL_RVALID || S_AXIL_RREADY) |
| begin |
| S_AXIL_RDATA <= 0; |
| case(arskd_addr) |
| CTRL_ADDR: begin |
| S_AXIL_RDATA[CTRL_ERR_BIT] <= r_err; |
| S_AXIL_RDATA[CTRL_ABORT_BIT] <= r_abort; |
| S_AXIL_RDATA[CTRL_INTEN_BIT] <= r_int_enable; |
| S_AXIL_RDATA[CTRL_INT_BIT] <= r_int; |
| S_AXIL_RDATA[CTRL_BUSY_BIT] <= r_busy; |
| end |
| // Unused: |
| TBLLO_ADDR: |
| S_AXIL_RDATA <= wide_tbl[C_AXIL_DATA_WIDTH-1:0]; |
| TBLHI_ADDR: |
| S_AXIL_RDATA <= wide_tbl[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]; |
| default: begin end |
| endcase |
| |
| if (OPT_LOWPOWER && (!axil_read_ready || !arskd_valid)) |
| S_AXIL_RDATA <= 0; |
| end |
| // }}} |
| |
| // S_AXIL_RRESP |
| // {{{ |
| always @(*) |
| S_AXIL_RRESP = AXI_OKAY; |
| // }}} |
| // }}} // AXI-lite read |
| // }}} // AXI-lite (all) |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // DMA control |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // Prefix: dmac for the sub DMA control interface |
| // Prefix: sdma for the sub DMA master interface |
| axidma #( |
| // {{{ |
| .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH), |
| .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), |
| .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), |
| .OPT_UNALIGNED(OPT_UNALIGNED), |
| .OPT_WRAPMEM(OPT_WRAPMEM), |
| .LGMAXBURST(LGMAXBURST), |
| .LGFIFO(LGFIFO), |
| .LGLEN(LGLEN), |
| .AXI_READ_ID(DMA_READ_ID), |
| .AXI_WRITE_ID(DMA_WRITE_ID), |
| .ABORT_KEY(ABORT_KEY) |
| // }}} |
| ) subdma( |
| // {{{ |
| .S_AXI_ACLK(S_AXI_ACLK), |
| .S_AXI_ARESETN(S_AXI_ARESETN), |
| // |
| // The AXI4-lite control interface |
| // {{{ |
| .S_AXIL_AWVALID(dmac_wvalid), // Merge AW & W channels:DMA ok w/ |
| .S_AXIL_AWREADY(dmac_awready_ignored), |
| .S_AXIL_AWADDR( dmac_waddr), |
| .S_AXIL_AWPROT( 3'h0), // Internally ignored |
| // |
| .S_AXIL_WVALID(dmac_wvalid), |
| .S_AXIL_WREADY(dmac_wready), |
| .S_AXIL_WDATA( dmac_wdata), |
| .S_AXIL_WSTRB( dmac_wstrb), |
| // |
| .S_AXIL_BVALID(dmac_bvalid), |
| .S_AXIL_BREADY(1'b1), |
| .S_AXIL_BRESP( dmac_bresp), |
| // |
| .S_AXIL_ARVALID(!S_AXI_ARESETN), |
| .S_AXIL_ARREADY(dmac_arready), |
| .S_AXIL_ARADDR( DMA_CONTROL), |
| .S_AXIL_ARPROT( 3'h0), // Internally ignored |
| // |
| .S_AXIL_RVALID(dmac_rvalid), |
| .S_AXIL_RREADY(1'b1), |
| .S_AXIL_RDATA( dmac_rdata), |
| .S_AXIL_RRESP( dmac_rresp), |
| // }}} |
| // |
| // The AXI Master (DMA) interface |
| // {{{ |
| .M_AXI_AWVALID(M_AXI_AWVALID), |
| .M_AXI_AWREADY(M_AXI_AWREADY), |
| .M_AXI_AWID( M_AXI_AWID), |
| .M_AXI_AWADDR( M_AXI_AWADDR), |
| .M_AXI_AWLEN( M_AXI_AWLEN), |
| .M_AXI_AWSIZE( M_AXI_AWSIZE), |
| .M_AXI_AWBURST(M_AXI_AWBURST), |
| .M_AXI_AWLOCK( M_AXI_AWLOCK), |
| .M_AXI_AWCACHE(M_AXI_AWCACHE), |
| .M_AXI_AWPROT( M_AXI_AWPROT), |
| .M_AXI_AWQOS( M_AXI_AWQOS), |
| // |
| // |
| .M_AXI_WVALID(M_AXI_WVALID), |
| .M_AXI_WREADY(M_AXI_WREADY), |
| `ifdef AXI3 |
| .M_AXI_WID(M_AXI_WID), |
| `endif |
| .M_AXI_WDATA(M_AXI_WDATA), |
| .M_AXI_WSTRB(M_AXI_WSTRB), |
| .M_AXI_WLAST(M_AXI_WLAST), |
| // |
| // |
| .M_AXI_BVALID(M_AXI_BVALID), |
| .M_AXI_BREADY(M_AXI_BREADY), |
| .M_AXI_BID( M_AXI_BID), |
| .M_AXI_BRESP( M_AXI_BRESP), |
| // }}} |
| // AXI master read interface |
| // {{{ |
| // The read channel needs to be arbitrated |
| .M_AXI_ARVALID(sdma_arvalid), |
| .M_AXI_ARREADY(sdma_arready), |
| .M_AXI_ARID(sdma_arid), |
| .M_AXI_ARADDR(sdma_araddr), |
| .M_AXI_ARLEN(sdma_arlen), |
| .M_AXI_ARSIZE(sdma_arsize), |
| .M_AXI_ARBURST(sdma_arburst), |
| .M_AXI_ARLOCK(sdma_arlock), |
| .M_AXI_ARCACHE(sdma_arcache), |
| .M_AXI_ARPROT(sdma_arprot), |
| .M_AXI_ARQOS(sdma_arqos), |
| // |
| .M_AXI_RVALID(M_AXI_RVALID && M_AXI_RID == DMA_READ_ID), |
| .M_AXI_RREADY(sdma_rready_ignored), // Known to be one |
| .M_AXI_RID( DMA_READ_ID), |
| .M_AXI_RDATA(M_AXI_RDATA), |
| .M_AXI_RLAST(M_AXI_RLAST), |
| .M_AXI_RRESP(M_AXI_RRESP), |
| // }}} |
| .o_int(dma_complete) |
| // }}} |
| ); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // AXI-Lite prefetch |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // The AXI_lite fetch submodule |
| // {{{ |
| axilfetch #( |
| // {{{ |
| .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), |
| .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), |
| .FETCH_LIMIT(2) |
| // }}} |
| ) pf ( |
| // {{{ |
| .S_AXI_ACLK(S_AXI_ACLK), |
| .S_AXI_ARESETN(S_AXI_ARESETN), |
| // |
| // "CPU" interface |
| // {{{ |
| .i_cpu_reset(!S_AXI_ARESETN), |
| .i_new_pc(new_pc), |
| .i_clear_cache(pf_clear_cache), |
| .i_ready(pf_ready), |
| .i_pc(ipc), |
| .o_insn(pf_insn), |
| .o_valid(pf_valid), |
| .o_pc(pf_pc), |
| .o_illegal(pf_illegal), |
| // }}} |
| // AXI-lite interface |
| // {{{ |
| .M_AXI_ARVALID(pf_axi_arvalid), |
| .M_AXI_ARREADY(pf_axi_arready), |
| .M_AXI_ARADDR( pf_axi_araddr), |
| .M_AXI_ARPROT( pf_axi_arprot), |
| // |
| .M_AXI_RVALID( M_AXI_RVALID && M_AXI_RID == PF_READ_ID), |
| .M_AXI_RREADY( pf_axi_rready_ignored), // Always 1'b1 |
| .M_AXI_RDATA( M_AXI_RDATA), |
| .M_AXI_RRESP( M_AXI_RRESP) |
| // }}} |
| // }}} |
| ); |
| // }}} |
| |
| assign pf_axi_arid = PF_READ_ID; |
| assign pf_axi_arlen = 8'h0; // Only read singletons |
| assign pf_axi_arsize = ADDRLSB[2:0]; |
| assign pf_axi_arburst = 2'b01; |
| assign pf_axi_arcache = 4'b0011; |
| assign pf_axi_arprot = 3'b100; |
| assign pf_axi_arqos = 4'h0; |
| |
| always @(*) |
| M_AXI_RREADY = 1'b1; |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // PF vs DMA arbiter |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // pf_wins_arbitration |
| // {{{ |
| always @(posedge S_AXI_ACLK) |
| if (!m_axi_arvalid || m_axi_arready) |
| begin |
| if (pf_axi_arvalid && !sdma_arvalid) |
| pf_wins_arbitration <= 1'b1; |
| else |
| pf_wins_arbitration <= 1'b0; |
| end |
| // }}} |
| |
| // m_axi_* |
| // {{{ |
| always @(*) |
| begin |
| if (pf_wins_arbitration) |
| begin |
| m_axi_arvalid = pf_axi_arvalid; |
| m_axi_arid = pf_axi_arid; |
| m_axi_araddr = pf_axi_araddr; |
| m_axi_arlen = pf_axi_arlen; |
| m_axi_arsize = pf_axi_arsize; |
| m_axi_arburst = pf_axi_arburst; |
| m_axi_arcache = pf_axi_arcache; |
| m_axi_arprot = pf_axi_arprot; |
| m_axi_arqos = pf_axi_arqos; |
| end else begin |
| m_axi_arvalid = sdma_arvalid; |
| m_axi_arid = sdma_arid; |
| m_axi_araddr = sdma_araddr; |
| m_axi_arlen = sdma_arlen; |
| m_axi_arsize = sdma_arsize; |
| m_axi_arburst = sdma_arburst; |
| m_axi_arcache = sdma_arcache; |
| m_axi_arprot = sdma_arprot; |
| m_axi_arqos = sdma_arqos; |
| end |
| end |
| // }}} |
| |
| // *_arready |
| // {{{ |
| always @(*) |
| begin |
| sdma_arready = m_axi_arready && !pf_wins_arbitration; |
| pf_axi_arready = m_axi_arready && pf_wins_arbitration; |
| end |
| // }}} |
| |
| // Outgoing AR skid buffer |
| // {{{ |
| skidbuffer #( |
| // {{{ |
| .OPT_LOWPOWER(OPT_LOWPOWER), |
| .OPT_OUTREG(1'b1), |
| .DW(C_AXI_ID_WIDTH + C_AXI_ADDR_WIDTH + 8 + 3 + 2 + 4 +3 + 4) |
| // }}} |
| ) marskd( |
| // {{{ |
| S_AXI_ACLK, !S_AXI_ARESETN, m_axi_arvalid, m_axi_arready, |
| { m_axi_arid, m_axi_araddr, m_axi_arlen, m_axi_arsize, |
| m_axi_arburst, m_axi_arcache, |
| m_axi_arprot, m_axi_arqos }, |
| M_AXI_ARVALID, M_AXI_ARREADY, |
| { M_AXI_ARID, M_AXI_ARADDR, M_AXI_ARLEN, M_AXI_ARSIZE, |
| M_AXI_ARBURST, M_AXI_ARCACHE, |
| M_AXI_ARPROT, M_AXI_ARQOS } |
| // }}} |
| ); |
| |
| `ifdef AXI3 |
| assign M_AXI_ARLOCK = 2'b00; // We don't use lock anyway |
| `else |
| assign M_AXI_ARLOCK = 1'b0; |
| `endif |
| // }}} |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // FSM Control states |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| axisgfsm #( |
| // {{{ |
| .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), |
| .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), |
| .ABORT_KEY(ABORT_KEY) |
| // }}} |
| ) fsm ( |
| // {{{ |
| .S_AXI_ACLK(S_AXI_ACLK), |
| .S_AXI_ARESETN(S_AXI_ARESETN), |
| // Control interface |
| // {{{ |
| .i_start(r_start), |
| .i_abort(r_abort), |
| .i_tbl_addr(r_tbl_addr), |
| .i_qos(r_qos), |
| .i_prot(r_prot), |
| .o_done(r_done), |
| .o_busy(r_busy), |
| .o_int(w_int), |
| .o_err(fsm_err), |
| .o_tbl_addr(tbl_addr), |
| // }}} |
| // Prefetch interface |
| // {{{ |
| .o_new_pc(new_pc), |
| .o_pf_clear_cache(pf_clear_cache), |
| .o_pf_ready(pf_ready), |
| .o_pf_pc(ipc), |
| .i_pf_valid(pf_valid), |
| .i_pf_insn(pf_insn), |
| .i_pf_pc(pf_pc), |
| .i_pf_illegal(pf_illegal), |
| // }}} |
| // DMA AXI-lite control interface |
| // {{{ |
| .o_dmac_wvalid(dmac_wvalid), |
| .i_dmac_wready(dmac_wready), |
| .o_dmac_waddr(dmac_waddr), |
| .o_dmac_wdata(dmac_wdata), |
| .o_dmac_wstrb(dmac_wstrb), |
| .i_dmac_rdata(dmac_rdata), |
| .i_dma_complete(dma_complete) |
| // }}} |
| |
| // }}} |
| ); |
| |
| // }}} |
| |
| // Make Verilator happy |
| // Verilator lint_off UNUSED |
| wire unused; |
| assign unused = &{ 1'b0, dmac_awready_ignored, dmac_bvalid, |
| dmac_bresp, dmac_rvalid, dmac_rresp, |
| S_AXIL_AWADDR[AXILLSB-1:0], S_AXIL_AWPROT, |
| S_AXIL_ARADDR[AXILLSB-1:0], S_AXIL_ARPROT, |
| M_AXI_RREADY, r_done, |
| new_widetbl[63:C_AXI_ADDR_WIDTH], |
| sdma_arlock, sdma_rready_ignored, |
| pf_axi_rready_ignored, dmac_arready }; |
| // Verilator lint_on UNUSED |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Formal properties (neither written, nor tested) |
| // {{{ |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| //////////////////////////////////////////////////////////////////////////////// |
| `ifdef FORMAL |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // The full formal verification of this core has not been completed. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // |
| reg f_past_valid; |
| |
| initial f_past_valid = 0; |
| always @(posedge S_AXI_ACLK) |
| f_past_valid <= 1; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // The control interface |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| faxil_slave #( |
| .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH), |
| .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH) |
| // |
| // ... |
| // |
| ) |
| faxil( |
| .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN), |
| // |
| .i_axi_awvalid(S_AXIL_AWVALID), |
| .i_axi_awready(S_AXIL_AWREADY), |
| .i_axi_awaddr(S_AXIL_AWADDR), |
| .i_axi_awprot(S_AXIL_AWPROT), |
| // |
| .i_axi_wvalid(S_AXIL_WVALID), |
| .i_axi_wready(S_AXIL_WREADY), |
| .i_axi_wdata( S_AXIL_WDATA), |
| .i_axi_wstrb( S_AXIL_WSTRB), |
| // |
| .i_axi_bvalid(S_AXIL_BVALID), |
| .i_axi_bready(S_AXIL_BREADY), |
| .i_axi_bresp( S_AXIL_BRESP), |
| // |
| .i_axi_arvalid(S_AXIL_ARVALID), |
| .i_axi_arready(S_AXIL_ARREADY), |
| .i_axi_araddr( S_AXIL_ARADDR), |
| .i_axi_arprot( S_AXIL_ARPROT), |
| // |
| .i_axi_rvalid(S_AXIL_RVALID), |
| .i_axi_rready(S_AXIL_RREADY), |
| .i_axi_rdata( S_AXIL_RDATA), |
| .i_axi_rresp( S_AXIL_RRESP) |
| // |
| // ... |
| // |
| ); |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // The main AXI data interface |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Formal contract checking |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Cover checks |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // }}} |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Careless assumptions (i.e. constraints) |
| // {{{ |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // |
| |
| // None (currently) |
| |
| // }}} |
| `endif |
| // }}} |
| endmodule |