异步FIFO代码及仿真结果(Verilog)

👉异步FIFO的分析请看:异步FIFO
注:代码附于文末
注:因为CSDN不支持Verilog高亮,先用截图的代码便于阅读。


1.代码截图

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.仿真结果

仿真结果如下:

  • 空满标志生成正常
  • 读写地址指针计数器正常
  • 数据存取正常
  • 格雷码逻辑正常
  • 指针跨时钟域同步正常

https://img-blog.csdnimg.cn/95ba561304f94b65a0e9fb6b26825d44.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1N1bW1lclhSVA==,size_16,color_FFFFFF,t_70

3.代码

代码如下:


module asy_fifo#(
	parameter  WIDTH=8,//FIFO的位宽
	parameter  DEEPTH=32,//FIFO的深度
	parameter  POINTER_WIDTH=5,//读写地址指针,指针向高位扩展一位
	parameter  ADDR_WIDTH=4//读写地址的位宽
	)
	(empty,full,r_data,w_clk,w_rstn,w_en,w_data,r_clk,r_rstn,r_en);

output     empty,full; //FIFO生成的空满信号
output reg [WIDTH-1:0] r_data;//从FIFO中读出的数据
input      w_clk,w_rstn;//读时钟域时钟、读时钟域复位信号
input      w_en;//读操作使能信号 
input      [WIDTH-1:0] w_data;//向FIFO中写入的数据
input      r_clk,r_rstn;//写时钟域时钟、写时钟域复位信号
input      r_en;//写操作使能信号


//定义:FIFO的buffer
reg        [WIDTH-1:0] fifo_mem [0:DEEPTH-1];

//定义:FIFO的读写指针
reg        [POINTER_WIDTH-1:0] w_pointer,r_pointer;
	
//定义:FIFO内数据对应的存取地址
wire       [ADDR_WIDTH-1:0]w_addr,r_addr;
	
//定义:转换为格雷码的指针
wire       [POINTER_WIDTH-1:0] w_gray_pointer;
wire       [POINTER_WIDTH-1:0] r_gray_pointer;

//定义:指针经过两级触发器同步时的指针
reg        [POINTER_WIDTH-1:0] w_pointer_d1,w_pointer_d2;
reg        [POINTER_WIDTH-1:0] r_pointer_d1,r_pointer_d2;

//===========写时钟域========
//写时钟域内,写数据进FIFO,并将写指针加1
assign  w_addr = w_pointer[POINTER_WIDTH-2:0];
always@(posedge w_clk or negedge w_rstn)begin
  if( w_en & (!full) ) begin
    fifo_mem[w_addr] <= w_data;  
  end
end
 //写指针计数器
always@(posedge w_clk or negedge w_rstn) begin
  if(!w_rstn) begin	
	w_pointer <= 0;
  end
  else if(w_en & (!full)) begin
	w_pointer <= w_pointer + 1;  
  end
  else begin
	w_pointer <= w_pointer; 
  end
end
//==========================

//===========读时钟域========
//读时钟域内,把FIFO内的数据取出,并将读指针加1
assign  r_addr = r_pointer[POINTER_WIDTH-2:0];
always@(posedge r_clk or negedge r_rstn)begin
  if(!r_rstn) begin
  	r_data <= 'h0;
  end
  else if( r_en & (!empty) ) begin
    r_data <= fifo_mem[r_addr] ;
  end
end
  //读指针计数器
always@(posedge r_clk or negedge r_rstn)begin
  if(!r_rstn) begin
  	r_pointer <= 0;
  end
  else if( r_en & (!empty) ) begin
    r_pointer <= r_pointer + 1 ;
  end
  else begin
	r_pointer <= r_pointer;  
  end
end
//==========================

//===========指针同步======
//将格雷码写指针同步到读时钟域
  //1.将写指针转换为格雷码的形式
assign w_gray_pointer = w_pointer^(w_pointer >> 1);
  //2.再将转换后的指针经过两级触发器同步
always@(posedge r_clk or negedge r_rstn) begin
  if(!r_rstn)begin
    {w_pointer_d1,w_pointer_d2} <= 0;
  end
  else begin
    {w_pointer_d2,w_pointer_d1} <= {w_pointer_d1,w_gray_pointer};
  end
end

//将格雷码读指针经过两级触发器同步到写时钟域
  //1.将读指针转换为格雷码的形式
assign r_gray_pointer = r_pointer^(r_pointer >> 1);
  //2.再将转换后的指针经过两级触发器同步
always@(posedge w_clk or negedge w_rstn) begin
  if(!w_rstn) begin
    {r_pointer_d1,r_pointer_d2} <= 0;
  end
  else begin
    {r_pointer_d2,r_pointer_d1} <= {r_pointer_d1,r_gray_pointer};
  end
end
//==========================

//判断空满
assign full = (
			    (r_pointer_d2[POINTER_WIDTH-1] != w_gray_pointer[POINTER_WIDTH-1]) && //最高位不同
       	  	    (r_pointer_d2[POINTER_WIDTH-2] != w_gray_pointer[POINTER_WIDTH-2]) && //次高位不同
                (r_pointer_d2[POINTER_WIDTH-3:0] == w_gray_pointer[POINTER_WIDTH-3:0])//其他低位相同
              );
assign empty = (w_pointer_d2 == r_gray_pointer);

endmodule


//==========TESTBENCH=========
`timescale 1ns/10ps
module TB;

parameter  WIDTH=8;
	
wire       empty,full; 
wire       [WIDTH-1:0] r_data;
reg        w_clk,w_rstn;
reg        w_en;  
reg        [WIDTH-1:0] w_data;
reg        r_clk,r_rstn;
reg        r_en;



asy_fifo dut(.empty(empty),
		    .full(full),
		    .r_data(r_data),
		    .w_clk(w_clk),
		    .w_rstn(w_rstn),
		    .w_en(w_en),
		    .w_data(w_data),
		    .r_clk(r_clk),
		    .r_rstn(r_rstn),
		    .r_en(r_en)
			);

//产生写时钟,100MHz
  initial begin 
    w_clk <= 0;
    forever begin
      #5 w_clk <= !w_clk;
    end
  end
  initial begin 
    #10 w_rstn <= 0;
    repeat(10) @(posedge w_clk);
    #1ns;
    w_rstn <= 1;
  end 
  
//产生读时钟,50MHz  
  initial begin 
    r_clk <= 0;
    forever begin
      #5 r_clk <= !r_clk;
    end
  end
  initial begin 
    #10 r_rstn <= 0;
    repeat(10) @(posedge w_clk);
    r_rstn <= 1;
  end 
  
  
initial begin
#10 w_en =  1'b1;
    w_data = #(0.01) $random;
    repeat(5) begin
      @(posedge w_clk);
      w_data = #(0.01) $random;    
    end
    @(posedge w_clk);
    w_en = 1'b0;
    w_data = #(0.01) $random;
   
#20 r_en = #(0.01) 1'b1; 
    repeat(5) begin
      @(posedge r_clk);
    end
    @(posedge r_clk);
    r_en = 1'b0;    

#20 w_en = 1'b1;  
    repeat(20) begin
      @(posedge w_clk);  
      w_data = #(0.01) $random;
    end
    @(posedge w_clk); 
    w_en =  1'b0;
    w_data = #(0.01) $random;
    r_en = #(0.01) 1'b1; 
    repeat(20) begin
      @(posedge r_clk);
    end
#50  $finish();
end

endmodule 
  • 13
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
异步FIFO是一种常用的数据缓冲器,在异步数据传输中起到了重要的作用。在Verilog仿真中,可以通过编Verilog代码来实现异步FIFO,并进行相应的仿真。 首先,需要定义FIFO的输入输出信号和内部寄存器。输入信号包括数据输入Din和使能信号Write_en,输出信号包括数据输出Dout和读使能信号Read_en。内部寄存器包括存储数据的寄存器FIFO_reg和读指针的寄存器Read_ptr和Write_ptr。 接下来,需要实现FIFO操作和读操作。对于操作,当Write_en信号为高电平时,将Din数据入到寄存器FIFO_reg中,并将指针Write_ptr加1;对于读操作,当Read_en信号为高电平时,将寄存器FIFO_reg中的数据输出到Dout,并将读指针Read_ptr加1。 同时,需要考虑FIFO的相关控制逻辑。例如,FIFO指针应该在操作完成后自增,而读指针应该在读操作完成后自增。当指针和读指针相同时,表示FIFO为空;当指针和读指针相差1时,表示FIFO为满。 最后,使用Verilog仿真工具(如ModelSim)对编异步FIFO进行仿真测试。可以通过编测试文件,对FIFO入和读取进行模拟,验证其功能的正确性和稳定性。在仿真过程中,可以观察FIFO的输入输出数据是否符合预期,并检查读指针的变化是否按照设计预期进行。 总之,通过编Verilog代码实现异步FIFO,并进行Verilog仿真测试,可以有效验证其功能和正确性,为后续的硬件开发和应用提供可靠的基础。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小verifier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值