module Asyn_FIFO(data_out, full, empty, data_in, wen, wclk, wrst,ren, rclk, rrst);
parameter datasize = 8;
parameter addrsize = 4;
input [datasize-1:0]data_in;
input wen,ren,wclk,rclk,wrst,rrst;
output [datasize-1:0]data_out;
output empty,full;
reg empty,full;
wire [datasize-1:0]data_out;
//写指针指向下一个要被写入的单元,复位时,指向第一个单元;读指针总指向当前要被读出的数据,复位时,指向第一个单元
wire [addrsize-1:0]raddr,waddr;
wire [addrsize:0]rbinnext,wbinnext,rptrnext,wptrnext;
wire empty_val,full_val;
reg [addrsize:0]rbin,wbin,rptr,wptr,rptr1,rptr2,wptr1,wptr2;
reg [datasize-1:0]memory[0:(1<<addrsize)-1];
assign data_out=memory[raddr]; //双口RAM,无条件读出,写使能并且写时钟来临才会写入
always @(posedge wclk) begin
if(wen&&!full) memory[waddr]<=data_in;
end
//同步wptr指针
always @(posedge rclk or negedge rrst) begin //读时钟来临,同步写地址,用于空满检测,打两拍
if(!rrst) {rptr2,rptr1}<=0;
else {rptr2,rptr1}<={rptr1,wptr};
end
//同步rptr指针
always @(posedge wclk or negedge wrst) //写时钟来临,同步读地址,用于空满检测,打两拍
begin if(!wrst) {wptr2,wptr1}<=0;
else {wptr2,wptr1}<={wptr1,rptr};
end
//产生raddr信号和empty信号
always @(posedge rclk or negedge rrst) //读时钟来临,更新读地址rbin(用于读时钟域,作为读地址),并将读地址转换为格雷码rptr(传递到写地址域,用于空满检测)
begin if(!rrst) {rbin,rptr}<=0;
else {rbin,rptr}<={rbinnext,rptrnext};
end
assign raddr=rbin[addrsize-1:0];//读数据的地址还是之前的读地址,但是在跨时钟的时候需要用格雷码来跨时钟;
assign rbinnext=rbin+(ren&&~empty);
assign rptrnext=(rbinnext>>1)^rbinnext; //生成raddr 读地址
//FIFO empty
assign empty_val=(rptrnext==rptr2);//读时钟域,比较读指针和同步过来的写指针,判断FIFO是否空
always @(posedge rclk or negedge rrst)
begin if(!rrst) empty<=1;
else empty<=empty_val;
end
//产生waddr信号和full信号
always @(posedge wclk or negedge wrst) //写时钟来临,更新写地址wbin(用于写时钟域,作为写地址),并将读地址转换为格雷码wptr(传递到读地址域,用于空满检测)
begin if(!wrst) {wbin,wptr}<=0;
else {wbin,wptr}<={wbinnext,wptrnext};
end
assign waddr=wbin[addrsize-1:0];
assign wbinnext=wbin+(wen&&~full);
assign wptrnext=(wbinnext>>1)^wbinnext; //生成waddr
//生成 Full
assign full_val=(wptrnext=={~wptr2[addrsize:addrsize-1],wptr2[addrsize-2:0]});
always @(posedge wclk or negedge wrst)
begin if(!rrst) full<=0;
else full<=full_val;
end
endmodule