rtl: rework dma AXIS cmd&status

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

View File

@ -1,19 +1,10 @@
// SPDX-License-Identifier: MIT
// AXIS compat wrapper above axi_dma_if_wrapper.
//
// Compatibility wrapper over axi_dma_if_wrapper.
//
// Uses 4 axis_if channels for statuses and descs
// s_axis_desc_cmd = one-beat command stream carrying read + write desc
// m_axis_desc_status = one-beat status stream carrying read + write status
//
// payload packing is LSB-first and one descriptor/status occupies one AXIS beat.
// tkeep/tstrb/tlast/id/dest/user on descriptor inputs are ignored.
// status outputs drive tkeep/tstrb to all ones, tlast to 1, and id/dest/user to 0.
//
// !!!!!: the original alexforencich axi_dma status outputs have
// no ready/backpressure signal. This wrapper adds a oneentry holding register
// for each status axis output, but if a downstream is not ready long enough for a
// second status to arrive while the first one is still pending, the core cannot
// be backpressured. In normal use keep status sinks ready, or add an external
// FIFO if long stalls are possible.
// read_enable and write_enable select which descriptor is used
`default_nettype none
@ -41,70 +32,56 @@ module axi_dma_axis_compat #(
parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0,
// Payload widths for descriptor/status AXIS streams.
parameter int unsigned READ_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH,
parameter int unsigned READ_DESC_AXIS_DATA_WIDTH = ((READ_DESC_PAYLOAD_WIDTH + 7) / 8) * 8,
parameter int unsigned READ_DESC_STATUS_PAYLOAD_WIDTH = TAG_WIDTH + 4,
parameter int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8,
parameter int unsigned WRITE_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned WRITE_DESC_AXIS_DATA_WIDTH = ((WRITE_DESC_PAYLOAD_WIDTH + 7) / 8) * 8,
parameter int unsigned WRITE_DESC_STATUS_PAYLOAD_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4,
parameter int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8
// Raw packed command/status widths. rounded up to full bytes
parameter int unsigned READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH,
parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH,
parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8,
parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4,
parameter int unsigned WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4,
parameter int unsigned DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH,
parameter int unsigned DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_STATUS_KEEP_WIDTH = DESC_STATUS_DATA_WIDTH / 8,
parameter int unsigned DESC_STATUS_USER_WIDTH = 2
)(
input logic clk,
input logic rst,
/*
* AXI read descriptor input, encoded as one AXIS beat.
* tdata layout, LSB first:
* addr | len | tag | id | dest | user
*/
axis_if.slave s_axis_read_desc,
// Unified command stream. One beat carries both read and write descriptors.
// Which parts are used is selected by read_enable/write_enable.
axis_if.slave s_axis_desc_cmd,
/*
* AXI read descriptor status output, encoded as one AXIS beat.
* tdata layout, LSB first:
* tag | error
*/
axis_if.master m_axis_read_desc_status,
// Unified status stream. One beat can carry read status, write status, or both.
// m_axis_desc_status.tuser[0] = read status valid in this beat
// m_axis_desc_status.tuser[1] = write status valid in this beat
axis_if.master m_axis_desc_status,
/*
* AXI stream read data output.
*/
// Original DMA data streams and AXI memory master, still exposed as normal
// generic interfaces.
axis_if.master m_axis_read_data,
/*
* AXI write descriptor input, encoded as one AXIS beat.
* tdata layout, LSB first:
* addr | len | tag
*/
axis_if.slave s_axis_write_desc,
/*
* AXI write descriptor status output, encoded as one AXIS beat.
* tdata layout, LSB first:
* len | tag | id | dest | user | error
*/
axis_if.master m_axis_write_desc_status,
/*
* AXI stream write data input.
*/
axis_if.slave s_axis_write_data,
/*
* AXI memory master interface.
*/
axi4_if.master m_axi,
/*
* Configuration.
*/
input logic read_enable,
input logic write_enable,
input logic write_abort
);
// --------------------------------------------------------------------------
// packing layout
// --------------------------------------------------------------------------
// s_axis_desc_cmd.tdata:
// [READ_DESC_*] read descriptor
// [WRITE_DESC_*] write descriptor
//
// m_axis_desc_status.tdata:
// [READ_STATUS_*] read descriptor status
// [WRITE_STATUS_*] write descriptor status
//
// offsets are LSB-first. padding in upper bits (if required)
localparam int unsigned RD_ADDR_LSB = 0;
localparam int unsigned RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH;
localparam int unsigned RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH;
@ -112,31 +89,26 @@ module axi_dma_axis_compat #(
localparam int unsigned RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH;
localparam int unsigned RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH;
localparam int unsigned RDS_TAG_LSB = 0;
localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH;
localparam int unsigned WR_BASE_LSB = READ_DESC_WIDTH;
localparam int unsigned WR_ADDR_LSB = WR_BASE_LSB;
localparam int unsigned WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH;
localparam int unsigned WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH;
localparam int unsigned WD_ADDR_LSB = 0;
localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH;
localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_WIDTH;
localparam int unsigned RD_STS_TAG_LSB = 0;
localparam int unsigned RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH;
localparam int unsigned WDS_LEN_LSB = 0;
localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH;
localparam int unsigned WDS_ID_LSB = WDS_TAG_LSB + TAG_WIDTH;
localparam int unsigned WDS_DEST_LSB = WDS_ID_LSB + AXIS_ID_WIDTH;
localparam int unsigned WDS_USER_LSB = WDS_DEST_LSB + AXIS_DEST_WIDTH;
localparam int unsigned WDS_ERROR_LSB = WDS_USER_LSB + AXIS_USER_WIDTH;
localparam int unsigned WR_STS_BASE_LSB = READ_STATUS_WIDTH;
localparam int unsigned WR_STS_LEN_LSB = WR_STS_BASE_LSB;
localparam int unsigned WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH;
localparam int unsigned WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH;
localparam int unsigned WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH;
localparam int unsigned WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH;
localparam int unsigned WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH;
localparam int unsigned READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8;
localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8;
localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8;
localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8;
logic rstn;
assign rstn = ~rst;
wire rstn = ~rst;
// --------------------------------------------------------------------------
// Internal DMA-specific descriptor/status interfaces expected by
// axi_dma_if_wrapper.
// Internal DMA-specific descriptor/status interfaces
// --------------------------------------------------------------------------
axi_dma_read_desc_if #(
@ -179,125 +151,33 @@ module axi_dma_axis_compat #(
);
// --------------------------------------------------------------------------
// AXIS read descriptor input -> DMA-specific read descriptor interface.
// Command AXIS -> read/write descriptors
// --------------------------------------------------------------------------
assign read_desc_if.req.addr = s_axis_read_desc.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH];
assign read_desc_if.req.len = s_axis_read_desc.req.t.data[RD_LEN_LSB +: LEN_WIDTH];
assign read_desc_if.req.tag = s_axis_read_desc.req.t.data[RD_TAG_LSB +: TAG_WIDTH];
assign read_desc_if.req.id = s_axis_read_desc.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH];
assign read_desc_if.req.dest = s_axis_read_desc.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH];
assign read_desc_if.req.user = s_axis_read_desc.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH];
assign read_desc_if.req.valid = s_axis_read_desc.req.t.valid;
assign s_axis_read_desc.resp.ready = read_desc_if.resp.ready;
wire cmd_valid = s_axis_desc_cmd.req.t.valid;
wire cmd_ready = (!read_enable || read_desc_if.resp.ready) &&
(!write_enable || write_desc_if.resp.ready);
// Only assert a descriptor valid when the complete command can be accepted.
// This prevents partial consumption when both read and write are enabled.
assign read_desc_if.req.valid = cmd_valid && read_enable && (!write_enable || write_desc_if.resp.ready);
assign write_desc_if.req.valid = cmd_valid && write_enable && (!read_enable || read_desc_if.resp.ready);
assign s_axis_desc_cmd.resp.ready = cmd_ready;
assign read_desc_if.req.addr = s_axis_desc_cmd.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH];
assign read_desc_if.req.len = s_axis_desc_cmd.req.t.data[RD_LEN_LSB +: LEN_WIDTH];
assign read_desc_if.req.tag = s_axis_desc_cmd.req.t.data[RD_TAG_LSB +: TAG_WIDTH];
assign read_desc_if.req.id = s_axis_desc_cmd.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH];
assign read_desc_if.req.dest = s_axis_desc_cmd.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH];
assign read_desc_if.req.user = s_axis_desc_cmd.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH];
assign write_desc_if.req.addr = s_axis_desc_cmd.req.t.data[WR_ADDR_LSB +: AXI_ADDR_WIDTH];
assign write_desc_if.req.len = s_axis_desc_cmd.req.t.data[WR_LEN_LSB +: LEN_WIDTH];
assign write_desc_if.req.tag = s_axis_desc_cmd.req.t.data[WR_TAG_LSB +: TAG_WIDTH];
// --------------------------------------------------------------------------
// AXIS write descriptor input -> DMA-specific write descriptor interface.
// --------------------------------------------------------------------------
assign write_desc_if.req.addr = s_axis_write_desc.req.t.data[WD_ADDR_LSB +: AXI_ADDR_WIDTH];
assign write_desc_if.req.len = s_axis_write_desc.req.t.data[WD_LEN_LSB +: LEN_WIDTH];
assign write_desc_if.req.tag = s_axis_write_desc.req.t.data[WD_TAG_LSB +: TAG_WIDTH];
assign write_desc_if.req.valid = s_axis_write_desc.req.t.valid;
assign s_axis_write_desc.resp.ready = write_desc_if.resp.ready;
// --------------------------------------------------------------------------
// DMA-specific read status -> AXIS read status output.
// --------------------------------------------------------------------------
logic [READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] read_status_payload;
logic [READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] read_status_data_q;
logic read_status_valid_q;
always_comb begin
read_status_payload = '0;
read_status_payload[RDS_TAG_LSB +: TAG_WIDTH] = read_desc_status_if.req.tag;
read_status_payload[RDS_ERROR_LSB +: 4] = read_desc_status_if.req.error;
end
always_ff @(posedge clk) begin
if (rst) begin
read_status_valid_q <= 1'b0;
read_status_data_q <= '0;
end else begin
if (!read_status_valid_q || m_axis_read_desc_status.resp.ready) begin
read_status_valid_q <= read_desc_status_if.req.valid;
if (read_desc_status_if.req.valid) begin
read_status_data_q <= read_status_payload;
end else begin
read_status_data_q <= '0;
end
end
end
end
always_comb begin
m_axis_read_desc_status.req.t.data = '0;
m_axis_read_desc_status.req.t.keep = '0;
m_axis_read_desc_status.req.t.strb = '0;
m_axis_read_desc_status.req.t.last = read_status_valid_q;
m_axis_read_desc_status.req.t.id = '0;
m_axis_read_desc_status.req.t.dest = '0;
m_axis_read_desc_status.req.t.user = '0;
m_axis_read_desc_status.req.t.valid = read_status_valid_q;
m_axis_read_desc_status.req.t.data[READ_DESC_STATUS_AXIS_DATA_WIDTH-1:0] = read_status_data_q;
m_axis_read_desc_status.req.t.keep[READ_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {READ_DESC_STATUS_AXIS_KEEP_WIDTH{read_status_valid_q}};
m_axis_read_desc_status.req.t.strb[READ_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {READ_DESC_STATUS_AXIS_KEEP_WIDTH{read_status_valid_q}};
end
// --------------------------------------------------------------------------
// DMA-specific write status -> AXIS write status output.
// --------------------------------------------------------------------------
logic [WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] write_status_payload;
logic [WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] write_status_data_q;
logic write_status_valid_q;
always_comb begin
write_status_payload = '0;
write_status_payload[WDS_LEN_LSB +: LEN_WIDTH] = write_desc_status_if.req.len;
write_status_payload[WDS_TAG_LSB +: TAG_WIDTH] = write_desc_status_if.req.tag;
write_status_payload[WDS_ID_LSB +: AXIS_ID_WIDTH] = write_desc_status_if.req.id;
write_status_payload[WDS_DEST_LSB +: AXIS_DEST_WIDTH] = write_desc_status_if.req.dest;
write_status_payload[WDS_USER_LSB +: AXIS_USER_WIDTH] = write_desc_status_if.req.user;
write_status_payload[WDS_ERROR_LSB +: 4] = write_desc_status_if.req.error;
end
always_ff @(posedge clk) begin
if (rst) begin
write_status_valid_q <= 1'b0;
write_status_data_q <= '0;
end else begin
if (!write_status_valid_q || m_axis_write_desc_status.resp.ready) begin
write_status_valid_q <= write_desc_status_if.req.valid;
if (write_desc_status_if.req.valid) begin
write_status_data_q <= write_status_payload;
end else begin
write_status_data_q <= '0;
end
end
end
end
always_comb begin
m_axis_write_desc_status.req.t.data = '0;
m_axis_write_desc_status.req.t.keep = '0;
m_axis_write_desc_status.req.t.strb = '0;
m_axis_write_desc_status.req.t.last = write_status_valid_q;
m_axis_write_desc_status.req.t.id = '0;
m_axis_write_desc_status.req.t.dest = '0;
m_axis_write_desc_status.req.t.user = '0;
m_axis_write_desc_status.req.t.valid = write_status_valid_q;
m_axis_write_desc_status.req.t.data[WRITE_DESC_STATUS_AXIS_DATA_WIDTH-1:0] = write_status_data_q;
m_axis_write_desc_status.req.t.keep[WRITE_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {WRITE_DESC_STATUS_AXIS_KEEP_WIDTH{write_status_valid_q}};
m_axis_write_desc_status.req.t.strb[WRITE_DESC_STATUS_AXIS_KEEP_WIDTH-1:0] = {WRITE_DESC_STATUS_AXIS_KEEP_WIDTH{write_status_valid_q}};
end
// --------------------------------------------------------------------------
// Lower-level wrapper: DMA-specific descriptor/status interfaces inside,
// ordinary AXIS data and AXI memory interfaces unchanged.
// Lower DMA wrapper
// --------------------------------------------------------------------------
axi_dma_if_wrapper #(
@ -307,6 +187,7 @@ module axi_dma_axis_compat #(
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_USER_WIDTH (AXI_USER_WIDTH),
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
@ -317,6 +198,7 @@ module axi_dma_axis_compat #(
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
.AXIS_USER_ENABLE (AXIS_USER_ENABLE),
.AXIS_USER_WIDTH (AXIS_USER_WIDTH),
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
@ -340,6 +222,110 @@ module axi_dma_axis_compat #(
.write_abort (write_abort)
);
// --------------------------------------------------------------------------
// Status capture and unified AXIS output
// --------------------------------------------------------------------------
// Forencich descriptor status channels have valid but no ready. A regular
// AXI-Stream output can be stalled
// this wrapper stores one pending read status and one pending write status.
// if you need more, please attack a fifo
logic rd_status_pending;
logic [TAG_WIDTH-1:0] rd_status_tag_q;
logic [3:0] rd_status_error_q;
logic wr_status_pending;
logic [LEN_WIDTH-1:0] wr_status_len_q;
logic [TAG_WIDTH-1:0] wr_status_tag_q;
logic [AXIS_ID_WIDTH-1:0] wr_status_id_q;
logic [AXIS_DEST_WIDTH-1:0] wr_status_dest_q;
logic [AXIS_USER_WIDTH-1:0] wr_status_user_q;
logic [3:0] wr_status_error_q;
wire status_valid = rd_status_pending || wr_status_pending;
wire status_fire = status_valid && m_axis_desc_status.resp.ready;
always_ff @(posedge clk) begin
if (rst) begin
rd_status_pending <= 1'b0;
wr_status_pending <= 1'b0;
rd_status_tag_q <= '0;
rd_status_error_q <= '0;
wr_status_len_q <= '0;
wr_status_tag_q <= '0;
wr_status_id_q <= '0;
wr_status_dest_q <= '0;
wr_status_user_q <= '0;
wr_status_error_q <= '0;
end else begin
if (status_fire) begin
rd_status_pending <= 1'b0;
wr_status_pending <= 1'b0;
end
if (read_desc_status_if.req.valid) begin
`ifndef SYNTHESIS
if (rd_status_pending && !status_fire) begin
$error("axi_dma_axis_compat: read status overflow; add a FIFO or do not backpressure status output");
end
`endif
rd_status_pending <= 1'b1;
rd_status_tag_q <= read_desc_status_if.req.tag;
rd_status_error_q <= read_desc_status_if.req.error;
end
if (write_desc_status_if.req.valid) begin
`ifndef SYNTHESIS
if (wr_status_pending && !status_fire) begin
$error("axi_dma_axis_compat: write status overflow; add a FIFO or do not backpressure status output");
end
`endif
wr_status_pending <= 1'b1;
wr_status_len_q <= write_desc_status_if.req.len;
wr_status_tag_q <= write_desc_status_if.req.tag;
wr_status_id_q <= write_desc_status_if.req.id;
wr_status_dest_q <= write_desc_status_if.req.dest;
wr_status_user_q <= write_desc_status_if.req.user;
wr_status_error_q <= write_desc_status_if.req.error;
end
end
end
logic [DESC_STATUS_DATA_WIDTH-1:0] status_tdata;
logic [DESC_STATUS_USER_WIDTH-1:0] status_tuser;
always_comb begin
status_tdata = '0;
status_tuser = '0;
if (rd_status_pending) begin
status_tdata[RD_STS_TAG_LSB +: TAG_WIDTH] = rd_status_tag_q;
status_tdata[RD_STS_ERROR_LSB +: 4] = rd_status_error_q;
status_tuser[0] = 1'b1;
end
if (wr_status_pending) begin
status_tdata[WR_STS_LEN_LSB +: LEN_WIDTH] = wr_status_len_q;
status_tdata[WR_STS_TAG_LSB +: TAG_WIDTH] = wr_status_tag_q;
status_tdata[WR_STS_ID_LSB +: AXIS_ID_WIDTH] = wr_status_id_q;
status_tdata[WR_STS_DEST_LSB +: AXIS_DEST_WIDTH] = wr_status_dest_q;
status_tdata[WR_STS_USER_LSB +: AXIS_USER_WIDTH] = wr_status_user_q;
status_tdata[WR_STS_ERROR_LSB +: 4] = wr_status_error_q;
status_tuser[1] = 1'b1;
end
end
assign m_axis_desc_status.req.t.data = status_tdata;
assign m_axis_desc_status.req.t.keep = {DESC_STATUS_KEEP_WIDTH{1'b1}};
assign m_axis_desc_status.req.t.strb = {DESC_STATUS_KEEP_WIDTH{1'b1}};
assign m_axis_desc_status.req.t.last = 1'b1;
assign m_axis_desc_status.req.t.id = '0;
assign m_axis_desc_status.req.t.dest = '0;
assign m_axis_desc_status.req.t.user = status_tuser;
assign m_axis_desc_status.req.t.valid = status_valid;
endmodule : axi_dma_axis_compat
`default_nettype wire

View File

@ -24,214 +24,143 @@ module tb_axi_dma_axis_compat #(
parameter int unsigned LEN_WIDTH = 20,
parameter int unsigned TAG_WIDTH = 8,
parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0
)();
parameter int unsigned ENABLE_UNALIGNED = 0,
// cocotb drives these directly
logic clk;
logic rst;
logic rstn;
parameter int unsigned READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH,
parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH,
parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8,
assign rstn = ~rst;
parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4,
parameter int unsigned WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4,
parameter int unsigned DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH,
parameter int unsigned DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_STATUS_KEEP_WIDTH = DESC_STATUS_DATA_WIDTH / 8,
parameter int unsigned DESC_STATUS_USER_WIDTH = 2
)(
input logic clk,
input logic rst,
// --------------------------------------------------------------------------
// Descriptor/status payload widths and tdata layouts.
// --------------------------------------------------------------------------
input logic read_enable,
input logic write_enable,
input logic write_abort,
localparam int unsigned READ_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH;
localparam int unsigned READ_DESC_AXIS_DATA_WIDTH = ((READ_DESC_PAYLOAD_WIDTH + 7) / 8) * 8;
localparam int unsigned READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8;
// Unified descriptor command AXIS, flat for cocotb
input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tkeep,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tstrb,
input logic s_axis_desc_cmd_tlast,
input logic s_axis_desc_cmd_tid,
input logic s_axis_desc_cmd_tdest,
input logic s_axis_desc_cmd_tuser,
input logic s_axis_desc_cmd_tvalid,
output logic s_axis_desc_cmd_tready,
localparam int unsigned READ_DESC_STATUS_PAYLOAD_WIDTH = TAG_WIDTH + 4;
localparam int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8;
localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8;
// Unified descriptor status AXIS, flat for cocotb
output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tkeep,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tstrb,
output logic m_axis_desc_status_tlast,
output logic m_axis_desc_status_tid,
output logic m_axis_desc_status_tdest,
output logic [DESC_STATUS_USER_WIDTH-1:0] m_axis_desc_status_tuser,
output logic m_axis_desc_status_tvalid,
input logic m_axis_desc_status_tready,
localparam int unsigned WRITE_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH;
localparam int unsigned WRITE_DESC_AXIS_DATA_WIDTH = ((WRITE_DESC_PAYLOAD_WIDTH + 7) / 8) * 8;
localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8;
// Read data AXIS output
output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata,
output logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep,
output logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tstrb,
output logic m_axis_read_data_tlast,
output logic [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid,
output logic [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest,
output logic [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser,
output logic m_axis_read_data_tvalid,
input logic m_axis_read_data_tready,
localparam int unsigned WRITE_DESC_STATUS_PAYLOAD_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4;
localparam int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8;
localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8;
// Write data AXIS input
input logic [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata,
input logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep,
input logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tstrb,
input logic s_axis_write_data_tlast,
input logic [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid,
input logic [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest,
input logic [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser,
input logic s_axis_write_data_tvalid,
output logic s_axis_write_data_tready,
localparam int unsigned RD_ADDR_LSB = 0;
localparam int unsigned RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH;
localparam int unsigned RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH;
localparam int unsigned RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH;
localparam int unsigned RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH;
localparam int unsigned RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH;
// AXI memory master, flat for cocotb AxiRam
output logic [AXI_ID_WIDTH-1:0] m_axi_awid,
output logic [AXI_ADDR_WIDTH-1:0] m_axi_awaddr,
output logic [7:0] m_axi_awlen,
output logic [2:0] m_axi_awsize,
output logic [1:0] m_axi_awburst,
output logic m_axi_awlock,
output logic [3:0] m_axi_awcache,
output logic [2:0] m_axi_awprot,
output logic [3:0] m_axi_awqos,
output logic [3:0] m_axi_awregion,
output logic [AXI_USER_WIDTH-1:0] m_axi_awuser,
output logic m_axi_awvalid,
input logic m_axi_awready,
localparam int unsigned RDS_TAG_LSB = 0;
localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH;
output logic [AXI_DATA_WIDTH-1:0] m_axi_wdata,
output logic [AXI_STRB_WIDTH-1:0] m_axi_wstrb,
output logic m_axi_wlast,
output logic [AXI_USER_WIDTH-1:0] m_axi_wuser,
output logic m_axi_wvalid,
input logic m_axi_wready,
localparam int unsigned WD_ADDR_LSB = 0;
localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH;
localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_WIDTH;
input logic [AXI_ID_WIDTH-1:0] m_axi_bid,
input logic [1:0] m_axi_bresp,
input logic [AXI_USER_WIDTH-1:0] m_axi_buser,
input logic m_axi_bvalid,
output logic m_axi_bready,
localparam int unsigned WDS_LEN_LSB = 0;
localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH;
localparam int unsigned WDS_ID_LSB = WDS_TAG_LSB + TAG_WIDTH;
localparam int unsigned WDS_DEST_LSB = WDS_ID_LSB + AXIS_ID_WIDTH;
localparam int unsigned WDS_USER_LSB = WDS_DEST_LSB + AXIS_DEST_WIDTH;
localparam int unsigned WDS_ERROR_LSB = WDS_USER_LSB + AXIS_USER_WIDTH;
output logic [AXI_ID_WIDTH-1:0] m_axi_arid,
output logic [AXI_ADDR_WIDTH-1:0] m_axi_araddr,
output logic [7:0] m_axi_arlen,
output logic [2:0] m_axi_arsize,
output logic [1:0] m_axi_arburst,
output logic m_axi_arlock,
output logic [3:0] m_axi_arcache,
output logic [2:0] m_axi_arprot,
output logic [3:0] m_axi_arqos,
output logic [3:0] m_axi_arregion,
output logic [AXI_USER_WIDTH-1:0] m_axi_aruser,
output logic m_axi_arvalid,
input logic m_axi_arready,
// --------------------------------------------------------------------------
// Flat descriptor/status ports visible to cocotb.
// --------------------------------------------------------------------------
input logic [AXI_ID_WIDTH-1:0] m_axi_rid,
input logic [AXI_DATA_WIDTH-1:0] m_axi_rdata,
input logic [1:0] m_axi_rresp,
input logic m_axi_rlast,
input logic [AXI_USER_WIDTH-1:0] m_axi_ruser,
input logic m_axi_rvalid,
output logic m_axi_rready
);
logic [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_addr;
logic [LEN_WIDTH-1:0] s_axis_read_desc_len;
logic [TAG_WIDTH-1:0] s_axis_read_desc_tag;
logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id;
logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest;
logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user;
logic s_axis_read_desc_valid;
logic s_axis_read_desc_ready;
logic [TAG_WIDTH-1:0] m_axis_read_desc_status_tag;
logic [3:0] m_axis_read_desc_status_error;
logic m_axis_read_desc_status_valid;
logic [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_addr;
logic [LEN_WIDTH-1:0] s_axis_write_desc_len;
logic [TAG_WIDTH-1:0] s_axis_write_desc_tag;
logic s_axis_write_desc_valid;
logic s_axis_write_desc_ready;
logic [LEN_WIDTH-1:0] m_axis_write_desc_status_len;
logic [TAG_WIDTH-1:0] m_axis_write_desc_status_tag;
logic [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id;
logic [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest;
logic [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user;
logic [3:0] m_axis_write_desc_status_error;
logic m_axis_write_desc_status_valid;
// --------------------------------------------------------------------------
// Flat AXIS data ports visible to cocotb.
// --------------------------------------------------------------------------
logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep;
logic [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tstrb;
logic m_axis_read_data_tvalid;
logic m_axis_read_data_tready;
logic m_axis_read_data_tlast;
logic [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid;
logic [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest;
logic [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser;
logic [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep;
logic [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tstrb;
logic s_axis_write_data_tvalid;
logic s_axis_write_data_tready;
logic s_axis_write_data_tlast;
logic [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid;
logic [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest;
logic [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser;
// --------------------------------------------------------------------------
// Flat AXI memory master ports visible to cocotb AxiRam.
// --------------------------------------------------------------------------
logic [AXI_ID_WIDTH-1:0] m_axi_awid;
logic [AXI_ADDR_WIDTH-1:0] m_axi_awaddr;
logic [7:0] m_axi_awlen;
logic [2:0] m_axi_awsize;
logic [1:0] m_axi_awburst;
logic m_axi_awlock;
logic [3:0] m_axi_awcache;
logic [2:0] m_axi_awprot;
logic [3:0] m_axi_awqos;
logic [3:0] m_axi_awregion;
logic [AXI_USER_WIDTH-1:0] m_axi_awuser;
logic m_axi_awvalid;
logic m_axi_awready;
logic [AXI_DATA_WIDTH-1:0] m_axi_wdata;
logic [AXI_STRB_WIDTH-1:0] m_axi_wstrb;
logic m_axi_wlast;
logic [AXI_USER_WIDTH-1:0] m_axi_wuser;
logic m_axi_wvalid;
logic m_axi_wready;
logic [AXI_ID_WIDTH-1:0] m_axi_bid;
logic [1:0] m_axi_bresp;
logic [AXI_USER_WIDTH-1:0] m_axi_buser;
logic m_axi_bvalid;
logic m_axi_bready;
logic [AXI_ID_WIDTH-1:0] m_axi_arid;
logic [AXI_ADDR_WIDTH-1:0] m_axi_araddr;
logic [7:0] m_axi_arlen;
logic [2:0] m_axi_arsize;
logic [1:0] m_axi_arburst;
logic m_axi_arlock;
logic [3:0] m_axi_arcache;
logic [2:0] m_axi_arprot;
logic [3:0] m_axi_arqos;
logic [3:0] m_axi_arregion;
logic [AXI_USER_WIDTH-1:0] m_axi_aruser;
logic m_axi_arvalid;
logic m_axi_arready;
logic [AXI_ID_WIDTH-1:0] m_axi_rid;
logic [AXI_DATA_WIDTH-1:0] m_axi_rdata;
logic [1:0] m_axi_rresp;
logic m_axi_rlast;
logic [AXI_USER_WIDTH-1:0] m_axi_ruser;
logic m_axi_rvalid;
logic m_axi_rready;
// Configuration visible to cocotb
logic read_enable;
logic write_enable;
logic write_abort;
// --------------------------------------------------------------------------
// Local interface instances.
// --------------------------------------------------------------------------
wire rstn = ~rst;
axis_if #(
.DATA_W (READ_DESC_AXIS_DATA_WIDTH),
.KEEP_W (READ_DESC_AXIS_KEEP_WIDTH),
.DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) s_axis_read_desc_axis_if (
) desc_cmd_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (READ_DESC_STATUS_AXIS_DATA_WIDTH),
.KEEP_W (READ_DESC_STATUS_AXIS_KEEP_WIDTH),
.DATA_W (DESC_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) m_axis_read_desc_status_axis_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_DESC_AXIS_DATA_WIDTH),
.KEEP_W (WRITE_DESC_AXIS_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) s_axis_write_desc_axis_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_DESC_STATUS_AXIS_DATA_WIDTH),
.KEEP_W (WRITE_DESC_STATUS_AXIS_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) m_axis_write_desc_status_axis_if (
.USER_W (DESC_STATUS_USER_WIDTH)
) desc_status_if (
.aclk (clk),
.aresetn (rstn)
);
@ -242,7 +171,7 @@ module tb_axi_dma_axis_compat #(
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) m_axis_read_data_if (
) read_data_if (
.aclk (clk),
.aresetn (rstn)
);
@ -253,7 +182,7 @@ module tb_axi_dma_axis_compat #(
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) s_axis_write_data_if (
) write_data_if (
.aclk (clk),
.aresetn (rstn)
);
@ -268,99 +197,52 @@ module tb_axi_dma_axis_compat #(
.aresetn (rstn)
);
// --------------------------------------------------------------------------
// Flat descriptor/status <-> AXIS descriptor/status packing.
// --------------------------------------------------------------------------
always_comb begin
s_axis_read_desc_axis_if.req.t.data = '0;
s_axis_read_desc_axis_if.req.t.keep = {READ_DESC_AXIS_KEEP_WIDTH{s_axis_read_desc_valid}};
s_axis_read_desc_axis_if.req.t.strb = {READ_DESC_AXIS_KEEP_WIDTH{s_axis_read_desc_valid}};
s_axis_read_desc_axis_if.req.t.last = s_axis_read_desc_valid;
s_axis_read_desc_axis_if.req.t.id = '0;
s_axis_read_desc_axis_if.req.t.dest = '0;
s_axis_read_desc_axis_if.req.t.user = '0;
s_axis_read_desc_axis_if.req.t.valid = s_axis_read_desc_valid;
s_axis_read_desc_axis_if.req.t.data[RD_ADDR_LSB +: AXI_ADDR_WIDTH] = s_axis_read_desc_addr;
s_axis_read_desc_axis_if.req.t.data[RD_LEN_LSB +: LEN_WIDTH] = s_axis_read_desc_len;
s_axis_read_desc_axis_if.req.t.data[RD_TAG_LSB +: TAG_WIDTH] = s_axis_read_desc_tag;
s_axis_read_desc_axis_if.req.t.data[RD_ID_LSB +: AXIS_ID_WIDTH] = s_axis_read_desc_id;
s_axis_read_desc_axis_if.req.t.data[RD_DEST_LSB +: AXIS_DEST_WIDTH] = s_axis_read_desc_dest;
s_axis_read_desc_axis_if.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH] = s_axis_read_desc_user;
end
assign s_axis_read_desc_ready = s_axis_read_desc_axis_if.resp.ready;
always_comb begin
m_axis_read_desc_status_tag = m_axis_read_desc_status_axis_if.req.t.data[RDS_TAG_LSB +: TAG_WIDTH];
m_axis_read_desc_status_error = m_axis_read_desc_status_axis_if.req.t.data[RDS_ERROR_LSB +: 4];
m_axis_read_desc_status_valid = m_axis_read_desc_status_axis_if.req.t.valid;
end
assign m_axis_read_desc_status_axis_if.resp.ready = 1'b1;
always_comb begin
s_axis_write_desc_axis_if.req.t.data = '0;
s_axis_write_desc_axis_if.req.t.keep = {WRITE_DESC_AXIS_KEEP_WIDTH{s_axis_write_desc_valid}};
s_axis_write_desc_axis_if.req.t.strb = {WRITE_DESC_AXIS_KEEP_WIDTH{s_axis_write_desc_valid}};
s_axis_write_desc_axis_if.req.t.last = s_axis_write_desc_valid;
s_axis_write_desc_axis_if.req.t.id = '0;
s_axis_write_desc_axis_if.req.t.dest = '0;
s_axis_write_desc_axis_if.req.t.user = '0;
s_axis_write_desc_axis_if.req.t.valid = s_axis_write_desc_valid;
s_axis_write_desc_axis_if.req.t.data[WD_ADDR_LSB +: AXI_ADDR_WIDTH] = s_axis_write_desc_addr;
s_axis_write_desc_axis_if.req.t.data[WD_LEN_LSB +: LEN_WIDTH] = s_axis_write_desc_len;
s_axis_write_desc_axis_if.req.t.data[WD_TAG_LSB +: TAG_WIDTH] = s_axis_write_desc_tag;
end
assign s_axis_write_desc_ready = s_axis_write_desc_axis_if.resp.ready;
always_comb begin
m_axis_write_desc_status_len = m_axis_write_desc_status_axis_if.req.t.data[WDS_LEN_LSB +: LEN_WIDTH];
m_axis_write_desc_status_tag = m_axis_write_desc_status_axis_if.req.t.data[WDS_TAG_LSB +: TAG_WIDTH];
m_axis_write_desc_status_id = m_axis_write_desc_status_axis_if.req.t.data[WDS_ID_LSB +: AXIS_ID_WIDTH];
m_axis_write_desc_status_dest = m_axis_write_desc_status_axis_if.req.t.data[WDS_DEST_LSB +: AXIS_DEST_WIDTH];
m_axis_write_desc_status_user = m_axis_write_desc_status_axis_if.req.t.data[WDS_USER_LSB +: AXIS_USER_WIDTH];
m_axis_write_desc_status_error = m_axis_write_desc_status_axis_if.req.t.data[WDS_ERROR_LSB +: 4];
m_axis_write_desc_status_valid = m_axis_write_desc_status_axis_if.req.t.valid;
end
assign m_axis_write_desc_status_axis_if.resp.ready = 1'b1;
// --------------------------------------------------------------------------
// Flat write stream -> interface.
// --------------------------------------------------------------------------
axis_flat_to_if #(
.DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_s_axis_write_data_flat_to_if (
.s_axis_tdata (s_axis_write_data_tdata),
.s_axis_tkeep (s_axis_write_data_tkeep),
.s_axis_tstrb (s_axis_write_data_tstrb),
.s_axis_tlast (s_axis_write_data_tlast),
.s_axis_tid (s_axis_write_data_tid),
.s_axis_tdest (s_axis_write_data_tdest),
.s_axis_tuser (s_axis_write_data_tuser),
.s_axis_tvalid (s_axis_write_data_tvalid),
.s_axis_tready (s_axis_write_data_tready),
.m_axis (s_axis_write_data_if)
.DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) u_desc_cmd_flat_to_if (
.s_axis_tdata (s_axis_desc_cmd_tdata),
.s_axis_tkeep (s_axis_desc_cmd_tkeep),
.s_axis_tstrb (s_axis_desc_cmd_tstrb),
.s_axis_tlast (s_axis_desc_cmd_tlast),
.s_axis_tid (s_axis_desc_cmd_tid),
.s_axis_tdest (s_axis_desc_cmd_tdest),
.s_axis_tuser (s_axis_desc_cmd_tuser),
.s_axis_tvalid (s_axis_desc_cmd_tvalid),
.s_axis_tready (s_axis_desc_cmd_tready),
.m_axis (desc_cmd_if)
);
axis_if_to_flat #(
.DATA_W (DESC_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH)
) u_desc_status_if_to_flat (
.s_axis (desc_status_if),
.m_axis_tdata (m_axis_desc_status_tdata),
.m_axis_tkeep (m_axis_desc_status_tkeep),
.m_axis_tstrb (m_axis_desc_status_tstrb),
.m_axis_tlast (m_axis_desc_status_tlast),
.m_axis_tid (m_axis_desc_status_tid),
.m_axis_tdest (m_axis_desc_status_tdest),
.m_axis_tuser (m_axis_desc_status_tuser),
.m_axis_tvalid (m_axis_desc_status_tvalid),
.m_axis_tready (m_axis_desc_status_tready)
);
// Wrapper read stream interface -> flat stream.
axis_if_to_flat #(
.DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_m_axis_read_data_if_to_flat (
.s_axis (m_axis_read_data_if),
) u_read_data_if_to_flat (
.s_axis (read_data_if),
.m_axis_tdata (m_axis_read_data_tdata),
.m_axis_tkeep (m_axis_read_data_tkeep),
.m_axis_tstrb (m_axis_read_data_tstrb),
@ -372,63 +254,23 @@ module tb_axi_dma_axis_compat #(
.m_axis_tready (m_axis_read_data_tready)
);
// Wrapper AXI interface -> flat AXI memory bus.
axi4_if_to_flat #(
.ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
) u_m_axi_if_to_flat (
.s_axi (m_axi_if),
.m_axi_awid (m_axi_awid),
.m_axi_awaddr (m_axi_awaddr),
.m_axi_awlen (m_axi_awlen),
.m_axi_awsize (m_axi_awsize),
.m_axi_awburst (m_axi_awburst),
.m_axi_awlock (m_axi_awlock),
.m_axi_awcache (m_axi_awcache),
.m_axi_awprot (m_axi_awprot),
.m_axi_awqos (m_axi_awqos),
.m_axi_awregion (m_axi_awregion),
.m_axi_awuser (m_axi_awuser),
.m_axi_awvalid (m_axi_awvalid),
.m_axi_awready (m_axi_awready),
.m_axi_wdata (m_axi_wdata),
.m_axi_wstrb (m_axi_wstrb),
.m_axi_wlast (m_axi_wlast),
.m_axi_wuser (m_axi_wuser),
.m_axi_wvalid (m_axi_wvalid),
.m_axi_wready (m_axi_wready),
.m_axi_bid (m_axi_bid),
.m_axi_bresp (m_axi_bresp),
.m_axi_buser (m_axi_buser),
.m_axi_bvalid (m_axi_bvalid),
.m_axi_bready (m_axi_bready),
.m_axi_arid (m_axi_arid),
.m_axi_araddr (m_axi_araddr),
.m_axi_arlen (m_axi_arlen),
.m_axi_arsize (m_axi_arsize),
.m_axi_arburst (m_axi_arburst),
.m_axi_arlock (m_axi_arlock),
.m_axi_arcache (m_axi_arcache),
.m_axi_arprot (m_axi_arprot),
.m_axi_arqos (m_axi_arqos),
.m_axi_arregion (m_axi_arregion),
.m_axi_aruser (m_axi_aruser),
.m_axi_arvalid (m_axi_arvalid),
.m_axi_arready (m_axi_arready),
.m_axi_rid (m_axi_rid),
.m_axi_rdata (m_axi_rdata),
.m_axi_rresp (m_axi_rresp),
.m_axi_rlast (m_axi_rlast),
.m_axi_ruser (m_axi_ruser),
.m_axi_rvalid (m_axi_rvalid),
.m_axi_rready (m_axi_rready)
axis_flat_to_if #(
.DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_write_data_flat_to_if (
.s_axis_tdata (s_axis_write_data_tdata),
.s_axis_tkeep (s_axis_write_data_tkeep),
.s_axis_tstrb (s_axis_write_data_tstrb),
.s_axis_tlast (s_axis_write_data_tlast),
.s_axis_tid (s_axis_write_data_tid),
.s_axis_tdest (s_axis_write_data_tdest),
.s_axis_tuser (s_axis_write_data_tuser),
.s_axis_tvalid (s_axis_write_data_tvalid),
.s_axis_tready (s_axis_write_data_tready),
.m_axis (write_data_if)
);
axi_dma_axis_compat #(
@ -451,27 +293,76 @@ module tb_axi_dma_axis_compat #(
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
.ENABLE_UNALIGNED (ENABLE_UNALIGNED),
.DESC_CMD_DATA_WIDTH (DESC_CMD_DATA_WIDTH),
.DESC_STATUS_DATA_WIDTH (DESC_STATUS_DATA_WIDTH),
.DESC_STATUS_USER_WIDTH (DESC_STATUS_USER_WIDTH)
) u_dut (
.clk (clk),
.rst (rst),
.s_axis_read_desc (s_axis_read_desc_axis_if),
.m_axis_read_desc_status (m_axis_read_desc_status_axis_if),
.m_axis_read_data (m_axis_read_data_if),
.s_axis_write_desc (s_axis_write_desc_axis_if),
.m_axis_write_desc_status (m_axis_write_desc_status_axis_if),
.s_axis_write_data (s_axis_write_data_if),
.s_axis_desc_cmd (desc_cmd_if),
.m_axis_desc_status (desc_status_if),
.m_axis_read_data (read_data_if),
.s_axis_write_data (write_data_if),
.m_axi (m_axi_if),
.read_enable (read_enable),
.write_enable (write_enable),
.write_abort (write_abort)
);
axi4_if_to_flat #(
.ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
) u_m_axi_if_to_flat (
.s_axi (m_axi_if),
.m_axi_awid (m_axi_awid),
.m_axi_awaddr (m_axi_awaddr),
.m_axi_awlen (m_axi_awlen),
.m_axi_awsize (m_axi_awsize),
.m_axi_awburst (m_axi_awburst),
.m_axi_awlock (m_axi_awlock),
.m_axi_awcache (m_axi_awcache),
.m_axi_awprot (m_axi_awprot),
.m_axi_awqos (m_axi_awqos),
.m_axi_awregion (m_axi_awregion),
.m_axi_awuser (m_axi_awuser),
.m_axi_awvalid (m_axi_awvalid),
.m_axi_awready (m_axi_awready),
.m_axi_wdata (m_axi_wdata),
.m_axi_wstrb (m_axi_wstrb),
.m_axi_wlast (m_axi_wlast),
.m_axi_wuser (m_axi_wuser),
.m_axi_wvalid (m_axi_wvalid),
.m_axi_wready (m_axi_wready),
.m_axi_bid (m_axi_bid),
.m_axi_bresp (m_axi_bresp),
.m_axi_buser (m_axi_buser),
.m_axi_bvalid (m_axi_bvalid),
.m_axi_bready (m_axi_bready),
.m_axi_arid (m_axi_arid),
.m_axi_araddr (m_axi_araddr),
.m_axi_arlen (m_axi_arlen),
.m_axi_arsize (m_axi_arsize),
.m_axi_arburst (m_axi_arburst),
.m_axi_arlock (m_axi_arlock),
.m_axi_arcache (m_axi_arcache),
.m_axi_arprot (m_axi_arprot),
.m_axi_arqos (m_axi_arqos),
.m_axi_arregion (m_axi_arregion),
.m_axi_aruser (m_axi_aruser),
.m_axi_arvalid (m_axi_arvalid),
.m_axi_arready (m_axi_arready),
.m_axi_rid (m_axi_rid),
.m_axi_rdata (m_axi_rdata),
.m_axi_rresp (m_axi_rresp),
.m_axi_rlast (m_axi_rlast),
.m_axi_ruser (m_axi_ruser),
.m_axi_rvalid (m_axi_rvalid),
.m_axi_rready (m_axi_rready)
);
endmodule : tb_axi_dma_axis_compat
`default_nettype wire

View File

@ -1,33 +1,127 @@
import itertools
import logging
import os
import cocotb
try:
import pytest
except ImportError: # pytest is only needed for the optional cocotb-test entrypoint
pytest = None
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.regression import TestFactory
from cocotb.triggers import RisingEdge, Combine
from cocotbext.axi import AxiBus, AxiRam
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
from cocotbext.axi.stream import define_stream
DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream(
"Desc",
signals=["addr", "len", "tag", "valid", "ready"],
optional_signals=["id", "dest", "user"],
)
AXI_ADDR_WIDTH = int(os.getenv("PARAM_AXI_ADDR_WIDTH", "16"))
LEN_WIDTH = int(os.getenv("PARAM_LEN_WIDTH", "20"))
TAG_WIDTH = int(os.getenv("PARAM_TAG_WIDTH", "8"))
AXIS_ID_WIDTH = int(os.getenv("PARAM_AXIS_ID_WIDTH", "8"))
AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8"))
AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1"))
DescStatusBus, DescStatusTransaction, DescStatusSource, DescStatusSink, DescStatusMonitor = define_stream(
"DescStatus",
signals=["tag", "error", "valid"],
optional_signals=["len", "id", "dest", "user"],
)
READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH
WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH
DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) // 8) * 8
DESC_CMD_BYTES = DESC_CMD_DATA_WIDTH // 8
READ_STATUS_WIDTH = TAG_WIDTH + 4
WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + \
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4
DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH
DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) // 8) * 8
DESC_STATUS_BYTES = DESC_STATUS_DATA_WIDTH // 8
RD_ADDR_LSB = 0
RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH
RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH
RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH
RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH
RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH
WR_BASE_LSB = READ_DESC_WIDTH
WR_ADDR_LSB = WR_BASE_LSB
WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH
WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH
RD_STS_TAG_LSB = 0
RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH
WR_STS_BASE_LSB = READ_STATUS_WIDTH
WR_STS_LEN_LSB = WR_STS_BASE_LSB
WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH
WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH
WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH
WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH
WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH
def mask(width):
return (1 << width) - 1
def put(value, lsb, width, field):
value &= ~(mask(width) << lsb)
value |= (int(field) & mask(width)) << lsb
return value
def get(value, lsb, width):
return (int(value) >> lsb) & mask(width)
def pack_desc_cmd(
*,
read_addr=0,
read_len=0,
read_tag=0,
read_id=0,
read_dest=0,
read_user=0,
write_addr=0,
write_len=0,
write_tag=0,
):
value = 0
value = put(value, RD_ADDR_LSB, AXI_ADDR_WIDTH, read_addr)
value = put(value, RD_LEN_LSB, LEN_WIDTH, read_len)
value = put(value, RD_TAG_LSB, TAG_WIDTH, read_tag)
value = put(value, RD_ID_LSB, AXIS_ID_WIDTH, read_id)
value = put(value, RD_DEST_LSB, AXIS_DEST_WIDTH, read_dest)
value = put(value, RD_USER_LSB, AXIS_USER_WIDTH, read_user)
value = put(value, WR_ADDR_LSB, AXI_ADDR_WIDTH, write_addr)
value = put(value, WR_LEN_LSB, LEN_WIDTH, write_len)
value = put(value, WR_TAG_LSB, TAG_WIDTH, write_tag)
return value.to_bytes(DESC_CMD_BYTES, byteorder="little")
def frame_to_int(frame):
return int.from_bytes(bytes(frame.tdata), byteorder="little")
def frame_tuser(frame):
try:
return int(frame.tuser)
except TypeError:
return int(frame.tuser[0]) if frame.tuser else 0
def unpack_status(frame):
value = frame_to_int(frame)
tuser = frame_tuser(frame)
return {
"read_valid": bool(tuser & 0x1),
"write_valid": bool(tuser & 0x2),
"read_tag": get(value, RD_STS_TAG_LSB, TAG_WIDTH),
"read_error": get(value, RD_STS_ERROR_LSB, 4),
"write_len": get(value, WR_STS_LEN_LSB, LEN_WIDTH),
"write_tag": get(value, WR_STS_TAG_LSB, TAG_WIDTH),
"write_id": get(value, WR_STS_ID_LSB, AXIS_ID_WIDTH),
"write_dest": get(value, WR_STS_DEST_LSB, AXIS_DEST_WIDTH),
"write_user": get(value, WR_STS_USER_LSB, AXIS_USER_WIDTH),
"write_error": get(value, WR_STS_ERROR_LSB, 4),
}
class TB:
@ -38,25 +132,14 @@ class TB:
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
# Cocotb still uses the original flat Forencich descriptor/status prefixes;
# the SV top packs/unpacks these into ordinary axis_if streams.
self.read_desc_source = DescSource(
DescBus.from_prefix(dut, "s_axis_read_desc"), dut.clk, dut.rst
self.desc_cmd_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst
)
self.read_desc_status_sink = DescStatusSink(
DescStatusBus.from_prefix(
dut, "m_axis_read_desc_status"), dut.clk, dut.rst
)
self.write_desc_source = DescSource(
DescBus.from_prefix(dut, "s_axis_write_desc"), dut.clk, dut.rst
)
self.write_desc_status_sink = DescStatusSink(
DescStatusBus.from_prefix(
dut, "m_axis_write_desc_status"), dut.clk, dut.rst
self.desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix(
dut, "m_axis_desc_status"), dut.clk, dut.rst
)
# Data streams are flat from cocotb's point of view, but the SV top
# sends them through axis_flat_to_if/axis_if_to_flat before/after DUT.
self.read_data_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_read_data"), dut.clk, dut.rst
)
@ -65,7 +148,6 @@ class TB:
dut, "s_axis_write_data"), dut.clk, dut.rst
)
# AXI memory model. The SV top routes this through the axi4_if adapters.
self.axi_ram = AxiRam(AxiBus.from_prefix(
dut, "m_axi"), dut.clk, dut.rst, size=2**16)
@ -73,22 +155,7 @@ class TB:
dut.write_enable.setimmediatevalue(0)
dut.write_abort.setimmediatevalue(0)
def set_idle_generator(self, generator=None):
if generator:
self.write_desc_source.set_pause_generator(generator())
self.write_data_source.set_pause_generator(generator())
self.read_desc_source.set_pause_generator(generator())
self.axi_ram.write_if.b_channel.set_pause_generator(generator())
self.axi_ram.read_if.r_channel.set_pause_generator(generator())
def set_backpressure_generator(self, generator=None):
if generator:
self.read_data_sink.set_pause_generator(generator())
self.axi_ram.write_if.aw_channel.set_pause_generator(generator())
self.axi_ram.write_if.w_channel.set_pause_generator(generator())
self.axi_ram.read_if.ar_channel.set_pause_generator(generator())
async def cycle_reset(self):
async def reset(self):
self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
@ -99,264 +166,149 @@ class TB:
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
async def send_desc_cmd(self, payload):
await self.desc_cmd_source.send(AxiStreamFrame(payload))
async def run_test_write(dut, data_in=None, idle_inserter=None, backpressure_inserter=None):
"""DMA write path stress test adapted from Forencich's axi_dma test."""
@cocotb.test()
async def test_axis_compat_write_path(dut):
tb = TB(dut)
byte_lanes = tb.axi_ram.write_if.byte_lanes
step_size = 1 if int(
os.getenv("PARAM_ENABLE_UNALIGNED", "0")) else byte_lanes
tag_count = 2 ** len(tb.write_desc_source.bus.tag)
cur_tag = 1
await tb.cycle_reset()
tb.set_idle_generator(idle_inserter)
tb.set_backpressure_generator(backpressure_inserter)
await tb.reset()
dut.write_enable.value = 1
dut.read_enable.value = 0
for length in list(range(1, byte_lanes * 4 + 1)) + [128]:
offsets = list(range(0, byte_lanes * 2, step_size))
offsets += list(range(4096 - byte_lanes * 2, 4096, step_size))
addr = 0x1000
tag = 0x12
data = bytes(range(1, 33))
for offset in offsets:
for diff in [-8, -2, -1, 0, 1, 2, 8]:
if length + diff < 1:
continue
tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32))
tb.log.info("write: length=%d offset=%d diff=%d",
length, offset, diff)
cmd = pack_desc_cmd(write_addr=addr, write_len=len(data), write_tag=tag)
addr = offset + 0x1000
expected_data = bytearray([x % 256 for x in range(length)])
stream_data = bytearray(
[x % 256 for x in range(length + diff)])
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,
),
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(data, tid=tag))
)
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
)
await cmd_task
await data_task
cur_tag = (cur_tag + 1) % tag_count
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
assert not status["read_valid"]
assert status["write_valid"]
assert status["write_tag"] == tag
assert status["write_len"] == len(data)
assert status["write_id"] == tag
assert status["write_error"] == 0
assert tb.axi_ram.read(addr - 8, len(data) +
16) == b"\xaa" * 8 + data + b"\xaa" * 8
async def run_test_read(dut, data_in=None, idle_inserter=None, backpressure_inserter=None):
"""DMA read path stress test adapted from Forencich's axi_dma test."""
@cocotb.test()
async def test_axis_compat_read_path(dut):
tb = TB(dut)
await tb.reset()
byte_lanes = tb.axi_ram.read_if.byte_lanes
step_size = 1 if int(
os.getenv("PARAM_ENABLE_UNALIGNED", "0")) else byte_lanes
tag_count = 2 ** len(tb.read_desc_source.bus.tag)
cur_tag = 1
await tb.cycle_reset()
tb.set_idle_generator(idle_inserter)
tb.set_backpressure_generator(backpressure_inserter)
dut.write_enable.value = 0
dut.read_enable.value = 1
for length in list(range(1, byte_lanes * 4 + 1)) + [128]:
offsets = list(range(0, byte_lanes * 2, step_size))
offsets += list(range(4096 - byte_lanes * 2, 4096, step_size))
addr = 0x1100
tag = 0x34
data = bytes((x * 3) % 256 for x in range(48))
for offset in offsets:
tb.log.info("read: length=%d offset=%d", length, offset)
tb.axi_ram.write(addr, data)
addr = offset + 0x1000
test_data = bytearray([x % 256 for x in range(length)])
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,
),
cmd = pack_desc_cmd(
read_addr=addr,
read_len=len(data),
read_tag=tag,
read_id=tag,
read_dest=0,
read_user=0,
)
await tb.read_desc_source.send(
DescTransaction(addr=addr, len=len(
test_data), tag=cur_tag, id=cur_tag)
await tb.send_desc_cmd(cmd)
data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == data
assert int(data_frame.tid) == tag
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
assert status["read_valid"]
assert not status["write_valid"]
assert status["read_tag"] == tag
assert status["read_error"] == 0
@cocotb.test()
async def test_axis_compat_read_and_write_same_command(dut):
tb = TB(dut)
await tb.reset()
dut.write_enable.value = 1
dut.read_enable.value = 1
read_addr = 0x1200
write_addr = 0x1300
read_tag = 0x41
write_tag = 0x42
read_data = bytes((x + 7) % 256 for x in range(24))
write_data = bytes((x + 100) % 256 for x in range(24))
tb.axi_ram.write(read_addr, read_data)
tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16))
cmd = pack_desc_cmd(
read_addr=read_addr,
read_len=len(read_data),
read_tag=read_tag,
read_id=read_tag,
write_addr=write_addr,
write_len=len(write_data),
write_tag=write_tag,
)
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_axis_compat.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_axis_compat_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")
)
dma_if_rtl_dir = os.environ.get(
"DMA_IF_RTL_DIR", os.path.join(project_root, "rtl", "dma")
)
wrapper_rtl_dir = os.environ.get(
"WRAPPER_RTL_DIR", os.path.join(project_root, "rtl", "wrappers")
)
forencich_rtl_dir = os.environ.get(
"FORENCICH_AXI_RTL_DIR",
os.path.join(project_root, "external", "verilog-axi", "rtl"),
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag))
)
dut = "tb_axi_dma_axis_compat"
module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut
await cmd_task
await data_task
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,
}
data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == read_data
assert int(data_frame.tid) == read_tag
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
# status might be in 2 beats, depending on timings
seen_read = False
seen_write = False
verilog_sources = [
os.path.join(axi_if_rtl_dir, "axi_pkg.sv"),
os.path.join(axi_if_rtl_dir, "axi_if.sv"),
os.path.join(axi_if_rtl_dir, "axis_if.sv"),
os.path.join(axi_if_rtl_dir, "axi4_flat_to_if.sv"),
os.path.join(axi_if_rtl_dir, "axi4_if_to_flat.sv"),
os.path.join(axi_if_rtl_dir, "axis_flat_to_if.sv"),
os.path.join(axi_if_rtl_dir, "axis_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_desc_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_read_desc_status_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_read_desc_status_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_write_desc_status_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_write_desc_status_if_to_flat.sv"),
os.path.join(forencich_rtl_dir, "axi_dma.v"),
os.path.join(forencich_rtl_dir, "axi_dma_rd.v"),
os.path.join(forencich_rtl_dir, "axi_dma_wr.v"),
os.path.join(wrapper_rtl_dir, "axi_dma_if_wrapper.sv"),
os.path.join(wrapper_rtl_dir, "axi_dma_axis_compat.sv"),
os.path.join(tests_dir, "tb_axi_dma_axis_compat.sv"),
]
for _ in range(2):
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
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))
if status["read_valid"]:
assert status["read_tag"] == read_tag
assert status["read_error"] == 0
seen_read = True
extra_args = []
if os.getenv("SIM", "verilator") == "verilator":
extra_args += ["--trace-structs"]
if status["write_valid"]:
assert status["write_tag"] == write_tag
assert status["write_len"] == len(write_data)
assert status["write_error"] == 0
seen_write = True
cocotb_test.simulator.run(
python_search=[tests_dir],
verilog_sources=verilog_sources,
includes=[axi_if_rtl_dir, dma_if_rtl_dir,
wrapper_rtl_dir, forencich_rtl_dir],
toplevel=toplevel,
module=module,
parameters=parameters,
sim_build=sim_build,
extra_env=extra_env,
waves=bool(int(os.getenv("WAVES", "0"))),
extra_args=extra_args,
)
if seen_read and seen_write:
break
assert seen_read
assert seen_write
assert tb.axi_ram.read(write_addr - 8, len(write_data) +
16) == b"\xaa" * 8 + write_data + b"\xaa" * 8