Verilog实现异步fifo


代码

module tb_asyn_fifo #(
		parameter WIDTH = 16,
		parameter DEPTH = 8
)(
		input 					w_clk,
		input 					r_clk,
		input 	[WIDTH-1:0]	w_data,
		input 					wr_en,
		input 					re_en,
		input					rst_n,
		
		output	wire			full,
		output	wire			empty,				
		output	[WIDTH-1:0]   r_data
);

reg	[$clog2(DEPTH):0] w_addr;
reg	[$clog2(DEPTH):0] r_addr;

//write
always@(posedge w_clk or negedge rst_n)
begin
		if(!rst_n)
		begin
			w_addr <= 'd0;
		end
		else if(wr_en && !full)
				begin
					w_addr <= w_addr + 1'd1;
				end
end

//read
always@(posedge r_clk or negedge rst_n)
begin
	if(!rst_n)
	begin
		r_addr <= 'd0;
	end
	else if(re_en && !empty)
			begin
				r_addr <= r_addr + 1'd1;
			end
end

wire	[$clog2(DEPTH):0] w_addr_gray;
wire	[$clog2(DEPTH):0] r_addr_gray;

reg 	[$clog2(DEPTH):0]	w_addr_gray1;
reg 	[$clog2(DEPTH):0]	w_addr_gray2;
reg 	[$clog2(DEPTH):0]	r_addr_gray1;
reg 	[$clog2(DEPTH):0]	r_addr_gray2;

assign w_addr_gray = (w_addr >> 1)^w_addr;
assign r_addr_gray = (r_addr >> 1)^r_addr;

//full
always@(posedge r_clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			w_addr_gray1 <= 'd0;
			w_addr_gray2 <= 'd0;
		end
	else begin
			w_addr_gray1 <= w_addr_gray;
			w_addr_gray2 <= w_addr_gray1;
		 end
end

//empty
always@(posedge w_clk or negedge rst_n)
begin
	if(!rst_n)
	begin
		r_addr_gray1 <= 'd0;
		r_addr_gray2 <= 'd0;
	end
	else begin
			r_addr_gray1 <= r_addr_gray;
			r_addr_gray2 <= r_addr_gray1;
		 end
end

//full  empty
assign empty    =	(w_addr_gray2 == r_addr_gray)?1'b1:1'b0;
assign full     =	((w_addr_gray[$clog2(DEPTH)-2:0] == r_addr_gray2[$clog2(DEPTH)-2:0]) && ((w_addr_gray[$clog2(DEPTH):$clog2(DEPTH)-1] != r_addr_gray2[$clog2(DEPTH):$clog2(DEPTH)-1])))?1'b1:1'b0;														//高两位不同,其余相同

//ram
dual_port_ram #(
	.WIDTH(WIDTH),
	.DEPTH(DEPTH)

)dual_port_ram_init(
	.w_clk(w_clk),
	.r_clk(r_clk),
	.w_data(w_data),
	.r_data(r_data),
	.w_en(wr_en && !full),
	.r_en(re_en && !empty),
	.w_addr(w_addr[$clog2(DEPTH)-1:0]),
	.r_addr(r_addr[$clog2(DEPTH)-1:0])
);
endmodule

ram:

module dual_port_ram #(
		parameter WIDTH = 16,
		parameter DEPTH = 8
)(
		input							w_clk,		
		input							r_clk,
	
		input	[WIDTH-1:0]			    w_data,
		input							w_en,
		input							r_en,
		input	[$clog2(DEPTH)-1:0]	    w_addr,
		input	[$clog2(DEPTH)-1:0]	    r_addr,
		
		output	reg   [WIDTH-1:0] 		r_data
);

reg [WIDTH-1:0] dual_port_ram[0:DEPTH-1];

always @(posedge w_clk )
begin
	if(w_en)
		dual_port_ram[w_addr] <= w_data;
end

always @(posedge r_clk)
begin
	if(r_en)
		r_data <= dual_port_ram[r_addr] ;
end
endmodule

仿真:

`timescale 1ns / 1ps

module asyn_fifo_tb;
    
    reg 						      rst_n;
								
	reg 						      wr_clk;
	reg 						      wr_en;
	reg 	      [15:0]	          data_in;
	wire						      full;
				
	reg 						      rd_clk;
	reg 						      rd_en;
	wire	      [15:0]	          data_out;
	wire	 					      empty;

    asyn_fifo asyn_fifo_inst
	(
		  .rst_n      (rst_n),
								
		  .w_clk     (wr_clk),
		  .wr_en      (wr_en),
		  .w_data    (data_in),
		  .full       (full),
				
		  .r_clk     (rd_clk),
		  .re_en      (rd_en),
		  .r_data   (data_out),
		  .empty      (empty)
);
    
    initial wr_clk = 0;
    always#10 wr_clk = ~wr_clk;
    
    initial rd_clk = 0;
    always#30 rd_clk = ~rd_clk;
    
    always@(posedge wr_clk or negedge rst_n)begin
        if(!rst_n)
            data_in <= 'd0;
        else if(wr_en)
            data_in <= data_in + 1'b1;
        else
            data_in <= data_in;
    end
    
    initial begin
        rst_n = 0;
        wr_en = 0;
        rd_en = 0;
        #200;
        rst_n = 1;
        wr_en = 1;
        #600;
        wr_en = 0;
        rd_en = 1;
        #1000;
        rd_en = 0;
        $stop; 
    end
    
endmodule


posted @ 2025-02-22 20:00  心随鸥鹭齐舒羽  阅读(57)  评论(0)    收藏  举报