题解 | 异步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