题解 | #异步FIFO#
异步FIFO
http://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf
异步FIFO的原理在这里不做赘述,有需要移步知乎,说几个提交过程中遇到的问题:
- 指针变换为格雷码之后,此格雷码属于异步时钟域中的组合逻辑,所以第一步必须在本时钟域进行同步(此步骤是错的,会导致FIFO的空满判断错误,原则上写时钟域写指针实时。读时钟域读指针实时),然后再进行常规CDC处理,从而判断空满时也是源时钟域中的信号和目的时钟域中的同步信号进行比较。
- 注意题目给的双口RAM模块写和读操作是没有判断空满标志的,因此给此模块输入的时候一定是将空满判断之后的信号输入。
`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
);
//=======================================parameter
parameter ADDR_WIDTH = $clog2(DEPTH);
//=======================================defination
reg [ADDR_WIDTH : 0] wpnt;
reg [ADDR_WIDTH : 0] rpnt;
wire [ADDR_WIDTH - 1: 0] wpnt_addr;
wire [ADDR_WIDTH - 1: 0] rpnt_addr;
wire [ADDR_WIDTH : 0] wpnt_gray;
wire [ADDR_WIDTH : 0] rpnt_gray;
reg [ADDR_WIDTH : 0] wpnt_gray_r;
reg [ADDR_WIDTH : 0] rpnt_gray_r;
reg [ADDR_WIDTH : 0] wpnt_gray_r_r;
reg [ADDR_WIDTH : 0] rpnt_gray_r_r;
wire wenc;
wire renc;
//=======================================output
//pnt
always@(posedge wclk or negedge wrstn)begin
if(!wrstn) wpnt <= 'd0;
else if(!wfull && winc) wpnt <= wpnt + 1'b1;
end
always@(posedge rclk or negedge rrstn)begin
if(!rrstn) rpnt <= 'd0;
else if(!rempty && rinc) rpnt <= rpnt + 1'b1;
end
//gray & sync
assign wpnt_gray = wpnt ^ (wpnt >> 1);
assign rpnt_gray = rpnt ^ (rpnt >> 1);
always@(posedge rclk or negedge rrstn)begin
if(!rrstn) {wpnt_gray_r_r, wpnt_gray_r} <= 'd0;
else {wpnt_gray_r_r, wpnt_gray_r} <= {wpnt_gray_r, wpnt_gray};
end
always@(posedge wclk or negedge wrstn)begin
if(!wrstn) {rpnt_gray_r_r, rpnt_gray_r} <= 'd0;
else {rpnt_gray_r_r, rpnt_gray_r} <= {rpnt_gray_r, rpnt_gray};
end
//full & empty
assign wfull = wpnt_gray == {~rpnt_gray_r_r[ADDR_WIDTH : ADDR_WIDTH - 1], rpnt_gray_r_r[ADDR_WIDTH - 2 : 0]};
assign rempty = rpnt_gray == wpnt_gray_r_r;
//ram ctrl
assign wpnt_addr = wpnt[ADDR_WIDTH - 1 : 0];
assign rpnt_addr = rpnt[ADDR_WIDTH - 1 : 0];
assign wenc = winc && !wfull;
assign renc = rinc && !rempty;
dual_port_RAM
#(
.DEPTH (DEPTH),
.WIDTH (WIDTH)
)
u_dual_port_RAM
(
.wclk (wclk),
.wenc (wenc),
.waddr (wpnt_addr), //深度对2取对数,得到地址的位宽。
.wdata (wdata) , //数据写入
.rclk (rclk),
.renc (renc),
.raddr(rpnt_addr) , //深度对2取对数,得到地址的位宽。
.rdata(rdata)//数据输出
);
endmodule