Files
ciciec2026_loongson/rtl/ip/cdma/snix_axi_cdma_csr.sv
2026-04-12 22:25:00 +08:00

193 lines
8.9 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// 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 57 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