题解 | 异步FIFO
异步FIFO
https://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf
`timescale 1ns/1ns
/***************************************RAM*****************************************/
//异步FIFO:读写不同时钟
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
reg [$clog2(DEPTH):0] waddr;//比双口RAM多一位,用来判断空满
reg [$clog2(DEPTH):0] raddr;
//---------读写指针----------------//
//waddr:写使能有效&没写满
always@(posedge wclk or negedge wrstn)
if(!wrstn)
waddr <= 0;
else if(winc && ~wfull)
waddr <= waddr + 1;
else
waddr <= waddr;
//raddr:读使能有效&没读空
always@(posedge rclk or negedge rrstn)
if(!rrstn)
raddr <= 0;
else if(rinc && ~rempty)
raddr <= raddr + 1;
else
raddr <= raddr;
//-----------空满信号判断:跨时钟域传输+格雷码转换------------//
//rempty(读空):写地址追上读地址(raddr=waddr)
//两者处于不同时钟域,需要先进行时钟域同步:打两拍(二进制有多为变换,选转换为格雷码)
//二进制转格雷码:右移之后再与自己异或
//多Bit跨时钟域传输(二进制多位一起跳变)转化为单bit传输(格雷码每次只变一位)
wire [$clog2(DEPTH):0] raddr_gray,waddr_gray;
assign waddr_gray = (waddr >> 1 ) ^ waddr;
assign raddr_gray = (raddr >> 1 ) ^ raddr;
//提交一直不成功,将格雷码在原时钟域跑一拍
reg [$clog2(DEPTH):0] raddr_gray1,waddr_gray1;
always@(posedge wclk or negedge wrstn)
if(!wrstn)
waddr_gray1 <= 0;
else
waddr_gray1 <= waddr_gray;
always@(posedge rclk or negedge rrstn)
if(!rrstn)
raddr_gray1 <= 0;
else
raddr_gray1 <= raddr_gray;
//跨时钟域同步:打两拍
reg [$clog2(DEPTH):0] raddr_1,raddr_2;
always@(posedge wclk or negedge wrstn)
if(!wrstn) begin
raddr_1 <= 0;
raddr_2 <= 0;
end
else begin
raddr_1 <= raddr_gray1;
raddr_2 <= raddr_1;
end
reg [$clog2(DEPTH):0] waddr_1,waddr_2;
always@(posedge rclk or negedge rrstn)
if(!rrstn) begin
waddr_1 <= 0;
waddr_2 <= 0;
end
else begin
waddr_1 <= waddr_gray1;
waddr_2 <= waddr_1;
end
//rempty(读空):写地址追上读地址(raddr=waddr)
assign rempty = (raddr_gray1==waddr_2);
//wfull(写满):写地址比读地址多一个FIFO的深度
//两者最高位和次高位相反,其他位相同
assign wfull = (waddr_gray1=={~raddr_2[$clog2(DEPTH):$clog2(DEPTH)-1],raddr_2[$clog2(DEPTH)-2 : 0]});
//异步FIFO是用格雷码比较,最高位和次高位都相反
//同步FIFO不需要跨时钟域,直接用二进制比较,只需要最高位相反就可
dual_port_RAM #(DEPTH,
WIDTH)
RAM(
.wclk(wclk),
.wenc(winc & ~wfull),
.waddr(waddr[$clog2(DEPTH)-1:0]),
.wdata(wdata),
.rclk(rclk),
.renc(rinc & ~rempty),
.raddr(raddr[$clog2(DEPTH)-1:0]),
.rdata(rdata)
);
endmodule
