feat(ip): add AXI CDMA IP
This commit is contained in:
192
rtl/ip/cdma/snix_axi_cdma_csr.sv
Normal file
192
rtl/ip/cdma/snix_axi_cdma_csr.sv
Normal file
@@ -0,0 +1,192 @@
|
||||
// ============================================================================
|
||||
// snix_axi_cdma_csr.sv
|
||||
// AXI-Lite CSR for snix_axi_cdma (memory-to-memory central DMA)
|
||||
//
|
||||
// Register Map (word-addressed, 32-bit registers):
|
||||
//
|
||||
// Offset Index Name Bits
|
||||
// 0x00 0 CDMA_CTRL [0] = start (write-1 pulse)
|
||||
// [1] = stop (write-1 pulse)
|
||||
// [5:3] = size (AXI AxSIZE)
|
||||
// [13:6] = len (AXI AxLEN)
|
||||
// [31:14] = reserved
|
||||
// 0x04 1 CDMA_NUM_BYTES [31:0] = transfer_len (byte count)
|
||||
// 0x08 2 CDMA_SRC_ADDR [31:0] = source base address
|
||||
// 0x0C 3 CDMA_DST_ADDR [31:0] = destination base address
|
||||
// 0x10 4 STATUS (read-only) [0] = done (sticky; cleared on start)
|
||||
// [31:1] = reserved
|
||||
// 0x14– 5–7 Reserved
|
||||
//
|
||||
// Differences from snix_axi_dma_csr:
|
||||
// - Single CTRL register (no separate WR/RD paths)
|
||||
// - Separate SRC_ADDR and DST_ADDR registers
|
||||
// - STATUS[0] is write-protected; hardware sets it, start clears it
|
||||
// ============================================================================
|
||||
module snix_axi_cdma_csr #(
|
||||
parameter int DATA_WIDTH = 32,
|
||||
parameter int ADDR_WIDTH = 4,
|
||||
parameter int NUM_REGS = 8)
|
||||
(input logic clk,
|
||||
input logic rst_n,
|
||||
// AXI-Lite interface
|
||||
input logic [ADDR_WIDTH-1:0] s_axil_awaddr,
|
||||
input logic s_axil_awvalid,
|
||||
output logic s_axil_awready,
|
||||
input logic [DATA_WIDTH-1:0] s_axil_wdata,
|
||||
input logic [DATA_WIDTH/8-1:0] s_axil_wstrb,
|
||||
input logic s_axil_wvalid,
|
||||
output logic s_axil_wready,
|
||||
output logic [1:0] s_axil_bresp,
|
||||
output logic s_axil_bvalid,
|
||||
input logic s_axil_bready,
|
||||
input logic [ADDR_WIDTH-1:0] s_axil_araddr,
|
||||
input logic s_axil_arvalid,
|
||||
output logic s_axil_arready,
|
||||
output logic [DATA_WIDTH-1:0] s_axil_rdata,
|
||||
output logic [1:0] s_axil_rresp,
|
||||
output logic s_axil_rvalid,
|
||||
input logic s_axil_rready,
|
||||
// Status from mm2mm engine: [0] = ctrl_done (single-cycle pulse)
|
||||
input logic [DATA_WIDTH-1:0] read_status_reg,
|
||||
// Register file
|
||||
output logic [NUM_REGS-1:0][DATA_WIDTH-1:0] config_status_reg);
|
||||
|
||||
localparam int AXIL_DATA_WIDTH = DATA_WIDTH;
|
||||
localparam int AXIL_ADDR_WIDTH = ADDR_WIDTH;
|
||||
localparam int ADDRLSB = $clog2(AXIL_DATA_WIDTH / 8);
|
||||
localparam int REG_INDEX_WIDTH = $clog2(NUM_REGS);
|
||||
|
||||
localparam int CDMA_CTRL_IDX = 0; // 0x00 used: pulse-clear start/stop bits
|
||||
//localparam int CDMA_NUM_BYTES_IDX = 1; // 0x04 decoded in snix_axi_cdma top
|
||||
//localparam int CDMA_SRC_ADDR_IDX = 2; // 0x08 decoded in snix_axi_cdma top
|
||||
//localparam int CDMA_DST_ADDR_IDX = 3; // 0x0C decoded in snix_axi_cdma top
|
||||
localparam int STATUS_IDX = 4; // 0x10 used: write-protect + done latch
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// AXI-Lite skid-buffer register slices (same topology as snix_axi_dma_csr)
|
||||
// -------------------------------------------------------------------------
|
||||
logic s_axil_awvalid_reg;
|
||||
logic [AXIL_ADDR_WIDTH-ADDRLSB-1:0] s_axil_awaddr_reg;
|
||||
logic s_axil_arvalid_reg;
|
||||
logic [AXIL_ADDR_WIDTH-ADDRLSB-1:0] s_axil_araddr_reg;
|
||||
logic s_axil_wvalid_reg;
|
||||
logic [AXIL_DATA_WIDTH-1:0] s_axil_wdata_reg;
|
||||
logic [AXIL_DATA_WIDTH/8-1:0] s_axil_wstrb_reg;
|
||||
|
||||
logic s_axil_write_ready, s_axil_read_ready;
|
||||
logic [31:0] awaddr_index, araddr_index;
|
||||
|
||||
assign s_axil_write_ready = s_axil_awvalid_reg & s_axil_wvalid_reg &
|
||||
(!s_axil_bvalid | s_axil_bready);
|
||||
assign s_axil_read_ready = s_axil_arvalid_reg & (!s_axil_rvalid | s_axil_rready);
|
||||
|
||||
assign awaddr_index = {{(32-REG_INDEX_WIDTH){1'b0}}, s_axil_awaddr_reg[REG_INDEX_WIDTH-1:0]};
|
||||
assign araddr_index = {{(32-REG_INDEX_WIDTH){1'b0}}, s_axil_araddr_reg[REG_INDEX_WIDTH-1:0]};
|
||||
|
||||
snix_register_slice #(.DATA_WIDTH(AXIL_ADDR_WIDTH - ADDRLSB)) reg_slice_u0 (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
.s_axis_tdata (s_axil_awaddr[AXIL_ADDR_WIDTH-1:ADDRLSB]),
|
||||
.s_axis_tvalid(s_axil_awvalid),
|
||||
.s_axis_tready(s_axil_awready),
|
||||
.m_axis_tdata (s_axil_awaddr_reg),
|
||||
.m_axis_tvalid(s_axil_awvalid_reg),
|
||||
.m_axis_tready(s_axil_write_ready));
|
||||
|
||||
snix_register_slice #(.DATA_WIDTH(AXIL_DATA_WIDTH + AXIL_DATA_WIDTH/8)) reg_slice_u1 (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
.s_axis_tdata ({s_axil_wdata, s_axil_wstrb}),
|
||||
.s_axis_tvalid(s_axil_wvalid),
|
||||
.s_axis_tready(s_axil_wready),
|
||||
.m_axis_tdata ({s_axil_wdata_reg, s_axil_wstrb_reg}),
|
||||
.m_axis_tvalid(s_axil_wvalid_reg),
|
||||
.m_axis_tready(s_axil_write_ready));
|
||||
|
||||
snix_register_slice #(.DATA_WIDTH(AXIL_ADDR_WIDTH - ADDRLSB)) reg_slice_u2 (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
.s_axis_tdata (s_axil_araddr[AXIL_ADDR_WIDTH-1:ADDRLSB]),
|
||||
.s_axis_tvalid(s_axil_arvalid),
|
||||
.s_axis_tready(s_axil_arready),
|
||||
.m_axis_tdata (s_axil_araddr_reg),
|
||||
.m_axis_tvalid(s_axil_arvalid_reg),
|
||||
.m_axis_tready(s_axil_read_ready));
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// B channel
|
||||
// -------------------------------------------------------------------------
|
||||
always_ff @(posedge clk or negedge rst_n)
|
||||
if (!rst_n) s_axil_bvalid <= 1'b0;
|
||||
else if (s_axil_write_ready) s_axil_bvalid <= 1'b1;
|
||||
else if (s_axil_bready) s_axil_bvalid <= 1'b0;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// R channel
|
||||
// -------------------------------------------------------------------------
|
||||
always_ff @(posedge clk or negedge rst_n)
|
||||
if (!rst_n) s_axil_rvalid <= 1'b0;
|
||||
else if (s_axil_read_ready) s_axil_rvalid <= 1'b1;
|
||||
else if (s_axil_rready) s_axil_rvalid <= 1'b0;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Control / status signal aliases (combinatorial from register file)
|
||||
// -------------------------------------------------------------------------
|
||||
logic ctrl_start, ctrl_stop, ctrl_done;
|
||||
|
||||
assign ctrl_start = config_status_reg[CDMA_CTRL_IDX][0];
|
||||
assign ctrl_stop = config_status_reg[CDMA_CTRL_IDX][1];
|
||||
assign ctrl_done = read_status_reg[0];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Register file
|
||||
//
|
||||
// Write priority (highest to lowest within an always_ff block):
|
||||
// 1. AXI-Lite write (byte-enable; STATUS_IDX is write-protected)
|
||||
// 2. Pulse-clear: start[0] and stop[1] are single-cycle strobes
|
||||
// 3. STATUS latch: done bit set by hardware, cleared when start fires
|
||||
//
|
||||
// Note on NBA priority: later assignments in program order win, so
|
||||
// "config_status_reg <= config_status_reg" is safely overridden by
|
||||
// the per-bit assignments that follow in the else branch.
|
||||
// -------------------------------------------------------------------------
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
config_status_reg <= '0;
|
||||
|
||||
end else if (s_axil_write_ready &&
|
||||
awaddr_index < NUM_REGS &&
|
||||
awaddr_index != STATUS_IDX) begin // STATUS is read-only
|
||||
for (int i = 0; i < AXIL_DATA_WIDTH/8; i++) begin
|
||||
config_status_reg[awaddr_index][8*i +: 8] <=
|
||||
s_axil_wstrb_reg[i] ? s_axil_wdata_reg[8*i +: 8]
|
||||
: config_status_reg[awaddr_index][8*i +: 8];
|
||||
end
|
||||
|
||||
end else begin
|
||||
config_status_reg <= config_status_reg;
|
||||
|
||||
// Pulse-clear: start and stop are one-cycle strobes
|
||||
if (ctrl_start) config_status_reg[CDMA_CTRL_IDX][0] <= 1'b0;
|
||||
if (ctrl_stop) config_status_reg[CDMA_CTRL_IDX][1] <= 1'b0;
|
||||
|
||||
// STATUS[0]: cleared when a new transfer starts; set sticky on done
|
||||
if (ctrl_start) config_status_reg[STATUS_IDX][0] <= 1'b0;
|
||||
if (ctrl_done) config_status_reg[STATUS_IDX][0] <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Read data mux
|
||||
// -------------------------------------------------------------------------
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n)
|
||||
s_axil_rdata <= '0;
|
||||
else if (s_axil_read_ready && araddr_index < NUM_REGS)
|
||||
s_axil_rdata <= config_status_reg[araddr_index];
|
||||
end
|
||||
|
||||
assign s_axil_bresp = 2'b00;
|
||||
assign s_axil_rresp = 2'b00;
|
||||
|
||||
endmodule : snix_axi_cdma_csr
|
||||
Reference in New Issue
Block a user