verilog异步复位jk触发器_Verilog实现FPGA同步与异步FIFO

FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

作用: FIFO用于不同时钟域之间的数据传输, 比如FIFO的一端是AD数据采集, 另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为 1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而 DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的

分类:FIFO的分类根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

若输入输出总线为同一时钟域,FIFO只是作为缓存使用,用同步FIFO即可,此时,FIFO在同一时钟下工作,FIFO的写使能、读使能、满信号、空信号、输入输出数据等各种信号都在同一时钟沿打入或输出。


若输入输出为不同时钟域,FIFO作时钟协同作用,需要采用异步FIFO,此时,FIFO在读与写分别在各自时钟下工作,FIFO的写使能、写满信号、输 入数据等各种输入信号都在同一输入时钟沿打入或输出。读使能、读空信号、输出数据等各种输出信号都在同一输出时钟沿打入或输出。

设计:FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生益处或读空的状态出现,必须保证FIFO在满的情况下,不 能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。

读写指针的工作原理
  读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。

  写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)

FIFO的“空”/“满”检测
  FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。

  当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:

b7e65ecf156cbed4eec7355822ed3c89.png

  当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:

f81882d29618acf0a140c082b58a2487.png

为了区分到底是满状态还是空状态,可以采用以下方法:

在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2n的 FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB 作为折回标志位,而低3位作为地址指针。

如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;

.........................................................................................................................................

一、同步FIFO的Verilog代码

在modlesim中验证过。


  1. /******************************************************

  2. A fifo controller verilog description.

  3. ******************************************************/

  4. module fifo(datain, rd, wr, rst, clk, dataout, full, empty);

  5. input [7:0] datain;

  6. input rd, wr, rst, clk;

  7. output [7:0] dataout;

  8. output full, empty;

  9. wire [7:0] dataout;

  10. reg full_in, empty_in;

  11. reg [7:0] mem [15:0];

  12. reg [3:0] rp, wp;

  13. assign full = full_in;

  14. assign empty = empty_in;

  15. // memory read out

  16. assign dataout = mem[rp];

  17. // memory write in

  18. always@(posedge clk) begin

  19. if(wr && ~full_in) mem[wp]<=datain;

  20. end

  21. // memory write pointer increment

  22. always@(posedge clk or negedge rst) begin

  23. if(!rst) wp<=0;

  24. else begin

  25. if(wr && ~full_in) wp<= wp+1'b1;

  26. end

  27. end

  28. // memory read pointer increment

  29. always@(posedge clk or negedge rst)begin

  30. if(!rst) rp <= 0;

  31. else begin

  32. if(rd && ~empty_in) rp <= rp + 1'b1;

  33. end

  34. end

  35. // Full signal generate

  36. always@(posedge clk or negedge rst) begin

  37. if(!rst) full_in <= 1'b0;

  38. else begin

  39. if( (~rd && wr)&&((wp==rp-1)||(rp==4'h0&&wp==4'hf)))

  40. full_in <= 1'b1;

  41. else if(full_in && rd) full_in <= 1'b0;

  42. end

  43. end

  44. // Empty signal generate

  45. always@(posedge clk or negedge rst) begin

  46. if(!rst) empty_in <= 1'b1;

  47. else begin

  48. if((rd&&~wr)&&(rp==wp-1 || (rp==4'hf&&wp==4'h0)))

  49. empty_in<=1'b1;

  50. else if(empty_in && wr) empty_in<=1'b0;

  51. end

  52. end

  53. endmodule

二、异步FIFO

(1)由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?

  跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较

  解决方法加两级寄存器同步 + 格雷码(目的都是消除亚稳态)

1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第 一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能 力较弱的系统直接崩溃。

06aa6db73fbf798a94a73cc1a01d9208.png

2.将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步 多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将 地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

那么,多位二进制码如何转化为格雷码?

53a5fe245f0e33a31e2021950b387cc3.png

换一种描述方法:

5467a295b7c8c923b8c43caf9f8fbf56.png

verilog代码实现就一句:assign gray_code = (bin_code>>1) ^ bin_code;

(2)在格雷码域如何判断空与满?

这里直接给出结论:

  判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针rd2_wp每一位完全相同;

  判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针wr2_rp高两位不相同,其余各位完全相同;

f6a92d95425e6c9dc6b7029bed05ec2d.png

(3)Verilog实现

这个是基于RAM的异步FIFO代码,个人认为代码结构简单易懂,非常适合于考试中填写。


  1. module fifo

  2. #(

  3.   parameter WSIZE = 8;

  4.   parameter DSIZE = 32;

  5. )

  6. (

  7.   input wr_clk,

  8.   input rst,

  9.   input wr_en,

  10.   input [WSIZE-1 : 0]din,

  11.   input rd_clk,

  12.   input rd_en,

  13.   output [WSIZE-1 : 0]dout,

  14.   output reg rempty,

  15.   output reg wfull

  16. );

  17. //定义变量

  18. reg [WSIZE-1 :0] mem [DSIZE-1 : 0];

  19. reg [WSIZE-1 : 0] waddr,raddr;

  20. reg [WSIZE : 0] wbin,rbin,wbin_next,rbin_next;

  21. reg [WSIZE : 0] wgray_next,rgray_next;

  22. reg [WSIZE : 0] wp,rp;

  23. reg [WSIZE : 0] wr1_rp,wr2_rp,rd1_wp,rd2_wp;

  24. wire rempty_val,wfull_val;

  25. //输出数据

  26. assign dout = mem[raddr];

  27. //输入数据

  28. always@(posedge wr_clk)

  29. if(wr_en && !wfull)

  30.     mem[waddr] <= din;

  31. //1.产生存储实体的读地址raddr; 2.将普通二进制转化为格雷码,并赋给读指针rp

  32. always@(posedge rd_clk or negedge rst_n)

  33. if(!rst_n)

  34.     {rbin,rp} <= 0;

  35. else

  36.     {rbin,rp} <= {rbin_next,rgray_next};

  37. assign raddr = rbin[WSIZE-1 : 0];

  38. assign rbin_next = rbin + (rd_en & ~rempty);

  39. assign rgray_next = rbin_next ^ (rbin_next >> 1);

  40. //1.产生存储实体的写地址waddr; 2.将普通二进制转化为格雷码,并赋给写指针wp

  41. always@(posedge wr_clk or negedge rst_n)

  42. if(!rst_n)

  43.     {wbin,wp} <= 0;

  44. else

  45.     {wbin,wp} <= {wbin_next,wgray_next};

  46. assign waddr = wbin[WSIZE-1 : 0];

  47. assign wbin_next = wbin + (wr_en & ~wfull);

  48. assign wgray_next = wbin_next ^ (wbin_next >> 1);

  49. //将读指针rp同步到写时钟域

  50. always@(posedge wr_clk or negedge rst_n)

  51. if(!rst_n)

  52.     {wr2_rp,wr1_rp} <= 0;

  53. else

  54.     {wr2_rp,wr1_rp} <= {wr1_rp,rp};

  55. //将写指针wp同步到读时钟域

  56. always@(posedge rd_clk or negedge rst_n)

  57. if(!rst_n)

  58.     {rd2_wp,rd1_wp} <= 0;

  59. else

  60.     {rd2_wp,rd1_wp} <= {rd1_wp,wp};

  61. //产生读空信号rempty

  62. assign rempty_val = (rd2_wp == rgray_next);

  63. always@(posedge rd_clk or negedge rst_n)

  64. if(rst_n)

  65.     rempty <= 1'b1;

  66.   else

  67.     rempty <= rempty_val;

  68. //产生写满信号wfull

  69. assign wfull_val = ((~(wr2_rp[WSIZE : WSIZE-1]),wr2_rp[WSIZE-2 : 0]) == wgray_next);

  70. always@(posedge wr_clk or negedge rst_n)

  71.   if(!rst_n)

  72.     wfull <= 1'b0;

  73. else

  74.     wfull <= wfull_val;

  75. endmodule
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值