网上异步FIFO有很多,但是异步位宽的很少,要么就调用IP,有的时候修改起来又要改IP比较麻烦,所以自己就简单写了个异步时钟异步位宽的FIFO,望大家品鉴,谢谢!
关于FIFO的含义等,这里就不在赘述了,网上有很多。下面直接上代码:
module async_fifo #(
parameter WDATA_WIDTH = 8, //写位宽
parameter RDATA_WIDTH = 64, //读位宽
parameter WFIFO_DEPTH = 1024 , //写深度
parameter RFIFO_DEPTH = (WFIFO_DEPTH*WDATA_WIDTH)/RDATA_WIDTH
)(
input rst_n ,
input wr_clk ,
input wr_en ,
input [WDATA_WIDTH-1:0] wr_data ,
output [$clog2(WFIFO_DEPTH)-1:0]wr_data_cont,
output wrfull ,
input rd_clk ,
input rd_en ,
output [RDATA_WIDTH-1:0] rd_data ,
output [$clog2(RFIFO_DEPTH)-1:0]rd_data_cont,
output rdempty
);
localparam DATA_WIDTH = (WDATA_WIDTH>RDATA_WIDTH) ? RDATA_WIDTH : WDATA_WIDTH;
localparam FIFO_DEPTH = (WDATA_WIDTH>RDATA_WIDTH) ? RFIFO_DEPTH : WFIFO_DEPTH;
localparam WR_BURST_LEN = (WDATA_WIDTH>RDATA_WIDTH) ? (WDATA_WIDTH/RDATA_WIDTH) : 1'b1 ;
localparam RD_BURST_LEN = (RDATA_WIDTH>WDATA_WIDTH) ? (RDATA_WIDTH/WDATA_WIDTH) : 1'b1 ;
(* ram_style="block" *) reg [DATA_WIDTH-1:0] fifo_memory[FIFO_DEPTH-1:0];
//write clock domain
reg [$clog2(FIFO_DEPTH):0] wr_ptr;
wire[$clog2(FIFO_DEPTH)-1:0] wr_ptr_actual;
reg [$clog2(FIFO_DEPTH):0] wr_gray_ptr1;
reg [$clog2(FIFO_DEPTH):0] wr_bin_ptr2;
reg [$clog2(FIFO_DEPTH):0] wr_bin_ptr1;
reg [$clog2(FIFO_DEPTH):0] wr_rd_bin_ptr_reg;
wire[$clog2(WFIFO_DEPTH)-1:0] wr_data_cont_reg;
//read clock domain
reg [$clog2(FIFO_DEPTH):0] rd_ptr;
wire[$clog2(FIFO_DEPTH)-1:0] rd_ptr_actual;
reg [$clog2(FIFO_DEPTH):0] rd_gray_ptr1;
reg [$clog2(FIFO_DEPTH):0] rd_bin_ptr2;
reg [$clog2(FIFO_DEPTH):0] rd_bin_ptr1;
reg [$clog2(FIFO_DEPTH):0] rd_wr_bin_ptr_reg;
wire[$clog2(WFIFO_DEPTH)-1:0] rd_data_cont_reg;
reg [$clog2(FIFO_DEPTH):0] wr_ptr_g;
reg [$clog2(FIFO_DEPTH):0] rd_ptr_g;
wire[$clog2(FIFO_DEPTH):0] rd_gray_ptr ;
wire[$clog2(FIFO_DEPTH):0] wr_gray_ptr ;
assign rd_ptr_actual = rd_ptr[$clog2(FIFO_DEPTH)-1:0];
assign wr_ptr_actual = wr_ptr[$clog2(FIFO_DEPTH)-1:0];
assign wr_gray_ptr = (wr_ptr_g)^((wr_ptr_g)>>1);
assign rd_gray_ptr = (rd_ptr_g)^((rd_ptr_g)>>1);
//write clock domain
integer m=0;
always @ (posedge wr_clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
wr_ptr_g <= 0;
end
else if (!wrfull && wr_en)begin
wr_ptr <= wr_ptr + WR_BURST_LEN;
wr_ptr_g <= (wr_ptr + WR_BURST_LEN)/WR_BURST_LEN;
end
end
always @ (posedge wr_clk ) begin
if (!wrfull && wr_en )begin
for(m=0;m<WR_BURST_LEN;m=m+1) begin
fifo_memory[wr_ptr_actual + m] <= wr_data[m*DATA_WIDTH +: DATA_WIDTH];
end
end
end
always @ (posedge wr_clk or negedge rst_n) begin
if (!rst_n)begin
rd_gray_ptr1 <= 0;
rd_bin_ptr1 <= 0;
rd_bin_ptr2 <= 0;
wr_rd_bin_ptr_reg <= 0;
end
else begin
rd_gray_ptr1 <= rd_gray_ptr;
rd_bin_ptr1 <= gray2bin(rd_gray_ptr1);
rd_bin_ptr2 <= (rd_bin_ptr1*RD_BURST_LEN)/WR_BURST_LEN;
wr_rd_bin_ptr_reg <= rd_bin_ptr2;
end
end
assign wr_data_cont_reg = (wr_rd_bin_ptr_reg[$clog2(WFIFO_DEPTH)]==wr_ptr_g[$clog2(WFIFO_DEPTH)])?
(wr_ptr_g[$clog2(WFIFO_DEPTH)-1:0]-wr_rd_bin_ptr_reg[$clog2(WFIFO_DEPTH)-1:0]):
WFIFO_DEPTH-(wr_rd_bin_ptr_reg[$clog2(WFIFO_DEPTH)-1:0]-wr_ptr_g[$clog2(WFIFO_DEPTH)-1:0]);
assign wrfull = (wr_rd_bin_ptr_reg[$clog2(WFIFO_DEPTH)]!=wr_ptr_g[$clog2(WFIFO_DEPTH)]) &&
(wr_ptr_g[$clog2(WFIFO_DEPTH)-1:0]==wr_rd_bin_ptr_reg[$clog2(WFIFO_DEPTH)-1:0]);
assign wr_data_cont = wr_data_cont_reg;
//**************************************************************************************************************//
//**************************************************************************************************************//
//**************************************************************************************************************//
//**************************************************************************************************************//
//read clock domain
// integer n=0;
always @ (posedge rd_clk or negedge rst_n) begin
if (!rst_n) begin
rd_ptr <= 0;
// rd_data <= 0;
rd_ptr_g <= 0;
end
else if (!rdempty && rd_en)begin
rd_ptr <= (rd_ptr + RD_BURST_LEN);
rd_ptr_g <= (rd_ptr + RD_BURST_LEN)/RD_BURST_LEN;
// for(n=0;n<RD_BURST_LEN;n=n+1) begin
// rd_data[n*DATA_WIDTH +: DATA_WIDTH] <= fifo_memory[rd_ptr_actual + n];
// end
end
end
genvar n;
generate
for(n=0;n<RD_BURST_LEN;n=n+1) begin
assign rd_data[n*DATA_WIDTH +: DATA_WIDTH] = fifo_memory[rd_ptr_actual + n];
end
endgenerate
always @ (posedge rd_clk or negedge rst_n) begin
if (!rst_n)begin
wr_gray_ptr1 <= 0;
wr_bin_ptr1 <= 0;
wr_bin_ptr2 <= 0;
rd_wr_bin_ptr_reg <= 0;
end
else begin
wr_gray_ptr1 <= wr_gray_ptr;
wr_bin_ptr1 <= gray2bin(wr_gray_ptr1);
wr_bin_ptr2 <= (wr_bin_ptr1*WR_BURST_LEN)/RD_BURST_LEN;
rd_wr_bin_ptr_reg <= wr_bin_ptr2;
end
end
assign rd_data_cont_reg = (rd_wr_bin_ptr_reg[$clog2(RFIFO_DEPTH)]==rd_ptr_g[$clog2(RFIFO_DEPTH)])?
(rd_wr_bin_ptr_reg[$clog2(RFIFO_DEPTH)-1:0]-rd_ptr_g[$clog2(RFIFO_DEPTH)-1:0]):
RFIFO_DEPTH-(rd_ptr_g[$clog2(RFIFO_DEPTH)-1:0]-rd_wr_bin_ptr_reg[$clog2(RFIFO_DEPTH)-1:0]);
assign rdempty = (rd_wr_bin_ptr_reg[$clog2(RFIFO_DEPTH):0]==rd_ptr_g[$clog2(RFIFO_DEPTH):0]);
assign rd_data_cont = rd_data_cont_reg;
function [$clog2(FIFO_DEPTH):0] gray2bin;
input [$clog2(FIFO_DEPTH):0] gray_i;
integer i;
reg [$clog2(FIFO_DEPTH):0] temp;
begin
temp[$clog2(FIFO_DEPTH)] = gray_i[$clog2(FIFO_DEPTH)];
for(i = $clog2(FIFO_DEPTH) ; i >0; i = i - 1) begin
temp[i-1] = temp[i] ^ gray_i[i-1];
end
gray2bin = temp;
end
endfunction
endmodule
读写位宽可通过参数配置,读写位宽需要有倍数关系,如 写:8bit,读16bit;
FIFO深度是以写FIFO深度计算的,最好是2的幂级数。
在跨时钟域上还是采用格雷码格式。
代码中接口上加了读写计数端口,如果去掉计数端口,在比较满空时,可以直接用格雷码比较,不需要转回二进制数。