题解 | #异步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 ); reg [$clog2(DEPTH):0] waddr_bin;//二进制地址 reg [$clog2(DEPTH):0] raddr_bin; always @(posedge wclk or negedge wrstn) begin if(!wrstn) begin waddr_bin <= 0; end else begin waddr_bin <= (winc && !wfull) ? waddr_bin + 1'b1 : waddr_bin; end end always @(posedge rclk or negedge rrstn) begin if(!rrstn) begin raddr_bin <= 0; end else begin raddr_bin <= (rinc && !rempty) ? raddr_bin + 1'b1 : raddr_bin; end end //二进制地址转换为格雷码地址 reg [$clog2(DEPTH):0] waddr_gray;//格雷码地址 reg [$clog2(DEPTH):0] raddr_gray; /*always @(posedge wclk or negedge wrstn) begin if(!wrstn) begin waddr_gray <= 0; end else begin waddr_gray <= waddr_bin ^ (waddr_bin >> 1); end end always @(posedge rclk or negedge rrstn) begin if(!rrstn) begin raddr_gray <= 0; end else begin raddr_gray <= raddr_bin ^ (raddr_bin >> 1); end end*/ wire [$clog2(DEPTH):0] waddr_gray_wire; wire [$clog2(DEPTH):0] raddr_gray_wire; assign waddr_gray_wire = waddr_bin ^ (waddr_bin >> 1); assign raddr_gray_wire = raddr_bin ^ (raddr_bin >> 1); always @(posedge wclk or negedge wrstn) begin if(!wrstn) begin waddr_gray <= 0; end else begin waddr_gray <= waddr_gray_wire; end end always @(posedge rclk or negedge rrstn) begin if(!rrstn) begin raddr_gray <= 0; end else begin raddr_gray <= raddr_gray_wire; end end //打两拍,跨时钟域处理方法 reg [$clog2(DEPTH):0] waddr_gray1; reg [$clog2(DEPTH):0] raddr_gray1; reg [$clog2(DEPTH):0] waddr_gray2; reg [$clog2(DEPTH):0] raddr_gray2; always @(posedge wclk or negedge wrstn) begin//读指针同步器 使用写时钟的两级触发器采集读指针,输出到数据写入控制器 if(!wrstn) begin raddr_gray1 <= 0; raddr_gray2 <= 0; end else begin raddr_gray1 <= raddr_gray; raddr_gray2 <= raddr_gray1; end end always @(posedge rclk or negedge rrstn) begin//写指针同步器 使用读时钟的两级触发器采集写指针,输出到数据读取控制器 if(!rrstn) begin waddr_gray1 <= 0; waddr_gray2 <= 0; end else begin waddr_gray1 <= waddr_gray; waddr_gray2 <= waddr_gray1; end end assign rempty = (raddr_gray == waddr_gray2);//读写指针相等时,读空 assign wfull = (waddr_gray == {~raddr_gray2[$clog2(DEPTH):$clog2(DEPTH)-1],raddr_gray2[$clog2(DEPTH)-2:0]});//写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满 wire [$clog2(DEPTH)-1:0] waddr; wire [$clog2(DEPTH)-1:0] raddr; assign waddr = waddr_bin[$clog2(DEPTH)-1:0]; assign raddr = raddr_bin[$clog2(DEPTH)-1:0]; dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH)) U_dual_port_RAM ( .wclk(wclk), .wenc(winc & !wfull),//写使能为1且未写满才能写 .waddr(waddr[$clog2(DEPTH)-1:0]), //写地址 .wdata(wdata), //数据写入 .rclk(rclk), .renc(rinc & !rempty),//读使能为1且不是空 .raddr(raddr[$clog2(DEPTH)-1:0]), //读地址 .rdata(rdata) //数据输出 ); endmodule