1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-02-07 06:44:09 +08:00
oh/common/hdl/oh_fifo_generic.v
2020-01-28 18:12:57 -05:00

158 lines
5.0 KiB
Verilog

//#############################################################################
//# Function: Generic Async FIFO #
//# Based on article by Clifford Cummings, #
// "Simulation and Synthesis Techniques for Asynchronous FIFO Design" #
//# (SNUG2002) #
//#############################################################################
//# Author: Andreas Olofsson #
//# License: MIT (see LICENSE file in OH! repository) #
//#############################################################################
module oh_fifo_generic #(parameter DW = 104, // FIFO width
parameter DEPTH = 32, // FIFO depth (entries)
parameter PROG_FULL = (DEPTH/2),// full threshold
parameter AW = $clog2(DEPTH) // read count width
)
(
input nreset, // asynch active low reset for wr_clk
input wr_clk, // write clock
input wr_en, // write enable
input [DW-1:0] din, // write data
input rd_clk, // read clock
input rd_en, // read enable
output [DW-1:0] dout, // read data
output empty, // fifo is empty
output full, // fifo is full
output prog_full, // fifo is "half empty"
output [AW-1:0] rd_count, // NOT IMPLEMENTED
output [AW-1:0] wr_count // NOT IMPLEMENTED
);
//regs
reg [AW:0] wr_addr; // extra bit for wraparound comparison
reg [AW:0] wr_addr_ahead; // extra bit for wraparound comparison
reg [AW:0] rd_addr;
wire [AW:0] rd_addr_gray;
wire [AW:0] wr_addr_gray;
wire [AW:0] rd_addr_gray_sync;
wire [AW:0] wr_addr_gray_sync;
wire [AW:0] rd_addr_sync;
wire [AW:0] wr_addr_sync;
wire wr_nreset;
wire rd_nreset;
//###########################
//# Full/empty indicators
//###########################
// uses one extra bit for compare to track wraparound pointers
// careful clock synchronization done using gray codes
// could get rid of gray2bin for rd_addr_sync...
// fifo indicators
assign empty = (rd_addr_gray[AW:0] == wr_addr_gray_sync[AW:0]);
// fifo full
assign full = (wr_addr[AW-1:0] == rd_addr_sync[AW-1:0]) &
(wr_addr[AW] != rd_addr_sync[AW]);
// programmable full
assign prog_full = (wr_addr_ahead[AW-1:0] == rd_addr_sync[AW-1:0]) &
(wr_addr_ahead[AW] != rd_addr_sync[AW]);
//###########################
//# Reset synchronizers
//###########################
oh_rsync wr_rsync (.nrst_out (wr_nreset),
.clk (wr_clk),
.nrst_in (nreset));
oh_rsync rd_rsync (.nrst_out (rd_nreset),
.clk (rd_clk),
.nrst_in (nreset));
//###########################
//#write side address counter
//###########################
always @ ( posedge wr_clk or negedge wr_nreset)
if(!wr_nreset)
wr_addr[AW:0] <= 'b0;
else if(wr_en)
wr_addr[AW:0] <= wr_addr[AW:0] + 'd1;
//address lookahead for prog_full indicator
always @ (posedge wr_clk or negedge wr_nreset)
if(!wr_nreset)
wr_addr_ahead[AW:0] <= 'b0;
else if(~prog_full)
wr_addr_ahead[AW:0] <= wr_addr[AW:0] + PROG_FULL;
//###########################
//# Synchronize to read clk
//###########################
// convert to gray code (only one bit can toggle)
oh_bin2gray #(.DW(AW+1))
wr_b2g (.out (wr_addr_gray[AW:0]),
.in (wr_addr[AW:0]));
// synchronize to read clock
oh_dsync wr_sync[AW:0] (.dout (wr_addr_gray_sync[AW:0]),
.clk (rd_clk),
.nreset(rd_nreset),
.din (wr_addr_gray[AW:0]));
//###########################
//#read side address counter
//###########################
always @ ( posedge rd_clk or negedge rd_nreset)
if(!rd_nreset)
rd_addr[AW:0] <= 'd0;
else if(rd_en)
rd_addr[AW:0] <= rd_addr[AW:0] + 'd1;
//###########################
//# Synchronize to write clk
//###########################
//covert to gray (can't have multiple bits toggling)
oh_bin2gray #(.DW(AW+1))
rd_b2g (.out (rd_addr_gray[AW:0]),
.in (rd_addr[AW:0]));
//synchronize to wr clock
oh_dsync rd_sync[AW:0] (.dout (rd_addr_gray_sync[AW:0]),
.clk (wr_clk),
.nreset (wr_nreset),
.din (rd_addr_gray[AW:0]));
//convert back to binary (for ease of use, rd_count)
oh_gray2bin #(.DW(AW+1))
rd_g2b (.out (rd_addr_sync[AW:0]),
.in (rd_addr_gray_sync[AW:0]));
//###########################
//#dual ported memory
//###########################
oh_memory_dp #(.DW(DW),
.DEPTH(DEPTH))
fifo_mem(// Outputs
.rd_dout (dout[DW-1:0]),
// Inputs
.wr_clk (wr_clk),
.wr_en (wr_en),
.wr_wem ({(DW){1'b1}}),
.wr_addr (wr_addr[AW-1:0]),
.wr_din (din[DW-1:0]),
.rd_clk (rd_clk),
.rd_en (rd_en),
.rd_addr (rd_addr[AW-1:0]));
endmodule // oh_fifo_generic