Compare commits

...

3 Commits

Author SHA1 Message Date
f64848fb59 Merge pull request 'add wrappers 1.0' (#1) from dev into master
Reviewed-on: #1
2026-07-10 17:31:51 +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
4 changed files with 651 additions and 26 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

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 ('0),
.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_tdest),
.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 ('0),
.ID_W ('0),
.DEST_W ('0),
.USER_W ('0)
) 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 ('0),
.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 (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_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 ('0),
.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_if_wrapper
`default_nettype wire

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,14 +1,12 @@
# 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
SLVERR = 2
REG0_CTRL = 0x00
REG0_CTRL = 0x00
REG1_STATUS = 0x04
REG2_CONFIG = 0x08
REG_INVALID = 0x0C # N_REGS=3, so index 3 is bad
@ -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()