题解 | 异步FIFO
异步FIFO
https://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf
`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 //读数据
);
//第一部分:RAM存储器
reg [$clog2(DEPTH) : 0] waddr; //写地址
reg [$clog2(DEPTH) : 0] raddr; //读地址
wire wclken; //写使能
wire rclken; //读使能
assign wclken = winc & (~wfull);
assign rclken = rinc & (~rempty);
dual_port_RAM #(
.WIDTH(WIDTH),
.DEPTH(DEPTH)
) dual_port_RAM_inst(
.wclk (wclk ),
.wenc (wclken),
.waddr(waddr[$clog2(DEPTH) - 1 : 0]),
.wdata(wdata),
.rclk (rclk ),
.renc (rclken),
.raddr(raddr[$clog2(DEPTH) - 1 : 0]),
.rdata(rdata)
);
//第二部分:数据写入控制器
reg [$clog2(DEPTH) : 0] wptr;//写指针
wire [$clog2(DEPTH) : 0] wptr_gray;
reg [$clog2(DEPTH) : 0] wq2_rptr;//打第二拍
assign wptr_gray = waddr ^ (waddr >> 1);
always@(posedge wclk or negedge wrstn) begin
if(!wrstn)
wptr <= 0;
else
wptr <= wptr_gray;
end
always@(posedge wclk or negedge wrstn) begin
if(!wrstn)
waddr <= 0;
else if(wclken)
waddr <= waddr + 1;
else
waddr <= waddr;
end
assign wfull = ({~wq2_rptr[$clog2(DEPTH):$clog2(DEPTH)-1],wq2_rptr[$clog2(DEPTH) - 2 : 0]} == wptr)? 1 : 0;//写时钟域的格雷码与被同步到写时钟域的读指针高两位不同
//比如对8位,写指向0,格雷码为0000,读指向8,格雷码为1100
//第三部分:数据读取控制器
reg [$clog2(DEPTH) : 0] rptr;
wire [$clog2(DEPTH) : 0] rptr_gray;
reg [$clog2(DEPTH) : 0] rq2_wptr;
assign rptr_gray = raddr ^ (raddr >> 1);
always@(posedge rclk or negedge rrstn) begin
if(!rrstn)
rptr <= 0;
else
rptr <= rptr_gray;
end
always@(posedge rclk or negedge rrstn) begin
if(!rrstn)
raddr <= 0;
else if(rclken)
raddr <= raddr + 1;
else
raddr <= raddr;
end
assign rempty = (rq2_wptr == rptr) ? 1 : 0;//读时钟域的格雷码与被同步到读时钟域的读指针相同
//第四、五部分:读、写指针同步器
reg [$clog2(DEPTH) : 0] wq1_rptr;
reg [$clog2(DEPTH) : 0] rq1_wptr;
always@(posedge wclk or negedge wrstn) begin
if(!wrstn) begin
wq1_rptr <= 0;
wq2_rptr <= 0; end
else begin
wq1_rptr <= rptr;
wq2_rptr <= wq1_rptr; end
end
always@(posedge rclk or negedge rrstn) begin
if(!rrstn) begin
rq1_wptr <= 0;
rq2_wptr <= 0;
end
else begin
rq1_wptr <= wptr;
rq2_wptr <= rq1_wptr;
end
end
endmodule

查看12道真题和解析