113 lines
3.8 KiB
Systemverilog
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
|