题解 | #异步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
);
    localparam ADDR = $clog2(DEPTH);

    //ram
    reg [ADDR:0] waddr, raddr;
    dual_port_RAM #(.DEPTH(16), .WIDTH(8))
    u_dual_port_RAM (
        .wclk(wclk),
        .wenc(winc & ~wfull),
        .waddr(waddr[ADDR-1:0]),  //深度对2取对数,得到地址的位宽。
        .wdata(wdata),      	//数据写入
        .rclk(rclk),
        .renc(rinc & ~rempty),
        .raddr(raddr[ADDR-1:0]), //深度对2取对数,得到地址的位宽。
        .rdata(rdata)
    );

    //读写逻辑
    always @(posedge wclk, negedge wrstn) begin
        if (~wrstn) begin
            waddr <= 'd0;
        end
        else if (winc & ~wfull) begin
            waddr <= waddr + 1'd1;
        end
		else begin
            waddr <= waddr;
        end
    end

    always @(posedge rclk, negedge rrstn) begin
        if (~rrstn) begin
            raddr <= 'd0;
        end
        else if (rinc & ~rempty) begin
            raddr <= raddr + 1'd1;
        end
		else begin
            raddr <= raddr;
        end
    end

    //格雷码
    wire [ADDR:0] waddr_g, raddr_g;
	reg [ADDR:0] waddr_g_d, raddr_g_d; //为牛客网题目特别添加,读写指针在本地打一拍
    reg [ADDR:0] waddr_g_d1, waddr_g_d2, raddr_g_d1, raddr_g_d2;
    //assign waddr_g = {waddr[ADDR], {waddr[ADDR-1:0] ^ waddr[ADDR:1]}};
    //assign raddr_g = {raddr[ADDR], {raddr[ADDR-1:0] ^ raddr[ADDR:1]}};
    assign waddr_g = waddr ^ (waddr >>> 1);
    assign raddr_g = raddr ^ (raddr >>> 1);

    //读指针,写指针同步器(打两拍)
    always @(posedge rclk, negedge rrstn) begin
        if (~rrstn) begin
            raddr_g_d  <= 'd0;
        end
        else begin
            raddr_g_d  <= raddr_g;
        end
    end

    always @(posedge wclk, negedge wrstn) begin
        if (~wrstn) begin
            waddr_g_d  <= 'd0;
        end
        else begin
            waddr_g_d  <= waddr_g;
        end
    end

    always @(posedge wclk, negedge wrstn) begin
        if (~wrstn) begin
            raddr_g_d1 <= 'd0;
            raddr_g_d2 <= 'd0;
        end
        else begin
            raddr_g_d1 <= raddr_g_d;
            raddr_g_d2 <= raddr_g_d1;
        end
    end

    always @(posedge rclk, negedge rrstn) begin
        if (~rrstn) begin
            waddr_g_d1 <= 'd0;
            waddr_g_d2 <= 'd0;
        end
        else begin
            waddr_g_d1 <= waddr_g_d;
            waddr_g_d2 <= waddr_g_d1;
        end
    end

    //写满,读空
    assign wfull = (waddr_g_d == {~raddr_g_d2[ADDR:ADDR-1], raddr_g_d2[ADDR-2:0]});
    assign rempty = (waddr_g_d2 == raddr_g_d);

endmodule

全部评论

相关推荐

听说改名字就能收到offer哈:Radis写错了兄弟
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务