Compare commits

..

2 Commits

Author SHA1 Message Date
e4311214eb Merge pull request 'fix: axi dma wrapper' (#2) from dev into master
Reviewed-on: #2
2026-07-14 12:14:48 +03:00
a0a7137064 fix: axi dma wrapper 2026-07-10 18:59:50 +03:00
4 changed files with 318 additions and 252 deletions

View File

@ -271,7 +271,7 @@ module axi_dma_wrapper #(
// local read descriptor interface -> DMA flat input // local read descriptor interface -> DMA flat input
axis_if_to_flat #( axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH), .DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W ('0), .KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
@ -282,7 +282,7 @@ module axi_dma_wrapper #(
.m_axis_tstrb (), .m_axis_tstrb (),
.m_axis_tlast (), .m_axis_tlast (),
.m_axis_tid (dma_s_axis_read_desc_id), .m_axis_tid (dma_s_axis_read_desc_id),
.m_axis_tdest (dma_s_axis_read_desc_tdest), .m_axis_tdest (dma_s_axis_read_desc_dest),
.m_axis_tuser (dma_s_axis_read_desc_user), .m_axis_tuser (dma_s_axis_read_desc_user),
.m_axis_tvalid (dma_s_axis_read_desc_valid), .m_axis_tvalid (dma_s_axis_read_desc_valid),
.m_axis_tready (dma_s_axis_read_desc_ready) .m_axis_tready (dma_s_axis_read_desc_ready)
@ -291,10 +291,10 @@ module axi_dma_wrapper #(
// DMA read descriptor status flat output -> local status interface // DMA read descriptor status flat output -> local status interface
axis_flat_to_if #( axis_flat_to_if #(
.DATA_W (TAG_WIDTH + 4), .DATA_W (TAG_WIDTH + 4),
.KEEP_W ('0), .KEEP_W (AXIS_KEEP_WIDTH),
.ID_W ('0), .ID_W (AXIS_ID_WIDTH),
.DEST_W ('0), .DEST_W (AXIS_DEST_WIDTH),
.USER_W ('0) .USER_W (AXIS_USER_WIDTH)
) i_read_desc_status_f2i( ) i_read_desc_status_f2i(
.s_axis_tdata ({dma_m_axis_read_desc_status_tag, dma_m_axis_read_desc_status_error}), .s_axis_tdata ({dma_m_axis_read_desc_status_tag, dma_m_axis_read_desc_status_error}),
.s_axis_tkeep (), .s_axis_tkeep (),
@ -311,7 +311,7 @@ module axi_dma_wrapper #(
// local write descriptor interface -> DMA flat input // local write descriptor interface -> DMA flat input
axis_if_to_flat #( axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH), .DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W ('0), .KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
@ -321,9 +321,9 @@ module axi_dma_wrapper #(
.m_axis_tkeep (), .m_axis_tkeep (),
.m_axis_tstrb (), .m_axis_tstrb (),
.m_axis_tlast (), .m_axis_tlast (),
.m_axis_tid (dma_s_axis_write_desc_id), .m_axis_tid (),
.m_axis_tdest (dma_s_axis_write_desc_tdest), .m_axis_tdest (),
.m_axis_tuser (dma_s_axis_write_desc_user), .m_axis_tuser (),
.m_axis_tvalid (dma_s_axis_write_desc_valid), .m_axis_tvalid (dma_s_axis_write_desc_valid),
.m_axis_tready (dma_s_axis_write_desc_ready) .m_axis_tready (dma_s_axis_write_desc_ready)
); );
@ -332,7 +332,7 @@ module axi_dma_wrapper #(
// DMA write descriptor status flat output -> local status interface // DMA write descriptor status flat output -> local status interface
axis_flat_to_if #( axis_flat_to_if #(
.DATA_W (TAG_WIDTH + LEN_WIDTH + 4), .DATA_W (TAG_WIDTH + LEN_WIDTH + 4),
.KEEP_W ('0), .KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
@ -448,6 +448,6 @@ module axi_dma_wrapper #(
.m_axi (m_axi) .m_axi (m_axi)
); );
endmodule : axi_dma_if_wrapper endmodule : axi_dma_wrapper
`default_nettype wire `default_nettype wire

View File

@ -44,8 +44,8 @@ VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_rd.v
VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_wr.v VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_wr.v
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_if_wrapper.sv VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_wrapper.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_compat.sv
VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_axis_compat.sv VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_axis_compat.sv
COMPILE_ARGS += -I$(AXI_IF_RTL_DIR) COMPILE_ARGS += -I$(AXI_IF_RTL_DIR)

View File

@ -26,18 +26,14 @@ module tb_axi_dma_axis_compat #(
parameter int unsigned ENABLE_SG = 0, parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0, parameter int unsigned ENABLE_UNALIGNED = 0,
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 READ_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH, parameter int unsigned READ_DESC_DATA_WIDTH = ((READ_DESC_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH, parameter int unsigned WRITE_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8, parameter int unsigned WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8, parameter int unsigned READ_STATUS_RAW_WIDTH = TAG_WIDTH + 4,
parameter int unsigned READ_STATUS_DATA_WIDTH = ((READ_STATUS_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4, parameter int unsigned WRITE_STATUS_RAW_WIDTH = LEN_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 WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) / 8) * 8
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 clk,
input logic rst, input logic rst,
@ -46,27 +42,35 @@ module tb_axi_dma_axis_compat #(
input logic write_enable, input logic write_enable,
input logic write_abort, input logic write_abort,
// Unified descriptor command AXIS, flat for cocotb // Read descriptor AXIS input, flat for cocotb.
input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata, // tdata layout: {addr, len, tag}; id/dest/user use AXIS sidebands.
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tkeep, input logic [READ_DESC_DATA_WIDTH-1:0] s_axis_read_desc_tdata,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tstrb, input logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_tid,
input logic s_axis_desc_cmd_tlast, input logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_tdest,
input logic s_axis_desc_cmd_tid, input logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_tuser,
input logic s_axis_desc_cmd_tdest, input logic s_axis_read_desc_tvalid,
input logic s_axis_desc_cmd_tuser, output logic s_axis_read_desc_tready,
input logic s_axis_desc_cmd_tvalid,
output logic s_axis_desc_cmd_tready,
// Unified descriptor status AXIS, flat for cocotb // Read descriptor status AXIS output.
output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata, // tdata layout: {tag, error}.
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tkeep, output logic [READ_STATUS_DATA_WIDTH-1:0] m_axis_read_desc_status_tdata,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tstrb, output logic m_axis_read_desc_status_tvalid,
output logic m_axis_desc_status_tlast, input logic m_axis_read_desc_status_tready,
output logic m_axis_desc_status_tid,
output logic m_axis_desc_status_tdest, // Write descriptor AXIS input.
output logic [DESC_STATUS_USER_WIDTH-1:0] m_axis_desc_status_tuser, // tdata layout: {addr, len, tag}.
output logic m_axis_desc_status_tvalid, input logic [WRITE_DESC_DATA_WIDTH-1:0] s_axis_write_desc_tdata,
input logic m_axis_desc_status_tready, input logic s_axis_write_desc_tvalid,
output logic s_axis_write_desc_tready,
// Write descriptor status AXIS output.
// tdata layout: {len, tag, error}; id/dest/user use AXIS sidebands.
output logic [WRITE_STATUS_DATA_WIDTH-1:0] m_axis_write_desc_status_tdata,
output logic [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_tid,
output logic [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_tdest,
output logic [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_tuser,
output logic m_axis_write_desc_status_tvalid,
input logic m_axis_write_desc_status_tready,
// Read data AXIS output // Read data AXIS output
output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata,
@ -144,23 +148,45 @@ module tb_axi_dma_axis_compat #(
wire rstn = ~rst; wire rstn = ~rst;
axis_if #( axis_if #(
.DATA_W (DESC_CMD_DATA_WIDTH), .DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH), .KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (1), .ID_W (AXIS_ID_WIDTH),
.DEST_W (1), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (1) .USER_W (AXIS_USER_WIDTH)
) desc_cmd_if ( ) read_desc_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
axis_if #( axis_if #(
.DATA_W (DESC_STATUS_DATA_WIDTH), .DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH), .KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH) .USER_W (1)
) desc_status_if ( ) read_desc_status_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_DESC_DATA_WIDTH),
.KEEP_W (WRITE_DESC_DATA_WIDTH / 8),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) write_desc_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_STATUS_DATA_WIDTH),
.KEEP_W (WRITE_STATUS_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) write_desc_status_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
@ -198,41 +224,79 @@ module tb_axi_dma_axis_compat #(
); );
axis_flat_to_if #( axis_flat_to_if #(
.DATA_W (DESC_CMD_DATA_WIDTH), .DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH), .KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (1), .ID_W (AXIS_ID_WIDTH),
.DEST_W (1), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (1) .USER_W (AXIS_USER_WIDTH)
) u_desc_cmd_flat_to_if ( ) u_read_desc_flat_to_if (
.s_axis_tdata (s_axis_desc_cmd_tdata), .s_axis_tdata (s_axis_read_desc_tdata),
.s_axis_tkeep (s_axis_desc_cmd_tkeep), .s_axis_tkeep ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tstrb (s_axis_desc_cmd_tstrb), .s_axis_tstrb ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tlast (s_axis_desc_cmd_tlast), .s_axis_tlast (1'b1),
.s_axis_tid (s_axis_desc_cmd_tid), .s_axis_tid (s_axis_read_desc_tid),
.s_axis_tdest (s_axis_desc_cmd_tdest), .s_axis_tdest (s_axis_read_desc_tdest),
.s_axis_tuser (s_axis_desc_cmd_tuser), .s_axis_tuser (s_axis_read_desc_tuser),
.s_axis_tvalid (s_axis_desc_cmd_tvalid), .s_axis_tvalid (s_axis_read_desc_tvalid),
.s_axis_tready (s_axis_desc_cmd_tready), .s_axis_tready (s_axis_read_desc_tready),
.m_axis (desc_cmd_if) .m_axis (read_desc_if)
); );
axis_if_to_flat #( axis_if_to_flat #(
.DATA_W (DESC_STATUS_DATA_WIDTH), .DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH), .KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH) .USER_W (1)
) u_desc_status_if_to_flat ( ) u_read_desc_status_if_to_flat (
.s_axis (desc_status_if), .s_axis (read_desc_status_if),
.m_axis_tdata (m_axis_desc_status_tdata), .m_axis_tdata (m_axis_read_desc_status_tdata),
.m_axis_tkeep (m_axis_desc_status_tkeep), .m_axis_tkeep (),
.m_axis_tstrb (m_axis_desc_status_tstrb), .m_axis_tstrb (),
.m_axis_tlast (m_axis_desc_status_tlast), .m_axis_tlast (),
.m_axis_tid (m_axis_desc_status_tid), .m_axis_tid (),
.m_axis_tdest (m_axis_desc_status_tdest), .m_axis_tdest (),
.m_axis_tuser (m_axis_desc_status_tuser), .m_axis_tuser (),
.m_axis_tvalid (m_axis_desc_status_tvalid), .m_axis_tvalid (m_axis_read_desc_status_tvalid),
.m_axis_tready (m_axis_desc_status_tready) .m_axis_tready (m_axis_read_desc_status_tready)
);
axis_flat_to_if #(
.DATA_W (WRITE_DESC_DATA_WIDTH),
.KEEP_W (WRITE_DESC_DATA_WIDTH / 8),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) u_write_desc_flat_to_if (
.s_axis_tdata (s_axis_write_desc_tdata),
.s_axis_tkeep ({(WRITE_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tstrb ({(WRITE_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tlast (1'b1),
.s_axis_tid (1'b0),
.s_axis_tdest (1'b0),
.s_axis_tuser (1'b0),
.s_axis_tvalid (s_axis_write_desc_tvalid),
.s_axis_tready (s_axis_write_desc_tready),
.m_axis (write_desc_if)
);
axis_if_to_flat #(
.DATA_W (WRITE_STATUS_DATA_WIDTH),
.KEEP_W (WRITE_STATUS_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_write_desc_status_if_to_flat (
.s_axis (write_desc_status_if),
.m_axis_tdata (m_axis_write_desc_status_tdata),
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (m_axis_write_desc_status_tid),
.m_axis_tdest (m_axis_write_desc_status_tdest),
.m_axis_tuser (m_axis_write_desc_status_tuser),
.m_axis_tvalid (m_axis_write_desc_status_tvalid),
.m_axis_tready (m_axis_write_desc_status_tready)
); );
axis_if_to_flat #( axis_if_to_flat #(
@ -273,7 +337,7 @@ module tb_axi_dma_axis_compat #(
.m_axis (write_data_if) .m_axis (write_data_if)
); );
axi_dma_axis_compat #( axi_dma_wrapper #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH), .AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH), .AXI_STRB_WIDTH (AXI_STRB_WIDTH),
@ -293,16 +357,15 @@ module tb_axi_dma_axis_compat #(
.LEN_WIDTH (LEN_WIDTH), .LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH), .TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG), .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 ( ) u_dut (
.clk (clk), .clk (clk),
.rst (rst), .rst (rst),
.s_axis_desc_cmd (desc_cmd_if), .s_axis_read_desc (read_desc_if),
.m_axis_desc_status (desc_status_if), .m_axis_read_desc_status (read_desc_status_if),
.m_axis_read_data (read_data_if), .m_axis_read_data (read_data_if),
.s_axis_write_desc (write_desc_if),
.m_axis_write_desc_status (write_desc_status_if),
.s_axis_write_data (write_data_if), .s_axis_write_data (write_data_if),
.m_axi (m_axi_if), .m_axi (m_axi_if),
.read_enable (read_enable), .read_enable (read_enable),

View File

@ -3,7 +3,7 @@ import os
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Combine from cocotb.triggers import RisingEdge
from cocotbext.axi import AxiBus, AxiRam from cocotbext.axi import AxiBus, AxiRam
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
@ -16,42 +16,33 @@ AXIS_ID_WIDTH = int(os.getenv("PARAM_AXIS_ID_WIDTH", "8"))
AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8")) AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8"))
AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1")) AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1"))
READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \ READ_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH READ_DESC_DATA_WIDTH = ((READ_DESC_RAW_WIDTH + 7) // 8) * 8
WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH READ_DESC_BYTES = READ_DESC_DATA_WIDTH // 8
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_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + \ WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) // 8) * 8
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4 WRITE_DESC_BYTES = WRITE_DESC_DATA_WIDTH // 8
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 READ_STATUS_RAW_WIDTH = TAG_WIDTH + 4
RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH READ_STATUS_DATA_WIDTH = ((READ_STATUS_RAW_WIDTH + 7) // 8) * 8
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 WRITE_STATUS_RAW_WIDTH = LEN_WIDTH + TAG_WIDTH + 4
WR_ADDR_LSB = WR_BASE_LSB WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) // 8) * 8
WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH
WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH
RD_STS_TAG_LSB = 0 # axi_dma_wrapper maps descriptor tdata as {addr, len, tag}.
RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH DESC_TAG_LSB = 0
DESC_LEN_LSB = DESC_TAG_LSB + TAG_WIDTH
DESC_ADDR_LSB = DESC_LEN_LSB + LEN_WIDTH
WR_STS_BASE_LSB = READ_STATUS_WIDTH # Read status tdata is {tag, error}.
WR_STS_LEN_LSB = WR_STS_BASE_LSB READ_STATUS_ERROR_LSB = 0
WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH READ_STATUS_TAG_LSB = READ_STATUS_ERROR_LSB + 4
WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH
WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH # Write status tdata is {len, tag, error}.
WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH WRITE_STATUS_ERROR_LSB = 0
WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH WRITE_STATUS_TAG_LSB = WRITE_STATUS_ERROR_LSB + 4
WRITE_STATUS_LEN_LSB = WRITE_STATUS_TAG_LSB + TAG_WIDTH
def mask(width): def mask(width):
@ -68,59 +59,60 @@ def get(value, lsb, width):
return (int(value) >> lsb) & mask(width) return (int(value) >> lsb) & mask(width)
def pack_desc_cmd( def pack_read_desc(*, addr, length, tag, axis_id=0, dest=0, user=0):
*,
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 = 0
value = put(value, RD_ADDR_LSB, AXI_ADDR_WIDTH, read_addr) value = put(value, DESC_TAG_LSB, TAG_WIDTH, tag)
value = put(value, RD_LEN_LSB, LEN_WIDTH, read_len) value = put(value, DESC_LEN_LSB, LEN_WIDTH, length)
value = put(value, RD_TAG_LSB, TAG_WIDTH, read_tag) value = put(value, DESC_ADDR_LSB, AXI_ADDR_WIDTH, addr)
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) return AxiStreamFrame(
value = put(value, WR_LEN_LSB, LEN_WIDTH, write_len) value.to_bytes(READ_DESC_BYTES, byteorder="little"),
value = put(value, WR_TAG_LSB, TAG_WIDTH, write_tag) tid=axis_id,
tdest=dest,
tuser=user,
)
return value.to_bytes(DESC_CMD_BYTES, byteorder="little")
def pack_write_desc(*, addr, length, tag):
value = 0
value = put(value, DESC_TAG_LSB, TAG_WIDTH, tag)
value = put(value, DESC_LEN_LSB, LEN_WIDTH, length)
value = put(value, DESC_ADDR_LSB, AXI_ADDR_WIDTH, addr)
return AxiStreamFrame(
value.to_bytes(WRITE_DESC_BYTES, byteorder="little")
)
def frame_to_int(frame): def frame_to_int(frame):
return int.from_bytes(bytes(frame.tdata), byteorder="little") return int.from_bytes(bytes(frame.tdata), byteorder="little")
def frame_tuser(frame): def frame_sideband(frame, name):
value = getattr(frame, name)
try: try:
return int(frame.tuser) return int(value)
except TypeError: except TypeError:
return int(frame.tuser[0]) if frame.tuser else 0 return int(value[0]) if value else 0
def unpack_status(frame): def unpack_read_status(frame):
value = frame_to_int(frame) value = frame_to_int(frame)
tuser = frame_tuser(frame)
return { return {
"read_valid": bool(tuser & 0x1), "tag": get(value, READ_STATUS_TAG_LSB, TAG_WIDTH),
"write_valid": bool(tuser & 0x2), "error": get(value, READ_STATUS_ERROR_LSB, 4),
"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), def unpack_write_status(frame):
"write_id": get(value, WR_STS_ID_LSB, AXIS_ID_WIDTH), value = frame_to_int(frame)
"write_dest": get(value, WR_STS_DEST_LSB, AXIS_DEST_WIDTH), return {
"write_user": get(value, WR_STS_USER_LSB, AXIS_USER_WIDTH), "len": get(value, WRITE_STATUS_LEN_LSB, LEN_WIDTH),
"write_error": get(value, WR_STS_ERROR_LSB, 4), "tag": get(value, WRITE_STATUS_TAG_LSB, TAG_WIDTH),
"error": get(value, WRITE_STATUS_ERROR_LSB, 4),
"id": frame_sideband(frame, "tid"),
"dest": frame_sideband(frame, "tdest"),
"user": frame_sideband(frame, "tuser"),
} }
@ -132,24 +124,45 @@ class TB:
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start()) cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
self.desc_cmd_source = AxiStreamSource( self.read_desc_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst AxiStreamBus.from_prefix(dut, "s_axis_read_desc"),
dut.clk,
dut.rst,
) )
self.desc_status_sink = AxiStreamSink( self.read_desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix( AxiStreamBus.from_prefix(dut, "m_axis_read_desc_status"),
dut, "m_axis_desc_status"), dut.clk, dut.rst dut.clk,
dut.rst,
)
self.write_desc_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_write_desc"),
dut.clk,
dut.rst,
)
self.write_desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_write_desc_status"),
dut.clk,
dut.rst,
) )
self.read_data_sink = AxiStreamSink( self.read_data_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_read_data"), dut.clk, dut.rst AxiStreamBus.from_prefix(dut, "m_axis_read_data"),
dut.clk,
dut.rst,
) )
self.write_data_source = AxiStreamSource( self.write_data_source = AxiStreamSource(
AxiStreamBus.from_prefix( AxiStreamBus.from_prefix(dut, "s_axis_write_data"),
dut, "s_axis_write_data"), dut.clk, dut.rst dut.clk,
dut.rst,
) )
self.axi_ram = AxiRam(AxiBus.from_prefix( self.axi_ram = AxiRam(
dut, "m_axi"), dut.clk, dut.rst, size=2**16) AxiBus.from_prefix(dut, "m_axi"),
dut.clk,
dut.rst,
size=2**AXI_ADDR_WIDTH,
)
dut.read_enable.setimmediatevalue(0) dut.read_enable.setimmediatevalue(0)
dut.write_enable.setimmediatevalue(0) dut.write_enable.setimmediatevalue(0)
@ -166,8 +179,11 @@ class TB:
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
async def send_desc_cmd(self, payload): async def send_read_desc(self, **kwargs):
await self.desc_cmd_source.send(AxiStreamFrame(payload)) await self.read_desc_source.send(pack_read_desc(**kwargs))
async def send_write_desc(self, **kwargs):
await self.write_desc_source.send(pack_write_desc(**kwargs))
@cocotb.test() @cocotb.test()
@ -184,28 +200,26 @@ async def test_axis_compat_write_path(dut):
tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32)) tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32))
cmd = pack_desc_cmd(write_addr=addr, write_len=len(data), write_tag=tag) desc_task = cocotb.start_soon(
tb.send_write_desc(addr=addr, length=len(data), tag=tag)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) )
data_task = cocotb.start_soon( data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(data, tid=tag)) tb.write_data_source.send(AxiStreamFrame(data, tid=tag))
) )
await cmd_task await desc_task
await data_task await data_task
status_frame = await tb.desc_status_sink.recv() status = unpack_write_status(await tb.write_desc_status_sink.recv())
status = unpack_status(status_frame)
assert not status["read_valid"] assert status["tag"] == tag
assert status["write_valid"] assert status["len"] == len(data)
assert status["write_tag"] == tag assert status["id"] == tag
assert status["write_len"] == len(data) assert status["error"] == 0
assert status["write_id"] == tag
assert status["write_error"] == 0
assert tb.axi_ram.read(addr - 8, len(data) + assert tb.axi_ram.read(addr - 8, len(data) + 16) == (
16) == b"\xaa" * 8 + data + b"\xaa" * 8 b"\xaa" * 8 + data + b"\xaa" * 8
)
@cocotb.test() @cocotb.test()
@ -222,32 +236,26 @@ async def test_axis_compat_read_path(dut):
tb.axi_ram.write(addr, data) tb.axi_ram.write(addr, data)
cmd = pack_desc_cmd( await tb.send_read_desc(
read_addr=addr, addr=addr,
read_len=len(data), length=len(data),
read_tag=tag, tag=tag,
read_id=tag, axis_id=tag,
read_dest=0, dest=0,
read_user=0, user=0,
) )
await tb.send_desc_cmd(cmd)
data_frame = await tb.read_data_sink.recv() data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == data assert bytes(data_frame.tdata) == data
assert int(data_frame.tid) == tag assert frame_sideband(data_frame, "tid") == tag
status_frame = await tb.desc_status_sink.recv() status = unpack_read_status(await tb.read_desc_status_sink.recv())
status = unpack_status(status_frame) assert status["tag"] == tag
assert status["error"] == 0
assert status["read_valid"]
assert not status["write_valid"]
assert status["read_tag"] == tag
assert status["read_error"] == 0
@cocotb.test() @cocotb.test()
async def test_axis_compat_read_and_write_same_command(dut): async def test_axis_compat_read_and_write_independent_desc_ports(dut):
tb = TB(dut) tb = TB(dut)
await tb.reset() await tb.reset()
@ -264,51 +272,46 @@ async def test_axis_compat_read_and_write_same_command(dut):
tb.axi_ram.write(read_addr, read_data) tb.axi_ram.write(read_addr, read_data)
tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16)) tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16))
cmd = pack_desc_cmd( read_desc_task = cocotb.start_soon(
read_addr=read_addr, tb.send_read_desc(
read_len=len(read_data), addr=read_addr,
read_tag=read_tag, length=len(read_data),
read_id=read_tag, tag=read_tag,
write_addr=write_addr, axis_id=read_tag,
write_len=len(write_data), dest=0,
write_tag=write_tag, user=0,
) )
)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) write_desc_task = cocotb.start_soon(
data_task = cocotb.start_soon( tb.send_write_desc(
addr=write_addr,
length=len(write_data),
tag=write_tag,
)
)
write_data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag)) tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag))
) )
await cmd_task await read_desc_task
await data_task await write_desc_task
await write_data_task
data_frame = await tb.read_data_sink.recv() data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == read_data assert bytes(data_frame.tdata) == read_data
assert int(data_frame.tid) == read_tag assert frame_sideband(data_frame, "tid") == read_tag
# status might be in 2 beats, depending on timings read_status = unpack_read_status(await tb.read_desc_status_sink.recv())
seen_read = False write_status = unpack_write_status(await tb.write_desc_status_sink.recv())
seen_write = False
for _ in range(2): assert read_status["tag"] == read_tag
status_frame = await tb.desc_status_sink.recv() assert read_status["error"] == 0
status = unpack_status(status_frame)
if status["read_valid"]: assert write_status["tag"] == write_tag
assert status["read_tag"] == read_tag assert write_status["len"] == len(write_data)
assert status["read_error"] == 0 assert write_status["id"] == write_tag
seen_read = True assert write_status["error"] == 0
if status["write_valid"]: assert tb.axi_ram.read(write_addr - 8, len(write_data) + 16) == (
assert status["write_tag"] == write_tag b"\xaa" * 8 + write_data + b"\xaa" * 8
assert status["write_len"] == len(write_data) )
assert status["write_error"] == 0
seen_write = True
if seen_read and seen_write:
break
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