Compare commits

21 Commits
master ... dev

Author SHA1 Message Date
c18233e16a chore: add some notes 2026-06-09 18:11:27 +03:00
fbfb869f00 tests: add test wrapper flat2if 2026-06-09 18:07:42 +03:00
4e8141d13f tests: use forencich test for DMA wrapper 2026-06-09 18:06:48 +03:00
a2e330d193 rtl: add dma simple wrapper 2026-06-09 18:06:14 +03:00
ea478fa0c7 fix: path in makefile 2026-06-09 18:05:55 +03:00
35ed5d6e6e tests: add dma test 2026-06-09 18:00:12 +03:00
f95f6b5b86 tests: add simple axis loopback module 2026-06-09 17:55:21 +03:00
fc89ab10c4 infra: add forencich repository as a submodule 2026-06-09 17:37:55 +03:00
30fa85d01c tests: add axis loopback test 2026-06-09 17:23:07 +03:00
7e7008ed87 tests: add wrapper for axis 2026-06-09 17:22:25 +03:00
52a2bccc77 rtl: add axis iface 2026-06-09 17:18:56 +03:00
43e2460124 docs: add README for tests 2026-06-09 16:00:58 +03:00
f1c760349f tests: add more cases for base test 2026-06-09 15:38:06 +03:00
6374dba26d infra: update gitignore 2026-06-09 15:30:07 +03:00
c0fb75e7c3 tests: add simple read/write test 2026-06-09 15:29:59 +03:00
bc91a8b3ea tb: add converters top file 2026-06-09 15:29:03 +03:00
563c1d3b69 tests: add simple loopback module 2026-06-09 15:07:36 +03:00
0c314bf2ae infra: add gitignore for sim builds 2026-06-09 14:59:49 +03:00
79ea1b3486 infra: add tb mock and test makefile 2026-06-09 14:57:16 +03:00
67f075b2e5 refactor: add rtl dir 2026-06-09 14:56:46 +03:00
e31436d909 fix: missing ; 2026-06-09 14:23:34 +03:00
32 changed files with 2000 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
**sim_build**
**pycache**

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "external/verilog-axi"]
path = external/verilog-axi
url = https://github.com/alexforencich/verilog-axi

View File

@ -0,0 +1,384 @@
// SPDX-License-Identifier: MIT
//
// Thin SystemVerilog interface wrapper around alexforencich/verilog-axi axi_dma.v.
//
// Policy used here:
// * AXI memory master is exposed as axi4_if.master.
// * AXI-Stream read/write data paths are exposed as axis_if master/slave.
// * Descriptor and status channels remain flat because they are DMA-specific
// command/status streams, not generic AXI-Stream data interfaces.
`default_nettype none
module axi_dma_if_wrapper #(
parameter int unsigned AXI_DATA_WIDTH = 32,
parameter int unsigned AXI_ADDR_WIDTH = 16,
parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8,
parameter int unsigned AXI_ID_WIDTH = 8,
parameter int unsigned AXI_USER_WIDTH = 1,
parameter int unsigned AXI_MAX_BURST_LEN = 16,
parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH,
parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8,
parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8,
parameter int unsigned AXIS_LAST_ENABLE = 1,
parameter int unsigned AXIS_ID_ENABLE = 1,
parameter int unsigned AXIS_ID_WIDTH = 8,
parameter int unsigned AXIS_DEST_ENABLE = 0,
parameter int unsigned AXIS_DEST_WIDTH = 8,
parameter int unsigned AXIS_USER_ENABLE = 1,
parameter int unsigned AXIS_USER_WIDTH = 1,
parameter int unsigned LEN_WIDTH = 20,
parameter int unsigned TAG_WIDTH = 8,
parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0
)(
input logic clk,
input logic rst,
/*
* AXI read descriptor input, kept flat intentionally.
*/
input logic [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_addr,
input logic [LEN_WIDTH-1:0] s_axis_read_desc_len,
input logic [TAG_WIDTH-1:0] s_axis_read_desc_tag,
input logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id,
input logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest,
input logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user,
input logic s_axis_read_desc_valid,
output logic s_axis_read_desc_ready,
/*
* AXI read descriptor status output, kept flat intentionally.
*/
output logic [TAG_WIDTH-1:0] m_axis_read_desc_status_tag,
output logic [3:0] m_axis_read_desc_status_error,
output logic m_axis_read_desc_status_valid,
/*
* AXI stream read data output.
*/
axis_if.master m_axis_read_data,
/*
* AXI write descriptor input, kept flat intentionally.
*/
input logic [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_addr,
input logic [LEN_WIDTH-1:0] s_axis_write_desc_len,
input logic [TAG_WIDTH-1:0] s_axis_write_desc_tag,
input logic s_axis_write_desc_valid,
output logic s_axis_write_desc_ready,
/*
* AXI write descriptor status output, kept flat intentionally.
*/
output logic [LEN_WIDTH-1:0] m_axis_write_desc_status_len,
output logic [TAG_WIDTH-1:0] m_axis_write_desc_status_tag,
output logic [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id,
output logic [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest,
output logic [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user,
output logic [3:0] m_axis_write_desc_status_error,
output logic m_axis_write_desc_status_valid,
/*
* 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
);
// --------------------------------------------------------------------------
// Flat wires connected to original alexforencich axi_dma.v
// --------------------------------------------------------------------------
logic [AXIS_DATA_WIDTH-1:0] dma_m_axis_read_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] dma_m_axis_read_data_tkeep;
logic dma_m_axis_read_data_tvalid;
logic dma_m_axis_read_data_tready;
logic dma_m_axis_read_data_tlast;
logic [AXIS_ID_WIDTH-1:0] dma_m_axis_read_data_tid;
logic [AXIS_DEST_WIDTH-1:0] dma_m_axis_read_data_tdest;
logic [AXIS_USER_WIDTH-1:0] dma_m_axis_read_data_tuser;
logic [AXIS_DATA_WIDTH-1:0] dma_s_axis_write_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] dma_s_axis_write_data_tkeep;
logic [AXIS_KEEP_WIDTH-1:0] unused_s_axis_write_data_tstrb;
logic dma_s_axis_write_data_tvalid;
logic dma_s_axis_write_data_tready;
logic dma_s_axis_write_data_tlast;
logic [AXIS_ID_WIDTH-1:0] dma_s_axis_write_data_tid;
logic [AXIS_DEST_WIDTH-1:0] dma_s_axis_write_data_tdest;
logic [AXIS_USER_WIDTH-1:0] dma_s_axis_write_data_tuser;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_awid;
logic [AXI_ADDR_WIDTH-1:0] dma_m_axi_awaddr;
logic [7:0] dma_m_axi_awlen;
logic [2:0] dma_m_axi_awsize;
logic [1:0] dma_m_axi_awburst;
logic dma_m_axi_awlock;
logic [3:0] dma_m_axi_awcache;
logic [2:0] dma_m_axi_awprot;
logic dma_m_axi_awvalid;
logic dma_m_axi_awready;
logic [AXI_DATA_WIDTH-1:0] dma_m_axi_wdata;
logic [AXI_STRB_WIDTH-1:0] dma_m_axi_wstrb;
logic dma_m_axi_wlast;
logic dma_m_axi_wvalid;
logic dma_m_axi_wready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_bid;
logic [1:0] dma_m_axi_bresp;
logic dma_m_axi_bvalid;
logic dma_m_axi_bready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_arid;
logic [AXI_ADDR_WIDTH-1:0] dma_m_axi_araddr;
logic [7:0] dma_m_axi_arlen;
logic [2:0] dma_m_axi_arsize;
logic [1:0] dma_m_axi_arburst;
logic dma_m_axi_arlock;
logic [3:0] dma_m_axi_arcache;
logic [2:0] dma_m_axi_arprot;
logic dma_m_axi_arvalid;
logic dma_m_axi_arready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_rid;
logic [AXI_DATA_WIDTH-1:0] dma_m_axi_rdata;
logic [1:0] dma_m_axi_rresp;
logic dma_m_axi_rlast;
logic dma_m_axi_rvalid;
logic dma_m_axi_rready;
logic [AXI_USER_WIDTH-1:0] unused_m_axi_buser;
logic [AXI_USER_WIDTH-1:0] unused_m_axi_ruser;
// Original DMA: flat ports only.
axi_dma #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
.AXIS_LAST_ENABLE (AXIS_LAST_ENABLE),
.AXIS_ID_ENABLE (AXIS_ID_ENABLE),
.AXIS_ID_WIDTH (AXIS_ID_WIDTH),
.AXIS_DEST_ENABLE (AXIS_DEST_ENABLE),
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
.AXIS_USER_ENABLE (AXIS_USER_ENABLE),
.AXIS_USER_WIDTH (AXIS_USER_WIDTH),
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
) u_axi_dma (
.clk (clk),
.rst (rst),
.s_axis_read_desc_addr (s_axis_read_desc_addr),
.s_axis_read_desc_len (s_axis_read_desc_len),
.s_axis_read_desc_tag (s_axis_read_desc_tag),
.s_axis_read_desc_id (s_axis_read_desc_id),
.s_axis_read_desc_dest (s_axis_read_desc_dest),
.s_axis_read_desc_user (s_axis_read_desc_user),
.s_axis_read_desc_valid (s_axis_read_desc_valid),
.s_axis_read_desc_ready (s_axis_read_desc_ready),
.m_axis_read_desc_status_tag (m_axis_read_desc_status_tag),
.m_axis_read_desc_status_error (m_axis_read_desc_status_error),
.m_axis_read_desc_status_valid (m_axis_read_desc_status_valid),
.m_axis_read_data_tdata (dma_m_axis_read_data_tdata),
.m_axis_read_data_tkeep (dma_m_axis_read_data_tkeep),
.m_axis_read_data_tvalid (dma_m_axis_read_data_tvalid),
.m_axis_read_data_tready (dma_m_axis_read_data_tready),
.m_axis_read_data_tlast (dma_m_axis_read_data_tlast),
.m_axis_read_data_tid (dma_m_axis_read_data_tid),
.m_axis_read_data_tdest (dma_m_axis_read_data_tdest),
.m_axis_read_data_tuser (dma_m_axis_read_data_tuser),
.s_axis_write_desc_addr (s_axis_write_desc_addr),
.s_axis_write_desc_len (s_axis_write_desc_len),
.s_axis_write_desc_tag (s_axis_write_desc_tag),
.s_axis_write_desc_valid (s_axis_write_desc_valid),
.s_axis_write_desc_ready (s_axis_write_desc_ready),
.m_axis_write_desc_status_len (m_axis_write_desc_status_len),
.m_axis_write_desc_status_tag (m_axis_write_desc_status_tag),
.m_axis_write_desc_status_id (m_axis_write_desc_status_id),
.m_axis_write_desc_status_dest (m_axis_write_desc_status_dest),
.m_axis_write_desc_status_user (m_axis_write_desc_status_user),
.m_axis_write_desc_status_error (m_axis_write_desc_status_error),
.m_axis_write_desc_status_valid (m_axis_write_desc_status_valid),
.s_axis_write_data_tdata (dma_s_axis_write_data_tdata),
.s_axis_write_data_tkeep (dma_s_axis_write_data_tkeep),
.s_axis_write_data_tvalid (dma_s_axis_write_data_tvalid),
.s_axis_write_data_tready (dma_s_axis_write_data_tready),
.s_axis_write_data_tlast (dma_s_axis_write_data_tlast),
.s_axis_write_data_tid (dma_s_axis_write_data_tid),
.s_axis_write_data_tdest (dma_s_axis_write_data_tdest),
.s_axis_write_data_tuser (dma_s_axis_write_data_tuser),
.m_axi_awid (dma_m_axi_awid),
.m_axi_awaddr (dma_m_axi_awaddr),
.m_axi_awlen (dma_m_axi_awlen),
.m_axi_awsize (dma_m_axi_awsize),
.m_axi_awburst (dma_m_axi_awburst),
.m_axi_awlock (dma_m_axi_awlock),
.m_axi_awcache (dma_m_axi_awcache),
.m_axi_awprot (dma_m_axi_awprot),
.m_axi_awvalid (dma_m_axi_awvalid),
.m_axi_awready (dma_m_axi_awready),
.m_axi_wdata (dma_m_axi_wdata),
.m_axi_wstrb (dma_m_axi_wstrb),
.m_axi_wlast (dma_m_axi_wlast),
.m_axi_wvalid (dma_m_axi_wvalid),
.m_axi_wready (dma_m_axi_wready),
.m_axi_bid (dma_m_axi_bid),
.m_axi_bresp (dma_m_axi_bresp),
.m_axi_bvalid (dma_m_axi_bvalid),
.m_axi_bready (dma_m_axi_bready),
.m_axi_arid (dma_m_axi_arid),
.m_axi_araddr (dma_m_axi_araddr),
.m_axi_arlen (dma_m_axi_arlen),
.m_axi_arsize (dma_m_axi_arsize),
.m_axi_arburst (dma_m_axi_arburst),
.m_axi_arlock (dma_m_axi_arlock),
.m_axi_arcache (dma_m_axi_arcache),
.m_axi_arprot (dma_m_axi_arprot),
.m_axi_arvalid (dma_m_axi_arvalid),
.m_axi_arready (dma_m_axi_arready),
.m_axi_rid (dma_m_axi_rid),
.m_axi_rdata (dma_m_axi_rdata),
.m_axi_rresp (dma_m_axi_rresp),
.m_axi_rlast (dma_m_axi_rlast),
.m_axi_rvalid (dma_m_axi_rvalid),
.m_axi_rready (dma_m_axi_rready),
.read_enable (read_enable),
.write_enable (write_enable),
.write_abort (write_abort)
);
// DMA read data flat output -> local axis_if.master
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_m_axis_read_data_flat_to_if (
.s_axis_tdata (dma_m_axis_read_data_tdata),
.s_axis_tkeep (dma_m_axis_read_data_tkeep),
.s_axis_tstrb (dma_m_axis_read_data_tkeep), // axi_dma has no tstrb; mirror tkeep
.s_axis_tlast (dma_m_axis_read_data_tlast),
.s_axis_tid (dma_m_axis_read_data_tid),
.s_axis_tdest (dma_m_axis_read_data_tdest),
.s_axis_tuser (dma_m_axis_read_data_tuser),
.s_axis_tvalid (dma_m_axis_read_data_tvalid),
.s_axis_tready (dma_m_axis_read_data_tready),
.m_axis (m_axis_read_data)
);
// local axis_if.slave -> DMA write data flat input
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_s_axis_write_data_if_to_flat (
.s_axis (s_axis_write_data),
.m_axis_tdata (dma_s_axis_write_data_tdata),
.m_axis_tkeep (dma_s_axis_write_data_tkeep),
.m_axis_tstrb (unused_s_axis_write_data_tstrb),
.m_axis_tlast (dma_s_axis_write_data_tlast),
.m_axis_tid (dma_s_axis_write_data_tid),
.m_axis_tdest (dma_s_axis_write_data_tdest),
.m_axis_tuser (dma_s_axis_write_data_tuser),
.m_axis_tvalid(dma_s_axis_write_data_tvalid),
.m_axis_tready(dma_s_axis_write_data_tready)
);
// DMA AXI master flat output -> local axi4_if.master
axi4_flat_to_if #(
.ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
) u_m_axi_flat_to_if (
.s_axi_awid (dma_m_axi_awid),
.s_axi_awaddr (dma_m_axi_awaddr),
.s_axi_awlen (dma_m_axi_awlen),
.s_axi_awsize (dma_m_axi_awsize),
.s_axi_awburst (dma_m_axi_awburst),
.s_axi_awlock (dma_m_axi_awlock),
.s_axi_awcache (dma_m_axi_awcache),
.s_axi_awprot (dma_m_axi_awprot),
.s_axi_awqos (4'd0),
.s_axi_awregion (4'd0),
.s_axi_awuser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_awvalid (dma_m_axi_awvalid),
.s_axi_awready (dma_m_axi_awready),
.s_axi_wdata (dma_m_axi_wdata),
.s_axi_wstrb (dma_m_axi_wstrb),
.s_axi_wlast (dma_m_axi_wlast),
.s_axi_wuser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_wvalid (dma_m_axi_wvalid),
.s_axi_wready (dma_m_axi_wready),
.s_axi_bid (dma_m_axi_bid),
.s_axi_bresp (dma_m_axi_bresp),
.s_axi_buser (unused_m_axi_buser),
.s_axi_bvalid (dma_m_axi_bvalid),
.s_axi_bready (dma_m_axi_bready),
.s_axi_arid (dma_m_axi_arid),
.s_axi_araddr (dma_m_axi_araddr),
.s_axi_arlen (dma_m_axi_arlen),
.s_axi_arsize (dma_m_axi_arsize),
.s_axi_arburst (dma_m_axi_arburst),
.s_axi_arlock (dma_m_axi_arlock),
.s_axi_arcache (dma_m_axi_arcache),
.s_axi_arprot (dma_m_axi_arprot),
.s_axi_arqos (4'd0),
.s_axi_arregion (4'd0),
.s_axi_aruser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_arvalid (dma_m_axi_arvalid),
.s_axi_arready (dma_m_axi_arready),
.s_axi_rid (dma_m_axi_rid),
.s_axi_rdata (dma_m_axi_rdata),
.s_axi_rresp (dma_m_axi_rresp),
.s_axi_rlast (dma_m_axi_rlast),
.s_axi_ruser (unused_m_axi_ruser),
.s_axi_rvalid (dma_m_axi_rvalid),
.s_axi_rready (dma_m_axi_rready),
.m_axi (m_axi)
);
endmodule : axi_dma_if_wrapper
`default_nettype wire

View File

@ -92,7 +92,7 @@ endpackage : axi_pkg
__name``_b_chan_t b; \
logic ar_ready; \
__name``_r_chan_t r; \
} __name``_resp_t
} __name``_resp_t;
`define AXI4L_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t, __user_t) \
typedef struct packed { \
@ -137,4 +137,4 @@ endpackage : axi_pkg
__name``_b_chan_t b; \
logic ar_ready; \
__name``_r_chan_t r; \
} __name``_resp_t
} __name``_resp_t;

View File

@ -0,0 +1,30 @@
module axis_flat_to_if #(
parameter int unsigned DATA_W = 64,
parameter int unsigned KEEP_W = DATA_W / 8,
parameter int unsigned ID_W = 8,
parameter int unsigned DEST_W = 8,
parameter int unsigned USER_W = 1
)(
input logic [DATA_W-1:0] s_axis_tdata,
input logic [KEEP_W-1:0] s_axis_tkeep,
input logic [KEEP_W-1:0] s_axis_tstrb,
input logic s_axis_tlast,
input logic [ID_W-1:0] s_axis_tid,
input logic [DEST_W-1:0] s_axis_tdest,
input logic [USER_W-1:0] s_axis_tuser,
input logic s_axis_tvalid,
output logic s_axis_tready,
axis_if.master m_axis
);
assign m_axis.req.t.data = s_axis_tdata;
assign m_axis.req.t.keep = s_axis_tkeep;
assign m_axis.req.t.strb = s_axis_tstrb;
assign m_axis.req.t.last = s_axis_tlast;
assign m_axis.req.t.id = s_axis_tid;
assign m_axis.req.t.dest = s_axis_tdest;
assign m_axis.req.t.user = s_axis_tuser;
assign m_axis.req.t.valid = s_axis_tvalid;
assign s_axis_tready = m_axis.resp.ready;
endmodule : axis_flat_to_if

47
axi/rtl/axis_if.sv Normal file
View File

@ -0,0 +1,47 @@
`define AXIS_TYPEDEF_ALL(__name, __data_t, __keep_t, __strb_t, __id_t, __dest_t, __user_t) \
typedef struct packed { \
__data_t data; \
__keep_t keep; \
__strb_t strb; \
logic last; \
__id_t id; \
__dest_t dest; \
__user_t user; \
logic valid; \
} __name``_chan_t; \
typedef struct packed { \
__name``_chan_t t; \
} __name``_req_t; \
typedef struct packed { \
logic ready; \
} __name``_resp_t;
interface axis_if #(
parameter int unsigned DATA_W = 64,
parameter int unsigned KEEP_W = DATA_W / 8,
parameter int unsigned ID_W = 8,
parameter int unsigned DEST_W = 8,
parameter int unsigned USER_W = 1
)(
input logic aclk,
input logic aresetn
);
typedef logic [DATA_W-1:0] data_t;
typedef logic [KEEP_W-1:0] keep_t;
typedef logic [KEEP_W-1:0] strb_t;
typedef logic [ID_W-1:0] id_t;
typedef logic [DEST_W-1:0] dest_t;
typedef logic [USER_W-1:0] user_t;
`AXIS_TYPEDEF_ALL(axis, data_t, keep_t, strb_t, id_t, dest_t, user_t)
axis_req_t req;
axis_resp_t resp;
modport master (input aclk, aresetn, output req, input resp);
modport slave (input aclk, aresetn, input req, output resp);
modport monitor (input aclk, aresetn, input req, input resp);
endinterface : axis_if

View File

@ -0,0 +1,30 @@
module axis_if_to_flat #(
parameter int unsigned DATA_W = 64,
parameter int unsigned KEEP_W = DATA_W / 8,
parameter int unsigned ID_W = 8,
parameter int unsigned DEST_W = 8,
parameter int unsigned USER_W = 1
)(
axis_if.slave s_axis,
output logic [DATA_W-1:0] m_axis_tdata,
output logic [KEEP_W-1:0] m_axis_tkeep,
output logic [KEEP_W-1:0] m_axis_tstrb,
output logic m_axis_tlast,
output logic [ID_W-1:0] m_axis_tid,
output logic [DEST_W-1:0] m_axis_tdest,
output logic [USER_W-1:0] m_axis_tuser,
output logic m_axis_tvalid,
input logic m_axis_tready
);
assign m_axis_tdata = s_axis.req.t.data;
assign m_axis_tkeep = s_axis.req.t.keep;
assign m_axis_tstrb = s_axis.req.t.strb;
assign m_axis_tlast = s_axis.req.t.last;
assign m_axis_tid = s_axis.req.t.id;
assign m_axis_tdest = s_axis.req.t.dest;
assign m_axis_tuser = s_axis.req.t.user;
assign m_axis_tvalid = s_axis.req.t.valid;
assign s_axis.resp.ready = m_axis_tready;
endmodule : axis_if_to_flat

View File

@ -0,0 +1,32 @@
# Minimal cocotb + cocotbext-axi test for compact AXI interface loopback.
# Run:
# make SIM=verilator
# or:
# make SIM=questa
TOPLEVEL_LANG = verilog
SIM ?= verilator
PWD := $(shell pwd)
RTL_DIR = $(PWD)/../../rtl
VERILOG_SOURCES += $(RTL_DIR)/axi_pkg.sv
VERILOG_SOURCES += $(RTL_DIR)/axi_if.sv
VERILOG_SOURCES += $(RTL_DIR)/axi4_flat_to_if.sv
VERILOG_SOURCES += $(RTL_DIR)/axi4_if_to_flat.sv
VERILOG_SOURCES += $(PWD)/tb_axi4_loopback.sv
VERILOG_SOURCES += $(PWD)/axi4_loopback.sv
TOPLEVEL = tb_axi4_loopback
MODULE = test_axi4_loopback
ifeq ($(SIM),verilator)
EXTRA_ARGS += --trace --trace-structs
EXTRA_ARGS += -I$(PWD)/rtl
COMPILE_ARGS += -Wno-fatal
COMPILE_ARGS += -I$(PWD)/rtl
endif
include $(shell cocotb-config --makefiles)/Makefile.sim

View File

@ -0,0 +1,21 @@
# AXI compact interface cocotb loopback test
Тест интерфейсов и конвертеров flat-if.
```text
cocotb AxiMaster
-> flat AXI signals
-> axi4_flat_to_if
-> compact axi4_if
-> axi4_loopback DUT
-> compact axi4_if
-> axi4_if_to_flat
-> flat AXI signals
-> cocotb AxiRam
```
Зависимости
```bash
pip install cocotb cocotbext-axi
```

View File

@ -0,0 +1,17 @@
`timescale 1ns/1ps
module axi4_loopback #(
parameter int unsigned ADDR_W = 32,
parameter int unsigned DATA_W = 64,
parameter int unsigned ID_W = 4,
parameter int unsigned USER_W = 1
)(
axi4_if.slave s_axi,
axi4_if.master m_axi
);
// compact loopback/pasthrough to test ifaces
assign m_axi.req = s_axi.req;
assign s_axi.resp = m_axi.resp;
endmodule

View File

@ -0,0 +1,252 @@
`timescale 1ns/1ps
module tb_axi4_loopback #(
parameter int unsigned ADDR_W = 32,
parameter int unsigned DATA_W = 64,
parameter int unsigned ID_W = 4,
parameter int unsigned USER_W = 1
)(
input logic clk,
input logic rst,
// slave-side flat AXI port (cocotb driven)
input logic [ID_W-1:0] s_axi_awid,
input logic [ADDR_W-1:0] s_axi_awaddr,
input logic [7:0] s_axi_awlen,
input logic [2:0] s_axi_awsize,
input logic [1:0] s_axi_awburst,
input logic s_axi_awlock,
input logic [3:0] s_axi_awcache,
input logic [2:0] s_axi_awprot,
input logic [3:0] s_axi_awqos,
input logic [3:0] s_axi_awregion,
input logic [USER_W-1:0] s_axi_awuser,
input logic s_axi_awvalid,
output logic s_axi_awready,
input logic [DATA_W-1:0] s_axi_wdata,
input logic [DATA_W/8-1:0] s_axi_wstrb,
input logic s_axi_wlast,
input logic [USER_W-1:0] s_axi_wuser,
input logic s_axi_wvalid,
output logic s_axi_wready,
output logic [ID_W-1:0] s_axi_bid,
output logic [1:0] s_axi_bresp,
output logic [USER_W-1:0] s_axi_buser,
output logic s_axi_bvalid,
input logic s_axi_bready,
input logic [ID_W-1:0] s_axi_arid,
input logic [ADDR_W-1:0] s_axi_araddr,
input logic [7:0] s_axi_arlen,
input logic [2:0] s_axi_arsize,
input logic [1:0] s_axi_arburst,
input logic s_axi_arlock,
input logic [3:0] s_axi_arcache,
input logic [2:0] s_axi_arprot,
input logic [3:0] s_axi_arqos,
input logic [3:0] s_axi_arregion,
input logic [USER_W-1:0] s_axi_aruser,
input logic s_axi_arvalid,
output logic s_axi_arready,
output logic [ID_W-1:0] s_axi_rid,
output logic [DATA_W-1:0] s_axi_rdata,
output logic [1:0] s_axi_rresp,
output logic s_axi_rlast,
output logic [USER_W-1:0] s_axi_ruser,
output logic s_axi_rvalid,
input logic s_axi_rready,
// master-side flat AXI port for coco-tb
output logic [ID_W-1:0] m_axi_awid,
output logic [ADDR_W-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 [USER_W-1:0] m_axi_awuser,
output logic m_axi_awvalid,
input logic m_axi_awready,
output logic [DATA_W-1:0] m_axi_wdata,
output logic [DATA_W/8-1:0] m_axi_wstrb,
output logic m_axi_wlast,
output logic [USER_W-1:0] m_axi_wuser,
output logic m_axi_wvalid,
input logic m_axi_wready,
input logic [ID_W-1:0] m_axi_bid,
input logic [1:0] m_axi_bresp,
input logic [USER_W-1:0] m_axi_buser,
input logic m_axi_bvalid,
output logic m_axi_bready,
output logic [ID_W-1:0] m_axi_arid,
output logic [ADDR_W-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 [USER_W-1:0] m_axi_aruser,
output logic m_axi_arvalid,
input logic m_axi_arready,
input logic [ID_W-1:0] m_axi_rid,
input logic [DATA_W-1:0] m_axi_rdata,
input logic [1:0] m_axi_rresp,
input logic m_axi_rlast,
input logic [USER_W-1:0] m_axi_ruser,
input logic m_axi_rvalid,
output logic m_axi_rready
);
logic aresetn;
assign aresetn = ~rst;
axi4_if #(
.ADDR_W(ADDR_W),
.DATA_W(DATA_W),
.ID_W(ID_W),
.USER_W(USER_W)
) axi_in (
.aclk(clk),
.aresetn(aresetn)
);
axi4_if #(
.ADDR_W(ADDR_W),
.DATA_W(DATA_W),
.ID_W(ID_W),
.USER_W(USER_W)
) axi_out (
.aclk(clk),
.aresetn(aresetn)
);
axi4_flat_to_if #(
.ADDR_W(ADDR_W),
.DATA_W(DATA_W),
.ID_W(ID_W),
.USER_W(USER_W)
) u_flat_to_if (
.s_axi_awid (s_axi_awid),
.s_axi_awaddr (s_axi_awaddr),
.s_axi_awlen (s_axi_awlen),
.s_axi_awsize (s_axi_awsize),
.s_axi_awburst (s_axi_awburst),
.s_axi_awlock (s_axi_awlock),
.s_axi_awcache (s_axi_awcache),
.s_axi_awprot (s_axi_awprot),
.s_axi_awqos (s_axi_awqos),
.s_axi_awregion (s_axi_awregion),
.s_axi_awuser (s_axi_awuser),
.s_axi_awvalid (s_axi_awvalid),
.s_axi_awready (s_axi_awready),
.s_axi_wdata (s_axi_wdata),
.s_axi_wstrb (s_axi_wstrb),
.s_axi_wlast (s_axi_wlast),
.s_axi_wuser (s_axi_wuser),
.s_axi_wvalid (s_axi_wvalid),
.s_axi_wready (s_axi_wready),
.s_axi_bid (s_axi_bid),
.s_axi_bresp (s_axi_bresp),
.s_axi_buser (s_axi_buser),
.s_axi_bvalid (s_axi_bvalid),
.s_axi_bready (s_axi_bready),
.s_axi_arid (s_axi_arid),
.s_axi_araddr (s_axi_araddr),
.s_axi_arlen (s_axi_arlen),
.s_axi_arsize (s_axi_arsize),
.s_axi_arburst (s_axi_arburst),
.s_axi_arlock (s_axi_arlock),
.s_axi_arcache (s_axi_arcache),
.s_axi_arprot (s_axi_arprot),
.s_axi_arqos (s_axi_arqos),
.s_axi_arregion (s_axi_arregion),
.s_axi_aruser (s_axi_aruser),
.s_axi_arvalid (s_axi_arvalid),
.s_axi_arready (s_axi_arready),
.s_axi_rid (s_axi_rid),
.s_axi_rdata (s_axi_rdata),
.s_axi_rresp (s_axi_rresp),
.s_axi_rlast (s_axi_rlast),
.s_axi_ruser (s_axi_ruser),
.s_axi_rvalid (s_axi_rvalid),
.s_axi_rready (s_axi_rready),
.m_axi (axi_in)
);
axi4_loopback #(
.ADDR_W(ADDR_W),
.DATA_W(DATA_W),
.ID_W(ID_W),
.USER_W(USER_W)
) dut (
.s_axi(axi_in),
.m_axi(axi_out)
);
axi4_if_to_flat #(
.ADDR_W(ADDR_W),
.DATA_W(DATA_W),
.ID_W(ID_W),
.USER_W(USER_W)
) u_if_to_flat (
.s_axi (axi_out),
.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

View File

@ -0,0 +1,104 @@
import itertools
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Timer
from cocotbext.axi import AxiBus, AxiMaster, AxiRam
class TB:
def __init__(self, dut):
self.dut = dut
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
# Forencich-style connection:
# s_axi: cocotb AXI master -> flat_to_if -> compact AXI interface
# m_axi: compact AXI interface -> if_to_flat -> cocotb AXI RAM
self.master = AxiMaster(AxiBus.from_prefix(
dut, "s_axi"), dut.clk, dut.rst)
self.ram = AxiRam(AxiBus.from_prefix(dut, "m_axi"),
dut.clk, dut.rst, size=2**20)
async def reset(self):
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
def cycle_pause():
return itertools.cycle([1, 1, 1, 0])
@cocotb.test()
async def run_basic_write_read_test(dut):
# simple loopback test..
tb = TB(dut)
await tb.reset()
test_data = bytes(range(64))
addr = 0x1000
await tb.master.write(addr, test_data)
read_data = await tb.master.read(addr, len(test_data))
assert bytes(read_data.data) == test_data
@cocotb.test()
async def run_unaligned_and_burst_test(dut):
tb = TB(dut)
await tb.reset()
cases = [
(0x0003, bytes([0x11, 0x22, 0x33, 0x44, 0x55])),
(0x0127, bytes((x * 7 + 3) & 0xFF for x in range(37))),
(0x2000, bytes(range(256))),
(0x2F1B, bytes((255 - x) & 0xFF for x in range(191))),
]
for addr, payload in cases:
await tb.master.write(addr, payload)
read_data = await tb.master.read(addr, len(payload))
assert bytes(read_data.data) == payload
def set_pause_if_available(dut, channel):
if hasattr(channel, "set_pause_generator"):
channel.set_pause_generator(cycle_pause())
else:
dut._log.warning(
"Channel %r has no set_pause_generator(); backpressure skipped", channel)
@cocotb.test()
async def run_backpressure_test(dut):
tb = TB(dut)
# Optional stress, close to alexforencich/cocotbext-axi examples.
# It exercises valid/ready stalls on the channels that support pause generators.
for channel in [
tb.master.write_if.aw_channel,
tb.master.write_if.w_channel,
tb.master.read_if.ar_channel,
tb.ram.write_if.b_channel,
tb.ram.read_if.r_channel,
]:
set_pause_if_available(dut, channel)
await tb.reset()
payload = bytes((x * 13 + 5) & 0xFF for x in range(1024))
addr = 0x4000
await tb.master.write(addr, payload)
await Timer(100, units="ns")
read_data = await tb.master.read(addr, len(payload))
assert bytes(read_data.data) == payload

View File

@ -0,0 +1,81 @@
TOPLEVEL_LANG = verilog
SIM ?= verilator
PWD := $(shell pwd)
PROJECT_ROOT ?= $(abspath $(PWD)/../../..)
AXI_IF_RTL_DIR ?= $(PROJECT_ROOT)/axi/rtl
FORENCICH_AXI_RTL_DIR ?= $(PROJECT_ROOT)/external/verilog-axi/rtl
TB_DIR ?= $(PWD)
TOPLEVEL = tb_axi_dma_wrapper
MODULE = test_axi_dma_wrapper
export PYTHONPATH := $(TB_DIR):$(PYTHONPATH)
# Parameters for a quick make-based run. The pytest entrypoint can be used for
# wider parameter sweeps.
AXI_DATA_WIDTH ?= 32
AXI_ADDR_WIDTH ?= 16
AXI_ID_WIDTH ?= 8
AXI_USER_WIDTH ?= 1
AXI_MAX_BURST_LEN ?= 16
ENABLE_UNALIGNED ?= 0
AXI_STRB_WIDTH := $(shell expr $(AXI_DATA_WIDTH) / 8)
AXIS_DATA_WIDTH ?= $(AXI_DATA_WIDTH)
AXIS_KEEP_WIDTH := $(shell expr $(AXIS_DATA_WIDTH) / 8)
AXIS_KEEP_ENABLE := $(shell [ $(AXIS_DATA_WIDTH) -gt 8 ] && echo 1 || echo 0)
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_pkg.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_if.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axis_if.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi4_flat_to_if.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi4_if_to_flat.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axis_flat_to_if.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axis_if_to_flat.sv
VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma.v
VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_rd.v
VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_wr.v
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_if_wrapper.sv
VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_wrapper.sv
COMPILE_ARGS += -I$(AXI_IF_RTL_DIR)
COMPILE_ARGS += -I$(WRAPPER_RTL_DIR)
COMPILE_ARGS += -I$(FORENCICH_AXI_RTL_DIR)
# took this from forencich to silence 100+ warnings
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH -Wno-CASEINCOMPLETE
ifeq ($(SIM),verilator)
EXTRA_ARGS += --trace
EXTRA_ARGS += --trace-structs
EXTRA_ARGS += -GAXI_DATA_WIDTH=$(AXI_DATA_WIDTH)
EXTRA_ARGS += -GAXI_ADDR_WIDTH=$(AXI_ADDR_WIDTH)
EXTRA_ARGS += -GAXI_STRB_WIDTH=$(AXI_STRB_WIDTH)
EXTRA_ARGS += -GAXI_ID_WIDTH=$(AXI_ID_WIDTH)
EXTRA_ARGS += -GAXI_USER_WIDTH=$(AXI_USER_WIDTH)
EXTRA_ARGS += -GAXI_MAX_BURST_LEN=$(AXI_MAX_BURST_LEN)
EXTRA_ARGS += -GAXIS_DATA_WIDTH=$(AXIS_DATA_WIDTH)
EXTRA_ARGS += -GAXIS_KEEP_ENABLE=$(AXIS_KEEP_ENABLE)
EXTRA_ARGS += -GAXIS_KEEP_WIDTH=$(AXIS_KEEP_WIDTH)
EXTRA_ARGS += -GAXIS_LAST_ENABLE=1
EXTRA_ARGS += -GAXIS_ID_ENABLE=1
EXTRA_ARGS += -GAXIS_ID_WIDTH=8
EXTRA_ARGS += -GAXIS_DEST_ENABLE=0
EXTRA_ARGS += -GAXIS_DEST_WIDTH=8
EXTRA_ARGS += -GAXIS_USER_ENABLE=1
EXTRA_ARGS += -GAXIS_USER_WIDTH=1
EXTRA_ARGS += -GLEN_WIDTH=20
EXTRA_ARGS += -GTAG_WIDTH=8
EXTRA_ARGS += -GENABLE_SG=0
EXTRA_ARGS += -GENABLE_UNALIGNED=$(ENABLE_UNALIGNED)
endif
export PARAM_AXI_DATA_WIDTH=$(AXI_DATA_WIDTH)
export PARAM_ENABLE_UNALIGNED=$(ENABLE_UNALIGNED)
include $(shell cocotb-config --makefiles)/Makefile.sim

View File

@ -0,0 +1,349 @@
// Cocotb still sees flat Forencich-style signal names
// this top converts those flat signals to local interfaces
`default_nettype none
module tb_axi_dma_wrapper #(
parameter int unsigned AXI_DATA_WIDTH = 32,
parameter int unsigned AXI_ADDR_WIDTH = 16,
parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8,
parameter int unsigned AXI_ID_WIDTH = 8,
parameter int unsigned AXI_USER_WIDTH = 1,
parameter int unsigned AXI_MAX_BURST_LEN = 16,
parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH,
parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8,
parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8,
parameter int unsigned AXIS_LAST_ENABLE = 1,
parameter int unsigned AXIS_ID_ENABLE = 1,
parameter int unsigned AXIS_ID_WIDTH = 8,
parameter int unsigned AXIS_DEST_ENABLE = 0,
parameter int unsigned AXIS_DEST_WIDTH = 8,
parameter int unsigned AXIS_USER_ENABLE = 1,
parameter int unsigned AXIS_USER_WIDTH = 1,
parameter int unsigned LEN_WIDTH = 20,
parameter int unsigned TAG_WIDTH = 8,
parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0
)();
// cocotb drives these directly
logic clk;
logic rst;
logic rstn;
assign rstn = ~rst;
// --------------------------------------------------------------------------
// Flat descriptor/status ports visible to cocotb
// --------------------------------------------------------------------------
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
// --------------------------------------------------------------------------
axis_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)
) m_axis_read_data_if (
.aclk (clk),
.aresetn (rstn)
);
axis_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)
) s_axis_write_data_if (
.aclk (clk),
.aresetn (rstn)
);
axi4_if #(
.ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
) m_axi_if (
.aclk (clk),
.aresetn (rstn)
);
// cocotb 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)
);
// wrapper read stream interface -> cocotb 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),
.m_axis_tdata (m_axis_read_data_tdata),
.m_axis_tkeep (m_axis_read_data_tkeep),
.m_axis_tstrb (m_axis_read_data_tstrb),
.m_axis_tlast (m_axis_read_data_tlast),
.m_axis_tid (m_axis_read_data_tid),
.m_axis_tdest (m_axis_read_data_tdest),
.m_axis_tuser (m_axis_read_data_tuser),
.m_axis_tvalid (m_axis_read_data_tvalid),
.m_axis_tready (m_axis_read_data_tready)
);
// wrapper AXI interface -> cocotb 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)
);
axi_dma_if_wrapper #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_USER_WIDTH (AXI_USER_WIDTH),
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
.AXIS_LAST_ENABLE (AXIS_LAST_ENABLE),
.AXIS_ID_ENABLE (AXIS_ID_ENABLE),
.AXIS_ID_WIDTH (AXIS_ID_WIDTH),
.AXIS_DEST_ENABLE (AXIS_DEST_ENABLE),
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
.AXIS_USER_ENABLE (AXIS_USER_ENABLE),
.AXIS_USER_WIDTH (AXIS_USER_WIDTH),
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
) u_dut (
.clk (clk),
.rst (rst),
.s_axis_read_desc_addr (s_axis_read_desc_addr),
.s_axis_read_desc_len (s_axis_read_desc_len),
.s_axis_read_desc_tag (s_axis_read_desc_tag),
.s_axis_read_desc_id (s_axis_read_desc_id),
.s_axis_read_desc_dest (s_axis_read_desc_dest),
.s_axis_read_desc_user (s_axis_read_desc_user),
.s_axis_read_desc_valid (s_axis_read_desc_valid),
.s_axis_read_desc_ready (s_axis_read_desc_ready),
.m_axis_read_desc_status_tag (m_axis_read_desc_status_tag),
.m_axis_read_desc_status_error (m_axis_read_desc_status_error),
.m_axis_read_desc_status_valid (m_axis_read_desc_status_valid),
.m_axis_read_data (m_axis_read_data_if),
.s_axis_write_desc_addr (s_axis_write_desc_addr),
.s_axis_write_desc_len (s_axis_write_desc_len),
.s_axis_write_desc_tag (s_axis_write_desc_tag),
.s_axis_write_desc_valid (s_axis_write_desc_valid),
.s_axis_write_desc_ready (s_axis_write_desc_ready),
.m_axis_write_desc_status_len (m_axis_write_desc_status_len),
.m_axis_write_desc_status_tag (m_axis_write_desc_status_tag),
.m_axis_write_desc_status_id (m_axis_write_desc_status_id),
.m_axis_write_desc_status_dest (m_axis_write_desc_status_dest),
.m_axis_write_desc_status_user (m_axis_write_desc_status_user),
.m_axis_write_desc_status_error (m_axis_write_desc_status_error),
.m_axis_write_desc_status_valid (m_axis_write_desc_status_valid),
.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)
);
endmodule : tb_axi_dma_wrapper
`default_nettype wire

View File

@ -0,0 +1,375 @@
# SPDX-License-Identifier: MIT
"""
Adapted cocotb/pytest tests for tb_axi_dma_wrapper.
This file is based on alexforencich/verilog-axi/tb/axi_dma/test_axi_dma.py
and keeps the same cocotb-facing flat prefixes. The SystemVerilog test top
routes these flat signals through local axi4_if/axis_if converters and then
through axi_dma_if_wrapper.
Original copyright:
Copyright (c) 2020 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
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 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"],
)
DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream(
"DescStatus",
signals=["tag", "error", "valid"],
optional_signals=["len", "id", "dest", "user"],
)
class TB:
def __init__(self, dut):
self.dut = dut
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
# Descriptor/status streams remain flat on purpose: they are specific
# to Forencich DMA, not generic AXIS interfaces in our library.
self.read_desc_source = DescSource(
DescBus.from_prefix(dut, "s_axis_read_desc"), 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
)
# 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
)
self.write_data_source = AxiStreamSource(
AxiStreamBus.from_prefix(
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)
dut.read_enable.setimmediatevalue(0)
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):
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 1
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
self.dut.rst.value = 0
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
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."""
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)
dut.write_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))
for offset in offsets:
for diff in [-8, -2, -1, 0, 1, 2, 8]:
if length + diff < 1:
continue
tb.log.info("write: length=%d offset=%d diff=%d",
length, offset, diff)
addr = offset + 0x1000
expected_data = bytearray([x % 256 for x in range(length)])
stream_data = bytearray(
[x % 256 for x in range(length + diff)])
tb.axi_ram.write(addr - 128, b"\xaa" *
(len(expected_data) + 256))
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 = await tb.write_desc_status_sink.recv()
tb.log.info("write status: %s", status)
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)
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."""
tb = TB(dut)
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.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))
for offset in offsets:
tb.log.info("read: length=%d offset=%d", length, offset)
addr = offset + 0x1000
test_data = bytearray([x % 256 for x in range(length)])
tb.axi_ram.write(addr - 128, b"\xaa" * (len(test_data) + 256))
tb.axi_ram.write(addr, test_data)
tb.log.debug(
"%s",
tb.axi_ram.hexdump_str(
(addr & ~0xF) - 16,
(((addr & 0xF) + length - 1) & ~0xF) + 48,
),
)
await tb.read_desc_source.send(
DescTransaction(addr=addr, len=len(
test_data), tag=cur_tag, id=cur_tag)
)
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)
def cycle_pause():
return itertools.cycle([1, 1, 1, 0])
# 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()
# -----------------------------------------------------------------------------
# Optional pytest entrypoint via cocotb-test.
# Run from this directory with: pytest -q test_axi_dma_wrapper.py
# -----------------------------------------------------------------------------
def _sanitize_node_name(name):
return name.replace("[", "-").replace("]", "").replace("/", "_")
if pytest is not None:
@pytest.mark.parametrize("axi_data_width", [8, 16, 32])
@pytest.mark.parametrize("unaligned", [0, 1])
def test_axi_dma_wrapper_pytest(request, axi_data_width, unaligned):
import cocotb_test.simulator
tests_dir = os.path.abspath(os.path.dirname(__file__))
project_root = os.path.abspath(os.path.join(tests_dir, "..", ".."))
axi_if_rtl_dir = os.environ.get(
"AXI_IF_RTL_DIR", os.path.join(project_root, "rtl", "axi")
)
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"),
)
dut = "tb_axi_dma_wrapper"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut
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,
}
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(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(tests_dir, "tb_axi_dma_wrapper.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, 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,
)

View File

@ -0,0 +1,23 @@
TOPLEVEL_LANG = verilog
SIM ?= verilator
PWD := $(shell pwd)
RTL_DIR ?= $(PWD)/../../rtl
TB_DIR ?= $(PWD)
TOPLEVEL = tb_axis_loopback
MODULE = test_axis_loopback
VERILOG_SOURCES += $(RTL_DIR)/axis_if.sv
VERILOG_SOURCES += $(RTL_DIR)/axis_flat_to_if.sv
VERILOG_SOURCES += $(RTL_DIR)/axis_if_to_flat.sv
VERILOG_SOURCES += $(RTL_DIR)/axis_desc_flat_to_if.sv
VERILOG_SOURCES += $(RTL_DIR)/axis_desc_if_to_flat.sv
VERILOG_SOURCES += $(TB_DIR)/axis_loopback.sv
VERILOG_SOURCES += $(TB_DIR)/tb_axis_loopback.sv
COMPILE_ARGS += -I$(RTL_DIR)
EXTRA_ARGS += --trace
EXTRA_ARGS += --trace-structs
include $(shell cocotb-config --makefiles)/Makefile.sim

View File

@ -0,0 +1,13 @@
module axis_loopback #(
parameter int unsigned DATA_W = 64,
parameter int unsigned KEEP_W = DATA_W / 8,
parameter int unsigned ID_W = 8,
parameter int unsigned DEST_W = 8,
parameter int unsigned USER_W = 1
)(
axis_if.slave s_axis,
axis_if.master m_axis
);
assign m_axis.req = s_axis.req;
assign s_axis.resp = m_axis.resp;
endmodule : axis_loopback

View File

@ -0,0 +1,108 @@
module tb_axis_loopback;
localparam int unsigned DATA_W = 64;
localparam int unsigned KEEP_W = DATA_W / 8;
localparam int unsigned ID_W = 8;
localparam int unsigned DEST_W = 8;
localparam int unsigned USER_W = 8;
logic clk = 1'b0;
logic rst = 1'b1;
logic rst_n;
assign rst_n = ~rst;
// Classic AXI-Stream flat input side
logic [DATA_W-1:0] s_axis_tdata;
logic [KEEP_W-1:0] s_axis_tkeep;
logic [KEEP_W-1:0] s_axis_tstrb;
logic s_axis_tlast;
logic [ID_W-1:0] s_axis_tid;
logic [DEST_W-1:0] s_axis_tdest;
logic [USER_W-1:0] s_axis_tuser;
logic s_axis_tvalid;
logic s_axis_tready;
// Classic AXI-Stream flat output side
logic [DATA_W-1:0] m_axis_tdata;
logic [KEEP_W-1:0] m_axis_tkeep;
logic [KEEP_W-1:0] m_axis_tstrb;
logic m_axis_tlast;
logic [ID_W-1:0] m_axis_tid;
logic [DEST_W-1:0] m_axis_tdest;
logic [USER_W-1:0] m_axis_tuser;
logic m_axis_tvalid;
logic m_axis_tready;
axis_if #(
.DATA_W(DATA_W),
.KEEP_W(KEEP_W),
.ID_W(ID_W),
.DEST_W(DEST_W),
.USER_W(USER_W)
) s_axis_if (
.aclk(clk),
.aresetn(rst_n)
);
axis_if #(
.DATA_W(DATA_W),
.KEEP_W(KEEP_W),
.ID_W(ID_W),
.DEST_W(DEST_W),
.USER_W(USER_W)
) m_axis_if (
.aclk(clk),
.aresetn(rst_n)
);
axis_flat_to_if #(
.DATA_W(DATA_W),
.KEEP_W(KEEP_W),
.ID_W(ID_W),
.DEST_W(DEST_W),
.USER_W(USER_W)
) axis_in_conv_inst (
.s_axis_tdata (s_axis_tdata),
.s_axis_tkeep (s_axis_tkeep),
.s_axis_tstrb (s_axis_tstrb),
.s_axis_tlast (s_axis_tlast),
.s_axis_tid (s_axis_tid),
.s_axis_tdest (s_axis_tdest),
.s_axis_tuser (s_axis_tuser),
.s_axis_tvalid (s_axis_tvalid),
.s_axis_tready (s_axis_tready),
.m_axis (s_axis_if)
);
axis_loopback #(
.DATA_W(DATA_W),
.KEEP_W(KEEP_W),
.ID_W(ID_W),
.DEST_W(DEST_W),
.USER_W(USER_W)
) axis_dut_inst (
.s_axis(s_axis_if),
.m_axis(m_axis_if)
);
axis_if_to_flat #(
.DATA_W(DATA_W),
.KEEP_W(KEEP_W),
.ID_W(ID_W),
.DEST_W(DEST_W),
.USER_W(USER_W)
) axis_out_conv_inst (
.s_axis (m_axis_if),
.m_axis_tdata (m_axis_tdata),
.m_axis_tkeep (m_axis_tkeep),
.m_axis_tstrb (m_axis_tstrb),
.m_axis_tlast (m_axis_tlast),
.m_axis_tid (m_axis_tid),
.m_axis_tdest (m_axis_tdest),
.m_axis_tuser (m_axis_tuser),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready)
);
endmodule : tb_axis_loopback

View File

@ -0,0 +1,126 @@
import random
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Timer, with_timeout
CLK_PERIOD_NS = 10
async def reset_dut(dut):
dut.rst.value = 1
dut.s_axis_tdata.value = 0
dut.s_axis_tkeep.value = 0
dut.s_axis_tstrb.value = 0
dut.s_axis_tlast.value = 0
dut.s_axis_tid.value = 0
dut.s_axis_tdest.value = 0
dut.s_axis_tuser.value = 0
dut.s_axis_tvalid.value = 0
dut.m_axis_tready.value = 0
for _ in range(5):
await RisingEdge(dut.clk)
dut.rst.value = 0
for _ in range(2):
await RisingEdge(dut.clk)
async def send_axis_beat(dut, beat):
dut.s_axis_tdata.value = beat["data"]
dut.s_axis_tkeep.value = beat["keep"]
dut.s_axis_tstrb.value = beat["strb"]
dut.s_axis_tlast.value = beat["last"]
dut.s_axis_tid.value = beat["id"]
dut.s_axis_tdest.value = beat["dest"]
dut.s_axis_tuser.value = beat["user"]
dut.s_axis_tvalid.value = 1
while True:
await RisingEdge(dut.clk)
if int(dut.s_axis_tready.value):
break
dut.s_axis_tvalid.value = 0
async def recv_axis_beats(dut, count, ready_pattern=None):
beats = []
cycle = 0
while len(beats) < count:
ready = 1
if ready_pattern is not None:
ready = ready_pattern[cycle % len(ready_pattern)]
dut.m_axis_tready.value = ready
await RisingEdge(dut.clk)
if ready and int(dut.m_axis_tvalid.value):
beats.append({
"data": int(dut.m_axis_tdata.value),
"keep": int(dut.m_axis_tkeep.value),
"strb": int(dut.m_axis_tstrb.value),
"last": int(dut.m_axis_tlast.value),
"id": int(dut.m_axis_tid.value),
"dest": int(dut.m_axis_tdest.value),
"user": int(dut.m_axis_tuser.value),
})
cycle += 1
dut.m_axis_tready.value = 0
return beats
@cocotb.test()
async def test_axis_loopback_basic(dut):
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD_NS, units="ns").start())
await reset_dut(dut)
tx = [
{"data": 0x1122334455667788, "keep": 0xFF, "strb": 0xFF,
"last": 0, "id": 1, "dest": 2, "user": 3},
{"data": 0xAABBCCDDEEFF0011, "keep": 0xFF, "strb": 0xFF,
"last": 1, "id": 1, "dest": 2, "user": 4},
]
rx_task = cocotb.start_soon(recv_axis_beats(dut, len(tx)))
for beat in tx:
await send_axis_beat(dut, beat)
rx = await with_timeout(rx_task, 1, "us")
assert rx == tx
@cocotb.test()
async def test_axis_loopback_with_backpressure(dut):
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD_NS, units="ns").start())
await reset_dut(dut)
random.seed(1)
tx = []
for i in range(16):
tx.append({
"data": random.getrandbits(64),
"keep": 0xFF,
"strb": 0xFF,
"last": int(i == 15),
"id": i & 0xFF,
"dest": (i + 1) & 0xFF,
"user": (i + 2) & 0xFF,
})
rx_task = cocotb.start_soon(recv_axis_beats(
dut, len(tx), ready_pattern=[1, 0, 1, 1, 0]))
for beat in tx:
await send_axis_beat(dut, beat)
rx = await with_timeout(rx_task, 5, "us")
assert rx == tx

1
external/verilog-axi vendored Submodule

Submodule external/verilog-axi added at 516bd5dadc