rtl: rework dma AXIS cmd&status

This commit is contained in:
Phil
2026-06-24 13:32:42 +03:00
parent 3cc1235eea
commit cf45646812
3 changed files with 660 additions and 831 deletions

View File

@ -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

View File

@ -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