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

113 lines
3.8 KiB
Systemverilog

// ============================================================================
// snix_async_fifo.sv
//
// SystemVerilog implementation of an async FIFO. If targeting FPGAs, this
// implementation is intended to infer BRAM.
//
// Largely based on ZipCPU's async FIFO work:
// https://zipcpu.com/blog/2018/07/06/afifo.html
// ============================================================================
module snix_async_fifo #(parameter DATA_WIDTH = 8,
parameter FIFO_DEPTH = 4)
(input logic wclk,
input logic wrst_n,
input logic wr_en,
input logic [DATA_WIDTH-1:0] wdata,
output logic wfull,
input logic rclk,
input logic rrst_n,
input logic rd_en,
output logic [DATA_WIDTH-1:0] rdata,
output logic [$clog2(FIFO_DEPTH):0] rd_n_items,
output logic rempty);
localparam ADDR_W = $clog2(FIFO_DEPTH);
localparam NFF = 2;
logic [ADDR_W:0] rd_addr, next_rd_addr;
logic [ADDR_W:0] wr_addr, next_wr_addr;
logic [ADDR_W:0] rgray, wgray;
logic [ADDR_W:0] rd_wgray, wr_rgray, rd_wbin;// rd_n_items;
(* ASYNC_REG = "TRUE" *) logic [(ADDR_W+1)*(NFF-1)-1:0] rgray_cross, wgray_cross;
(* ram_style = "block" *) logic [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
logic [DATA_WIDTH-1:0] lcl_rd_data;
logic lcl_read, lcl_rd_empty;
assign next_wr_addr = wr_addr + 1'b1;
always_ff @(posedge wclk or negedge wrst_n)
if(!wrst_n) begin
wr_addr <= (ADDR_W+1)'(0);
wgray <= (ADDR_W+1)'(0);
end
else if(wr_en && !wfull) begin
wr_addr <= next_wr_addr;
wgray <= next_wr_addr ^ (next_wr_addr>>1);
end
always_ff @(posedge rclk or negedge rrst_n)
if(!rrst_n) begin
rd_addr <= (ADDR_W+1)'(0);
rgray <= (ADDR_W+1)'(0);
end
else if(lcl_read && !lcl_rd_empty) begin
rd_addr <= next_rd_addr;
rgray <= next_rd_addr ^ (next_rd_addr>>1);
end
always_ff @(posedge wclk)
mem[wr_addr[ADDR_W-1:0]] <= wdata;
assign next_rd_addr = rd_addr + 1'b1;
assign lcl_rd_data = mem[rd_addr[ADDR_W-1:0]];
always_ff @(posedge wclk or negedge wrst_n)
if(!wrst_n) begin
wr_rgray <= (ADDR_W+1)'(0);
rgray_cross <= (ADDR_W+1)'(0);
end
else begin
{wr_rgray, rgray_cross} <= {rgray_cross, rgray};
end
always_ff @(posedge rclk or negedge rrst_n)
if(!rrst_n) begin
rd_wgray <= (ADDR_W+1)'(0);
wgray_cross <= (ADDR_W+1)'(0);
rd_n_items <= (ADDR_W+1)'(0);
end
else begin
{rd_wgray, wgray_cross} <= {wgray_cross, wgray};
rd_n_items <= (rd_wbin - rd_addr) & ((1 << (ADDR_W + 1)) - 1);
end
always_comb begin
rd_wbin[ADDR_W] = rd_wgray[ADDR_W];
for (int i = ADDR_W-1; i >= 0; i--) begin
rd_wbin[i] = rd_wbin[i+1] ^ rd_wgray[i];
end
end
assign wfull = wr_rgray == {~wgray[ADDR_W], wgray[ADDR_W-1:0]};
assign lcl_rd_empty = rd_wgray == rgray;
assign lcl_read = rempty || rd_en;
always_ff @(posedge rclk or negedge rrst_n)
if(!rrst_n) begin
rempty <= 1'b1;
end
else if(lcl_read) begin
rempty <= lcl_rd_empty;
end
always_ff @(posedge rclk)
if(lcl_read)
rdata <= lcl_rd_data;
endmodule: snix_async_fifo