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. // read_enable and write_enable select which descriptor is used
// 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.
`default_nettype none `default_nettype none
@ -41,102 +32,83 @@ module axi_dma_axis_compat #(
parameter int unsigned ENABLE_SG = 0, parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0, parameter int unsigned ENABLE_UNALIGNED = 0,
// Payload widths for descriptor/status AXIS streams. // Raw packed command/status widths. rounded up to full bytes
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_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 WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned READ_DESC_STATUS_PAYLOAD_WIDTH = TAG_WIDTH + 4, parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH,
parameter int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8, parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned WRITE_DESC_PAYLOAD_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH, parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8,
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 READ_STATUS_WIDTH = TAG_WIDTH + 4,
parameter int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8 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 clk,
input logic rst, input logic rst,
/* // Unified command stream. One beat carries both read and write descriptors.
* AXI read descriptor input, encoded as one AXIS beat. // Which parts are used is selected by read_enable/write_enable.
* tdata layout, LSB first: axis_if.slave s_axis_desc_cmd,
* addr | len | tag | id | dest | user
*/
axis_if.slave s_axis_read_desc,
/* // Unified status stream. One beat can carry read status, write status, or both.
* AXI read descriptor status output, encoded as one AXIS beat. // m_axis_desc_status.tuser[0] = read status valid in this beat
* tdata layout, LSB first: // m_axis_desc_status.tuser[1] = write status valid in this beat
* tag | error axis_if.master m_axis_desc_status,
*/
axis_if.master m_axis_read_desc_status,
/* // Original DMA data streams and AXI memory master, still exposed as normal
* AXI stream read data output. // generic interfaces.
*/
axis_if.master m_axis_read_data, 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, axis_if.slave s_axis_write_data,
/*
* AXI memory master interface.
*/
axi4_if.master m_axi, axi4_if.master m_axi,
/*
* Configuration.
*/
input logic read_enable, input logic read_enable,
input logic write_enable, input logic write_enable,
input logic write_abort input logic write_abort
); );
localparam int unsigned RD_ADDR_LSB = 0; // --------------------------------------------------------------------------
localparam int unsigned RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH; // packing layout
localparam int unsigned RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH; // --------------------------------------------------------------------------
localparam int unsigned RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH; // s_axis_desc_cmd.tdata:
localparam int unsigned RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH; // [READ_DESC_*] read descriptor
localparam int unsigned RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH; // [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 RDS_TAG_LSB = 0; localparam int unsigned RD_ADDR_LSB = 0;
localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH; 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;
localparam int unsigned WD_ADDR_LSB = 0; localparam int unsigned WR_BASE_LSB = READ_DESC_WIDTH;
localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH; localparam int unsigned WR_ADDR_LSB = WR_BASE_LSB;
localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_WIDTH; 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 WDS_LEN_LSB = 0; localparam int unsigned RD_STS_TAG_LSB = 0;
localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH; localparam int unsigned RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_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 READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8; localparam int unsigned WR_STS_BASE_LSB = READ_STATUS_WIDTH;
localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8; localparam int unsigned WR_STS_LEN_LSB = WR_STS_BASE_LSB;
localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8; localparam int unsigned WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH;
localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8; 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;
logic rstn; wire rstn = ~rst;
assign rstn = ~rst;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Internal DMA-specific descriptor/status interfaces expected by // Internal DMA-specific descriptor/status interfaces
// axi_dma_if_wrapper.
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
axi_dma_read_desc_if #( 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]; wire cmd_valid = s_axis_desc_cmd.req.t.valid;
assign read_desc_if.req.len = s_axis_read_desc.req.t.data[RD_LEN_LSB +: LEN_WIDTH]; wire cmd_ready = (!read_enable || read_desc_if.resp.ready) &&
assign read_desc_if.req.tag = s_axis_read_desc.req.t.data[RD_TAG_LSB +: TAG_WIDTH]; (!write_enable || write_desc_if.resp.ready);
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]; // Only assert a descriptor valid when the complete command can be accepted.
assign read_desc_if.req.user = s_axis_read_desc.req.t.data[RD_USER_LSB +: AXIS_USER_WIDTH]; // This prevents partial consumption when both read and write are enabled.
assign read_desc_if.req.valid = s_axis_read_desc.req.t.valid; assign read_desc_if.req.valid = cmd_valid && read_enable && (!write_enable || write_desc_if.resp.ready);
assign s_axis_read_desc.resp.ready = read_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. // Lower DMA wrapper
// --------------------------------------------------------------------------
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.
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
axi_dma_if_wrapper #( axi_dma_if_wrapper #(
@ -307,6 +187,7 @@ module axi_dma_axis_compat #(
.AXI_ID_WIDTH (AXI_ID_WIDTH), .AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_USER_WIDTH (AXI_USER_WIDTH), .AXI_USER_WIDTH (AXI_USER_WIDTH),
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN), .AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH), .AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE), .AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH), .AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
@ -317,29 +198,134 @@ module axi_dma_axis_compat #(
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH), .AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
.AXIS_USER_ENABLE (AXIS_USER_ENABLE), .AXIS_USER_ENABLE (AXIS_USER_ENABLE),
.AXIS_USER_WIDTH (AXIS_USER_WIDTH), .AXIS_USER_WIDTH (AXIS_USER_WIDTH),
.LEN_WIDTH (LEN_WIDTH), .LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH), .TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG), .ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED) .ENABLE_UNALIGNED (ENABLE_UNALIGNED)
) u_axi_dma_if_wrapper ( ) u_axi_dma_if_wrapper (
.clk (clk), .clk (clk),
.rst (rst), .rst (rst),
.s_axis_read_desc (read_desc_if), .s_axis_read_desc (read_desc_if),
.m_axis_read_desc_status (read_desc_status_if), .m_axis_read_desc_status (read_desc_status_if),
.m_axis_read_data (m_axis_read_data), .m_axis_read_data (m_axis_read_data),
.s_axis_write_desc (write_desc_if), .s_axis_write_desc (write_desc_if),
.m_axis_write_desc_status (write_desc_status_if), .m_axis_write_desc_status (write_desc_status_if),
.s_axis_write_data (s_axis_write_data), .s_axis_write_data (s_axis_write_data),
.m_axi (m_axi), .m_axi (m_axi),
.read_enable (read_enable), .read_enable (read_enable),
.write_enable (write_enable), .write_enable (write_enable),
.write_abort (write_abort) .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 endmodule : axi_dma_axis_compat
`default_nettype wire `default_nettype wire

View File

@ -24,214 +24,143 @@ module tb_axi_dma_axis_compat #(
parameter int unsigned LEN_WIDTH = 20, parameter int unsigned LEN_WIDTH = 20,
parameter int unsigned TAG_WIDTH = 8, parameter int unsigned TAG_WIDTH = 8,
parameter int unsigned ENABLE_SG = 0, parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0 parameter int unsigned ENABLE_UNALIGNED = 0,
)();
// cocotb drives these directly parameter int unsigned READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH,
logic clk; parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
logic rst; parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH,
logic rstn; 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,
// -------------------------------------------------------------------------- input logic read_enable,
// Descriptor/status payload widths and tdata layouts. 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; // Unified descriptor command AXIS, flat for cocotb
localparam int unsigned READ_DESC_AXIS_DATA_WIDTH = ((READ_DESC_PAYLOAD_WIDTH + 7) / 8) * 8; input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata,
localparam int unsigned READ_DESC_AXIS_KEEP_WIDTH = READ_DESC_AXIS_DATA_WIDTH / 8; 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; // Unified descriptor status AXIS, flat for cocotb
localparam int unsigned READ_DESC_STATUS_AXIS_DATA_WIDTH = ((READ_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8; output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata,
localparam int unsigned READ_DESC_STATUS_AXIS_KEEP_WIDTH = READ_DESC_STATUS_AXIS_DATA_WIDTH / 8; 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; // Read data AXIS output
localparam int unsigned WRITE_DESC_AXIS_DATA_WIDTH = ((WRITE_DESC_PAYLOAD_WIDTH + 7) / 8) * 8; output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata,
localparam int unsigned WRITE_DESC_AXIS_KEEP_WIDTH = WRITE_DESC_AXIS_DATA_WIDTH / 8; 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; // Write data AXIS input
localparam int unsigned WRITE_DESC_STATUS_AXIS_DATA_WIDTH = ((WRITE_DESC_STATUS_PAYLOAD_WIDTH + 7) / 8) * 8; input logic [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata,
localparam int unsigned WRITE_DESC_STATUS_AXIS_KEEP_WIDTH = WRITE_DESC_STATUS_AXIS_DATA_WIDTH / 8; 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; // AXI memory master, flat for cocotb AxiRam
localparam int unsigned RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH; output logic [AXI_ID_WIDTH-1:0] m_axi_awid,
localparam int unsigned RD_TAG_LSB = RD_LEN_LSB + LEN_WIDTH; output logic [AXI_ADDR_WIDTH-1:0] m_axi_awaddr,
localparam int unsigned RD_ID_LSB = RD_TAG_LSB + TAG_WIDTH; output logic [7:0] m_axi_awlen,
localparam int unsigned RD_DEST_LSB = RD_ID_LSB + AXIS_ID_WIDTH; output logic [2:0] m_axi_awsize,
localparam int unsigned RD_USER_LSB = RD_DEST_LSB + AXIS_DEST_WIDTH; 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; output logic [AXI_DATA_WIDTH-1:0] m_axi_wdata,
localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_WIDTH; 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; input logic [AXI_ID_WIDTH-1:0] m_axi_bid,
localparam int unsigned WD_LEN_LSB = WD_ADDR_LSB + AXI_ADDR_WIDTH; input logic [1:0] m_axi_bresp,
localparam int unsigned WD_TAG_LSB = WD_LEN_LSB + LEN_WIDTH; 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; output logic [AXI_ID_WIDTH-1:0] m_axi_arid,
localparam int unsigned WDS_TAG_LSB = WDS_LEN_LSB + LEN_WIDTH; output logic [AXI_ADDR_WIDTH-1:0] m_axi_araddr,
localparam int unsigned WDS_ID_LSB = WDS_TAG_LSB + TAG_WIDTH; output logic [7:0] m_axi_arlen,
localparam int unsigned WDS_DEST_LSB = WDS_ID_LSB + AXIS_ID_WIDTH; output logic [2:0] m_axi_arsize,
localparam int unsigned WDS_USER_LSB = WDS_DEST_LSB + AXIS_DEST_WIDTH; output logic [1:0] m_axi_arburst,
localparam int unsigned WDS_ERROR_LSB = WDS_USER_LSB + AXIS_USER_WIDTH; 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,
// -------------------------------------------------------------------------- input logic [AXI_ID_WIDTH-1:0] m_axi_rid,
// Flat descriptor/status ports visible to cocotb. 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; wire rstn = ~rst;
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 #( axis_if #(
.DATA_W (READ_DESC_AXIS_DATA_WIDTH), .DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (READ_DESC_AXIS_KEEP_WIDTH), .KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (1) .USER_W (1)
) s_axis_read_desc_axis_if ( ) desc_cmd_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
axis_if #( axis_if #(
.DATA_W (READ_DESC_STATUS_AXIS_DATA_WIDTH), .DATA_W (DESC_STATUS_DATA_WIDTH),
.KEEP_W (READ_DESC_STATUS_AXIS_KEEP_WIDTH), .KEEP_W (DESC_STATUS_KEEP_WIDTH),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (1) .USER_W (DESC_STATUS_USER_WIDTH)
) m_axis_read_desc_status_axis_if ( ) desc_status_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 (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
@ -242,7 +171,7 @@ module tb_axi_dma_axis_compat #(
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
) m_axis_read_data_if ( ) read_data_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
@ -253,7 +182,7 @@ module tb_axi_dma_axis_compat #(
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
) s_axis_write_data_if ( ) write_data_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
@ -268,99 +197,52 @@ module tb_axi_dma_axis_compat #(
.aresetn (rstn) .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 #( axis_flat_to_if #(
.DATA_W (AXIS_DATA_WIDTH), .DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH), .KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH), .ID_W (1),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (1),
.USER_W (AXIS_USER_WIDTH) .USER_W (1)
) u_s_axis_write_data_flat_to_if ( ) u_desc_cmd_flat_to_if (
.s_axis_tdata (s_axis_write_data_tdata), .s_axis_tdata (s_axis_desc_cmd_tdata),
.s_axis_tkeep (s_axis_write_data_tkeep), .s_axis_tkeep (s_axis_desc_cmd_tkeep),
.s_axis_tstrb (s_axis_write_data_tstrb), .s_axis_tstrb (s_axis_desc_cmd_tstrb),
.s_axis_tlast (s_axis_write_data_tlast), .s_axis_tlast (s_axis_desc_cmd_tlast),
.s_axis_tid (s_axis_write_data_tid), .s_axis_tid (s_axis_desc_cmd_tid),
.s_axis_tdest (s_axis_write_data_tdest), .s_axis_tdest (s_axis_desc_cmd_tdest),
.s_axis_tuser (s_axis_write_data_tuser), .s_axis_tuser (s_axis_desc_cmd_tuser),
.s_axis_tvalid (s_axis_write_data_tvalid), .s_axis_tvalid (s_axis_desc_cmd_tvalid),
.s_axis_tready (s_axis_write_data_tready), .s_axis_tready (s_axis_desc_cmd_tready),
.m_axis (s_axis_write_data_if) .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 #( axis_if_to_flat #(
.DATA_W (AXIS_DATA_WIDTH), .DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH), .KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH), .ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
) u_m_axis_read_data_if_to_flat ( ) u_read_data_if_to_flat (
.s_axis (m_axis_read_data_if), .s_axis (read_data_if),
.m_axis_tdata (m_axis_read_data_tdata), .m_axis_tdata (m_axis_read_data_tdata),
.m_axis_tkeep (m_axis_read_data_tkeep), .m_axis_tkeep (m_axis_read_data_tkeep),
.m_axis_tstrb (m_axis_read_data_tstrb), .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) .m_axis_tready (m_axis_read_data_tready)
); );
// Wrapper AXI interface -> flat AXI memory bus. axis_flat_to_if #(
axi4_if_to_flat #( .DATA_W (AXIS_DATA_WIDTH),
.ADDR_W (AXI_ADDR_WIDTH), .KEEP_W (AXIS_KEEP_WIDTH),
.DATA_W (AXI_DATA_WIDTH), .ID_W (AXIS_ID_WIDTH),
.ID_W (AXI_ID_WIDTH), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXI_USER_WIDTH) .USER_W (AXIS_USER_WIDTH)
) u_m_axi_if_to_flat ( ) u_write_data_flat_to_if (
.s_axi (m_axi_if), .s_axis_tdata (s_axis_write_data_tdata),
.s_axis_tkeep (s_axis_write_data_tkeep),
.m_axi_awid (m_axi_awid), .s_axis_tstrb (s_axis_write_data_tstrb),
.m_axi_awaddr (m_axi_awaddr), .s_axis_tlast (s_axis_write_data_tlast),
.m_axi_awlen (m_axi_awlen), .s_axis_tid (s_axis_write_data_tid),
.m_axi_awsize (m_axi_awsize), .s_axis_tdest (s_axis_write_data_tdest),
.m_axi_awburst (m_axi_awburst), .s_axis_tuser (s_axis_write_data_tuser),
.m_axi_awlock (m_axi_awlock), .s_axis_tvalid (s_axis_write_data_tvalid),
.m_axi_awcache (m_axi_awcache), .s_axis_tready (s_axis_write_data_tready),
.m_axi_awprot (m_axi_awprot), .m_axis (write_data_if)
.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_axis_compat #( axi_dma_axis_compat #(
@ -451,25 +293,74 @@ module tb_axi_dma_axis_compat #(
.LEN_WIDTH (LEN_WIDTH), .LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH), .TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG), .ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED) .ENABLE_UNALIGNED (ENABLE_UNALIGNED),
.DESC_CMD_DATA_WIDTH (DESC_CMD_DATA_WIDTH),
.DESC_STATUS_DATA_WIDTH (DESC_STATUS_DATA_WIDTH),
.DESC_STATUS_USER_WIDTH (DESC_STATUS_USER_WIDTH)
) u_dut ( ) u_dut (
.clk (clk), .clk (clk),
.rst (rst), .rst (rst),
.s_axis_desc_cmd (desc_cmd_if),
.m_axis_desc_status (desc_status_if),
.m_axis_read_data (read_data_if),
.s_axis_write_data (write_data_if),
.m_axi (m_axi_if),
.read_enable (read_enable),
.write_enable (write_enable),
.write_abort (write_abort)
);
.s_axis_read_desc (s_axis_read_desc_axis_if), axi4_if_to_flat #(
.m_axis_read_desc_status (m_axis_read_desc_status_axis_if), .ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.m_axis_read_data (m_axis_read_data_if), .ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
.s_axis_write_desc (s_axis_write_desc_axis_if), ) u_m_axi_if_to_flat (
.m_axis_write_desc_status (m_axis_write_desc_status_axis_if), .s_axi (m_axi_if),
.m_axi_awid (m_axi_awid),
.s_axis_write_data (s_axis_write_data_if), .m_axi_awaddr (m_axi_awaddr),
.m_axi (m_axi_if), .m_axi_awlen (m_axi_awlen),
.m_axi_awsize (m_axi_awsize),
.read_enable (read_enable), .m_axi_awburst (m_axi_awburst),
.write_enable (write_enable), .m_axi_awlock (m_axi_awlock),
.write_abort (write_abort) .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 endmodule : tb_axi_dma_axis_compat

View File

@ -1,33 +1,127 @@
import itertools
import logging import logging
import os import os
import cocotb 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.clock import Clock
from cocotb.triggers import RisingEdge from cocotb.triggers import RisingEdge, Combine
from cocotb.regression import TestFactory
from cocotbext.axi import AxiBus, AxiRam from cocotbext.axi import AxiBus, AxiRam
from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink from cocotbext.axi import AxiStreamBus, AxiStreamFrame, AxiStreamSource, AxiStreamSink
from cocotbext.axi.stream import define_stream
DescBus, DescTransaction, DescSource, DescSink, DescMonitor = define_stream( AXI_ADDR_WIDTH = int(os.getenv("PARAM_AXI_ADDR_WIDTH", "16"))
"Desc", LEN_WIDTH = int(os.getenv("PARAM_LEN_WIDTH", "20"))
signals=["addr", "len", "tag", "valid", "ready"], TAG_WIDTH = int(os.getenv("PARAM_TAG_WIDTH", "8"))
optional_signals=["id", "dest", "user"], 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( READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \
"DescStatus", AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH
signals=["tag", "error", "valid"], WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
optional_signals=["len", "id", "dest", "user"], 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: class TB:
@ -38,25 +132,14 @@ class TB:
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start()) cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
# Cocotb still uses the original flat Forencich descriptor/status prefixes; self.desc_cmd_source = AxiStreamSource(
# the SV top packs/unpacks these into ordinary axis_if streams. AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst
self.read_desc_source = DescSource(
DescBus.from_prefix(dut, "s_axis_read_desc"), dut.clk, dut.rst
) )
self.read_desc_status_sink = DescStatusSink( self.desc_status_sink = AxiStreamSink(
DescStatusBus.from_prefix( AxiStreamBus.from_prefix(
dut, "m_axis_read_desc_status"), dut.clk, dut.rst dut, "m_axis_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( self.read_data_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_read_data"), dut.clk, dut.rst AxiStreamBus.from_prefix(dut, "m_axis_read_data"), dut.clk, dut.rst
) )
@ -65,7 +148,6 @@ class TB:
dut, "s_axis_write_data"), dut.clk, dut.rst 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( self.axi_ram = AxiRam(AxiBus.from_prefix(
dut, "m_axi"), dut.clk, dut.rst, size=2**16) dut, "m_axi"), dut.clk, dut.rst, size=2**16)
@ -73,22 +155,7 @@ class TB:
dut.write_enable.setimmediatevalue(0) dut.write_enable.setimmediatevalue(0)
dut.write_abort.setimmediatevalue(0) dut.write_abort.setimmediatevalue(0)
def set_idle_generator(self, generator=None): async def reset(self):
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) self.dut.rst.setimmediatevalue(0)
await RisingEdge(self.dut.clk) await RisingEdge(self.dut.clk)
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)
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) tb = TB(dut)
await tb.reset()
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 dut.write_enable.value = 1
dut.read_enable.value = 0
for length in list(range(1, byte_lanes * 4 + 1)) + [128]: addr = 0x1000
offsets = list(range(0, byte_lanes * 2, step_size)) tag = 0x12
offsets += list(range(4096 - byte_lanes * 2, 4096, step_size)) data = bytes(range(1, 33))
for offset in offsets: tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32))
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", cmd = pack_desc_cmd(write_addr=addr, write_len=len(data), write_tag=tag)
length, offset, diff)
addr = offset + 0x1000 cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
expected_data = bytearray([x % 256 for x in range(length)]) data_task = cocotb.start_soon(
stream_data = bytearray( tb.write_data_source.send(AxiStreamFrame(data, tid=tag))
[x % 256 for x in range(length + diff)]) )
tb.axi_ram.write(addr - 128, b"\xaa" * await cmd_task
(len(expected_data) + 256)) await data_task
await tb.write_desc_source.send( status_frame = await tb.desc_status_sink.recv()
DescTransaction(addr=addr, len=len( status = unpack_status(status_frame)
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() assert not status["read_valid"]
tb.log.info("write status: %s", status) assert status["write_valid"]
assert status["write_tag"] == tag
assert status["write_len"] == len(data)
assert status["write_id"] == tag
assert status["write_error"] == 0
transferred_len = min(len(expected_data), len(stream_data)) assert tb.axi_ram.read(addr - 8, len(data) +
16) == b"\xaa" * 8 + data + b"\xaa" * 8
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): @cocotb.test()
"""DMA read path stress test adapted from Forencich's axi_dma test.""" async def test_axis_compat_read_path(dut):
tb = TB(dut) tb = TB(dut)
await tb.reset()
byte_lanes = tb.axi_ram.read_if.byte_lanes dut.write_enable.value = 0
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 dut.read_enable.value = 1
for length in list(range(1, byte_lanes * 4 + 1)) + [128]: addr = 0x1100
offsets = list(range(0, byte_lanes * 2, step_size)) tag = 0x34
offsets += list(range(4096 - byte_lanes * 2, 4096, step_size)) data = bytes((x * 3) % 256 for x in range(48))
for offset in offsets: tb.axi_ram.write(addr, data)
tb.log.info("read: length=%d offset=%d", length, offset)
addr = offset + 0x1000 cmd = pack_desc_cmd(
test_data = bytearray([x % 256 for x in range(length)]) read_addr=addr,
read_len=len(data),
read_tag=tag,
read_id=tag,
read_dest=0,
read_user=0,
)
tb.axi_ram.write(addr - 128, b"\xaa" * (len(test_data) + 256)) await tb.send_desc_cmd(cmd)
tb.axi_ram.write(addr, test_data)
tb.log.debug( data_frame = await tb.read_data_sink.recv()
"%s", assert bytes(data_frame.tdata) == data
tb.axi_ram.hexdump_str( assert int(data_frame.tid) == tag
(addr & ~0xF) - 16,
(((addr & 0xF) + length - 1) & ~0xF) + 48,
),
)
await tb.read_desc_source.send( status_frame = await tb.desc_status_sink.recv()
DescTransaction(addr=addr, len=len( status = unpack_status(status_frame)
test_data), tag=cur_tag, id=cur_tag)
)
status = await tb.read_desc_status_sink.recv() assert status["read_valid"]
read_data = await tb.read_data_sink.recv() assert not status["write_valid"]
assert status["read_tag"] == tag
tb.log.info("read status: %s", status) assert status["read_error"] == 0
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(): @cocotb.test()
return itertools.cycle([1, 1, 1, 0]) async def test_axis_compat_read_and_write_same_command(dut):
tb = TB(dut)
await tb.reset()
dut.write_enable.value = 1
dut.read_enable.value = 1
# When imported by cocotb inside a simulator, generate the actual cocotb tests. read_addr = 0x1200
if cocotb.SIM_NAME: write_addr = 0x1300
for test in [run_test_write, run_test_read]: read_tag = 0x41
factory = TestFactory(test) write_tag = 0x42
factory.add_option("idle_inserter", [None, cycle_pause]) read_data = bytes((x + 7) % 256 for x in range(24))
factory.add_option("backpressure_inserter", [None, cycle_pause]) write_data = bytes((x + 100) % 256 for x in range(24))
factory.generate_tests()
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(
# Optional pytest entrypoint via cocotb-test. read_addr=read_addr,
# Run from this directory with: pytest -q test_axi_dma_axis_compat.py read_len=len(read_data),
# ----------------------------------------------------------------------------- read_tag=read_tag,
read_id=read_tag,
write_addr=write_addr,
write_len=len(write_data),
write_tag=write_tag,
)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag))
)
def _sanitize_node_name(name): await cmd_task
return name.replace("[", "-").replace("]", "").replace("/", "_") await data_task
data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == read_data
assert int(data_frame.tid) == read_tag
if pytest is not None: # status might be in 2 beats, depending on timings
@pytest.mark.parametrize("axi_data_width", [8, 16, 32]) seen_read = False
@pytest.mark.parametrize("unaligned", [0, 1]) seen_write = False
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__)) for _ in range(2):
project_root = os.path.abspath(os.path.join(tests_dir, "..", "..")) status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
axi_if_rtl_dir = os.environ.get( if status["read_valid"]:
"AXI_IF_RTL_DIR", os.path.join(project_root, "rtl", "axi") assert status["read_tag"] == read_tag
) assert status["read_error"] == 0
dma_if_rtl_dir = os.environ.get( seen_read = True
"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"),
)
dut = "tb_axi_dma_axis_compat" if status["write_valid"]:
module = os.path.splitext(os.path.basename(__file__))[0] assert status["write_tag"] == write_tag
toplevel = dut assert status["write_len"] == len(write_data)
assert status["write_error"] == 0
seen_write = True
parameters = { if seen_read and seen_write:
"AXI_DATA_WIDTH": axi_data_width, break
"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 assert seen_read
parameters["AXIS_DATA_WIDTH"] = parameters["AXI_DATA_WIDTH"] assert seen_write
parameters["AXIS_KEEP_ENABLE"] = int(parameters["AXIS_DATA_WIDTH"] > 8) assert tb.axi_ram.read(write_addr - 8, len(write_data) +
parameters["AXIS_KEEP_WIDTH"] = parameters["AXIS_DATA_WIDTH"] // 8 16) == b"\xaa" * 8 + write_data + b"\xaa" * 8
parameters["AXIS_LAST_ENABLE"] = 1
verilog_sources = [
os.path.join(axi_if_rtl_dir, "axi_pkg.sv"),
os.path.join(axi_if_rtl_dir, "axi_if.sv"),
os.path.join(axi_if_rtl_dir, "axis_if.sv"),
os.path.join(axi_if_rtl_dir, "axi4_flat_to_if.sv"),
os.path.join(axi_if_rtl_dir, "axi4_if_to_flat.sv"),
os.path.join(axi_if_rtl_dir, "axis_flat_to_if.sv"),
os.path.join(axi_if_rtl_dir, "axis_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_desc_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_read_desc_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_read_desc_status_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_read_desc_status_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir, "axi_dma_write_desc_if_to_flat.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_write_desc_status_flat_to_if.sv"),
os.path.join(dma_if_rtl_dir,
"axi_dma_write_desc_status_if_to_flat.sv"),
os.path.join(forencich_rtl_dir, "axi_dma.v"),
os.path.join(forencich_rtl_dir, "axi_dma_rd.v"),
os.path.join(forencich_rtl_dir, "axi_dma_wr.v"),
os.path.join(wrapper_rtl_dir, "axi_dma_if_wrapper.sv"),
os.path.join(wrapper_rtl_dir, "axi_dma_axis_compat.sv"),
os.path.join(tests_dir, "tb_axi_dma_axis_compat.sv"),
]
extra_env = {f"PARAM_{k}": str(v) for k, v in parameters.items()}
sim_build = os.path.join(
tests_dir, "sim_build", _sanitize_node_name(request.node.name))
extra_args = []
if os.getenv("SIM", "verilator") == "verilator":
extra_args += ["--trace-structs"]
cocotb_test.simulator.run(
python_search=[tests_dir],
verilog_sources=verilog_sources,
includes=[axi_if_rtl_dir, dma_if_rtl_dir,
wrapper_rtl_dir, forencich_rtl_dir],
toplevel=toplevel,
module=module,
parameters=parameters,
sim_build=sim_build,
extra_env=extra_env,
waves=bool(int(os.getenv("WAVES", "0"))),
extra_args=extra_args,
)