Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c18233e16a | |||
| fbfb869f00 | |||
| 4e8141d13f | |||
| a2e330d193 | |||
| ea478fa0c7 | |||
| 35ed5d6e6e | |||
| f95f6b5b86 | |||
| fc89ab10c4 | |||
| 30fa85d01c | |||
| 7e7008ed87 | |||
| 52a2bccc77 | |||
| 43e2460124 | |||
| f1c760349f | |||
| 6374dba26d | |||
| c0fb75e7c3 | |||
| bc91a8b3ea | |||
| 563c1d3b69 | |||
| 0c314bf2ae | |||
| 79ea1b3486 | |||
| 67f075b2e5 | |||
| e31436d909 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
**sim_build**
|
||||
**pycache**
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "external/verilog-axi"]
|
||||
path = external/verilog-axi
|
||||
url = https://github.com/alexforencich/verilog-axi
|
||||
384
axi/rtl/axi_dma_if_wrapper.sv
Normal file
384
axi/rtl/axi_dma_if_wrapper.sv
Normal 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
|
||||
@ -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;
|
||||
30
axi/rtl/axis_flat_to_if.sv
Normal file
30
axi/rtl/axis_flat_to_if.sv
Normal 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
47
axi/rtl/axis_if.sv
Normal 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
|
||||
|
||||
30
axi/rtl/axis_if_to_flat.sv
Normal file
30
axi/rtl/axis_if_to_flat.sv
Normal 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
|
||||
32
axi/tb/axi_cocotb_loopback_test/Makefile
Normal file
32
axi/tb/axi_cocotb_loopback_test/Makefile
Normal 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
|
||||
21
axi/tb/axi_cocotb_loopback_test/README.md
Normal file
21
axi/tb/axi_cocotb_loopback_test/README.md
Normal 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
|
||||
```
|
||||
17
axi/tb/axi_cocotb_loopback_test/axi4_loopback.sv
Normal file
17
axi/tb/axi_cocotb_loopback_test/axi4_loopback.sv
Normal 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
|
||||
252
axi/tb/axi_cocotb_loopback_test/tb_axi4_loopback.sv
Normal file
252
axi/tb/axi_cocotb_loopback_test/tb_axi4_loopback.sv
Normal 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
|
||||
104
axi/tb/axi_cocotb_loopback_test/test_axi4_loopback.py
Normal file
104
axi/tb/axi_cocotb_loopback_test/test_axi4_loopback.py
Normal 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
|
||||
81
axi/tb/axi_dma_wrapper/Makefile
Normal file
81
axi/tb/axi_dma_wrapper/Makefile
Normal 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
|
||||
349
axi/tb/axi_dma_wrapper/tb_axi_dma_wrapper.sv
Normal file
349
axi/tb/axi_dma_wrapper/tb_axi_dma_wrapper.sv
Normal 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
|
||||
375
axi/tb/axi_dma_wrapper/test_axi_dma_wrapper.py
Normal file
375
axi/tb/axi_dma_wrapper/test_axi_dma_wrapper.py
Normal 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,
|
||||
)
|
||||
23
axi/tb/axis_cocotb_loopback_test/Makefile
Normal file
23
axi/tb/axis_cocotb_loopback_test/Makefile
Normal 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
|
||||
13
axi/tb/axis_cocotb_loopback_test/axis_loopback.sv
Normal file
13
axi/tb/axis_cocotb_loopback_test/axis_loopback.sv
Normal 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
|
||||
108
axi/tb/axis_cocotb_loopback_test/tb_axis_loopback.sv
Normal file
108
axi/tb/axis_cocotb_loopback_test/tb_axis_loopback.sv
Normal 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
|
||||
126
axi/tb/axis_cocotb_loopback_test/test_axis_loopback.py
Normal file
126
axi/tb/axis_cocotb_loopback_test/test_axis_loopback.py
Normal 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
1
external/verilog-axi
vendored
Submodule
Submodule external/verilog-axi added at 516bd5dadc
Reference in New Issue
Block a user