// AXIS compat wrapper above axi_dma_if_wrapper. // // // s_axis_desc_cmd = one-beat command stream carrying read + write desc // m_axis_desc_status = one-beat status stream carrying read + write status // // read_enable and write_enable select which descriptor is used `default_nettype none module axi_dma_axis_compat #( parameter int unsigned AXI_DATA_WIDTH = 32, parameter int unsigned AXI_ADDR_WIDTH = 16, parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8, parameter int unsigned AXI_ID_WIDTH = 8, parameter int unsigned AXI_USER_WIDTH = 1, parameter int unsigned AXI_MAX_BURST_LEN = 16, parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH, parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8, parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8, parameter int unsigned AXIS_LAST_ENABLE = 1, parameter int unsigned AXIS_ID_ENABLE = 1, parameter int unsigned AXIS_ID_WIDTH = 8, parameter int unsigned AXIS_DEST_ENABLE = 0, parameter int unsigned AXIS_DEST_WIDTH = 8, parameter int unsigned AXIS_USER_ENABLE = 1, parameter int unsigned AXIS_USER_WIDTH = 1, parameter int unsigned LEN_WIDTH = 20, parameter int unsigned TAG_WIDTH = 8, parameter int unsigned ENABLE_SG = 0, parameter int unsigned ENABLE_UNALIGNED = 0, // Raw packed command/status widths. rounded up to full bytes parameter int unsigned READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH, parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH, parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH, parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8, parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8, parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4, parameter int unsigned WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4, parameter int unsigned DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH, parameter int unsigned DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) / 8) * 8, parameter int unsigned DESC_STATUS_KEEP_WIDTH = DESC_STATUS_DATA_WIDTH / 8, parameter int unsigned DESC_STATUS_USER_WIDTH = 2 )( input logic clk, input logic rst, // Unified command stream. One beat carries both read and write descriptors. // Which parts are used is selected by read_enable/write_enable. axis_if.slave s_axis_desc_cmd, // Unified status stream. One beat can carry read status, write status, or both. // m_axis_desc_status.tuser[0] = read status valid in this beat // m_axis_desc_status.tuser[1] = write status valid in this beat axis_if.master m_axis_desc_status, // Original DMA data streams and AXI memory master, still exposed as normal // generic interfaces. axis_if.master m_axis_read_data, axis_if.slave s_axis_write_data, axi4_if.master m_axi, input logic read_enable, input logic write_enable, input logic write_abort ); // -------------------------------------------------------------------------- // packing layout // -------------------------------------------------------------------------- // s_axis_desc_cmd.tdata: // [READ_DESC_*] read descriptor // [WRITE_DESC_*] write descriptor // // m_axis_desc_status.tdata: // [READ_STATUS_*] read descriptor status // [WRITE_STATUS_*] write descriptor status // // offsets are LSB-first. padding in upper bits (if required) localparam int unsigned RD_ADDR_LSB = 0; localparam int unsigned RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH; localparam int unsigned RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH; localparam int unsigned RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH; localparam int unsigned RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH; localparam int unsigned RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH; localparam int unsigned WR_BASE_LSB = READ_DESC_WIDTH; localparam int unsigned WR_ADDR_LSB = WR_BASE_LSB; localparam int unsigned WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH; localparam int unsigned WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH; localparam int unsigned RD_STS_TAG_LSB = 0; localparam int unsigned RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH; localparam int unsigned WR_STS_BASE_LSB = READ_STATUS_WIDTH; localparam int unsigned WR_STS_LEN_LSB = WR_STS_BASE_LSB; localparam int unsigned WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH; localparam int unsigned WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH; localparam int unsigned WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH; localparam int unsigned WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH; localparam int unsigned WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH; wire rstn = ~rst; // -------------------------------------------------------------------------- // Internal DMA-specific descriptor/status interfaces // -------------------------------------------------------------------------- axi_dma_read_desc_if #( .ADDR_W (AXI_ADDR_WIDTH), .LEN_W (LEN_WIDTH), .TAG_W (TAG_WIDTH), .ID_W (AXIS_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH), .USER_W (AXIS_USER_WIDTH) ) read_desc_if ( .aclk (clk), .aresetn (rstn) ); axi_dma_read_desc_status_if #( .TAG_W (TAG_WIDTH) ) read_desc_status_if ( .aclk (clk), .aresetn (rstn) ); axi_dma_write_desc_if #( .ADDR_W (AXI_ADDR_WIDTH), .LEN_W (LEN_WIDTH), .TAG_W (TAG_WIDTH) ) write_desc_if ( .aclk (clk), .aresetn (rstn) ); axi_dma_write_desc_status_if #( .LEN_W (LEN_WIDTH), .TAG_W (TAG_WIDTH), .ID_W (AXIS_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH), .USER_W (AXIS_USER_WIDTH) ) write_desc_status_if ( .aclk (clk), .aresetn (rstn) ); // -------------------------------------------------------------------------- // Command AXIS -> read/write descriptors // -------------------------------------------------------------------------- wire cmd_valid = s_axis_desc_cmd.req.t.valid; wire cmd_ready = (!read_enable || read_desc_if.resp.ready) && (!write_enable || write_desc_if.resp.ready); // Only assert a descriptor valid when the complete command can be accepted. // This prevents partial consumption when both read and write are enabled. assign read_desc_if.req.valid = cmd_valid && read_enable && (!write_enable || write_desc_if.resp.ready); assign write_desc_if.req.valid = cmd_valid && write_enable && (!read_enable || read_desc_if.resp.ready); assign s_axis_desc_cmd.resp.ready = cmd_ready; assign read_desc_if.req.addr = s_axis_desc_cmd.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH]; assign read_desc_if.req.len = s_axis_desc_cmd.req.t.data[RD_LEN_LSB +: LEN_WIDTH]; assign read_desc_if.req.tag = s_axis_desc_cmd.req.t.data[RD_TAG_LSB +: TAG_WIDTH]; assign read_desc_if.req.id = s_axis_desc_cmd.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH]; assign read_desc_if.req.dest = s_axis_desc_cmd.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH]; assign read_desc_if.req.user = s_axis_desc_cmd.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH]; assign write_desc_if.req.addr = s_axis_desc_cmd.req.t.data[WR_ADDR_LSB +: AXI_ADDR_WIDTH]; assign write_desc_if.req.len = s_axis_desc_cmd.req.t.data[WR_LEN_LSB +: LEN_WIDTH]; assign write_desc_if.req.tag = s_axis_desc_cmd.req.t.data[WR_TAG_LSB +: TAG_WIDTH]; // -------------------------------------------------------------------------- // Lower DMA wrapper // -------------------------------------------------------------------------- axi_dma_if_wrapper #( .AXI_DATA_WIDTH (AXI_DATA_WIDTH), .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), .AXI_STRB_WIDTH (AXI_STRB_WIDTH), .AXI_ID_WIDTH (AXI_ID_WIDTH), .AXI_USER_WIDTH (AXI_USER_WIDTH), .AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN), .AXIS_DATA_WIDTH (AXIS_DATA_WIDTH), .AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE), .AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH), .AXIS_LAST_ENABLE (AXIS_LAST_ENABLE), .AXIS_ID_ENABLE (AXIS_ID_ENABLE), .AXIS_ID_WIDTH (AXIS_ID_WIDTH), .AXIS_DEST_ENABLE (AXIS_DEST_ENABLE), .AXIS_DEST_WIDTH (AXIS_DEST_WIDTH), .AXIS_USER_ENABLE (AXIS_USER_ENABLE), .AXIS_USER_WIDTH (AXIS_USER_WIDTH), .LEN_WIDTH (LEN_WIDTH), .TAG_WIDTH (TAG_WIDTH), .ENABLE_SG (ENABLE_SG), .ENABLE_UNALIGNED (ENABLE_UNALIGNED) ) u_axi_dma_if_wrapper ( .clk (clk), .rst (rst), .s_axis_read_desc (read_desc_if), .m_axis_read_desc_status (read_desc_status_if), .m_axis_read_data (m_axis_read_data), .s_axis_write_desc (write_desc_if), .m_axis_write_desc_status (write_desc_status_if), .s_axis_write_data (s_axis_write_data), .m_axi (m_axi), .read_enable (read_enable), .write_enable (write_enable), .write_abort (write_abort) ); // -------------------------------------------------------------------------- // Status capture and unified AXIS output // -------------------------------------------------------------------------- // Forencich descriptor status channels have valid but no ready. A regular // AXI-Stream output can be stalled // this wrapper stores one pending read status and one pending write status. // if you need more, please attack a fifo logic rd_status_pending; logic [TAG_WIDTH-1:0] rd_status_tag_q; logic [3:0] rd_status_error_q; logic wr_status_pending; logic [LEN_WIDTH-1:0] wr_status_len_q; logic [TAG_WIDTH-1:0] wr_status_tag_q; logic [AXIS_ID_WIDTH-1:0] wr_status_id_q; logic [AXIS_DEST_WIDTH-1:0] wr_status_dest_q; logic [AXIS_USER_WIDTH-1:0] wr_status_user_q; logic [3:0] wr_status_error_q; wire status_valid = rd_status_pending || wr_status_pending; wire status_fire = status_valid && m_axis_desc_status.resp.ready; always_ff @(posedge clk) begin if (rst) begin rd_status_pending <= 1'b0; wr_status_pending <= 1'b0; rd_status_tag_q <= '0; rd_status_error_q <= '0; wr_status_len_q <= '0; wr_status_tag_q <= '0; wr_status_id_q <= '0; wr_status_dest_q <= '0; wr_status_user_q <= '0; wr_status_error_q <= '0; end else begin if (status_fire) begin rd_status_pending <= 1'b0; wr_status_pending <= 1'b0; end if (read_desc_status_if.req.valid) begin `ifndef SYNTHESIS if (rd_status_pending && !status_fire) begin $error("axi_dma_axis_compat: read status overflow; add a FIFO or do not backpressure status output"); end `endif rd_status_pending <= 1'b1; rd_status_tag_q <= read_desc_status_if.req.tag; rd_status_error_q <= read_desc_status_if.req.error; end if (write_desc_status_if.req.valid) begin `ifndef SYNTHESIS if (wr_status_pending && !status_fire) begin $error("axi_dma_axis_compat: write status overflow; add a FIFO or do not backpressure status output"); end `endif wr_status_pending <= 1'b1; wr_status_len_q <= write_desc_status_if.req.len; wr_status_tag_q <= write_desc_status_if.req.tag; wr_status_id_q <= write_desc_status_if.req.id; wr_status_dest_q <= write_desc_status_if.req.dest; wr_status_user_q <= write_desc_status_if.req.user; wr_status_error_q <= write_desc_status_if.req.error; end end end logic [DESC_STATUS_DATA_WIDTH-1:0] status_tdata; logic [DESC_STATUS_USER_WIDTH-1:0] status_tuser; always_comb begin status_tdata = '0; status_tuser = '0; if (rd_status_pending) begin status_tdata[RD_STS_TAG_LSB +: TAG_WIDTH] = rd_status_tag_q; status_tdata[RD_STS_ERROR_LSB +: 4] = rd_status_error_q; status_tuser[0] = 1'b1; end if (wr_status_pending) begin status_tdata[WR_STS_LEN_LSB +: LEN_WIDTH] = wr_status_len_q; status_tdata[WR_STS_TAG_LSB +: TAG_WIDTH] = wr_status_tag_q; status_tdata[WR_STS_ID_LSB +: AXIS_ID_WIDTH] = wr_status_id_q; status_tdata[WR_STS_DEST_LSB +: AXIS_DEST_WIDTH] = wr_status_dest_q; status_tdata[WR_STS_USER_LSB +: AXIS_USER_WIDTH] = wr_status_user_q; status_tdata[WR_STS_ERROR_LSB +: 4] = wr_status_error_q; status_tuser[1] = 1'b1; end end assign m_axis_desc_status.req.t.data = status_tdata; assign m_axis_desc_status.req.t.keep = {DESC_STATUS_KEEP_WIDTH{1'b1}}; assign m_axis_desc_status.req.t.strb = {DESC_STATUS_KEEP_WIDTH{1'b1}}; assign m_axis_desc_status.req.t.last = 1'b1; assign m_axis_desc_status.req.t.id = '0; assign m_axis_desc_status.req.t.dest = '0; assign m_axis_desc_status.req.t.user = status_tuser; assign m_axis_desc_status.req.t.valid = status_valid; endmodule : axi_dma_axis_compat `default_nettype wire