异步FIFO框图
这里注意空满标志信号需要将读写指针先转换为格雷码在同步到各自时钟域进行比较。
大致步骤如下:
1.读时钟下,读指针按照规则在读时钟下加一计数,写时钟一样。
2.将读写指针转换为格雷码,且在当前时钟域寄存打一拍
3.将转换好的格雷码进行打两拍跨时钟处理。即读指针格雷码在写时钟下打两拍,写指针格雷码在读时钟下打两拍。
4,比较产生空满标志。读空即将 同步过来的写指针格雷码与读指针格雷码判断是否相等。
写满即将 同步过来的读指针格雷码与写指针格雷码判断,注意这里是格雷码比较,我们知道二进制码判断满的条件为最高位不等其余位相等,但格雷码判断满的条件为,高两位取反相等,其余位相等
这是因为二进制码转换为格雷码为移位异或得到。
`timescale 1ns/1ns
/***************************************RAM*****************************************/
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] rpoint,wpoint;
//写指针
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)begin
wpoint <= 0;
end
else if(winc&&(!wfull))begin
wpoint <= wpoint + 1;
end
end
//读指针
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)begin
rpoint <= 0;
end
else if(rinc&&(!rempty))begin
rpoint <= rpoint + 1;
end
end
wire [$clog2(DEPTH):0]rpoint_g,wpoint_g;
assign rpoint_g = (rpoint>>1)^rpoint;//转换为格雷码
assign wpoint_g = (wpoint>>1)^wpoint;
reg [$clog2(DEPTH):0] rpoint_g_r,wpoint_g_r;//当前时钟寄存一拍
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)begin
wpoint_g_r <= 0;
end
else begin
wpoint_g_r <= wpoint_g;
end
end
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)begin
rpoint_g_r <= 0;
end
else begin
rpoint_g_r <= rpoint_g;
end
end
reg [$clog2(DEPTH):0] rpoint_g_r_r_r,wpoint_g_r_r_r;//异步时钟打两拍
reg [$clog2(DEPTH):0] rpoint_g_r_r,wpoint_g_r_r;
//跨时钟打两拍
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)begin
rpoint_g_r_r<=0;
rpoint_g_r_r_r<=0;
end
else begin
rpoint_g_r_r <= rpoint_g_r;
rpoint_g_r_r_r <= rpoint_g_r_r;
end
end
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)begin
wpoint_g_r_r<=0;
wpoint_g_r_r_r<=0;
end
else begin
wpoint_g_r_r <= wpoint_g_r;
wpoint_g_r_r_r <= wpoint_g_r_r;
end
end
assign rempty = (wpoint_g_r_r_r == rpoint_g_r);
//assign wfull = (~wpoint_g_r_r[$clog2(DEPTH):$clog2(DEPTH)-1]==rpoint_g[$clog2(DEPTH):$clog2(DEPTH)-1])&&(wpoint_g_r_r[$clog2(DEPTH)-2:0]==rpoint_g[$clog2(DEPTH)-2:0]);
assign wfull = (wpoint_g_r == {~rpoint_g_r_r_r[$clog2(DEPTH):$clog2(DEPTH)-1],rpoint_g_r_r_r[$clog2(DEPTH)-2:0]});
dual_port_RAM #(.DEPTH(DEPTH),
.WIDTH(WIDTH))
inst (
.wclk(wclk),
.wenc(winc&(~wfull)),
.waddr(wpoint[$clog2(DEPTH)-1:0]), //深度对2取对数,得到地址的位宽。
.wdata(wdata), //数据写入
.rclk(rclk),
.renc(rinc&(~rempty)),
.raddr(rpoint[$clog2(DEPTH)-1:0]), //深度对2取对数,得到地址的位宽。
.rdata(rdata) //数据输出
);
endmodule