7 Commits

11 changed files with 574 additions and 309 deletions

View File

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

View File

@ -1,37 +1,64 @@
package dma_axil_reg_map_pkg;
localparam int unsigned DMA_AXIL_REG_MAP_N_REGS = 4;
localparam int unsigned DMA_AXIL_REG_MAP_N_REGS = 6;
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_RO = 3'd1;
localparam logic [2:0] REG_BIT_RW = 3'd2;
localparam logic [2:0] REG_BIT_W1S = 3'd3;
localparam logic [2:0] REG_BIT_W1C = 3'd4;
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;
typedef logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0][2:0] reg_mode_map_t;
localparam logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0][2:0] DMA_AXIL_REG_MAP_REG_MODE = '{
function automatic reg_mode_map_t make_dma_reg_mode();
reg_mode_map_t mode;
'{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}
};
mode = '0;
localparam logic [DMA_AXIL_REG_MAP_N_REGS-1:0][31:0] DMA_AXIL_REG_MAP_REG_RST = '{
32'h0000_0000,
32'h0000_0000,
32'h0000_0000,
32'h0000_0000,
32'h0000_0000,
32'h0000_0000
};
// По умолчанию всё reserved
for (int reg_idx = 0; reg_idx < DMA_AXIL_REG_MAP_N_REGS; reg_idx++) begin
for (int bit_idx = 0; bit_idx < 32; bit_idx++) begin
mode[reg_idx][bit_idx] = REG_BIT_RSVD;
end
end
// 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

View File

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

View File

@ -271,7 +271,7 @@ module axi_dma_wrapper #(
// local read descriptor interface -> DMA flat input
axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W ('0),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
@ -282,7 +282,7 @@ module axi_dma_wrapper #(
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (dma_s_axis_read_desc_id),
.m_axis_tdest (dma_s_axis_read_desc_tdest),
.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)
@ -291,10 +291,10 @@ module axi_dma_wrapper #(
// DMA read descriptor status flat output -> local status interface
axis_flat_to_if #(
.DATA_W (TAG_WIDTH + 4),
.KEEP_W ('0),
.ID_W ('0),
.DEST_W ('0),
.USER_W ('0)
.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 (),
@ -311,7 +311,7 @@ module axi_dma_wrapper #(
// local write descriptor interface -> DMA flat input
axis_if_to_flat #(
.DATA_W (AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH),
.KEEP_W ('0),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
@ -321,9 +321,9 @@ module axi_dma_wrapper #(
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (dma_s_axis_write_desc_id),
.m_axis_tdest (dma_s_axis_write_desc_tdest),
.m_axis_tuser (dma_s_axis_write_desc_user),
.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)
);
@ -332,7 +332,7 @@ module axi_dma_wrapper #(
// DMA write descriptor status flat output -> local status interface
axis_flat_to_if #(
.DATA_W (TAG_WIDTH + LEN_WIDTH + 4),
.KEEP_W ('0),
.KEEP_W (AXIS_KEEP_WIDTH),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
@ -448,6 +448,6 @@ module axi_dma_wrapper #(
.m_axi (m_axi)
);
endmodule : axi_dma_if_wrapper
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 [ID_W-1:0] id_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_resp_t 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/8-1:0] strb_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_resp_t resp;
modport master (input aclk, aresetn, output req, input resp);

View File

@ -1,6 +1,6 @@
module axil_cdc_wrapper #(
parameter int ADDR_WIDTH = 32
parameter int DATA_WIDTH = 32,
parameter int ADDR_WIDTH = 32,
parameter int DATA_WIDTH = 32
)(
input wire s_clk,
input wire s_rst,
@ -83,8 +83,8 @@ module axil_cdc_wrapper #(
);
axil_cdc #(
.ADDR_WIDTH (ADDR_WIDTH)
.DATA_WIDTH (DATA_WIDTH),
.ADDR_WIDTH (ADDR_WIDTH),
.DATA_WIDTH (DATA_WIDTH)
) i_axil_cdc (
.s_clk (s_clk),
.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 += $(AXI_IF_RTL_DIR)/axi_dma_if_wrapper.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_compat.sv
VERILOG_SOURCES += $(AXI_IF_RTL_DIR)/axi_dma_wrapper.sv
VERILOG_SOURCES += $(TB_DIR)/tb_axi_dma_axis_compat.sv
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_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 WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH,
parameter int unsigned DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_CMD_KEEP_WIDTH = DESC_CMD_DATA_WIDTH / 8,
parameter int unsigned READ_STATUS_WIDTH = TAG_WIDTH + 4,
parameter int unsigned WRITE_STATUS_WIDTH = LEN_WIDTH + TAG_WIDTH + AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH + 4,
parameter int unsigned DESC_STATUS_RAW_WIDTH = READ_STATUS_WIDTH + WRITE_STATUS_WIDTH,
parameter int unsigned DESC_STATUS_DATA_WIDTH = ((DESC_STATUS_RAW_WIDTH + 7) / 8) * 8,
parameter int unsigned DESC_STATUS_KEEP_WIDTH = DESC_STATUS_DATA_WIDTH / 8,
parameter int unsigned DESC_STATUS_USER_WIDTH = 2
parameter int unsigned READ_DESC_RAW_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 WRITE_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH,
parameter int unsigned WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) / 8) * 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 WRITE_STATUS_RAW_WIDTH = LEN_WIDTH + TAG_WIDTH + 4,
parameter int unsigned WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) / 8) * 8
)(
input logic clk,
input logic rst,
@ -46,27 +42,35 @@ module tb_axi_dma_axis_compat #(
input logic write_enable,
input logic write_abort,
// Unified descriptor command AXIS, flat for cocotb
input logic [DESC_CMD_DATA_WIDTH-1:0] s_axis_desc_cmd_tdata,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tkeep,
input logic [DESC_CMD_KEEP_WIDTH-1:0] s_axis_desc_cmd_tstrb,
input logic s_axis_desc_cmd_tlast,
input logic s_axis_desc_cmd_tid,
input logic s_axis_desc_cmd_tdest,
input logic s_axis_desc_cmd_tuser,
input logic s_axis_desc_cmd_tvalid,
output logic s_axis_desc_cmd_tready,
// Read descriptor AXIS input, flat for cocotb.
// tdata layout: {addr, len, tag}; id/dest/user use AXIS sidebands.
input logic [READ_DESC_DATA_WIDTH-1:0] s_axis_read_desc_tdata,
input logic [AXIS_ID_WIDTH-1:0] s_axis_read_desc_tid,
input logic [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_tdest,
input logic [AXIS_USER_WIDTH-1:0] s_axis_read_desc_tuser,
input logic s_axis_read_desc_tvalid,
output logic s_axis_read_desc_tready,
// Unified descriptor status AXIS, flat for cocotb
output logic [DESC_STATUS_DATA_WIDTH-1:0] m_axis_desc_status_tdata,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tkeep,
output logic [DESC_STATUS_KEEP_WIDTH-1:0] m_axis_desc_status_tstrb,
output logic m_axis_desc_status_tlast,
output logic m_axis_desc_status_tid,
output logic m_axis_desc_status_tdest,
output logic [DESC_STATUS_USER_WIDTH-1:0] m_axis_desc_status_tuser,
output logic m_axis_desc_status_tvalid,
input logic m_axis_desc_status_tready,
// Read descriptor status AXIS output.
// tdata layout: {tag, error}.
output logic [READ_STATUS_DATA_WIDTH-1:0] m_axis_read_desc_status_tdata,
output logic m_axis_read_desc_status_tvalid,
input logic m_axis_read_desc_status_tready,
// Write descriptor AXIS input.
// tdata layout: {addr, len, tag}.
input logic [WRITE_DESC_DATA_WIDTH-1:0] s_axis_write_desc_tdata,
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
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;
axis_if #(
.DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) desc_cmd_if (
.DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) read_desc_if (
.aclk (clk),
.aresetn (rstn)
);
axis_if #(
.DATA_W (DESC_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH),
.DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.ID_W (1),
.DEST_W (1),
.USER_W (DESC_STATUS_USER_WIDTH)
) desc_status_if (
.USER_W (1)
) 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),
.aresetn (rstn)
);
@ -198,41 +224,79 @@ module tb_axi_dma_axis_compat #(
);
axis_flat_to_if #(
.DATA_W (DESC_CMD_DATA_WIDTH),
.KEEP_W (DESC_CMD_KEEP_WIDTH),
.ID_W (1),
.DEST_W (1),
.USER_W (1)
) u_desc_cmd_flat_to_if (
.s_axis_tdata (s_axis_desc_cmd_tdata),
.s_axis_tkeep (s_axis_desc_cmd_tkeep),
.s_axis_tstrb (s_axis_desc_cmd_tstrb),
.s_axis_tlast (s_axis_desc_cmd_tlast),
.s_axis_tid (s_axis_desc_cmd_tid),
.s_axis_tdest (s_axis_desc_cmd_tdest),
.s_axis_tuser (s_axis_desc_cmd_tuser),
.s_axis_tvalid (s_axis_desc_cmd_tvalid),
.s_axis_tready (s_axis_desc_cmd_tready),
.m_axis (desc_cmd_if)
.DATA_W (READ_DESC_DATA_WIDTH),
.KEEP_W (READ_DESC_DATA_WIDTH / 8),
.ID_W (AXIS_ID_WIDTH),
.DEST_W (AXIS_DEST_WIDTH),
.USER_W (AXIS_USER_WIDTH)
) u_read_desc_flat_to_if (
.s_axis_tdata (s_axis_read_desc_tdata),
.s_axis_tkeep ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tstrb ({(READ_DESC_DATA_WIDTH/8){1'b1}}),
.s_axis_tlast (1'b1),
.s_axis_tid (s_axis_read_desc_tid),
.s_axis_tdest (s_axis_read_desc_tdest),
.s_axis_tuser (s_axis_read_desc_tuser),
.s_axis_tvalid (s_axis_read_desc_tvalid),
.s_axis_tready (s_axis_read_desc_tready),
.m_axis (read_desc_if)
);
axis_if_to_flat #(
.DATA_W (DESC_STATUS_DATA_WIDTH),
.KEEP_W (DESC_STATUS_KEEP_WIDTH),
.DATA_W (READ_STATUS_DATA_WIDTH),
.KEEP_W (READ_STATUS_DATA_WIDTH / 8),
.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)
.USER_W (1)
) u_read_desc_status_if_to_flat (
.s_axis (read_desc_status_if),
.m_axis_tdata (m_axis_read_desc_status_tdata),
.m_axis_tkeep (),
.m_axis_tstrb (),
.m_axis_tlast (),
.m_axis_tid (),
.m_axis_tdest (),
.m_axis_tuser (),
.m_axis_tvalid (m_axis_read_desc_status_tvalid),
.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 #(
@ -273,7 +337,7 @@ module tb_axi_dma_axis_compat #(
.m_axis (write_data_if)
);
axi_dma_axis_compat #(
axi_dma_wrapper #(
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_STRB_WIDTH (AXI_STRB_WIDTH),
@ -293,16 +357,15 @@ module tb_axi_dma_axis_compat #(
.LEN_WIDTH (LEN_WIDTH),
.TAG_WIDTH (TAG_WIDTH),
.ENABLE_SG (ENABLE_SG),
.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)
.ENABLE_UNALIGNED (ENABLE_UNALIGNED)
) u_dut (
.clk (clk),
.rst (rst),
.s_axis_desc_cmd (desc_cmd_if),
.m_axis_desc_status (desc_status_if),
.s_axis_read_desc (read_desc_if),
.m_axis_read_desc_status (read_desc_status_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),
.m_axi (m_axi_if),
.read_enable (read_enable),

View File

@ -3,7 +3,7 @@ import os
import cocotb
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 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_USER_WIDTH = int(os.getenv("PARAM_AXIS_USER_WIDTH", "1"))
READ_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH + \
AXIS_ID_WIDTH + AXIS_DEST_WIDTH + AXIS_USER_WIDTH
WRITE_DESC_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
DESC_CMD_RAW_WIDTH = READ_DESC_WIDTH + WRITE_DESC_WIDTH
DESC_CMD_DATA_WIDTH = ((DESC_CMD_RAW_WIDTH + 7) // 8) * 8
DESC_CMD_BYTES = DESC_CMD_DATA_WIDTH // 8
READ_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
READ_DESC_DATA_WIDTH = ((READ_DESC_RAW_WIDTH + 7) // 8) * 8
READ_DESC_BYTES = READ_DESC_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
WRITE_DESC_RAW_WIDTH = AXI_ADDR_WIDTH + LEN_WIDTH + TAG_WIDTH
WRITE_DESC_DATA_WIDTH = ((WRITE_DESC_RAW_WIDTH + 7) // 8) * 8
WRITE_DESC_BYTES = WRITE_DESC_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
READ_STATUS_RAW_WIDTH = TAG_WIDTH + 4
READ_STATUS_DATA_WIDTH = ((READ_STATUS_RAW_WIDTH + 7) // 8) * 8
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
WRITE_STATUS_RAW_WIDTH = LEN_WIDTH + TAG_WIDTH + 4
WRITE_STATUS_DATA_WIDTH = ((WRITE_STATUS_RAW_WIDTH + 7) // 8) * 8
RD_STS_TAG_LSB = 0
RD_STS_ERROR_LSB = RD_STS_TAG_LSB + TAG_WIDTH
# axi_dma_wrapper maps descriptor tdata as {addr, len, tag}.
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
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
# Read status tdata is {tag, error}.
READ_STATUS_ERROR_LSB = 0
READ_STATUS_TAG_LSB = READ_STATUS_ERROR_LSB + 4
# Write status tdata is {len, tag, error}.
WRITE_STATUS_ERROR_LSB = 0
WRITE_STATUS_TAG_LSB = WRITE_STATUS_ERROR_LSB + 4
WRITE_STATUS_LEN_LSB = WRITE_STATUS_TAG_LSB + TAG_WIDTH
def mask(width):
@ -68,59 +59,60 @@ 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,
):
def pack_read_desc(*, addr, length, tag, axis_id=0, dest=0, user=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, 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)
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 AxiStreamFrame(
value.to_bytes(READ_DESC_BYTES, byteorder="little"),
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):
return int.from_bytes(bytes(frame.tdata), byteorder="little")
def frame_tuser(frame):
def frame_sideband(frame, name):
value = getattr(frame, name)
try:
return int(frame.tuser)
return int(value)
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)
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),
"tag": get(value, READ_STATUS_TAG_LSB, TAG_WIDTH),
"error": get(value, READ_STATUS_ERROR_LSB, 4),
}
def unpack_write_status(frame):
value = frame_to_int(frame)
return {
"len": get(value, WRITE_STATUS_LEN_LSB, LEN_WIDTH),
"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())
self.desc_cmd_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_desc_cmd"), dut.clk, dut.rst
self.read_desc_source = AxiStreamSource(
AxiStreamBus.from_prefix(dut, "s_axis_read_desc"),
dut.clk,
dut.rst,
)
self.desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix(
dut, "m_axis_desc_status"), dut.clk, dut.rst
self.read_desc_status_sink = AxiStreamSink(
AxiStreamBus.from_prefix(dut, "m_axis_read_desc_status"),
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(
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(
AxiStreamBus.from_prefix(
dut, "s_axis_write_data"), dut.clk, dut.rst
AxiStreamBus.from_prefix(dut, "s_axis_write_data"),
dut.clk,
dut.rst,
)
self.axi_ram = AxiRam(AxiBus.from_prefix(
dut, "m_axi"), dut.clk, dut.rst, size=2**16)
self.axi_ram = AxiRam(
AxiBus.from_prefix(dut, "m_axi"),
dut.clk,
dut.rst,
size=2**AXI_ADDR_WIDTH,
)
dut.read_enable.setimmediatevalue(0)
dut.write_enable.setimmediatevalue(0)
@ -166,8 +179,11 @@ class TB:
await RisingEdge(self.dut.clk)
await RisingEdge(self.dut.clk)
async def send_desc_cmd(self, payload):
await self.desc_cmd_source.send(AxiStreamFrame(payload))
async def send_read_desc(self, **kwargs):
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()
@ -184,28 +200,26 @@ async def test_axis_compat_write_path(dut):
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)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
desc_task = cocotb.start_soon(
tb.send_write_desc(addr=addr, length=len(data), tag=tag)
)
data_task = cocotb.start_soon(
tb.write_data_source.send(AxiStreamFrame(data, tid=tag))
)
await cmd_task
await desc_task
await data_task
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
status = unpack_write_status(await tb.write_desc_status_sink.recv())
assert not status["read_valid"]
assert status["write_valid"]
assert status["write_tag"] == tag
assert status["write_len"] == len(data)
assert status["write_id"] == tag
assert status["write_error"] == 0
assert status["tag"] == tag
assert status["len"] == len(data)
assert status["id"] == tag
assert status["error"] == 0
assert tb.axi_ram.read(addr - 8, len(data) +
16) == b"\xaa" * 8 + data + b"\xaa" * 8
assert tb.axi_ram.read(addr - 8, len(data) + 16) == (
b"\xaa" * 8 + data + b"\xaa" * 8
)
@cocotb.test()
@ -222,32 +236,26 @@ async def test_axis_compat_read_path(dut):
tb.axi_ram.write(addr, data)
cmd = pack_desc_cmd(
read_addr=addr,
read_len=len(data),
read_tag=tag,
read_id=tag,
read_dest=0,
read_user=0,
await tb.send_read_desc(
addr=addr,
length=len(data),
tag=tag,
axis_id=tag,
dest=0,
user=0,
)
await tb.send_desc_cmd(cmd)
data_frame = await tb.read_data_sink.recv()
assert bytes(data_frame.tdata) == data
assert int(data_frame.tid) == tag
assert frame_sideband(data_frame, "tid") == tag
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
assert status["read_valid"]
assert not status["write_valid"]
assert status["read_tag"] == tag
assert status["read_error"] == 0
status = unpack_read_status(await tb.read_desc_status_sink.recv())
assert status["tag"] == tag
assert status["error"] == 0
@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)
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(write_addr - 8, b"\xaa" * (len(write_data) + 16))
cmd = pack_desc_cmd(
read_addr=read_addr,
read_len=len(read_data),
read_tag=read_tag,
read_id=read_tag,
write_addr=write_addr,
write_len=len(write_data),
write_tag=write_tag,
read_desc_task = cocotb.start_soon(
tb.send_read_desc(
addr=read_addr,
length=len(read_data),
tag=read_tag,
axis_id=read_tag,
dest=0,
user=0,
)
cmd_task = cocotb.start_soon(tb.send_desc_cmd(cmd))
data_task = cocotb.start_soon(
)
write_desc_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))
)
await cmd_task
await data_task
await read_desc_task
await write_desc_task
await write_data_task
data_frame = await tb.read_data_sink.recv()
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
seen_read = False
seen_write = False
read_status = unpack_read_status(await tb.read_desc_status_sink.recv())
write_status = unpack_write_status(await tb.write_desc_status_sink.recv())
for _ in range(2):
status_frame = await tb.desc_status_sink.recv()
status = unpack_status(status_frame)
assert read_status["tag"] == read_tag
assert read_status["error"] == 0
if status["read_valid"]:
assert status["read_tag"] == read_tag
assert status["read_error"] == 0
seen_read = True
assert write_status["tag"] == write_tag
assert write_status["len"] == len(write_data)
assert write_status["id"] == write_tag
assert write_status["error"] == 0
if status["write_valid"]:
assert status["write_tag"] == write_tag
assert status["write_len"] == len(write_data)
assert status["write_error"] == 0
seen_write = True
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
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_o;
logic [N_REGS-1:0][31:0] reg_pulse;
// debug probes visible from cocotb. they are useful for checking W1S pulse-like behavior
logic [31:0] reg0_o;
@ -157,7 +158,8 @@ module tb_axi4l_reg_map;
.rst_n (rst_n),
.s_axil (s_axil_if.slave),
.reg_i (reg_i),
.reg_o (reg_o)
.reg_o (reg_o),
.reg_pulse(reg_pulse)
);
endmodule

View File

@ -1,8 +1,6 @@
# SPDX-License-Identifier: MIT
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import ReadOnly, RisingEdge
from cocotbext.axi import AxiLiteBus, AxiLiteMaster
OKAY = 0
@ -35,7 +33,8 @@ def _read_data(resp):
class TB:
def __init__(self, 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):
self.dut.rst.value = 1
@ -114,28 +113,175 @@ async def test_rw_and_w1c_bits(dut):
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()
async def test_w1s_seen_on_reg_o_probe(dut):
"""Check that a W1S write creates a short-lived internal reg_o bit."""
async def test_reg_pulse_is_zero_after_reset(dut):
"""No W1 event may be reported after reset without a write."""
cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())
tb = TB(dut)
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 mon
samples = await monitor
assert seen, "W1S bit was never observed on reg0_o[0]"
assert await tb.read32(REG0_CTRL) == 0x00000004 # W1S reads as 0, W1C reset bit remains set
_assert_single_cycle_bit_pulse(samples, bit=0, name="reg_pulse[0][0]")
# 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()