From cf45646812ecb469aa7b56a2d1a5ce43be6f0f92 Mon Sep 17 00:00:00 2001 From: Phil Date: Wed, 24 Jun 2026 13:32:42 +0300 Subject: [PATCH] rtl: rework dma AXIS cmd&status --- axi/rtl/axi_dma_compat.sv | 414 ++++++------- .../tb_axi_dma_axis_compat.sv | 577 +++++++----------- .../test_axi_dma_axis_compat.py | 500 +++++++-------- 3 files changed, 660 insertions(+), 831 deletions(-) diff --git a/axi/rtl/axi_dma_compat.sv b/axi/rtl/axi_dma_compat.sv index d3bcf07..b423bf4 100644 --- a/axi/rtl/axi_dma_compat.sv +++ b/axi/rtl/axi_dma_compat.sv @@ -1,19 +1,10 @@ -// SPDX-License-Identifier: MIT +// AXIS compat wrapper above axi_dma_if_wrapper. // -// Compatibility wrapper over axi_dma_if_wrapper. // -// Uses 4 axis_if channels for statuses and descs +// s_axis_desc_cmd = one-beat command stream carrying read + write desc +// m_axis_desc_status = one-beat status stream carrying read + write status // -// payload packing is LSB-first and one descriptor/status occupies one AXIS beat. -// tkeep/tstrb/tlast/id/dest/user on descriptor inputs are ignored. -// status outputs drive tkeep/tstrb to all ones, tlast to 1, and id/dest/user to 0. -// -// !!!!!: the original alexforencich axi_dma status outputs have -// no ready/backpressure signal. This wrapper adds a oneentry holding register -// for each status axis output, but if a downstream is not ready long enough for a -// second status to arrive while the first one is still pending, the core cannot -// be backpressured. In normal use keep status sinks ready, or add an external -// FIFO if long stalls are possible. +// read_enable and write_enable select which descriptor is used `default_nettype none @@ -41,102 +32,83 @@ module axi_dma_axis_compat #( parameter int unsigned ENABLE_SG = 0, parameter int unsigned ENABLE_UNALIGNED = 0, - // Payload widths for descriptor/status AXIS streams. - parameter int unsigned READ_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH, - parameter int unsigned READ_DESC_AXIS_DATA_WIDTH = ((READ_DESC_PAYLOAD_WIDTH + 7) / 8) * 8, - parameter int unsigned READ_DESC_STATUS_PAYLOAD_WIDTH = TAG_WIDTH + 4, - parameter int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8, - parameter int unsigned WRITE_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH, - parameter int unsigned WRITE_DESC_AXIS_DATA_WIDTH = ((WRITE_DESC_PAYLOAD_WIDTH + 7) / 8) * 8, - parameter int unsigned WRITE_DESC_STATUS_PAYLOAD_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4, - parameter int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8 + // 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, - /* - * AXI read descriptor input, encoded as one AXIS beat. - * tdata layout, LSB first: - * addr | len | tag | id | dest | user - */ - axis_if.slave s_axis_read_desc, + // 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, - /* - * AXI read descriptor status output, encoded as one AXIS beat. - * tdata layout, LSB first: - * tag | error - */ - axis_if.master m_axis_read_desc_status, + // 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, - /* - * AXI stream read data output. - */ + // Original DMA data streams and AXI memory master, still exposed as normal + // generic interfaces. axis_if.master m_axis_read_data, - - /* - * AXI write descriptor input, encoded as one AXIS beat. - * tdata layout, LSB first: - * addr | len | tag - */ - axis_if.slave s_axis_write_desc, - - /* - * AXI write descriptor status output, encoded as one AXIS beat. - * tdata layout, LSB first: - * len | tag | id | dest | user | error - */ - axis_if.master m_axis_write_desc_status, - - /* - * AXI stream write data input. - */ axis_if.slave s_axis_write_data, - - /* - * AXI memory master interface. - */ axi4_if.master m_axi, - /* - * Configuration. - */ input logic read_enable, input logic write_enable, input logic write_abort ); - 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; + // -------------------------------------------------------------------------- + // 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 RDS_TAG_LSB = 0; - localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH; + 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 WD_ADDR_LSB = 0; - localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH; - localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_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 WDS_LEN_LSB = 0; - localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH; - localparam int unsigned WDS_ID_LSB = WDS_TAG_LSB + TAG_WIDTH; - localparam int unsigned WDS_DEST_LSB = WDS_ID_LSB + AXIS_ID_WIDTH; - localparam int unsigned WDS_USER_LSB = WDS_DEST_LSB + AXIS_DEST_WIDTH; - localparam int unsigned WDS_ERROR_LSB = WDS_USER_LSB + AXIS_USER_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 READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8; - localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8; - localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8; - localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8; + 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; - logic rstn; - assign rstn = ~rst; + wire rstn = ~rst; // -------------------------------------------------------------------------- - // Internal DMA-specific descriptor/status interfaces expected by - // axi_dma_if_wrapper. + // Internal DMA-specific descriptor/status interfaces // -------------------------------------------------------------------------- axi_dma_read_desc_if #( @@ -179,125 +151,33 @@ module axi_dma_axis_compat #( ); // -------------------------------------------------------------------------- - // AXIS read descriptor input -> DMA-specific read descriptor interface. + // Command AXIS -> read/write descriptors // -------------------------------------------------------------------------- - assign read_desc_if.req.addr = s_axis_read_desc.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH]; - assign read_desc_if.req.len = s_axis_read_desc.req.t.data[RD_LEN_LSB +: LEN_WIDTH]; - assign read_desc_if.req.tag = s_axis_read_desc.req.t.data[RD_TAG_LSB +: TAG_WIDTH]; - assign read_desc_if.req.id = s_axis_read_desc.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH]; - assign read_desc_if.req.dest = s_axis_read_desc.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH]; - assign read_desc_if.req.user = s_axis_read_desc.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH]; - assign read_desc_if.req.valid = s_axis_read_desc.req.t.valid; - assign s_axis_read_desc.resp.ready = read_desc_if.resp.ready; + 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]; // -------------------------------------------------------------------------- - // AXIS write descriptor input -> DMA-specific write descriptor interface. - // -------------------------------------------------------------------------- - - assign write_desc_if.req.addr = s_axis_write_desc.req.t.data[WD_ADDR_LSB +: AXI_ADDR_WIDTH]; - assign write_desc_if.req.len = s_axis_write_desc.req.t.data[WD_LEN_LSB +: LEN_WIDTH]; - assign write_desc_if.req.tag = s_axis_write_desc.req.t.data[WD_TAG_LSB +: TAG_WIDTH]; - assign write_desc_if.req.valid = s_axis_write_desc.req.t.valid; - assign s_axis_write_desc.resp.ready = write_desc_if.resp.ready; - - // -------------------------------------------------------------------------- - // DMA-specific read status -> AXIS read status output. - // -------------------------------------------------------------------------- - - logic [READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] read_status_payload; - logic [READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] read_status_data_q; - logic read_status_valid_q; - - always_comb begin - read_status_payload = '0; - read_status_payload[RDS_TAG_LSB +: TAG_WIDTH] = read_desc_status_if.req.tag; - read_status_payload[RDS_ERROR_LSB +: 4] = read_desc_status_if.req.error; - end - - always_ff @(posedge clk) begin - if (rst) begin - read_status_valid_q <= 1'b0; - read_status_data_q <= '0; - end else begin - if (!read_status_valid_q || m_axis_read_desc_status.resp.ready) begin - read_status_valid_q <= read_desc_status_if.req.valid; - if (read_desc_status_if.req.valid) begin - read_status_data_q <= read_status_payload; - end else begin - read_status_data_q <= '0; - end - end - end - end - - always_comb begin - m_axis_read_desc_status.req.t.data = '0; - m_axis_read_desc_status.req.t.keep = '0; - m_axis_read_desc_status.req.t.strb = '0; - m_axis_read_desc_status.req.t.last = read_status_valid_q; - m_axis_read_desc_status.req.t.id = '0; - m_axis_read_desc_status.req.t.dest = '0; - m_axis_read_desc_status.req.t.user = '0; - m_axis_read_desc_status.req.t.valid = read_status_valid_q; - - m_axis_read_desc_status.req.t.data[READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] = read_status_data_q; - m_axis_read_desc_status.req.t.keep[READ_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {READ_DESC_STATUS_AXIS_KEEP_WIDTH{read_status_valid_q}}; - m_axis_read_desc_status.req.t.strb[READ_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {READ_DESC_STATUS_AXIS_KEEP_WIDTH{read_status_valid_q}}; - end - - // -------------------------------------------------------------------------- - // DMA-specific write status -> AXIS write status output. - // -------------------------------------------------------------------------- - - logic [WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] write_status_payload; - logic [WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] write_status_data_q; - logic write_status_valid_q; - - always_comb begin - write_status_payload = '0; - write_status_payload[WDS_LEN_LSB +: LEN_WIDTH] = write_desc_status_if.req.len; - write_status_payload[WDS_TAG_LSB +: TAG_WIDTH] = write_desc_status_if.req.tag; - write_status_payload[WDS_ID_LSB +: AXIS_ID_WIDTH] = write_desc_status_if.req.id; - write_status_payload[WDS_DEST_LSB +: AXIS_DEST_WIDTH] = write_desc_status_if.req.dest; - write_status_payload[WDS_USER_LSB +: AXIS_USER_WIDTH] = write_desc_status_if.req.user; - write_status_payload[WDS_ERROR_LSB +: 4] = write_desc_status_if.req.error; - end - - always_ff @(posedge clk) begin - if (rst) begin - write_status_valid_q <= 1'b0; - write_status_data_q <= '0; - end else begin - if (!write_status_valid_q || m_axis_write_desc_status.resp.ready) begin - write_status_valid_q <= write_desc_status_if.req.valid; - if (write_desc_status_if.req.valid) begin - write_status_data_q <= write_status_payload; - end else begin - write_status_data_q <= '0; - end - end - end - end - - always_comb begin - m_axis_write_desc_status.req.t.data = '0; - m_axis_write_desc_status.req.t.keep = '0; - m_axis_write_desc_status.req.t.strb = '0; - m_axis_write_desc_status.req.t.last = write_status_valid_q; - m_axis_write_desc_status.req.t.id = '0; - m_axis_write_desc_status.req.t.dest = '0; - m_axis_write_desc_status.req.t.user = '0; - m_axis_write_desc_status.req.t.valid = write_status_valid_q; - - m_axis_write_desc_status.req.t.data[WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] = write_status_data_q; - m_axis_write_desc_status.req.t.keep[WRITE_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {WRITE_DESC_STATUS_AXIS_KEEP_WIDTH{write_status_valid_q}}; - m_axis_write_desc_status.req.t.strb[WRITE_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {WRITE_DESC_STATUS_AXIS_KEEP_WIDTH{write_status_valid_q}}; - end - - // -------------------------------------------------------------------------- - // Lower-level wrapper: DMA-specific descriptor/status interfaces inside, - // ordinary AXIS data and AXI memory interfaces unchanged. + // Lower DMA wrapper // -------------------------------------------------------------------------- axi_dma_if_wrapper #( @@ -307,6 +187,7 @@ module axi_dma_axis_compat #( .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), @@ -317,29 +198,134 @@ module axi_dma_axis_compat #( .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), + .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_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), + .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), + .m_axi (m_axi), - .read_enable (read_enable), - .write_enable (write_enable), - .write_abort (write_abort) + .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 diff --git a/axi/tb/axi_dma_axis_compat/tb_axi_dma_axis_compat.sv b/axi/tb/axi_dma_axis_compat/tb_axi_dma_axis_compat.sv index 92dadd1..b7269f3 100644 --- a/axi/tb/axi_dma_axis_compat/tb_axi_dma_axis_compat.sv +++ b/axi/tb/axi_dma_axis_compat/tb_axi_dma_axis_compat.sv @@ -24,214 +24,143 @@ module tb_axi_dma_axis_compat #( parameter int unsigned LEN_WIDTH = 20, parameter int unsigned TAG_WIDTH = 8, parameter int unsigned ENABLE_SG = 0, - parameter int unsigned ENABLE_UNALIGNED = 0 -)(); + parameter int unsigned ENABLE_UNALIGNED = 0, - // cocotb drives these directly - logic clk; - logic rst; - logic rstn; + 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, - assign rstn = ~rst; + 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, - // -------------------------------------------------------------------------- - // Descriptor/status payload widths and tdata layouts. - // -------------------------------------------------------------------------- + input logic read_enable, + input logic write_enable, + input logic write_abort, - localparam int unsigned READ_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH; - localparam int unsigned READ_DESC_AXIS_DATA_WIDTH = ((READ_DESC_PAYLOAD_WIDTH + 7) / 8) * 8; - localparam int unsigned READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8; + // Unified descriptor command AXIS, flat for cocotb + input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata, + input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tkeep, + input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tstrb, + input logic s_axis_desc_cmd_tlast, + input logic s_axis_desc_cmd_tid, + input logic s_axis_desc_cmd_tdest, + input logic s_axis_desc_cmd_tuser, + input logic s_axis_desc_cmd_tvalid, + output logic s_axis_desc_cmd_tready, - localparam int unsigned READ_DESC_STATUS_PAYLOAD_WIDTH = TAG_WIDTH + 4; - localparam int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8; - localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8; + // Unified descriptor status AXIS, flat for cocotb + output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata, + output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tkeep, + output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tstrb, + output logic m_axis_desc_status_tlast, + output logic m_axis_desc_status_tid, + output logic m_axis_desc_status_tdest, + output logic [DESC_STATUS_USER_WIDTH-1:0] m_axis_desc_status_tuser, + output logic m_axis_desc_status_tvalid, + input logic m_axis_desc_status_tready, - localparam int unsigned WRITE_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH; - localparam int unsigned WRITE_DESC_AXIS_DATA_WIDTH = ((WRITE_DESC_PAYLOAD_WIDTH + 7) / 8) * 8; - localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8; + // Read data AXIS output + output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, + output logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep, + output logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tstrb, + output logic m_axis_read_data_tlast, + output logic [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid, + output logic [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest, + output logic [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser, + output logic m_axis_read_data_tvalid, + input logic m_axis_read_data_tready, - localparam int unsigned WRITE_DESC_STATUS_PAYLOAD_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4; - localparam int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8; - localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8; + // Write data AXIS input + input logic [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata, + input logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep, + input logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tstrb, + input logic s_axis_write_data_tlast, + input logic [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid, + input logic [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest, + input logic [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser, + input logic s_axis_write_data_tvalid, + output logic s_axis_write_data_tready, - 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; + // AXI memory master, flat for cocotb AxiRam + output logic [AXI_ID_WIDTH-1:0] m_axi_awid, + output logic [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output logic [7:0] m_axi_awlen, + output logic [2:0] m_axi_awsize, + output logic [1:0] m_axi_awburst, + output logic m_axi_awlock, + output logic [3:0] m_axi_awcache, + output logic [2:0] m_axi_awprot, + output logic [3:0] m_axi_awqos, + output logic [3:0] m_axi_awregion, + output logic [AXI_USER_WIDTH-1:0] m_axi_awuser, + output logic m_axi_awvalid, + input logic m_axi_awready, - localparam int unsigned RDS_TAG_LSB = 0; - localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH; + output logic [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output logic [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output logic m_axi_wlast, + output logic [AXI_USER_WIDTH-1:0] m_axi_wuser, + output logic m_axi_wvalid, + input logic m_axi_wready, - localparam int unsigned WD_ADDR_LSB = 0; - localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH; - localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_WIDTH; + input logic [AXI_ID_WIDTH-1:0] m_axi_bid, + input logic [1:0] m_axi_bresp, + input logic [AXI_USER_WIDTH-1:0] m_axi_buser, + input logic m_axi_bvalid, + output logic m_axi_bready, - localparam int unsigned WDS_LEN_LSB = 0; - localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH; - localparam int unsigned WDS_ID_LSB = WDS_TAG_LSB + TAG_WIDTH; - localparam int unsigned WDS_DEST_LSB = WDS_ID_LSB + AXIS_ID_WIDTH; - localparam int unsigned WDS_USER_LSB = WDS_DEST_LSB + AXIS_DEST_WIDTH; - localparam int unsigned WDS_ERROR_LSB = WDS_USER_LSB + AXIS_USER_WIDTH; + output logic [AXI_ID_WIDTH-1:0] m_axi_arid, + output logic [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output logic [7:0] m_axi_arlen, + output logic [2:0] m_axi_arsize, + output logic [1:0] m_axi_arburst, + output logic m_axi_arlock, + output logic [3:0] m_axi_arcache, + output logic [2:0] m_axi_arprot, + output logic [3:0] m_axi_arqos, + output logic [3:0] m_axi_arregion, + output logic [AXI_USER_WIDTH-1:0] m_axi_aruser, + output logic m_axi_arvalid, + input logic m_axi_arready, - // -------------------------------------------------------------------------- - // Flat descriptor/status ports visible to cocotb. - // -------------------------------------------------------------------------- + input logic [AXI_ID_WIDTH-1:0] m_axi_rid, + input logic [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input logic [1:0] m_axi_rresp, + input logic m_axi_rlast, + input logic [AXI_USER_WIDTH-1:0] m_axi_ruser, + input logic m_axi_rvalid, + output logic m_axi_rready +); - logic [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_addr; - logic [LEN_WIDTH-1:0] s_axis_read_desc_len; - logic [TAG_WIDTH-1:0] s_axis_read_desc_tag; - logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id; - logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest; - logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user; - logic s_axis_read_desc_valid; - logic s_axis_read_desc_ready; - - logic [TAG_WIDTH-1:0] m_axis_read_desc_status_tag; - logic [3:0] m_axis_read_desc_status_error; - logic m_axis_read_desc_status_valid; - - logic [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_addr; - logic [LEN_WIDTH-1:0] s_axis_write_desc_len; - logic [TAG_WIDTH-1:0] s_axis_write_desc_tag; - logic s_axis_write_desc_valid; - logic s_axis_write_desc_ready; - - logic [LEN_WIDTH-1:0] m_axis_write_desc_status_len; - logic [TAG_WIDTH-1:0] m_axis_write_desc_status_tag; - logic [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id; - logic [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest; - logic [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user; - logic [3:0] m_axis_write_desc_status_error; - logic m_axis_write_desc_status_valid; - - // -------------------------------------------------------------------------- - // Flat AXIS data ports visible to cocotb. - // -------------------------------------------------------------------------- - - logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata; - logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep; - logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tstrb; - logic m_axis_read_data_tvalid; - logic m_axis_read_data_tready; - logic m_axis_read_data_tlast; - logic [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid; - logic [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest; - logic [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser; - - logic [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata; - logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep; - logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tstrb; - logic s_axis_write_data_tvalid; - logic s_axis_write_data_tready; - logic s_axis_write_data_tlast; - logic [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid; - logic [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest; - logic [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser; - - // -------------------------------------------------------------------------- - // Flat AXI memory master ports visible to cocotb AxiRam. - // -------------------------------------------------------------------------- - - logic [AXI_ID_WIDTH-1:0] m_axi_awid; - logic [AXI_ADDR_WIDTH-1:0] m_axi_awaddr; - logic [7:0] m_axi_awlen; - logic [2:0] m_axi_awsize; - logic [1:0] m_axi_awburst; - logic m_axi_awlock; - logic [3:0] m_axi_awcache; - logic [2:0] m_axi_awprot; - logic [3:0] m_axi_awqos; - logic [3:0] m_axi_awregion; - logic [AXI_USER_WIDTH-1:0] m_axi_awuser; - logic m_axi_awvalid; - logic m_axi_awready; - - logic [AXI_DATA_WIDTH-1:0] m_axi_wdata; - logic [AXI_STRB_WIDTH-1:0] m_axi_wstrb; - logic m_axi_wlast; - logic [AXI_USER_WIDTH-1:0] m_axi_wuser; - logic m_axi_wvalid; - logic m_axi_wready; - - logic [AXI_ID_WIDTH-1:0] m_axi_bid; - logic [1:0] m_axi_bresp; - logic [AXI_USER_WIDTH-1:0] m_axi_buser; - logic m_axi_bvalid; - logic m_axi_bready; - - logic [AXI_ID_WIDTH-1:0] m_axi_arid; - logic [AXI_ADDR_WIDTH-1:0] m_axi_araddr; - logic [7:0] m_axi_arlen; - logic [2:0] m_axi_arsize; - logic [1:0] m_axi_arburst; - logic m_axi_arlock; - logic [3:0] m_axi_arcache; - logic [2:0] m_axi_arprot; - logic [3:0] m_axi_arqos; - logic [3:0] m_axi_arregion; - logic [AXI_USER_WIDTH-1:0] m_axi_aruser; - logic m_axi_arvalid; - logic m_axi_arready; - - logic [AXI_ID_WIDTH-1:0] m_axi_rid; - logic [AXI_DATA_WIDTH-1:0] m_axi_rdata; - logic [1:0] m_axi_rresp; - logic m_axi_rlast; - logic [AXI_USER_WIDTH-1:0] m_axi_ruser; - logic m_axi_rvalid; - logic m_axi_rready; - - // Configuration visible to cocotb - logic read_enable; - logic write_enable; - logic write_abort; - - // -------------------------------------------------------------------------- - // Local interface instances. - // -------------------------------------------------------------------------- + wire rstn = ~rst; axis_if #( - .DATA_W (READ_DESC_AXIS_DATA_WIDTH), - .KEEP_W (READ_DESC_AXIS_KEEP_WIDTH), + .DATA_W (DESC_CMD_DATA_WIDTH), + .KEEP_W (DESC_CMD_KEEP_WIDTH), .ID_W (1), .DEST_W (1), .USER_W (1) - ) s_axis_read_desc_axis_if ( + ) desc_cmd_if ( .aclk (clk), .aresetn (rstn) ); axis_if #( - .DATA_W (READ_DESC_STATUS_AXIS_DATA_WIDTH), - .KEEP_W (READ_DESC_STATUS_AXIS_KEEP_WIDTH), + .DATA_W (DESC_STATUS_DATA_WIDTH), + .KEEP_W (DESC_STATUS_KEEP_WIDTH), .ID_W (1), .DEST_W (1), - .USER_W (1) - ) m_axis_read_desc_status_axis_if ( - .aclk (clk), - .aresetn (rstn) - ); - - axis_if #( - .DATA_W (WRITE_DESC_AXIS_DATA_WIDTH), - .KEEP_W (WRITE_DESC_AXIS_KEEP_WIDTH), - .ID_W (1), - .DEST_W (1), - .USER_W (1) - ) s_axis_write_desc_axis_if ( - .aclk (clk), - .aresetn (rstn) - ); - - axis_if #( - .DATA_W (WRITE_DESC_STATUS_AXIS_DATA_WIDTH), - .KEEP_W (WRITE_DESC_STATUS_AXIS_KEEP_WIDTH), - .ID_W (1), - .DEST_W (1), - .USER_W (1) - ) m_axis_write_desc_status_axis_if ( + .USER_W (DESC_STATUS_USER_WIDTH) + ) desc_status_if ( .aclk (clk), .aresetn (rstn) ); @@ -242,7 +171,7 @@ module tb_axi_dma_axis_compat #( .ID_W (AXIS_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH), .USER_W (AXIS_USER_WIDTH) - ) m_axis_read_data_if ( + ) read_data_if ( .aclk (clk), .aresetn (rstn) ); @@ -253,7 +182,7 @@ module tb_axi_dma_axis_compat #( .ID_W (AXIS_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH), .USER_W (AXIS_USER_WIDTH) - ) s_axis_write_data_if ( + ) write_data_if ( .aclk (clk), .aresetn (rstn) ); @@ -268,99 +197,52 @@ module tb_axi_dma_axis_compat #( .aresetn (rstn) ); - // -------------------------------------------------------------------------- - // Flat descriptor/status <-> AXIS descriptor/status packing. - // -------------------------------------------------------------------------- - - always_comb begin - s_axis_read_desc_axis_if.req.t.data = '0; - s_axis_read_desc_axis_if.req.t.keep = {READ_DESC_AXIS_KEEP_WIDTH{s_axis_read_desc_valid}}; - s_axis_read_desc_axis_if.req.t.strb = {READ_DESC_AXIS_KEEP_WIDTH{s_axis_read_desc_valid}}; - s_axis_read_desc_axis_if.req.t.last = s_axis_read_desc_valid; - s_axis_read_desc_axis_if.req.t.id = '0; - s_axis_read_desc_axis_if.req.t.dest = '0; - s_axis_read_desc_axis_if.req.t.user = '0; - s_axis_read_desc_axis_if.req.t.valid = s_axis_read_desc_valid; - - s_axis_read_desc_axis_if.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH] = s_axis_read_desc_addr; - s_axis_read_desc_axis_if.req.t.data[RD_LEN_LSB +: LEN_WIDTH] = s_axis_read_desc_len; - s_axis_read_desc_axis_if.req.t.data[RD_TAG_LSB +: TAG_WIDTH] = s_axis_read_desc_tag; - s_axis_read_desc_axis_if.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH] = s_axis_read_desc_id; - s_axis_read_desc_axis_if.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH] = s_axis_read_desc_dest; - s_axis_read_desc_axis_if.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH] = s_axis_read_desc_user; - end - - assign s_axis_read_desc_ready = s_axis_read_desc_axis_if.resp.ready; - - always_comb begin - m_axis_read_desc_status_tag = m_axis_read_desc_status_axis_if.req.t.data[RDS_TAG_LSB +: TAG_WIDTH]; - m_axis_read_desc_status_error = m_axis_read_desc_status_axis_if.req.t.data[RDS_ERROR_LSB +: 4]; - m_axis_read_desc_status_valid = m_axis_read_desc_status_axis_if.req.t.valid; - end - - assign m_axis_read_desc_status_axis_if.resp.ready = 1'b1; - - always_comb begin - s_axis_write_desc_axis_if.req.t.data = '0; - s_axis_write_desc_axis_if.req.t.keep = {WRITE_DESC_AXIS_KEEP_WIDTH{s_axis_write_desc_valid}}; - s_axis_write_desc_axis_if.req.t.strb = {WRITE_DESC_AXIS_KEEP_WIDTH{s_axis_write_desc_valid}}; - s_axis_write_desc_axis_if.req.t.last = s_axis_write_desc_valid; - s_axis_write_desc_axis_if.req.t.id = '0; - s_axis_write_desc_axis_if.req.t.dest = '0; - s_axis_write_desc_axis_if.req.t.user = '0; - s_axis_write_desc_axis_if.req.t.valid = s_axis_write_desc_valid; - - s_axis_write_desc_axis_if.req.t.data[WD_ADDR_LSB +: AXI_ADDR_WIDTH] = s_axis_write_desc_addr; - s_axis_write_desc_axis_if.req.t.data[WD_LEN_LSB +: LEN_WIDTH] = s_axis_write_desc_len; - s_axis_write_desc_axis_if.req.t.data[WD_TAG_LSB +: TAG_WIDTH] = s_axis_write_desc_tag; - end - - assign s_axis_write_desc_ready = s_axis_write_desc_axis_if.resp.ready; - - always_comb begin - m_axis_write_desc_status_len = m_axis_write_desc_status_axis_if.req.t.data[WDS_LEN_LSB +: LEN_WIDTH]; - m_axis_write_desc_status_tag = m_axis_write_desc_status_axis_if.req.t.data[WDS_TAG_LSB +: TAG_WIDTH]; - m_axis_write_desc_status_id = m_axis_write_desc_status_axis_if.req.t.data[WDS_ID_LSB +: AXIS_ID_WIDTH]; - m_axis_write_desc_status_dest = m_axis_write_desc_status_axis_if.req.t.data[WDS_DEST_LSB +: AXIS_DEST_WIDTH]; - m_axis_write_desc_status_user = m_axis_write_desc_status_axis_if.req.t.data[WDS_USER_LSB +: AXIS_USER_WIDTH]; - m_axis_write_desc_status_error = m_axis_write_desc_status_axis_if.req.t.data[WDS_ERROR_LSB +: 4]; - m_axis_write_desc_status_valid = m_axis_write_desc_status_axis_if.req.t.valid; - end - - assign m_axis_write_desc_status_axis_if.resp.ready = 1'b1; - - // -------------------------------------------------------------------------- - // Flat write stream -> interface. - // -------------------------------------------------------------------------- - axis_flat_to_if #( - .DATA_W (AXIS_DATA_WIDTH), - .KEEP_W (AXIS_KEEP_WIDTH), - .ID_W (AXIS_ID_WIDTH), - .DEST_W (AXIS_DEST_WIDTH), - .USER_W (AXIS_USER_WIDTH) - ) u_s_axis_write_data_flat_to_if ( - .s_axis_tdata (s_axis_write_data_tdata), - .s_axis_tkeep (s_axis_write_data_tkeep), - .s_axis_tstrb (s_axis_write_data_tstrb), - .s_axis_tlast (s_axis_write_data_tlast), - .s_axis_tid (s_axis_write_data_tid), - .s_axis_tdest (s_axis_write_data_tdest), - .s_axis_tuser (s_axis_write_data_tuser), - .s_axis_tvalid (s_axis_write_data_tvalid), - .s_axis_tready (s_axis_write_data_tready), - .m_axis (s_axis_write_data_if) + .DATA_W (DESC_CMD_DATA_WIDTH), + .KEEP_W (DESC_CMD_KEEP_WIDTH), + .ID_W (1), + .DEST_W (1), + .USER_W (1) + ) u_desc_cmd_flat_to_if ( + .s_axis_tdata (s_axis_desc_cmd_tdata), + .s_axis_tkeep (s_axis_desc_cmd_tkeep), + .s_axis_tstrb (s_axis_desc_cmd_tstrb), + .s_axis_tlast (s_axis_desc_cmd_tlast), + .s_axis_tid (s_axis_desc_cmd_tid), + .s_axis_tdest (s_axis_desc_cmd_tdest), + .s_axis_tuser (s_axis_desc_cmd_tuser), + .s_axis_tvalid (s_axis_desc_cmd_tvalid), + .s_axis_tready (s_axis_desc_cmd_tready), + .m_axis (desc_cmd_if) + ); + + axis_if_to_flat #( + .DATA_W (DESC_STATUS_DATA_WIDTH), + .KEEP_W (DESC_STATUS_KEEP_WIDTH), + .ID_W (1), + .DEST_W (1), + .USER_W (DESC_STATUS_USER_WIDTH) + ) u_desc_status_if_to_flat ( + .s_axis (desc_status_if), + .m_axis_tdata (m_axis_desc_status_tdata), + .m_axis_tkeep (m_axis_desc_status_tkeep), + .m_axis_tstrb (m_axis_desc_status_tstrb), + .m_axis_tlast (m_axis_desc_status_tlast), + .m_axis_tid (m_axis_desc_status_tid), + .m_axis_tdest (m_axis_desc_status_tdest), + .m_axis_tuser (m_axis_desc_status_tuser), + .m_axis_tvalid (m_axis_desc_status_tvalid), + .m_axis_tready (m_axis_desc_status_tready) ); - // Wrapper read stream interface -> flat stream. axis_if_to_flat #( .DATA_W (AXIS_DATA_WIDTH), .KEEP_W (AXIS_KEEP_WIDTH), .ID_W (AXIS_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH), .USER_W (AXIS_USER_WIDTH) - ) u_m_axis_read_data_if_to_flat ( - .s_axis (m_axis_read_data_if), + ) u_read_data_if_to_flat ( + .s_axis (read_data_if), .m_axis_tdata (m_axis_read_data_tdata), .m_axis_tkeep (m_axis_read_data_tkeep), .m_axis_tstrb (m_axis_read_data_tstrb), @@ -372,63 +254,23 @@ module tb_axi_dma_axis_compat #( .m_axis_tready (m_axis_read_data_tready) ); - // Wrapper AXI interface -> flat AXI memory bus. - axi4_if_to_flat #( - .ADDR_W (AXI_ADDR_WIDTH), - .DATA_W (AXI_DATA_WIDTH), - .ID_W (AXI_ID_WIDTH), - .USER_W (AXI_USER_WIDTH) - ) u_m_axi_if_to_flat ( - .s_axi (m_axi_if), - - .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_awregion (m_axi_awregion), - .m_axi_awuser (m_axi_awuser), - .m_axi_awvalid (m_axi_awvalid), - .m_axi_awready (m_axi_awready), - - .m_axi_wdata (m_axi_wdata), - .m_axi_wstrb (m_axi_wstrb), - .m_axi_wlast (m_axi_wlast), - .m_axi_wuser (m_axi_wuser), - .m_axi_wvalid (m_axi_wvalid), - .m_axi_wready (m_axi_wready), - - .m_axi_bid (m_axi_bid), - .m_axi_bresp (m_axi_bresp), - .m_axi_buser (m_axi_buser), - .m_axi_bvalid (m_axi_bvalid), - .m_axi_bready (m_axi_bready), - - .m_axi_arid (m_axi_arid), - .m_axi_araddr (m_axi_araddr), - .m_axi_arlen (m_axi_arlen), - .m_axi_arsize (m_axi_arsize), - .m_axi_arburst (m_axi_arburst), - .m_axi_arlock (m_axi_arlock), - .m_axi_arcache (m_axi_arcache), - .m_axi_arprot (m_axi_arprot), - .m_axi_arqos (m_axi_arqos), - .m_axi_arregion (m_axi_arregion), - .m_axi_aruser (m_axi_aruser), - .m_axi_arvalid (m_axi_arvalid), - .m_axi_arready (m_axi_arready), - - .m_axi_rid (m_axi_rid), - .m_axi_rdata (m_axi_rdata), - .m_axi_rresp (m_axi_rresp), - .m_axi_rlast (m_axi_rlast), - .m_axi_ruser (m_axi_ruser), - .m_axi_rvalid (m_axi_rvalid), - .m_axi_rready (m_axi_rready) + axis_flat_to_if #( + .DATA_W (AXIS_DATA_WIDTH), + .KEEP_W (AXIS_KEEP_WIDTH), + .ID_W (AXIS_ID_WIDTH), + .DEST_W (AXIS_DEST_WIDTH), + .USER_W (AXIS_USER_WIDTH) + ) u_write_data_flat_to_if ( + .s_axis_tdata (s_axis_write_data_tdata), + .s_axis_tkeep (s_axis_write_data_tkeep), + .s_axis_tstrb (s_axis_write_data_tstrb), + .s_axis_tlast (s_axis_write_data_tlast), + .s_axis_tid (s_axis_write_data_tid), + .s_axis_tdest (s_axis_write_data_tdest), + .s_axis_tuser (s_axis_write_data_tuser), + .s_axis_tvalid (s_axis_write_data_tvalid), + .s_axis_tready (s_axis_write_data_tready), + .m_axis (write_data_if) ); axi_dma_axis_compat #( @@ -451,25 +293,74 @@ module tb_axi_dma_axis_compat #( .LEN_WIDTH (LEN_WIDTH), .TAG_WIDTH (TAG_WIDTH), .ENABLE_SG (ENABLE_SG), - .ENABLE_UNALIGNED (ENABLE_UNALIGNED) + .ENABLE_UNALIGNED (ENABLE_UNALIGNED), + .DESC_CMD_DATA_WIDTH (DESC_CMD_DATA_WIDTH), + .DESC_STATUS_DATA_WIDTH (DESC_STATUS_DATA_WIDTH), + .DESC_STATUS_USER_WIDTH (DESC_STATUS_USER_WIDTH) ) u_dut ( - .clk (clk), - .rst (rst), + .clk (clk), + .rst (rst), + .s_axis_desc_cmd (desc_cmd_if), + .m_axis_desc_status (desc_status_if), + .m_axis_read_data (read_data_if), + .s_axis_write_data (write_data_if), + .m_axi (m_axi_if), + .read_enable (read_enable), + .write_enable (write_enable), + .write_abort (write_abort) + ); - .s_axis_read_desc (s_axis_read_desc_axis_if), - .m_axis_read_desc_status (m_axis_read_desc_status_axis_if), - - .m_axis_read_data (m_axis_read_data_if), - - .s_axis_write_desc (s_axis_write_desc_axis_if), - .m_axis_write_desc_status (m_axis_write_desc_status_axis_if), - - .s_axis_write_data (s_axis_write_data_if), - .m_axi (m_axi_if), - - .read_enable (read_enable), - .write_enable (write_enable), - .write_abort (write_abort) + axi4_if_to_flat #( + .ADDR_W (AXI_ADDR_WIDTH), + .DATA_W (AXI_DATA_WIDTH), + .ID_W (AXI_ID_WIDTH), + .USER_W (AXI_USER_WIDTH) + ) u_m_axi_if_to_flat ( + .s_axi (m_axi_if), + .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_awregion (m_axi_awregion), + .m_axi_awuser (m_axi_awuser), + .m_axi_awvalid (m_axi_awvalid), + .m_axi_awready (m_axi_awready), + .m_axi_wdata (m_axi_wdata), + .m_axi_wstrb (m_axi_wstrb), + .m_axi_wlast (m_axi_wlast), + .m_axi_wuser (m_axi_wuser), + .m_axi_wvalid (m_axi_wvalid), + .m_axi_wready (m_axi_wready), + .m_axi_bid (m_axi_bid), + .m_axi_bresp (m_axi_bresp), + .m_axi_buser (m_axi_buser), + .m_axi_bvalid (m_axi_bvalid), + .m_axi_bready (m_axi_bready), + .m_axi_arid (m_axi_arid), + .m_axi_araddr (m_axi_araddr), + .m_axi_arlen (m_axi_arlen), + .m_axi_arsize (m_axi_arsize), + .m_axi_arburst (m_axi_arburst), + .m_axi_arlock (m_axi_arlock), + .m_axi_arcache (m_axi_arcache), + .m_axi_arprot (m_axi_arprot), + .m_axi_arqos (m_axi_arqos), + .m_axi_arregion (m_axi_arregion), + .m_axi_aruser (m_axi_aruser), + .m_axi_arvalid (m_axi_arvalid), + .m_axi_arready (m_axi_arready), + .m_axi_rid (m_axi_rid), + .m_axi_rdata (m_axi_rdata), + .m_axi_rresp (m_axi_rresp), + .m_axi_rlast (m_axi_rlast), + .m_axi_ruser (m_axi_ruser), + .m_axi_rvalid (m_axi_rvalid), + .m_axi_rready (m_axi_rready) ); endmodule : tb_axi_dma_axis_compat diff --git a/axi/tb/axi_dma_axis_compat/test_axi_dma_axis_compat.py b/axi/tb/axi_dma_axis_compat/test_axi_dma_axis_compat.py index 6d65463..15c891c 100644 --- a/axi/tb/axi_dma_axis_compat/test_axi_dma_axis_compat.py +++ b/axi/tb/axi_dma_axis_compat/test_axi_dma_axis_compat.py @@ -1,33 +1,127 @@ -import itertools import logging import os import cocotb - -try: - import pytest -except ImportError: # pytest is only needed for the optional cocotb-test entrypoint - pytest = None from cocotb.clock import Clock -from cocotb.triggers import RisingEdge -from cocotb.regression import TestFactory +from cocotb.triggers import RisingEdge, Combine from cocotbext.axi import AxiBus, AxiRam from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink -from cocotbext.axi.stream import define_stream -DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream( - "Desc", - signals=["addr", "len", "tag", "valid", "ready"], - optional_signals=["id", "dest", "user"], -) +AXI_ADDR_WIDTH = int(os.getenv("PARAM_AXI_ADDR_WIDTH", "16")) +LEN_WIDTH = int(os.getenv("PARAM_LEN_WIDTH", "20")) +TAG_WIDTH = int(os.getenv("PARAM_TAG_WIDTH", "8")) +AXIS_ID_WIDTH = int(os.getenv("PARAM_AXIS_ID_WIDTH", "8")) +AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8")) +AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1")) -DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream( - "DescStatus", - signals=["tag", "error", "valid"], - optional_signals=["len", "id", "dest", "user"], -) +READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \ + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH +WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH +DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH +DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) // 8) * 8 +DESC_CMD_BYTES = DESC_CMD_DATA_WIDTH // 8 + +READ_STATUS_WIDTH = TAG_WIDTH + 4 +WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + \ + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4 +DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH +DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) // 8) * 8 +DESC_STATUS_BYTES = DESC_STATUS_DATA_WIDTH // 8 + +RD_ADDR_LSB = 0 +RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH +RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH +RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH +RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH +RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH + +WR_BASE_LSB = READ_DESC_WIDTH +WR_ADDR_LSB = WR_BASE_LSB +WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH +WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH + +RD_STS_TAG_LSB = 0 +RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH + +WR_STS_BASE_LSB = READ_STATUS_WIDTH +WR_STS_LEN_LSB = WR_STS_BASE_LSB +WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH +WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH +WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH +WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH +WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH + + +def mask(width): + return (1 << width) - 1 + + +def put(value, lsb, width, field): + value &= ~(mask(width) << lsb) + value |= (int(field) & mask(width)) << lsb + return value + + +def get(value, lsb, width): + return (int(value) >> lsb) & mask(width) + + +def pack_desc_cmd( + *, + read_addr=0, + read_len=0, + read_tag=0, + read_id=0, + read_dest=0, + read_user=0, + write_addr=0, + write_len=0, + write_tag=0, +): + value = 0 + value = put(value, RD_ADDR_LSB, AXI_ADDR_WIDTH, read_addr) + value = put(value, RD_LEN_LSB, LEN_WIDTH, read_len) + value = put(value, RD_TAG_LSB, TAG_WIDTH, read_tag) + value = put(value, RD_ID_LSB, AXIS_ID_WIDTH, read_id) + value = put(value, RD_DEST_LSB, AXIS_DEST_WIDTH, read_dest) + value = put(value, RD_USER_LSB, AXIS_USER_WIDTH, read_user) + + value = put(value, WR_ADDR_LSB, AXI_ADDR_WIDTH, write_addr) + value = put(value, WR_LEN_LSB, LEN_WIDTH, write_len) + value = put(value, WR_TAG_LSB, TAG_WIDTH, write_tag) + + return value.to_bytes(DESC_CMD_BYTES, byteorder="little") + + +def frame_to_int(frame): + return int.from_bytes(bytes(frame.tdata), byteorder="little") + + +def frame_tuser(frame): + try: + return int(frame.tuser) + except TypeError: + return int(frame.tuser[0]) if frame.tuser else 0 + + +def unpack_status(frame): + value = frame_to_int(frame) + tuser = frame_tuser(frame) + + return { + "read_valid": bool(tuser & 0x1), + "write_valid": bool(tuser & 0x2), + "read_tag": get(value, RD_STS_TAG_LSB, TAG_WIDTH), + "read_error": get(value, RD_STS_ERROR_LSB, 4), + "write_len": get(value, WR_STS_LEN_LSB, LEN_WIDTH), + "write_tag": get(value, WR_STS_TAG_LSB, TAG_WIDTH), + "write_id": get(value, WR_STS_ID_LSB, AXIS_ID_WIDTH), + "write_dest": get(value, WR_STS_DEST_LSB, AXIS_DEST_WIDTH), + "write_user": get(value, WR_STS_USER_LSB, AXIS_USER_WIDTH), + "write_error": get(value, WR_STS_ERROR_LSB, 4), + } class TB: @@ -38,25 +132,14 @@ class TB: cocotb.start_soon(Clock(dut.clk, 10, units="ns").start()) - # Cocotb still uses the original flat Forencich descriptor/status prefixes; - # the SV top packs/unpacks these into ordinary axis_if streams. - self.read_desc_source = DescSource( - DescBus.from_prefix(dut, "s_axis_read_desc"), dut.clk, dut.rst + self.desc_cmd_source = AxiStreamSource( + AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst ) - self.read_desc_status_sink = DescStatusSink( - DescStatusBus.from_prefix( - dut, "m_axis_read_desc_status"), dut.clk, dut.rst - ) - self.write_desc_source = DescSource( - DescBus.from_prefix(dut, "s_axis_write_desc"), dut.clk, dut.rst - ) - self.write_desc_status_sink = DescStatusSink( - DescStatusBus.from_prefix( - dut, "m_axis_write_desc_status"), dut.clk, dut.rst + self.desc_status_sink = AxiStreamSink( + AxiStreamBus.from_prefix( + dut, "m_axis_desc_status"), dut.clk, dut.rst ) - # Data streams are flat from cocotb's point of view, but the SV top - # sends them through axis_flat_to_if/axis_if_to_flat before/after DUT. self.read_data_sink = AxiStreamSink( AxiStreamBus.from_prefix(dut, "m_axis_read_data"), dut.clk, dut.rst ) @@ -65,7 +148,6 @@ class TB: dut, "s_axis_write_data"), dut.clk, dut.rst ) - # AXI memory model. The SV top routes this through the axi4_if adapters. self.axi_ram = AxiRam(AxiBus.from_prefix( dut, "m_axi"), dut.clk, dut.rst, size=2**16) @@ -73,22 +155,7 @@ class TB: dut.write_enable.setimmediatevalue(0) dut.write_abort.setimmediatevalue(0) - def set_idle_generator(self, generator=None): - if generator: - self.write_desc_source.set_pause_generator(generator()) - self.write_data_source.set_pause_generator(generator()) - self.read_desc_source.set_pause_generator(generator()) - self.axi_ram.write_if.b_channel.set_pause_generator(generator()) - self.axi_ram.read_if.r_channel.set_pause_generator(generator()) - - def set_backpressure_generator(self, generator=None): - if generator: - self.read_data_sink.set_pause_generator(generator()) - self.axi_ram.write_if.aw_channel.set_pause_generator(generator()) - self.axi_ram.write_if.w_channel.set_pause_generator(generator()) - self.axi_ram.read_if.ar_channel.set_pause_generator(generator()) - - async def cycle_reset(self): + async def reset(self): self.dut.rst.setimmediatevalue(0) await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk) @@ -99,264 +166,149 @@ class TB: await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk) + async def send_desc_cmd(self, payload): + await self.desc_cmd_source.send(AxiStreamFrame(payload)) -async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None): - """DMA write path stress test adapted from Forencich's axi_dma test.""" + +@cocotb.test() +async def test_axis_compat_write_path(dut): tb = TB(dut) - - byte_lanes = tb.axi_ram.write_if.byte_lanes - step_size = 1 if int( - os.getenv("PARAM_ENABLE_UNALIGNED", "0")) else byte_lanes - tag_count = 2 ** len(tb.write_desc_source.bus.tag) - cur_tag = 1 - - await tb.cycle_reset() - - tb.set_idle_generator(idle_inserter) - tb.set_backpressure_generator(backpressure_inserter) + await tb.reset() dut.write_enable.value = 1 + dut.read_enable.value = 0 - for length in list(range(1, byte_lanes * 4 + 1)) + [128]: - offsets = list(range(0, byte_lanes * 2, step_size)) - offsets += list(range(4096 - byte_lanes * 2, 4096, step_size)) + addr = 0x1000 + tag = 0x12 + data = bytes(range(1, 33)) - for offset in offsets: - for diff in [-8, -2, -1, 0, 1, 2, 8]: - if length + diff < 1: - continue + tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32)) - tb.log.info("write: length=%d offset=%d diff=%d", - length, offset, diff) + cmd = pack_desc_cmd(write_addr=addr, write_len=len(data), write_tag=tag) - addr = offset + 0x1000 - expected_data = bytearray([x % 256 for x in range(length)]) - stream_data = bytearray( - [x % 256 for x in range(length + diff)]) + cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) + data_task = cocotb.start_soon( + tb.write_data_source.send(AxiStreamFrame(data, tid=tag)) + ) - tb.axi_ram.write(addr - 128, b"\xaa" * - (len(expected_data) + 256)) + await cmd_task + await data_task - await tb.write_desc_source.send( - DescTransaction(addr=addr, len=len( - expected_data), tag=cur_tag) - ) - await tb.write_data_source.send(AxiStreamFrame(stream_data, tid=cur_tag)) + status_frame = await tb.desc_status_sink.recv() + status = unpack_status(status_frame) - status = await tb.write_desc_status_sink.recv() - tb.log.info("write status: %s", status) + assert not status["read_valid"] + assert status["write_valid"] + assert status["write_tag"] == tag + assert status["write_len"] == len(data) + assert status["write_id"] == tag + assert status["write_error"] == 0 - transferred_len = min(len(expected_data), len(stream_data)) - - assert int(status.len) == transferred_len - assert int(status.tag) == cur_tag - assert int(status.id) == cur_tag - assert int(status.error) == 0 - - tb.log.debug( - "%s", - tb.axi_ram.hexdump_str( - (addr & ~0xF) - 16, - (((addr & 0xF) + length - 1) & ~0xF) + 48, - ), - ) - - if len(expected_data) <= len(stream_data): - assert tb.axi_ram.read(addr - 8, len(expected_data) + 16) == ( - b"\xaa" * 8 + expected_data + b"\xaa" * 8 - ) - else: - assert tb.axi_ram.read(addr - 8, len(stream_data) + 16) == ( - b"\xaa" * 8 + stream_data + b"\xaa" * 8 - ) - - cur_tag = (cur_tag + 1) % tag_count - - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) + assert tb.axi_ram.read(addr - 8, len(data) + + 16) == b"\xaa" * 8 + data + b"\xaa" * 8 -async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None): - """DMA read path stress test adapted from Forencich's axi_dma test.""" +@cocotb.test() +async def test_axis_compat_read_path(dut): tb = TB(dut) + await tb.reset() - byte_lanes = tb.axi_ram.read_if.byte_lanes - step_size = 1 if int( - os.getenv("PARAM_ENABLE_UNALIGNED", "0")) else byte_lanes - tag_count = 2 ** len(tb.read_desc_source.bus.tag) - cur_tag = 1 - - await tb.cycle_reset() - - tb.set_idle_generator(idle_inserter) - tb.set_backpressure_generator(backpressure_inserter) - + dut.write_enable.value = 0 dut.read_enable.value = 1 - for length in list(range(1, byte_lanes * 4 + 1)) + [128]: - offsets = list(range(0, byte_lanes * 2, step_size)) - offsets += list(range(4096 - byte_lanes * 2, 4096, step_size)) + addr = 0x1100 + tag = 0x34 + data = bytes((x * 3) % 256 for x in range(48)) - for offset in offsets: - tb.log.info("read: length=%d offset=%d", length, offset) + tb.axi_ram.write(addr, data) - addr = offset + 0x1000 - test_data = bytearray([x % 256 for x in range(length)]) + cmd = pack_desc_cmd( + read_addr=addr, + read_len=len(data), + read_tag=tag, + read_id=tag, + read_dest=0, + read_user=0, + ) - tb.axi_ram.write(addr - 128, b"\xaa" * (len(test_data) + 256)) - tb.axi_ram.write(addr, test_data) + await tb.send_desc_cmd(cmd) - tb.log.debug( - "%s", - tb.axi_ram.hexdump_str( - (addr & ~0xF) - 16, - (((addr & 0xF) + length - 1) & ~0xF) + 48, - ), - ) + data_frame = await tb.read_data_sink.recv() + assert bytes(data_frame.tdata) == data + assert int(data_frame.tid) == tag - await tb.read_desc_source.send( - DescTransaction(addr=addr, len=len( - test_data), tag=cur_tag, id=cur_tag) - ) + status_frame = await tb.desc_status_sink.recv() + status = unpack_status(status_frame) - status = await tb.read_desc_status_sink.recv() - read_data = await tb.read_data_sink.recv() - - tb.log.info("read status: %s", status) - tb.log.info("read data: %s", read_data) - - assert int(status.tag) == cur_tag - assert int(status.error) == 0 - assert read_data.tdata == test_data - assert int(read_data.tid) == cur_tag - - cur_tag = (cur_tag + 1) % tag_count - - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) + assert status["read_valid"] + assert not status["write_valid"] + assert status["read_tag"] == tag + assert status["read_error"] == 0 -def cycle_pause(): - return itertools.cycle([1, 1, 1, 0]) +@cocotb.test() +async def test_axis_compat_read_and_write_same_command(dut): + tb = TB(dut) + await tb.reset() + dut.write_enable.value = 1 + dut.read_enable.value = 1 -# When imported by cocotb inside a simulator, generate the actual cocotb tests. -if cocotb.SIM_NAME: - for test in [run_test_write, run_test_read]: - factory = TestFactory(test) - factory.add_option("idle_inserter", [None, cycle_pause]) - factory.add_option("backpressure_inserter", [None, cycle_pause]) - factory.generate_tests() + read_addr = 0x1200 + write_addr = 0x1300 + read_tag = 0x41 + write_tag = 0x42 + read_data = bytes((x + 7) % 256 for x in range(24)) + write_data = bytes((x + 100) % 256 for x in range(24)) + tb.axi_ram.write(read_addr, read_data) + tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16)) -# ----------------------------------------------------------------------------- -# Optional pytest entrypoint via cocotb-test. -# Run from this directory with: pytest -q test_axi_dma_axis_compat.py -# ----------------------------------------------------------------------------- + cmd = pack_desc_cmd( + read_addr=read_addr, + read_len=len(read_data), + read_tag=read_tag, + read_id=read_tag, + write_addr=write_addr, + write_len=len(write_data), + write_tag=write_tag, + ) + cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) + data_task = cocotb.start_soon( + tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag)) + ) -def _sanitize_node_name(name): - return name.replace("[", "-").replace("]", "").replace("/", "_") + await cmd_task + await data_task + data_frame = await tb.read_data_sink.recv() + assert bytes(data_frame.tdata) == read_data + assert int(data_frame.tid) == read_tag -if pytest is not None: - @pytest.mark.parametrize("axi_data_width", [8, 16, 32]) - @pytest.mark.parametrize("unaligned", [0, 1]) - def test_axi_dma_axis_compat_pytest(request, axi_data_width, unaligned): - import cocotb_test.simulator + # status might be in 2 beats, depending on timings + seen_read = False + seen_write = False - tests_dir = os.path.abspath(os.path.dirname(__file__)) - project_root = os.path.abspath(os.path.join(tests_dir, "..", "..")) + for _ in range(2): + status_frame = await tb.desc_status_sink.recv() + status = unpack_status(status_frame) - axi_if_rtl_dir = os.environ.get( - "AXI_IF_RTL_DIR", os.path.join(project_root, "rtl", "axi") - ) - dma_if_rtl_dir = os.environ.get( - "DMA_IF_RTL_DIR", os.path.join(project_root, "rtl", "dma") - ) - wrapper_rtl_dir = os.environ.get( - "WRAPPER_RTL_DIR", os.path.join(project_root, "rtl", "wrappers") - ) - forencich_rtl_dir = os.environ.get( - "FORENCICH_AXI_RTL_DIR", - os.path.join(project_root, "external", "verilog-axi", "rtl"), - ) + if status["read_valid"]: + assert status["read_tag"] == read_tag + assert status["read_error"] == 0 + seen_read = True - dut = "tb_axi_dma_axis_compat" - module = os.path.splitext(os.path.basename(__file__))[0] - toplevel = dut + if status["write_valid"]: + assert status["write_tag"] == write_tag + assert status["write_len"] == len(write_data) + assert status["write_error"] == 0 + seen_write = True - parameters = { - "AXI_DATA_WIDTH": axi_data_width, - "AXI_ADDR_WIDTH": 16, - "AXI_ID_WIDTH": 8, - "AXI_USER_WIDTH": 1, - "AXI_MAX_BURST_LEN": 16, - "AXIS_ID_ENABLE": 1, - "AXIS_ID_WIDTH": 8, - "AXIS_DEST_ENABLE": 0, - "AXIS_DEST_WIDTH": 8, - "AXIS_USER_ENABLE": 1, - "AXIS_USER_WIDTH": 1, - "LEN_WIDTH": 20, - "TAG_WIDTH": 8, - "ENABLE_SG": 0, - "ENABLE_UNALIGNED": unaligned, - } + if seen_read and seen_write: + break - parameters["AXI_STRB_WIDTH"] = parameters["AXI_DATA_WIDTH"] // 8 - parameters["AXIS_DATA_WIDTH"] = parameters["AXI_DATA_WIDTH"] - parameters["AXIS_KEEP_ENABLE"] = int(parameters["AXIS_DATA_WIDTH"] > 8) - parameters["AXIS_KEEP_WIDTH"] = parameters["AXIS_DATA_WIDTH"] // 8 - parameters["AXIS_LAST_ENABLE"] = 1 - - verilog_sources = [ - os.path.join(axi_if_rtl_dir, "axi_pkg.sv"), - os.path.join(axi_if_rtl_dir, "axi_if.sv"), - os.path.join(axi_if_rtl_dir, "axis_if.sv"), - os.path.join(axi_if_rtl_dir, "axi4_flat_to_if.sv"), - os.path.join(axi_if_rtl_dir, "axi4_if_to_flat.sv"), - os.path.join(axi_if_rtl_dir, "axis_flat_to_if.sv"), - os.path.join(axi_if_rtl_dir, "axis_if_to_flat.sv"), - os.path.join(dma_if_rtl_dir, "axi_dma_desc_if.sv"), - os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_flat_to_if.sv"), - os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_if_to_flat.sv"), - os.path.join(dma_if_rtl_dir, - "axi_dma_read_desc_status_flat_to_if.sv"), - os.path.join(dma_if_rtl_dir, - "axi_dma_read_desc_status_if_to_flat.sv"), - os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_flat_to_if.sv"), - os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_if_to_flat.sv"), - os.path.join(dma_if_rtl_dir, - "axi_dma_write_desc_status_flat_to_if.sv"), - os.path.join(dma_if_rtl_dir, - "axi_dma_write_desc_status_if_to_flat.sv"), - os.path.join(forencich_rtl_dir, "axi_dma.v"), - os.path.join(forencich_rtl_dir, "axi_dma_rd.v"), - os.path.join(forencich_rtl_dir, "axi_dma_wr.v"), - os.path.join(wrapper_rtl_dir, "axi_dma_if_wrapper.sv"), - os.path.join(wrapper_rtl_dir, "axi_dma_axis_compat.sv"), - os.path.join(tests_dir, "tb_axi_dma_axis_compat.sv"), - ] - - extra_env = {f"PARAM_{k}": str(v) for k, v in parameters.items()} - sim_build = os.path.join( - tests_dir, "sim_build", _sanitize_node_name(request.node.name)) - - extra_args = [] - if os.getenv("SIM", "verilator") == "verilator": - extra_args += ["--trace-structs"] - - cocotb_test.simulator.run( - python_search=[tests_dir], - verilog_sources=verilog_sources, - includes=[axi_if_rtl_dir, dma_if_rtl_dir, - wrapper_rtl_dir, forencich_rtl_dir], - toplevel=toplevel, - module=module, - parameters=parameters, - sim_build=sim_build, - extra_env=extra_env, - waves=bool(int(os.getenv("WAVES", "0"))), - extra_args=extra_args, - ) + assert seen_read + assert seen_write + assert tb.axi_ram.read(write_addr - 8, len(write_data) + + 16) == b"\xaa" * 8 + write_data + b"\xaa" * 8