346 lines
14 KiB
Systemverilog
346 lines
14 KiB
Systemverilog
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Compatibility wrapper over axi_dma_if_wrapper.
|
|
//
|
|
// Uses 4 axis_if channels for statuses and descs
|
|
//
|
|
// 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.
|
|
|
|
`default_nettype none
|
|
|
|
module axi_dma_axis_compat #(
|
|
parameter int unsigned AXI_DATA_WIDTH = 32,
|
|
parameter int unsigned AXI_ADDR_WIDTH = 16,
|
|
parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8,
|
|
parameter int unsigned AXI_ID_WIDTH = 8,
|
|
parameter int unsigned AXI_USER_WIDTH = 1,
|
|
parameter int unsigned AXI_MAX_BURST_LEN = 16,
|
|
|
|
parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH,
|
|
parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8,
|
|
parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8,
|
|
parameter int unsigned AXIS_LAST_ENABLE = 1,
|
|
parameter int unsigned AXIS_ID_ENABLE = 1,
|
|
parameter int unsigned AXIS_ID_WIDTH = 8,
|
|
parameter int unsigned AXIS_DEST_ENABLE = 0,
|
|
parameter int unsigned AXIS_DEST_WIDTH = 8,
|
|
parameter int unsigned AXIS_USER_ENABLE = 1,
|
|
parameter int unsigned AXIS_USER_WIDTH = 1,
|
|
|
|
parameter int unsigned LEN_WIDTH = 20,
|
|
parameter int unsigned TAG_WIDTH = 8,
|
|
parameter int unsigned ENABLE_SG = 0,
|
|
parameter int unsigned ENABLE_UNALIGNED = 0,
|
|
|
|
// 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
|
|
)(
|
|
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,
|
|
|
|
/*
|
|
* AXI read descriptor status output, encoded as one AXIS beat.
|
|
* tdata layout, LSB first:
|
|
* tag | error
|
|
*/
|
|
axis_if.master m_axis_read_desc_status,
|
|
|
|
/*
|
|
* AXI stream read data output.
|
|
*/
|
|
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
|
|
);
|
|
|
|
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;
|
|
|
|
localparam int unsigned RDS_TAG_LSB = 0;
|
|
localparam int unsigned RDS_ERROR_LSB = RDS_TAG_LSB + TAG_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 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 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;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Internal DMA-specific descriptor/status interfaces expected by
|
|
// axi_dma_if_wrapper.
|
|
// --------------------------------------------------------------------------
|
|
|
|
axi_dma_read_desc_if #(
|
|
.ADDR_W (AXI_ADDR_WIDTH),
|
|
.LEN_W (LEN_WIDTH),
|
|
.TAG_W (TAG_WIDTH),
|
|
.ID_W (AXIS_ID_WIDTH),
|
|
.DEST_W (AXIS_DEST_WIDTH),
|
|
.USER_W (AXIS_USER_WIDTH)
|
|
) read_desc_if (
|
|
.aclk (clk),
|
|
.aresetn (rstn)
|
|
);
|
|
|
|
axi_dma_read_desc_status_if #(
|
|
.TAG_W (TAG_WIDTH)
|
|
) read_desc_status_if (
|
|
.aclk (clk),
|
|
.aresetn (rstn)
|
|
);
|
|
|
|
axi_dma_write_desc_if #(
|
|
.ADDR_W (AXI_ADDR_WIDTH),
|
|
.LEN_W (LEN_WIDTH),
|
|
.TAG_W (TAG_WIDTH)
|
|
) write_desc_if (
|
|
.aclk (clk),
|
|
.aresetn (rstn)
|
|
);
|
|
|
|
axi_dma_write_desc_status_if #(
|
|
.LEN_W (LEN_WIDTH),
|
|
.TAG_W (TAG_WIDTH),
|
|
.ID_W (AXIS_ID_WIDTH),
|
|
.DEST_W (AXIS_DEST_WIDTH),
|
|
.USER_W (AXIS_USER_WIDTH)
|
|
) write_desc_status_if (
|
|
.aclk (clk),
|
|
.aresetn (rstn)
|
|
);
|
|
|
|
// --------------------------------------------------------------------------
|
|
// AXIS read descriptor input -> DMA-specific read descriptor interface.
|
|
// --------------------------------------------------------------------------
|
|
|
|
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;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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.
|
|
// --------------------------------------------------------------------------
|
|
|
|
axi_dma_if_wrapper #(
|
|
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
|
|
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
|
|
.AXI_STRB_WIDTH (AXI_STRB_WIDTH),
|
|
.AXI_ID_WIDTH (AXI_ID_WIDTH),
|
|
.AXI_USER_WIDTH (AXI_USER_WIDTH),
|
|
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
|
|
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
|
|
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
|
|
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
|
|
.AXIS_LAST_ENABLE (AXIS_LAST_ENABLE),
|
|
.AXIS_ID_ENABLE (AXIS_ID_ENABLE),
|
|
.AXIS_ID_WIDTH (AXIS_ID_WIDTH),
|
|
.AXIS_DEST_ENABLE (AXIS_DEST_ENABLE),
|
|
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
|
|
.AXIS_USER_ENABLE (AXIS_USER_ENABLE),
|
|
.AXIS_USER_WIDTH (AXIS_USER_WIDTH),
|
|
.LEN_WIDTH (LEN_WIDTH),
|
|
.TAG_WIDTH (TAG_WIDTH),
|
|
.ENABLE_SG (ENABLE_SG),
|
|
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
|
|
) u_axi_dma_if_wrapper (
|
|
.clk (clk),
|
|
.rst (rst),
|
|
|
|
.s_axis_read_desc (read_desc_if),
|
|
.m_axis_read_desc_status (read_desc_status_if),
|
|
.m_axis_read_data (m_axis_read_data),
|
|
|
|
.s_axis_write_desc (write_desc_if),
|
|
.m_axis_write_desc_status (write_desc_status_if),
|
|
.s_axis_write_data (s_axis_write_data),
|
|
|
|
.m_axi (m_axi),
|
|
|
|
.read_enable (read_enable),
|
|
.write_enable (write_enable),
|
|
.write_abort (write_abort)
|
|
);
|
|
|
|
endmodule : axi_dma_axis_compat
|
|
|
|
`default_nettype wire
|