Compare commits

3 Commits

Author SHA1 Message Date
a0a7137064 fix: axi dma wrapper 2026-07-10 18:59:50 +03:00
3ddf9130d3 fix: reg_pulse support 2026-07-10 17:30:51 +03:00
f3450cb22a added axi dma wrapper 2026-06-25 13:42:31 +03:00
11 changed files with 988 additions and 324 deletions

View File

@ -12,7 +12,8 @@ module axi4l_reg_map #(
axi4l_if.slave s_axil, axi4l_if.slave s_axil,
input logic [N_REGS-1:0][31:0] reg_i, input logic [N_REGS-1:0][31:0] reg_i,
output logic [N_REGS-1:0][31:0] reg_o output logic [N_REGS-1:0][31:0] reg_o,
output logic [N_REGS-1:0][31:0] reg_pulse
); );
import axi_pkg::*; import axi_pkg::*;
@ -105,6 +106,7 @@ module axi4l_reg_map #(
rdata_q <= '0; rdata_q <= '0;
reg_o <= REG_RST; reg_o <= REG_RST;
end else begin end else begin
reg_pulse <= '0;
for (int r = 0; r < N_REGS; r++) begin for (int r = 0; r < N_REGS; r++) begin
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
if (reg_bit_mode_t'(REG_MODE[r][bit_idx]) == REG_BIT_W1S) if (reg_bit_mode_t'(REG_MODE[r][bit_idx]) == REG_BIT_W1S)
@ -136,12 +138,33 @@ module axi4l_reg_map #(
for (b = 0; b < 32; b = b + 1) begin for (b = 0; b < 32; b = b + 1) begin
if (wr_mask[b]) begin if (wr_mask[b]) begin
unique case (reg_bit_mode_t'(REG_MODE[wr_idx][b])) unique case (reg_bit_mode_t'(REG_MODE[wr_idx][b]))
REG_BIT_RSVD: begin end REG_BIT_RSVD: begin
REG_BIT_RO : begin bresp_q <= 2'b10; end end
REG_BIT_RW : rw_new[b] = wr_data32[b];
REG_BIT_W1S : if (wr_data32[b]) rw_new[b] = 1'b1; REG_BIT_RO: begin
REG_BIT_W1C : if (wr_data32[b]) rw_new[b] = 1'b0; bresp_q <= 2'b10;
default : begin end end
REG_BIT_RW: begin
rw_new[b] = wr_data32[b];
end
REG_BIT_W1S: begin
if (wr_data32[b]) begin
rw_new[b] = 1'b1;
reg_pulse[wr_idx][b] <= 1'b1;
end
end
REG_BIT_W1C: begin
if (wr_data32[b]) begin
rw_new[b] = 1'b0;
reg_pulse[wr_idx][b] <= 1'b1;
end
end
default: begin
end
endcase endcase
end end
end end
@ -174,6 +197,7 @@ module axi4l_reg_map #(
REG_BIT_W1C : rd_word[b] = reg_o[rd_idx][b]; REG_BIT_W1C : rd_word[b] = reg_o[rd_idx][b];
default : rd_word[b] = 1'b0; default : rd_word[b] = 1'b0;
endcase endcase
end end
end end

View File

@ -1,64 +1,37 @@
package dma_axil_reg_map_pkg; package dma_axil_reg_map_pkg;
localparam int unsigned DMA_AXIL_REG_MAP_N_REGS = 6; localparam int unsigned DMA_AXIL_REG_MAP_N_REGS = 4;
localparam int unsigned DMA_WRITE_DESC_CONTROL_REG = 0;
localparam int unsigned DMA_WRITE_DESC_ADDR_REG = 1;
localparam int unsigned DMA_WRITE_DESC_LEN_REG = 2;
localparam int unsigned DMA_READ_DESC_CONTROL_REG = 3;
localparam int unsigned DMA_READ_DESC_ADDR_REG = 4;
localparam int unsigned DMA_READ_DESC_LEN_REG = 5;
localparam logic [2:0] REG_BIT_RSVD = 3'd0; localparam logic [2:0] REG_BIT_RSVD = 3'd0;
localparam logic [2:0] REG_BIT_RO = 3'd1; localparam logic [2:0] REG_BIT_RO = 3'd1;
localparam logic [2:0] REG_BIT_RW = 3'd2; localparam logic [2:0] REG_BIT_RW = 3'd2;
localparam logic [2:0] REG_BIT_W1S = 3'd3; localparam logic [2:0] REG_BIT_W1S = 3'd3;
localparam logic [2:0] REG_BIT_W1C = 3'd4;
typedef logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0][2:0] reg_mode_map_t; localparam DMA_WRITE_DESC_CONTROL_REG = 0;
localparam DMA_WRITE_DESC_ADDR_REG = 1;
localparam DMA_WRITE_DESC_LEN_REG = 2;
localparam DMA_READ_DESC_CONTROL_REG = 3;
localparam DMA_READ_DESC_ADDR_REG = 4;
localparam DMA_READ_DESC_LEN_REG = 5;
function automatic reg_mode_map_t make_dma_reg_mode(); localparam logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0][2:0] DMA_AXIL_REG_MAP_REG_MODE = '{
reg_mode_map_t mode;
mode = '0; '{REG_BIT_RO, REG_BIT_W1S, default: REG_BIT_RSVD},
'{32{REG_BIT_RW}, default: REG_BIT_RSVD},
'{32{REG_BIT_RW}, default: REG_BIT_RSVD},
'{REG_BIT_RO, REG_BIT_W1S, default: REG_BIT_RSVD},
'{32{REG_BIT_RW}, default: REG_BIT_RSVD},
'{32{REG_BIT_RW}, default: REG_BIT_RSVD}
};
// По умолчанию всё reserved localparam logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0] DMA_AXIL_REG_MAP_REG_RST = '{
for (int reg_idx = 0; reg_idx < DMA_AXIL_REG_MAP_N_REGS; reg_idx++) begin 32'h0000_0000,
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin 32'h0000_0000,
mode[reg_idx][bit_idx] = REG_BIT_RSVD; 32'h0000_0000,
end 32'h0000_0000,
end 32'h0000_0000,
32'h0000_0000
// WRITE CONTROL };
mode[DMA_WRITE_DESC_CONTROL_REG][0] = REG_BIT_RO;
mode[DMA_WRITE_DESC_CONTROL_REG][1] = REG_BIT_W1S;
// WRITE ADDR
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
mode[DMA_WRITE_DESC_ADDR_REG][bit_idx] = REG_BIT_RW;
end
// WRITE LEN
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
mode[DMA_WRITE_DESC_LEN_REG][bit_idx] = REG_BIT_RW;
end
// READ CONTROL
mode[DMA_READ_DESC_CONTROL_REG][0] = REG_BIT_RO;
mode[DMA_READ_DESC_CONTROL_REG][1] = REG_BIT_W1S;
// READ ADDR
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
mode[DMA_READ_DESC_ADDR_REG][bit_idx] = REG_BIT_RW;
end
// READ LEN
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
mode[DMA_READ_DESC_LEN_REG][bit_idx] = REG_BIT_RW;
end
return mode;
endfunction
localparam reg_mode_map_t DMA_AXIL_REG_MAP_REG_MODE = make_dma_reg_mode();
endpackage endpackage

View File

@ -2,7 +2,7 @@ module axi_crossbar_wrapper #(
parameter int SLAVE_QTY = 3, parameter int SLAVE_QTY = 3,
parameter int MASTER_QTY = 3, parameter int MASTER_QTY = 3,
parameter int ADDR_WIDTH = 32, parameter int ADDR_WIDTH = 32,
parameter int DATA_WIDTH = 32, parameter int DATA_WIDTH = 32
parameter int STRB_WIDTH = (DATA_WIDTH/8) parameter int STRB_WIDTH = (DATA_WIDTH/8)
)( )(
input wire clk, input wire clk,

453
axi/rtl/axi_dma_wrapper.sv Normal file
View File

@ -0,0 +1,453 @@
// SPDX-License-Identifier: MIT
//
// SystemVerilog interface wrapper around alexforencich/verilog-axi axi_dma.v.
//
// AXI memory, AXI-Stream data, DMA descriptor, and DMA status channels are all
// exposed through compact interfaces. The original Forencich core remains
// untouched and is connected through local flat wires.
`default_nettype none
// DMA Specific wrappers & converters
module axi_dma_wrapper #(
parameter int unsigned AXI_DATA_WIDTH = 32,
parameter int unsigned AXI_ADDR_WIDTH = 16,
parameter int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8,
parameter int unsigned AXI_ID_WIDTH = 8,
parameter int unsigned AXI_USER_WIDTH = 1,
parameter int unsigned AXI_MAX_BURST_LEN = 16,
parameter int unsigned AXIS_DATA_WIDTH = AXI_DATA_WIDTH,
parameter int unsigned AXIS_KEEP_ENABLE = AXIS_DATA_WIDTH > 8,
parameter int unsigned AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8,
parameter int unsigned AXIS_LAST_ENABLE = 1,
parameter int unsigned AXIS_ID_ENABLE = 1,
parameter int unsigned AXIS_ID_WIDTH = 8,
parameter int unsigned AXIS_DEST_ENABLE = 0,
parameter int unsigned AXIS_DEST_WIDTH = 8,
parameter int unsigned AXIS_USER_ENABLE = 1,
parameter int unsigned AXIS_USER_WIDTH = 1,
parameter int unsigned LEN_WIDTH = 20,
parameter int unsigned TAG_WIDTH = 8,
parameter int unsigned ENABLE_SG = 0,
parameter int unsigned ENABLE_UNALIGNED = 0
)(
input logic clk,
input logic rst,
axis_if.slave s_axis_read_desc,
axis_if.master m_axis_read_desc_status,
axis_if.master m_axis_read_data,
axis_if.slave s_axis_write_desc,
axis_if.master m_axis_write_desc_status,
axis_if.slave s_axis_write_data,
axi4_if.master m_axi,
/*
* Configuration.
*/
input logic read_enable,
input logic write_enable,
input logic write_abort
);
// --------------------------------------------------------------------------
// Flat wires connected to original alexforencich axi_dma.v
// --------------------------------------------------------------------------
logic [AXI_ADDR_WIDTH-1:0] dma_s_axis_read_desc_addr;
logic [LEN_WIDTH-1:0] dma_s_axis_read_desc_len;
logic [TAG_WIDTH-1:0] dma_s_axis_read_desc_tag;
logic [AXIS_ID_WIDTH-1:0] dma_s_axis_read_desc_id;
logic [AXIS_DEST_WIDTH-1:0] dma_s_axis_read_desc_dest;
logic [AXIS_USER_WIDTH-1:0] dma_s_axis_read_desc_user;
logic dma_s_axis_read_desc_valid;
logic dma_s_axis_read_desc_ready;
logic [TAG_WIDTH-1:0] dma_m_axis_read_desc_status_tag;
logic [3:0] dma_m_axis_read_desc_status_error;
logic dma_m_axis_read_desc_status_valid;
logic [AXI_ADDR_WIDTH-1:0] dma_s_axis_write_desc_addr;
logic [LEN_WIDTH-1:0] dma_s_axis_write_desc_len;
logic [TAG_WIDTH-1:0] dma_s_axis_write_desc_tag;
logic dma_s_axis_write_desc_valid;
logic dma_s_axis_write_desc_ready;
logic [LEN_WIDTH-1:0] dma_m_axis_write_desc_status_len;
logic [TAG_WIDTH-1:0] dma_m_axis_write_desc_status_tag;
logic [AXIS_ID_WIDTH-1:0] dma_m_axis_write_desc_status_id;
logic [AXIS_DEST_WIDTH-1:0] dma_m_axis_write_desc_status_dest;
logic [AXIS_USER_WIDTH-1:0] dma_m_axis_write_desc_status_user;
logic [3:0] dma_m_axis_write_desc_status_error;
logic dma_m_axis_write_desc_status_valid;
logic [AXIS_DATA_WIDTH-1:0] dma_m_axis_read_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] dma_m_axis_read_data_tkeep;
logic dma_m_axis_read_data_tvalid;
logic dma_m_axis_read_data_tready;
logic dma_m_axis_read_data_tlast;
logic [AXIS_ID_WIDTH-1:0] dma_m_axis_read_data_tid;
logic [AXIS_DEST_WIDTH-1:0] dma_m_axis_read_data_tdest;
logic [AXIS_USER_WIDTH-1:0] dma_m_axis_read_data_tuser;
logic [AXIS_DATA_WIDTH-1:0] dma_s_axis_write_data_tdata;
logic [AXIS_KEEP_WIDTH-1:0] dma_s_axis_write_data_tkeep;
logic [AXIS_KEEP_WIDTH-1:0] unused_s_axis_write_data_tstrb;
logic dma_s_axis_write_data_tvalid;
logic dma_s_axis_write_data_tready;
logic dma_s_axis_write_data_tlast;
logic [AXIS_ID_WIDTH-1:0] dma_s_axis_write_data_tid;
logic [AXIS_DEST_WIDTH-1:0] dma_s_axis_write_data_tdest;
logic [AXIS_USER_WIDTH-1:0] dma_s_axis_write_data_tuser;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_awid;
logic [AXI_ADDR_WIDTH-1:0] dma_m_axi_awaddr;
logic [7:0] dma_m_axi_awlen;
logic [2:0] dma_m_axi_awsize;
logic [1:0] dma_m_axi_awburst;
logic dma_m_axi_awlock;
logic [3:0] dma_m_axi_awcache;
logic [2:0] dma_m_axi_awprot;
logic dma_m_axi_awvalid;
logic dma_m_axi_awready;
logic [AXI_DATA_WIDTH-1:0] dma_m_axi_wdata;
logic [AXI_STRB_WIDTH-1:0] dma_m_axi_wstrb;
logic dma_m_axi_wlast;
logic dma_m_axi_wvalid;
logic dma_m_axi_wready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_bid;
logic [1:0] dma_m_axi_bresp;
logic dma_m_axi_bvalid;
logic dma_m_axi_bready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_arid;
logic [AXI_ADDR_WIDTH-1:0] dma_m_axi_araddr;
logic [7:0] dma_m_axi_arlen;
logic [2:0] dma_m_axi_arsize;
logic [1:0] dma_m_axi_arburst;
logic dma_m_axi_arlock;
logic [3:0] dma_m_axi_arcache;
logic [2:0] dma_m_axi_arprot;
logic dma_m_axi_arvalid;
logic dma_m_axi_arready;
logic [AXI_ID_WIDTH-1:0] dma_m_axi_rid;
logic [AXI_DATA_WIDTH-1:0] dma_m_axi_rdata;
logic [1:0] dma_m_axi_rresp;
logic dma_m_axi_rlast;
logic dma_m_axi_rvalid;
logic dma_m_axi_rready;
logic [AXI_USER_WIDTH-1:0] unused_m_axi_buser;
logic [AXI_USER_WIDTH-1:0] unused_m_axi_ruser;
// Original DMA: flat ports only.
axi_dma #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_MAX_BURST_LEN (AXI_MAX_BURST_LEN),
.AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
.AXIS_KEEP_ENABLE (AXIS_KEEP_ENABLE),
.AXIS_KEEP_WIDTH (AXIS_KEEP_WIDTH),
.AXIS_LAST_ENABLE (AXIS_LAST_ENABLE),
.AXIS_ID_ENABLE (AXIS_ID_ENABLE),
.AXIS_ID_WIDTH (AXIS_ID_WIDTH),
.AXIS_DEST_ENABLE (AXIS_DEST_ENABLE),
.AXIS_DEST_WIDTH (AXIS_DEST_WIDTH),
.AXIS_USER_ENABLE (AXIS_USER_ENABLE),
.AXIS_USER_WIDTH (AXIS_USER_WIDTH),
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
) i_axi_dma (
.clk (clk),
.rst (rst),
.s_axis_read_desc_addr (dma_s_axis_read_desc_addr),
.s_axis_read_desc_len (dma_s_axis_read_desc_len),
.s_axis_read_desc_tag (dma_s_axis_read_desc_tag),
.s_axis_read_desc_id (dma_s_axis_read_desc_id),
.s_axis_read_desc_dest (dma_s_axis_read_desc_dest),
.s_axis_read_desc_user (dma_s_axis_read_desc_user),
.s_axis_read_desc_valid (dma_s_axis_read_desc_valid),
.s_axis_read_desc_ready (dma_s_axis_read_desc_ready),
.m_axis_read_desc_status_tag (dma_m_axis_read_desc_status_tag),
.m_axis_read_desc_status_error (dma_m_axis_read_desc_status_error),
.m_axis_read_desc_status_valid (dma_m_axis_read_desc_status_valid),
.m_axis_read_data_tdata (dma_m_axis_read_data_tdata),
.m_axis_read_data_tkeep (dma_m_axis_read_data_tkeep),
.m_axis_read_data_tvalid (dma_m_axis_read_data_tvalid),
.m_axis_read_data_tready (dma_m_axis_read_data_tready),
.m_axis_read_data_tlast (dma_m_axis_read_data_tlast),
.m_axis_read_data_tid (dma_m_axis_read_data_tid),
.m_axis_read_data_tdest (dma_m_axis_read_data_tdest),
.m_axis_read_data_tuser (dma_m_axis_read_data_tuser),
.s_axis_write_desc_addr (dma_s_axis_write_desc_addr),
.s_axis_write_desc_len (dma_s_axis_write_desc_len),
.s_axis_write_desc_tag (dma_s_axis_write_desc_tag),
.s_axis_write_desc_valid (dma_s_axis_write_desc_valid),
.s_axis_write_desc_ready (dma_s_axis_write_desc_ready),
.m_axis_write_desc_status_len (dma_m_axis_write_desc_status_len),
.m_axis_write_desc_status_tag (dma_m_axis_write_desc_status_tag),
.m_axis_write_desc_status_id (dma_m_axis_write_desc_status_id),
.m_axis_write_desc_status_dest (dma_m_axis_write_desc_status_dest),
.m_axis_write_desc_status_user (dma_m_axis_write_desc_status_user),
.m_axis_write_desc_status_error (dma_m_axis_write_desc_status_error),
.m_axis_write_desc_status_valid (dma_m_axis_write_desc_status_valid),
.s_axis_write_data_tdata (dma_s_axis_write_data_tdata),
.s_axis_write_data_tkeep (dma_s_axis_write_data_tkeep),
.s_axis_write_data_tvalid (dma_s_axis_write_data_tvalid),
.s_axis_write_data_tready (dma_s_axis_write_data_tready),
.s_axis_write_data_tlast (dma_s_axis_write_data_tlast),
.s_axis_write_data_tid (dma_s_axis_write_data_tid),
.s_axis_write_data_tdest (dma_s_axis_write_data_tdest),
.s_axis_write_data_tuser (dma_s_axis_write_data_tuser),
.m_axi_awid (dma_m_axi_awid),
.m_axi_awaddr (dma_m_axi_awaddr),
.m_axi_awlen (dma_m_axi_awlen),
.m_axi_awsize (dma_m_axi_awsize),
.m_axi_awburst (dma_m_axi_awburst),
.m_axi_awlock (dma_m_axi_awlock),
.m_axi_awcache (dma_m_axi_awcache),
.m_axi_awprot (dma_m_axi_awprot),
.m_axi_awvalid (dma_m_axi_awvalid),
.m_axi_awready (dma_m_axi_awready),
.m_axi_wdata (dma_m_axi_wdata),
.m_axi_wstrb (dma_m_axi_wstrb),
.m_axi_wlast (dma_m_axi_wlast),
.m_axi_wvalid (dma_m_axi_wvalid),
.m_axi_wready (dma_m_axi_wready),
.m_axi_bid (dma_m_axi_bid),
.m_axi_bresp (dma_m_axi_bresp),
.m_axi_bvalid (dma_m_axi_bvalid),
.m_axi_bready (dma_m_axi_bready),
.m_axi_arid (dma_m_axi_arid),
.m_axi_araddr (dma_m_axi_araddr),
.m_axi_arlen (dma_m_axi_arlen),
.m_axi_arsize (dma_m_axi_arsize),
.m_axi_arburst (dma_m_axi_arburst),
.m_axi_arlock (dma_m_axi_arlock),
.m_axi_arcache (dma_m_axi_arcache),
.m_axi_arprot (dma_m_axi_arprot),
.m_axi_arvalid (dma_m_axi_arvalid),
.m_axi_arready (dma_m_axi_arready),
.m_axi_rid (dma_m_axi_rid),
.m_axi_rdata (dma_m_axi_rdata),
.m_axi_rresp (dma_m_axi_rresp),
.m_axi_rlast (dma_m_axi_rlast),
.m_axi_rvalid (dma_m_axi_rvalid),
.m_axi_rready (dma_m_axi_rready),
.read_enable (read_enable),
.write_enable (write_enable),
.write_abort (write_abort)
);
// local read descriptor interface -> DMA flat input
axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) i_read_desc_cmd_i2f (
.s_axis (s_axis_read_desc),
.m_axis_tdata ({dma_s_axis_read_desc_addr, dma_s_axis_read_desc_len, dma_s_axis_read_desc_tag}),
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (dma_s_axis_read_desc_id),
.m_axis_tdest (dma_s_axis_read_desc_dest),
.m_axis_tuser (dma_s_axis_read_desc_user),
.m_axis_tvalid (dma_s_axis_read_desc_valid),
.m_axis_tready (dma_s_axis_read_desc_ready)
);
// DMA read descriptor status flat output -> local status interface
axis_flat_to_if #(
.DATA_W (TAG_WIDTH + 4),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) i_read_desc_status_f2i(
.s_axis_tdata ({dma_m_axis_read_desc_status_tag, dma_m_axis_read_desc_status_error}),
.s_axis_tkeep (),
.s_axis_tstrb (),
.s_axis_tlast (),
.s_axis_tid (),
.s_axis_tdest (),
.s_axis_tuser (),
.s_axis_tvalid (dma_m_axis_read_desc_status_valid),
.s_axis_tready (),
.m_axis (m_axis_read_desc_status)
);
// local write descriptor interface -> DMA flat input
axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) i_write_desc_cmd_i2f (
.s_axis (s_axis_write_desc),
.m_axis_tdata ({dma_s_axis_write_desc_addr, dma_s_axis_write_desc_len, dma_s_axis_write_desc_tag}),
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (),
.m_axis_tdest (),
.m_axis_tuser (),
.m_axis_tvalid (dma_s_axis_write_desc_valid),
.m_axis_tready (dma_s_axis_write_desc_ready)
);
// DMA write descriptor status flat output -> local status interface
axis_flat_to_if #(
.DATA_W (TAG_WIDTH + LEN_WIDTH + 4),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) i_write_desc_status_f2i(
.s_axis_tdata ({dma_m_axis_write_desc_status_len, dma_m_axis_write_desc_status_tag, dma_m_axis_write_desc_status_error}),
.s_axis_tkeep (),
.s_axis_tstrb (),
.s_axis_tlast (),
.s_axis_tid (dma_m_axis_write_desc_status_id),
.s_axis_tdest (dma_m_axis_write_desc_status_dest),
.s_axis_tuser (dma_m_axis_write_desc_status_user),
.s_axis_tvalid (dma_m_axis_write_desc_status_valid),
.s_axis_tready (),
.m_axis (m_axis_write_desc_status)
);
// DMA read data flat output -> local axis_if.master
axis_flat_to_if #(
.DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_m_axis_read_data_flat_to_if (
.s_axis_tdata (dma_m_axis_read_data_tdata),
.s_axis_tkeep (dma_m_axis_read_data_tkeep),
.s_axis_tstrb (dma_m_axis_read_data_tkeep), // axi_dma has no tstrb; mirror tkeep
.s_axis_tlast (dma_m_axis_read_data_tlast),
.s_axis_tid (dma_m_axis_read_data_tid),
.s_axis_tdest (dma_m_axis_read_data_tdest),
.s_axis_tuser (dma_m_axis_read_data_tuser),
.s_axis_tvalid (dma_m_axis_read_data_tvalid),
.s_axis_tready (dma_m_axis_read_data_tready),
.m_axis (m_axis_read_data)
);
// local axis_if.slave -> DMA write data flat input
axis_if_to_flat #(
.DATA_W (AXIS_DATA_WIDTH),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_s_axis_write_data_if_to_flat (
.s_axis (s_axis_write_data),
.m_axis_tdata (dma_s_axis_write_data_tdata),
.m_axis_tkeep (dma_s_axis_write_data_tkeep),
.m_axis_tstrb (unused_s_axis_write_data_tstrb),
.m_axis_tlast (dma_s_axis_write_data_tlast),
.m_axis_tid (dma_s_axis_write_data_tid),
.m_axis_tdest (dma_s_axis_write_data_tdest),
.m_axis_tuser (dma_s_axis_write_data_tuser),
.m_axis_tvalid(dma_s_axis_write_data_tvalid),
.m_axis_tready(dma_s_axis_write_data_tready)
);
// DMA AXI master flat output -> local axi4_if.master
axi4_flat_to_if #(
.ADDR_W (AXI_ADDR_WIDTH),
.DATA_W (AXI_DATA_WIDTH),
.ID_W (AXI_ID_WIDTH),
.USER_W (AXI_USER_WIDTH)
) u_m_axi_flat_to_if (
.s_axi_awid (dma_m_axi_awid),
.s_axi_awaddr (dma_m_axi_awaddr),
.s_axi_awlen (dma_m_axi_awlen),
.s_axi_awsize (dma_m_axi_awsize),
.s_axi_awburst (dma_m_axi_awburst),
.s_axi_awlock (dma_m_axi_awlock),
.s_axi_awcache (dma_m_axi_awcache),
.s_axi_awprot (dma_m_axi_awprot),
.s_axi_awqos (4'd0),
.s_axi_awregion (4'd0),
.s_axi_awuser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_awvalid (dma_m_axi_awvalid),
.s_axi_awready (dma_m_axi_awready),
.s_axi_wdata (dma_m_axi_wdata),
.s_axi_wstrb (dma_m_axi_wstrb),
.s_axi_wlast (dma_m_axi_wlast),
.s_axi_wuser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_wvalid (dma_m_axi_wvalid),
.s_axi_wready (dma_m_axi_wready),
.s_axi_bid (dma_m_axi_bid),
.s_axi_bresp (dma_m_axi_bresp),
.s_axi_buser (unused_m_axi_buser),
.s_axi_bvalid (dma_m_axi_bvalid),
.s_axi_bready (dma_m_axi_bready),
.s_axi_arid (dma_m_axi_arid),
.s_axi_araddr (dma_m_axi_araddr),
.s_axi_arlen (dma_m_axi_arlen),
.s_axi_arsize (dma_m_axi_arsize),
.s_axi_arburst (dma_m_axi_arburst),
.s_axi_arlock (dma_m_axi_arlock),
.s_axi_arcache (dma_m_axi_arcache),
.s_axi_arprot (dma_m_axi_arprot),
.s_axi_arqos (4'd0),
.s_axi_arregion (4'd0),
.s_axi_aruser ({AXI_USER_WIDTH{1'b0}}),
.s_axi_arvalid (dma_m_axi_arvalid),
.s_axi_arready (dma_m_axi_arready),
.s_axi_rid (dma_m_axi_rid),
.s_axi_rdata (dma_m_axi_rdata),
.s_axi_rresp (dma_m_axi_rresp),
.s_axi_rlast (dma_m_axi_rlast),
.s_axi_ruser (unused_m_axi_ruser),
.s_axi_rvalid (dma_m_axi_rvalid),
.s_axi_rready (dma_m_axi_rready),
.m_axi (m_axi)
);
endmodule : axi_dma_wrapper
`default_nettype wire

View File

@ -13,7 +13,7 @@ interface axi4_if #(
typedef logic [DATA_W/8-1:0] strb_t; typedef logic [DATA_W/8-1:0] strb_t;
typedef logic [ID_W-1:0] id_t; typedef logic [ID_W-1:0] id_t;
typedef logic [USER_W-1:0] user_t; typedef logic [USER_W-1:0] user_t;
`AXI4_TYPEDEF_ALL(axi, addr_t, data_t, strb_t, id_t, user_t); `AXI4_TYPEDEF_ALL(axi, addr_t, data_t, strb_t, id_t, user_t)
axi_req_t req; axi_req_t req;
axi_resp_t resp; axi_resp_t resp;
modport master (input aclk, aresetn, output req, input resp); modport master (input aclk, aresetn, output req, input resp);
@ -34,7 +34,7 @@ interface axi4l_if #(
typedef logic [DATA_W-1:0] data_t; typedef logic [DATA_W-1:0] data_t;
typedef logic [DATA_W/8-1:0] strb_t; typedef logic [DATA_W/8-1:0] strb_t;
typedef logic [USER_W-1:0] user_t; typedef logic [USER_W-1:0] user_t;
`AXI4L_TYPEDEF_ALL(axil, addr_t, data_t, strb_t, user_t); `AXI4L_TYPEDEF_ALL(axil, addr_t, data_t, strb_t, user_t)
axil_req_t req; axil_req_t req;
axil_resp_t resp; axil_resp_t resp;
modport master (input aclk, aresetn, output req, input resp); modport master (input aclk, aresetn, output req, input resp);

View File

@ -1,6 +1,6 @@
module axil_cdc_wrapper #( module axil_cdc_wrapper #(
parameter int ADDR_WIDTH = 32, parameter int ADDR_WIDTH = 32
parameter int DATA_WIDTH = 32 parameter int DATA_WIDTH = 32,
)( )(
input wire s_clk, input wire s_clk,
input wire s_rst, input wire s_rst,
@ -83,8 +83,8 @@ module axil_cdc_wrapper #(
); );
axil_cdc #( axil_cdc #(
.ADDR_WIDTH (ADDR_WIDTH), .ADDR_WIDTH (ADDR_WIDTH)
.DATA_WIDTH (DATA_WIDTH) .DATA_WIDTH (DATA_WIDTH),
) i_axil_cdc ( ) i_axil_cdc (
.s_clk (s_clk), .s_clk (s_clk),
.s_rst (s_rst), .s_rst (s_rst),

View File

@ -44,8 +44,8 @@ VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_rd.v
VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_wr.v VERILOG_SOURCES += $(FORENCICH_AXI_RTL_DIR)/axi_dma_wr.v
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_if_wrapper.sv VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_wrapper.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_compat.sv
VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_axis_compat.sv VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_axis_compat.sv
COMPILE_ARGS += -I$(AXI_IF_RTL_DIR) COMPILE_ARGS += -I$(AXI_IF_RTL_DIR)

View File

@ -26,18 +26,14 @@ module tb_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,
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_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH, parameter int unsigned READ_DESC_DATA_WIDTH = ((READ_DESC_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH, parameter int unsigned WRITE_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8, parameter int unsigned WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8, parameter int unsigned READ_STATUS_RAW_WIDTH = TAG_WIDTH + 4,
parameter int unsigned READ_STATUS_DATA_WIDTH = ((READ_STATUS_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4, parameter int unsigned WRITE_STATUS_RAW_WIDTH = LEN_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 WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) / 8) * 8
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,
@ -46,27 +42,35 @@ module tb_axi_dma_axis_compat #(
input logic write_enable, input logic write_enable,
input logic write_abort, input logic write_abort,
// Unified descriptor command AXIS, flat for cocotb // Read descriptor AXIS input, flat for cocotb.
input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata, // tdata layout: {addr, len, tag}; id/dest/user use AXIS sidebands.
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tkeep, input logic [READ_DESC_DATA_WIDTH-1:0] s_axis_read_desc_tdata,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tstrb, input logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_tid,
input logic s_axis_desc_cmd_tlast, input logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_tdest,
input logic s_axis_desc_cmd_tid, input logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_tuser,
input logic s_axis_desc_cmd_tdest, input logic s_axis_read_desc_tvalid,
input logic s_axis_desc_cmd_tuser, output logic s_axis_read_desc_tready,
input logic s_axis_desc_cmd_tvalid,
output logic s_axis_desc_cmd_tready,
// Unified descriptor status AXIS, flat for cocotb // Read descriptor status AXIS output.
output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata, // tdata layout: {tag, error}.
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tkeep, output logic [READ_STATUS_DATA_WIDTH-1:0] m_axis_read_desc_status_tdata,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tstrb, output logic m_axis_read_desc_status_tvalid,
output logic m_axis_desc_status_tlast, input logic m_axis_read_desc_status_tready,
output logic m_axis_desc_status_tid,
output logic m_axis_desc_status_tdest, // Write descriptor AXIS input.
output logic [DESC_STATUS_USER_WIDTH-1:0] m_axis_desc_status_tuser, // tdata layout: {addr, len, tag}.
output logic m_axis_desc_status_tvalid, input logic [WRITE_DESC_DATA_WIDTH-1:0] s_axis_write_desc_tdata,
input logic m_axis_desc_status_tready, input logic s_axis_write_desc_tvalid,
output logic s_axis_write_desc_tready,
// Write descriptor status AXIS output.
// tdata layout: {len, tag, error}; id/dest/user use AXIS sidebands.
output logic [WRITE_STATUS_DATA_WIDTH-1:0] m_axis_write_desc_status_tdata,
output logic [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_tid,
output logic [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_tdest,
output logic [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_tuser,
output logic m_axis_write_desc_status_tvalid,
input logic m_axis_write_desc_status_tready,
// Read data AXIS output // Read data AXIS output
output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, output logic [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata,
@ -144,23 +148,45 @@ module tb_axi_dma_axis_compat #(
wire rstn = ~rst; wire rstn = ~rst;
axis_if #( axis_if #(
.DATA_W (DESC_CMD_DATA_WIDTH), .DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH), .KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (1), .ID_W (AXIS_ID_WIDTH),
.DEST_W (1), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (1) .USER_W (AXIS_USER_WIDTH)
) desc_cmd_if ( ) read_desc_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
axis_if #( axis_if #(
.DATA_W (DESC_STATUS_DATA_WIDTH), .DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH), .KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH) .USER_W (1)
) desc_status_if ( ) read_desc_status_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_DESC_DATA_WIDTH),
.KEEP_W (WRITE_DESC_DATA_WIDTH / 8),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) write_desc_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (WRITE_STATUS_DATA_WIDTH),
.KEEP_W (WRITE_STATUS_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) write_desc_status_if (
.aclk (clk), .aclk (clk),
.aresetn (rstn) .aresetn (rstn)
); );
@ -198,41 +224,79 @@ module tb_axi_dma_axis_compat #(
); );
axis_flat_to_if #( axis_flat_to_if #(
.DATA_W (DESC_CMD_DATA_WIDTH), .DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH), .KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (1), .ID_W (AXIS_ID_WIDTH),
.DEST_W (1), .DEST_W (AXIS_DEST_WIDTH),
.USER_W (1) .USER_W (AXIS_USER_WIDTH)
) u_desc_cmd_flat_to_if ( ) u_read_desc_flat_to_if (
.s_axis_tdata (s_axis_desc_cmd_tdata), .s_axis_tdata (s_axis_read_desc_tdata),
.s_axis_tkeep (s_axis_desc_cmd_tkeep), .s_axis_tkeep ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tstrb (s_axis_desc_cmd_tstrb), .s_axis_tstrb ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tlast (s_axis_desc_cmd_tlast), .s_axis_tlast (1'b1),
.s_axis_tid (s_axis_desc_cmd_tid), .s_axis_tid (s_axis_read_desc_tid),
.s_axis_tdest (s_axis_desc_cmd_tdest), .s_axis_tdest (s_axis_read_desc_tdest),
.s_axis_tuser (s_axis_desc_cmd_tuser), .s_axis_tuser (s_axis_read_desc_tuser),
.s_axis_tvalid (s_axis_desc_cmd_tvalid), .s_axis_tvalid (s_axis_read_desc_tvalid),
.s_axis_tready (s_axis_desc_cmd_tready), .s_axis_tready (s_axis_read_desc_tready),
.m_axis (desc_cmd_if) .m_axis (read_desc_if)
); );
axis_if_to_flat #( axis_if_to_flat #(
.DATA_W (DESC_STATUS_DATA_WIDTH), .DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH), .KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.ID_W (1), .ID_W (1),
.DEST_W (1), .DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH) .USER_W (1)
) u_desc_status_if_to_flat ( ) u_read_desc_status_if_to_flat (
.s_axis (desc_status_if), .s_axis (read_desc_status_if),
.m_axis_tdata (m_axis_desc_status_tdata), .m_axis_tdata (m_axis_read_desc_status_tdata),
.m_axis_tkeep (m_axis_desc_status_tkeep), .m_axis_tkeep (),
.m_axis_tstrb (m_axis_desc_status_tstrb), .m_axis_tstrb (),
.m_axis_tlast (m_axis_desc_status_tlast), .m_axis_tlast (),
.m_axis_tid (m_axis_desc_status_tid), .m_axis_tid (),
.m_axis_tdest (m_axis_desc_status_tdest), .m_axis_tdest (),
.m_axis_tuser (m_axis_desc_status_tuser), .m_axis_tuser (),
.m_axis_tvalid (m_axis_desc_status_tvalid), .m_axis_tvalid (m_axis_read_desc_status_tvalid),
.m_axis_tready (m_axis_desc_status_tready) .m_axis_tready (m_axis_read_desc_status_tready)
);
axis_flat_to_if #(
.DATA_W (WRITE_DESC_DATA_WIDTH),
.KEEP_W (WRITE_DESC_DATA_WIDTH / 8),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) u_write_desc_flat_to_if (
.s_axis_tdata (s_axis_write_desc_tdata),
.s_axis_tkeep ({(WRITE_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tstrb ({(WRITE_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tlast (1'b1),
.s_axis_tid (1'b0),
.s_axis_tdest (1'b0),
.s_axis_tuser (1'b0),
.s_axis_tvalid (s_axis_write_desc_tvalid),
.s_axis_tready (s_axis_write_desc_tready),
.m_axis (write_desc_if)
);
axis_if_to_flat #(
.DATA_W (WRITE_STATUS_DATA_WIDTH),
.KEEP_W (WRITE_STATUS_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_write_desc_status_if_to_flat (
.s_axis (write_desc_status_if),
.m_axis_tdata (m_axis_write_desc_status_tdata),
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (m_axis_write_desc_status_tid),
.m_axis_tdest (m_axis_write_desc_status_tdest),
.m_axis_tuser (m_axis_write_desc_status_tuser),
.m_axis_tvalid (m_axis_write_desc_status_tvalid),
.m_axis_tready (m_axis_write_desc_status_tready)
); );
axis_if_to_flat #( axis_if_to_flat #(
@ -273,7 +337,7 @@ module tb_axi_dma_axis_compat #(
.m_axis (write_data_if) .m_axis (write_data_if)
); );
axi_dma_axis_compat #( axi_dma_wrapper #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH), .AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH), .AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH), .AXI_STRB_WIDTH (AXI_STRB_WIDTH),
@ -293,16 +357,15 @@ 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), .s_axis_read_desc (read_desc_if),
.m_axis_desc_status (desc_status_if), .m_axis_read_desc_status (read_desc_status_if),
.m_axis_read_data (read_data_if), .m_axis_read_data (read_data_if),
.s_axis_write_desc (write_desc_if),
.m_axis_write_desc_status (write_desc_status_if),
.s_axis_write_data (write_data_if), .s_axis_write_data (write_data_if),
.m_axi (m_axi_if), .m_axi (m_axi_if),
.read_enable (read_enable), .read_enable (read_enable),

View File

@ -3,7 +3,7 @@ import os
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Combine from cocotb.triggers import RisingEdge
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
@ -16,42 +16,33 @@ AXIS_ID_WIDTH = int(os.getenv("PARAM_AXIS_ID_WIDTH", "8"))
AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8")) AXIS_DEST_WIDTH = int(os.getenv("PARAM_AXIS_DEST_WIDTH", "8"))
AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1")) AXIS_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1"))
READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \ READ_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH READ_DESC_DATA_WIDTH = ((READ_DESC_RAW_WIDTH + 7) // 8) * 8
WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH READ_DESC_BYTES = READ_DESC_DATA_WIDTH // 8
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_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + \ WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) // 8) * 8
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4 WRITE_DESC_BYTES = WRITE_DESC_DATA_WIDTH // 8
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 READ_STATUS_RAW_WIDTH = TAG_WIDTH + 4
RD_LEN_LSB = RD_ADDR_LSB + AXI_ADDR_WIDTH READ_STATUS_DATA_WIDTH = ((READ_STATUS_RAW_WIDTH + 7) // 8) * 8
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 WRITE_STATUS_RAW_WIDTH = LEN_WIDTH + TAG_WIDTH + 4
WR_ADDR_LSB = WR_BASE_LSB WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) // 8) * 8
WR_LEN_LSB = WR_ADDR_LSB + AXI_ADDR_WIDTH
WR_TAG_LSB = WR_LEN_LSB + LEN_WIDTH
RD_STS_TAG_LSB = 0 # axi_dma_wrapper maps descriptor tdata as {addr, len, tag}.
RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH DESC_TAG_LSB = 0
DESC_LEN_LSB = DESC_TAG_LSB + TAG_WIDTH
DESC_ADDR_LSB = DESC_LEN_LSB + LEN_WIDTH
WR_STS_BASE_LSB = READ_STATUS_WIDTH # Read status tdata is {tag, error}.
WR_STS_LEN_LSB = WR_STS_BASE_LSB READ_STATUS_ERROR_LSB = 0
WR_STS_TAG_LSB = WR_STS_LEN_LSB + LEN_WIDTH READ_STATUS_TAG_LSB = READ_STATUS_ERROR_LSB + 4
WR_STS_ID_LSB = WR_STS_TAG_LSB + TAG_WIDTH
WR_STS_DEST_LSB = WR_STS_ID_LSB + AXIS_ID_WIDTH # Write status tdata is {len, tag, error}.
WR_STS_USER_LSB = WR_STS_DEST_LSB + AXIS_DEST_WIDTH WRITE_STATUS_ERROR_LSB = 0
WR_STS_ERROR_LSB = WR_STS_USER_LSB + AXIS_USER_WIDTH WRITE_STATUS_TAG_LSB = WRITE_STATUS_ERROR_LSB + 4
WRITE_STATUS_LEN_LSB = WRITE_STATUS_TAG_LSB + TAG_WIDTH
def mask(width): def mask(width):
@ -68,59 +59,60 @@ def get(value, lsb, width):
return (int(value) >> lsb) & mask(width) return (int(value) >> lsb) & mask(width)
def pack_desc_cmd( def pack_read_desc(*, addr, length, tag, axis_id=0, dest=0, user=0):
*,
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 = 0
value = put(value, RD_ADDR_LSB, AXI_ADDR_WIDTH, read_addr) value = put(value, DESC_TAG_LSB, TAG_WIDTH, tag)
value = put(value, RD_LEN_LSB, LEN_WIDTH, read_len) value = put(value, DESC_LEN_LSB, LEN_WIDTH, length)
value = put(value, RD_TAG_LSB, TAG_WIDTH, read_tag) value = put(value, DESC_ADDR_LSB, AXI_ADDR_WIDTH, addr)
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) return AxiStreamFrame(
value = put(value, WR_LEN_LSB, LEN_WIDTH, write_len) value.to_bytes(READ_DESC_BYTES, byteorder="little"),
value = put(value, WR_TAG_LSB, TAG_WIDTH, write_tag) tid=axis_id,
tdest=dest,
tuser=user,
)
return value.to_bytes(DESC_CMD_BYTES, byteorder="little")
def pack_write_desc(*, addr, length, tag):
value = 0
value = put(value, DESC_TAG_LSB, TAG_WIDTH, tag)
value = put(value, DESC_LEN_LSB, LEN_WIDTH, length)
value = put(value, DESC_ADDR_LSB, AXI_ADDR_WIDTH, addr)
return AxiStreamFrame(
value.to_bytes(WRITE_DESC_BYTES, byteorder="little")
)
def frame_to_int(frame): def frame_to_int(frame):
return int.from_bytes(bytes(frame.tdata), byteorder="little") return int.from_bytes(bytes(frame.tdata), byteorder="little")
def frame_tuser(frame): def frame_sideband(frame, name):
value = getattr(frame, name)
try: try:
return int(frame.tuser) return int(value)
except TypeError: except TypeError:
return int(frame.tuser[0]) if frame.tuser else 0 return int(value[0]) if value else 0
def unpack_status(frame): def unpack_read_status(frame):
value = frame_to_int(frame) value = frame_to_int(frame)
tuser = frame_tuser(frame)
return { return {
"read_valid": bool(tuser & 0x1), "tag": get(value, READ_STATUS_TAG_LSB, TAG_WIDTH),
"write_valid": bool(tuser & 0x2), "error": get(value, READ_STATUS_ERROR_LSB, 4),
"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), def unpack_write_status(frame):
"write_id": get(value, WR_STS_ID_LSB, AXIS_ID_WIDTH), value = frame_to_int(frame)
"write_dest": get(value, WR_STS_DEST_LSB, AXIS_DEST_WIDTH), return {
"write_user": get(value, WR_STS_USER_LSB, AXIS_USER_WIDTH), "len": get(value, WRITE_STATUS_LEN_LSB, LEN_WIDTH),
"write_error": get(value, WR_STS_ERROR_LSB, 4), "tag": get(value, WRITE_STATUS_TAG_LSB, TAG_WIDTH),
"error": get(value, WRITE_STATUS_ERROR_LSB, 4),
"id": frame_sideband(frame, "tid"),
"dest": frame_sideband(frame, "tdest"),
"user": frame_sideband(frame, "tuser"),
} }
@ -132,24 +124,45 @@ class TB:
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start()) cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
self.desc_cmd_source = AxiStreamSource( self.read_desc_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst AxiStreamBus.from_prefix(dut, "s_axis_read_desc"),
dut.clk,
dut.rst,
) )
self.desc_status_sink = AxiStreamSink( self.read_desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix( AxiStreamBus.from_prefix(dut, "m_axis_read_desc_status"),
dut, "m_axis_desc_status"), dut.clk, dut.rst dut.clk,
dut.rst,
)
self.write_desc_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_write_desc"),
dut.clk,
dut.rst,
)
self.write_desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_write_desc_status"),
dut.clk,
dut.rst,
) )
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,
) )
self.write_data_source = AxiStreamSource( self.write_data_source = AxiStreamSource(
AxiStreamBus.from_prefix( AxiStreamBus.from_prefix(dut, "s_axis_write_data"),
dut, "s_axis_write_data"), dut.clk, dut.rst dut.clk,
dut.rst,
) )
self.axi_ram = AxiRam(AxiBus.from_prefix( self.axi_ram = AxiRam(
dut, "m_axi"), dut.clk, dut.rst, size=2**16) AxiBus.from_prefix(dut, "m_axi"),
dut.clk,
dut.rst,
size=2**AXI_ADDR_WIDTH,
)
dut.read_enable.setimmediatevalue(0) dut.read_enable.setimmediatevalue(0)
dut.write_enable.setimmediatevalue(0) dut.write_enable.setimmediatevalue(0)
@ -166,8 +179,11 @@ 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): async def send_read_desc(self, **kwargs):
await self.desc_cmd_source.send(AxiStreamFrame(payload)) await self.read_desc_source.send(pack_read_desc(**kwargs))
async def send_write_desc(self, **kwargs):
await self.write_desc_source.send(pack_write_desc(**kwargs))
@cocotb.test() @cocotb.test()
@ -184,28 +200,26 @@ async def test_axis_compat_write_path(dut):
tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32)) tb.axi_ram.write(addr - 16, b"\xaa" * (len(data) + 32))
cmd = pack_desc_cmd(write_addr=addr, write_len=len(data), write_tag=tag) desc_task = cocotb.start_soon(
tb.send_write_desc(addr=addr, length=len(data), tag=tag)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) )
data_task = cocotb.start_soon( data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(data, tid=tag)) tb.write_data_source.send(AxiStreamFrame(data, tid=tag))
) )
await cmd_task await desc_task
await data_task await data_task
status_frame = await tb.desc_status_sink.recv() status = unpack_write_status(await tb.write_desc_status_sink.recv())
status = unpack_status(status_frame)
assert not status["read_valid"] assert status["tag"] == tag
assert status["write_valid"] assert status["len"] == len(data)
assert status["write_tag"] == tag assert status["id"] == tag
assert status["write_len"] == len(data) assert status["error"] == 0
assert status["write_id"] == tag
assert status["write_error"] == 0
assert tb.axi_ram.read(addr - 8, len(data) + assert tb.axi_ram.read(addr - 8, len(data) + 16) == (
16) == b"\xaa" * 8 + data + b"\xaa" * 8 b"\xaa" * 8 + data + b"\xaa" * 8
)
@cocotb.test() @cocotb.test()
@ -222,32 +236,26 @@ async def test_axis_compat_read_path(dut):
tb.axi_ram.write(addr, data) tb.axi_ram.write(addr, data)
cmd = pack_desc_cmd( await tb.send_read_desc(
read_addr=addr, addr=addr,
read_len=len(data), length=len(data),
read_tag=tag, tag=tag,
read_id=tag, axis_id=tag,
read_dest=0, dest=0,
read_user=0, user=0,
) )
await tb.send_desc_cmd(cmd)
data_frame = await tb.read_data_sink.recv() data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == data assert bytes(data_frame.tdata) == data
assert int(data_frame.tid) == tag assert frame_sideband(data_frame, "tid") == tag
status_frame = await tb.desc_status_sink.recv() status = unpack_read_status(await tb.read_desc_status_sink.recv())
status = unpack_status(status_frame) assert status["tag"] == tag
assert status["error"] == 0
assert status["read_valid"]
assert not status["write_valid"]
assert status["read_tag"] == tag
assert status["read_error"] == 0
@cocotb.test() @cocotb.test()
async def test_axis_compat_read_and_write_same_command(dut): async def test_axis_compat_read_and_write_independent_desc_ports(dut):
tb = TB(dut) tb = TB(dut)
await tb.reset() await tb.reset()
@ -264,51 +272,46 @@ async def test_axis_compat_read_and_write_same_command(dut):
tb.axi_ram.write(read_addr, read_data) tb.axi_ram.write(read_addr, read_data)
tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16)) tb.axi_ram.write(write_addr - 8, b"\xaa" * (len(write_data) + 16))
cmd = pack_desc_cmd( read_desc_task = cocotb.start_soon(
read_addr=read_addr, tb.send_read_desc(
read_len=len(read_data), addr=read_addr,
read_tag=read_tag, length=len(read_data),
read_id=read_tag, tag=read_tag,
write_addr=write_addr, axis_id=read_tag,
write_len=len(write_data), dest=0,
write_tag=write_tag, user=0,
) )
)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd)) write_desc_task = cocotb.start_soon(
data_task = cocotb.start_soon( tb.send_write_desc(
addr=write_addr,
length=len(write_data),
tag=write_tag,
)
)
write_data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag)) tb.write_data_source.send(AxiStreamFrame(write_data, tid=write_tag))
) )
await cmd_task await read_desc_task
await data_task await write_desc_task
await write_data_task
data_frame = await tb.read_data_sink.recv() data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == read_data assert bytes(data_frame.tdata) == read_data
assert int(data_frame.tid) == read_tag assert frame_sideband(data_frame, "tid") == read_tag
# status might be in 2 beats, depending on timings read_status = unpack_read_status(await tb.read_desc_status_sink.recv())
seen_read = False write_status = unpack_write_status(await tb.write_desc_status_sink.recv())
seen_write = False
for _ in range(2): assert read_status["tag"] == read_tag
status_frame = await tb.desc_status_sink.recv() assert read_status["error"] == 0
status = unpack_status(status_frame)
if status["read_valid"]: assert write_status["tag"] == write_tag
assert status["read_tag"] == read_tag assert write_status["len"] == len(write_data)
assert status["read_error"] == 0 assert write_status["id"] == write_tag
seen_read = True assert write_status["error"] == 0
if status["write_valid"]: assert tb.axi_ram.read(write_addr - 8, len(write_data) + 16) == (
assert status["write_tag"] == write_tag b"\xaa" * 8 + write_data + b"\xaa" * 8
assert status["write_len"] == len(write_data) )
assert status["write_error"] == 0
seen_write = True
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

View File

@ -92,6 +92,7 @@ module tb_axi4l_reg_map;
logic [N_REGS-1:0][31:0] reg_i; logic [N_REGS-1:0][31:0] reg_i;
logic [N_REGS-1:0][31:0] reg_o; logic [N_REGS-1:0][31:0] reg_o;
logic [N_REGS-1:0][31:0] reg_pulse;
// debug probes visible from cocotb. they are useful for checking W1S pulse-like behavior // debug probes visible from cocotb. they are useful for checking W1S pulse-like behavior
logic [31:0] reg0_o; logic [31:0] reg0_o;
@ -157,7 +158,8 @@ module tb_axi4l_reg_map;
.rst_n (rst_n), .rst_n (rst_n),
.s_axil (s_axil_if.slave), .s_axil (s_axil_if.slave),
.reg_i (reg_i), .reg_i (reg_i),
.reg_o (reg_o) .reg_o (reg_o),
.reg_pulse(reg_pulse)
); );
endmodule endmodule

View File

@ -1,8 +1,6 @@
# SPDX-License-Identifier: MIT
import cocotb import cocotb
from cocotb.clock import Clock from cocotb.clock import Clock
from cocotb.triggers import RisingEdge from cocotb.triggers import ReadOnly, RisingEdge
from cocotbext.axi import AxiLiteBus, AxiLiteMaster from cocotbext.axi import AxiLiteBus, AxiLiteMaster
OKAY = 0 OKAY = 0
@ -35,7 +33,8 @@ def _read_data(resp):
class TB: class TB:
def __init__(self, dut): def __init__(self, dut):
self.dut = dut self.dut = dut
self.axil = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil"), dut.clk, dut.rst) self.axil = AxiLiteMaster(AxiLiteBus.from_prefix(
dut, "s_axil"), dut.clk, dut.rst)
async def reset(self): async def reset(self):
self.dut.rst.value = 1 self.dut.rst.value = 1
@ -114,28 +113,175 @@ async def test_rw_and_w1c_bits(dut):
assert await tb.read32(REG2_CONFIG) == 0xDEADBEEF assert await tb.read32(REG2_CONFIG) == 0xDEADBEEF
def _reg0_pulse_value(dut):
"""Return REG0 pulse bits regardless of how the test wrapper exposes them."""
if hasattr(dut, "reg0_pulse"):
return int(dut.reg0_pulse.value)
if hasattr(dut, "reg_pulse"):
# Packed SystemVerilog array [N_REGS-1:0][31:0]: REG0 occupies bits 31:0.
return int(dut.reg_pulse.value) & 0xFFFF_FFFF
raise AssertionError(
"DUT must expose either reg0_pulse[31:0] or packed reg_pulse"
)
async def _sample_reg0_pulse_cycles(dut, cycles):
"""Sample REG0 pulse output after each active clock edge."""
samples = []
for _ in range(cycles):
await RisingEdge(dut.clk)
await ReadOnly()
samples.append(_reg0_pulse_value(dut))
return samples
def _assert_single_cycle_bit_pulse(samples, bit, name):
"""Check that one selected bit was high for exactly one sampled cycle."""
high_cycles = [
index for index, value in enumerate(samples)
if value & (1 << bit)
]
assert len(high_cycles) == 1, (
f"{name}: expected one high cycle, got {len(high_cycles)}; "
f"samples={[f'0x{value:08x}' for value in samples]}"
)
return high_cycles[0]
@cocotb.test() @cocotb.test()
async def test_w1s_seen_on_reg_o_probe(dut): async def test_reg_pulse_is_zero_after_reset(dut):
"""Check that a W1S write creates a short-lived internal reg_o bit.""" """No W1 event may be reported after reset without a write."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start()) cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut) tb = TB(dut)
await tb.reset() await tb.reset()
seen = False samples = await _sample_reg0_pulse_cycles(dut, 4)
assert not any(samples), (
"reg_pulse was asserted without a W1 write: "
f"{[f'0x{value:08x}' for value in samples]}"
)
async def monitor_w1s_bit():
nonlocal seen
for _ in range(20):
await RisingEdge(dut.clk)
if int(dut.reg0_o.value) & 0x1:
seen = True
mon = cocotb.start_soon(monitor_w1s_bit()) @cocotb.test()
async def test_w1s_generates_single_cycle_reg_pulse(dut):
"""Writing 1 to REG0[0] W1S must pulse REG0 pulse bit 0 once."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
monitor = cocotb.start_soon(_sample_reg0_pulse_cycles(dut, 20))
await tb.write32(REG0_CTRL, 0x00000001) await tb.write32(REG0_CTRL, 0x00000001)
await mon samples = await monitor
assert seen, "W1S bit was never observed on reg0_o[0]" _assert_single_cycle_bit_pulse(samples, bit=0, name="reg_pulse[0][0]")
assert await tb.read32(REG0_CTRL) == 0x00000004 # W1S reads as 0, W1C reset bit remains set
# No unrelated W1 bit may pulse.
assert not any(value & (1 << 2) for value in samples)
# W1S is a command bit and is not returned by reads.
assert await tb.read32(REG0_CTRL) == 0x00000004
@cocotb.test()
async def test_w1c_generates_single_cycle_reg_pulse(dut):
"""Writing 1 to REG0[2] W1C must pulse bit 2 once and clear state."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
assert await tb.read32(REG0_CTRL) == 0x00000004
monitor = cocotb.start_soon(_sample_reg0_pulse_cycles(dut, 20))
await tb.write32(REG0_CTRL, 0x00000004)
samples = await monitor
_assert_single_cycle_bit_pulse(samples, bit=2, name="reg_pulse[0][2]")
# No unrelated W1 bit may pulse.
assert not any(value & (1 << 0) for value in samples)
assert await tb.read32(REG0_CTRL) == 0x00000000
@cocotb.test()
async def test_zero_to_w1_and_rw_write_do_not_generate_reg_pulse(dut):
"""Only written ones in W1 fields may create reg_pulse."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
monitor = cocotb.start_soon(_sample_reg0_pulse_cycles(dut, 20))
# bit1 is RW; both W1 fields receive zero.
await tb.write32(REG0_CTRL, 0x00000002)
samples = await monitor
assert not any(samples), (
"RW write or zero written to W1 fields generated reg_pulse: "
f"{[f'0x{value:08x}' for value in samples]}"
)
assert await tb.read32(REG0_CTRL) == 0x00000006
@cocotb.test()
async def test_w1s_and_w1c_pulse_together(dut):
"""W1S and W1C ones in one AXI write must pulse on the same cycle."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
monitor = cocotb.start_soon(_sample_reg0_pulse_cycles(dut, 20))
await tb.write32(REG0_CTRL, 0x00000005)
samples = await monitor
w1s_cycle = _assert_single_cycle_bit_pulse(
samples, bit=0, name="reg_pulse[0][0]"
)
w1c_cycle = _assert_single_cycle_bit_pulse(
samples, bit=2, name="reg_pulse[0][2]"
)
assert w1s_cycle == w1c_cycle, (
"W1 bits written by one transaction pulsed on different cycles: "
f"W1S={w1s_cycle}, W1C={w1c_cycle}"
)
# Only bits 0 and 2 are allowed to pulse.
assert samples[w1s_cycle] == 0x00000005
assert await tb.read32(REG0_CTRL) == 0x00000000
@cocotb.test()
async def test_each_w1_write_creates_a_new_pulse(dut):
"""Two separate W1 writes must create two separate one-cycle pulses."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
await tb.reset()
monitor = cocotb.start_soon(_sample_reg0_pulse_cycles(dut, 40))
await tb.write32(REG0_CTRL, 0x00000001)
for _ in range(3):
await RisingEdge(dut.clk)
await tb.write32(REG0_CTRL, 0x00000001)
samples = await monitor
high_cycles = [
index for index, value in enumerate(samples)
if value & 0x1
]
assert len(high_cycles) == 2, (
f"expected two W1S pulses, got cycles {high_cycles}; "
f"samples={[f'0x{value:08x}' for value in samples]}"
)
assert high_cycles[1] > high_cycles[0] + 1, (
f"separate writes did not produce separate pulses: {high_cycles}"
)
@cocotb.test() @cocotb.test()