FPGA数字系统设计(10)——数据通讯fifo

FIFO执行的实现进入的的数据先读出,类似于一个桶状,先从输入端放入的输出一定会先从输出端输出。根据所处的时钟域不同,又可以分为同步FIFO和异步FIFO。
一、同步FIFO设计
RTL电路图

在这里插入图片描述

module FIFO_SAME(data_in, rd, wr, reset, clock, data_out, full, empty);
input [7:0] data_in;
input rd, wr, reset, clock;
output [7:0] data_out;
output full, empty;
wire [7:0] data_out;   //
reg full_in, empty_in;
reg [7:0] mem [15:0];
reg [3:0] rp, wp;   //读写指针
assign full = full_in;
assign empty = empty_in;
assign data_out = mem[rp];
always@(posedge clock)begin  //正常写入数据
 if(wr && ~full_in)
 mem[wp] <= data_in;
end
always@(posedge clock or negedge reset)begin
 if(!reset)
 begin
  wp <= 0;
 end
 else
 begin
  if(wr && ~full_in)
   wp <= wp + 1'b1;
 end
end
always@(posedge clock or negedge reset)begin
 if(!reset)
 begin
  rp <= 0;
 end
 else
 begin
  if(rd && ~empty_in)
  rp <= rp + 1'b1;
 end
end
always@(posedge clock or negedge reset)begin
 if(!reset)
 begin
  full_in <= 1'b0;
 end
 else
 begin
  if((~rd && wr)&&((wp == rp - 1)||(rp == 4'h0 && wp == 4'hf)))
  full_in <= 1'b1;
  else if(full_in && rd)
  full_in <= 1'b0;
 end
end
always@(posedge clock or negedge reset)begin
 if(!reset)
 begin
  empty_in <= 1'b1;
 end
 else
 begin
  if((rd && ~wr) && (rp == wp - 1 || (rp == 4'hf && wp == 4'h0)))
  empty_in <= 1'b1;
  else if (empty_in && wr)
  empty_in <= 1'b0;
 end
end
endmodule

仿真电路
在这里插入图片描述

二、异步FIFO设计
现如今的集成电路设计的主导思想之一就是同步化设计,即对所有事中控制器件(如触发器,RAM等)都是采用同一个时钟来控制。但在实际的应用系统中,实现完全的同步化的设计非常困难,这容易产生时钟偏差。与内联延迟大约成正比的时钟偏差成为时钟周期的重要部分,而同步设计中的跨芯片通信需要一个时钟周期以上的时间。在集成电路的设计中,往往不需要整体采用一个单一的时钟,因而完全避免了时钟的不确定性问题,这样一个系统中往往就含有数个时钟。

但多个时钟域带来的一个问题就是:不可避免地要完成数据在不同时钟间的传递(如高速模块与低速模块之间的数据传递)然而,在多个时钟域系统的设计中不同时钟域之间的数据传输仍然必须重新同步。通常的做法是采用对每位信号增加握手信号来解决这一问题,但这样会增加系统的复杂度且影响传输速度。异步FIFO(First In First Out)是解决这一问题的一种简便、快捷的解决方案。

在异步电路中,由于时钟之间周期和相位完全独立,所以数据的丢失概率不为零,如何设计一个高可靠性、高速的异步FIFO存储器便成为一个难点。同时在两个时钟域的交叠部分:满、空状态的产生,也是设计的重点之一。
在这里插入图片描述的顶层模块的端口和功能见下图在这里插入图片描述
亚稳态的处理
一个触发器进入亚稳态时,既无法预测该单元的输出电平,也无法预测何时输出才能稳定在某个正确的电平上。在这个稳定期间,触发器输出一些中间级电平,或者可能处于振荡状态,并且这种无用的输出电平可以沿信号通道上的各个触发器级联方式传播下去。亚稳态的产生就是因为在在同步电路系统中,如果触发器的建立时间或保持时间不满足,就可能产生亚稳态,此时触发器输出端Q在亚稳态是指触发器无法在某个规定时间段内达到一个可以确认的状态。逻辑误判有可能扩大故障面,造成电路后续灾难性后果。

建立时间是在时钟翻转之前数据输入必须有效的时间。
保持时间是在时钟沿之后输出输出必须仍然有效的时间。
当一个信号被寄存器所存时,如果信号和时钟之间不满足这个要求,Q的值是不确定的,并且在为未知的时刻会保持在高电平或低电平。此时寄存器进入了亚稳态(Metasability)。解决亚稳态的方法是添加同步器,使得在另一个时钟域采样时信号足够稳定。
同步器一般采用的是两级同步器,一级同步器仍然容易产生亚稳态,两级同步器都具有一个等待时钟周期的等待时间。这种同步器可以把一些亚稳态的值同步为确定值,但并不一定是正确值,同时有一些亚稳态也还是无法稳定成确定值,这种情况成为同步出错。由于同步出错的随机性,很难对它们进行跟踪。如果想再进一步降低亚稳态的出现的概率,可以再增加同步器的级数,但是太多的同步器会使系统的性能下降,所以系统中不会用到太多同步器,一般使用两个同步器已经足够了。
在这里插入图片描述

异步FIFO的主要难点就是空满状态的判断
如果写入电路要判断当前FIFO是否为满,就需要把写电路自身维持的写指针和读电路维持的读指针进行比较,这个读指针就需要送入写电路中,此时就发生了穿过时钟域的问题,也就是说,读指针要从读时钟域同步在写时钟域,然后参与判断,此时就需要前面介绍的同步器。
同样,对于空状态来说,这是读出电路所关心的状态,也是由读电路来维持的。如果读电路言判断当前FIFO是否为空,就需要把时钟域中的写指针取到读时钟域来,和读时钟域的读指针进行比较得出是否是空状态,同样跨越了时钟域。
在跨时钟域系统中希望出现错误的概率越低越好,此时格雷码无疑是最好的选择。因为格雷码是属于可靠性编码,是一种误差最小化的编码,它大大减少了由一个状态到下一个状态时电路的混淆。由于这种编码值改变其中的一位,和其他编码同时改变两位的情况更为可靠。
格雷码与二进制码的对应关系如下图所示:
在这里插入图片描述

由前面的介绍可知通过同步器之后稳定的值可能是1也可能是0,可能与输入的值相同也可能与输入的值不同。例如从十进制的7变到8,二进制码是从0111变为1000,把0111送到同步器之后,由于4为都要变化,所以4位都可能出现亚稳态,从而在同步器的输出端就会出现各种可能性,这样即使数据稳定下来,对整个电路的作用也很小。而如果是采用格雷码,是从0100变为1100,只是高位发生了改变,也就只有这一位可能会出现亚稳态的状态。这样经过同步器处理之后,输出端可能得到的值只有两种:0100或1100是正确的数值,如果得到这个输出自然是最好,但即使得到了0100的输出,也只是和原来的值相同,可以认为没有变化,这也不会对电路造成负面的影响。相比二进制代码那种变化后什么值都有可能的情况,格雷码显然是一种更易于接受的编码方式。

格雷码虽然在跨时钟域方面效果比较好,但在本身计数方面是不足的,也就是说还需要把格雷码转换成二进制码来计数,思维的格雷码转二进制码的代码部分如下:

bin[0] = gary[3] ^ gary[2] ^ gary[1] ^ gary[0] ;
bin[1] = gary[3] ^ gary[2] ^ gary[1] ;
bin[2] = gary[3] ^ gary[2] ;
bin[3] = gary[3] ;

这种结构也可以转换成for循环来完成,如写成一个模块的形式,代码如下

module gray2bin(bin, gray);
parameter SIZE = 4;
output [SIZE - 1:0]bin;
input [SIZE - 1:0]gray;
reg [SIZE - 1:0] bin;
integer i;
always@(gray)
for(i = 0, i, SIZE; i = i + 1) ;
bin[i] = ^(gray >> 1);
endmodule 

计数后还要变回格雷码,转换的方法与上述方式类似。
接下来就是重点:空满状态的判断
对于二进制判断空满状态的方法
空状态:当读指针和写指针的值一致时,则认为读指针与写指针经历了相同次数的循环,也就是说FIFO存储器处于空状态
满状态:如果两个指针除了最高位不同,其余位均相同则认为写指针比读指针多循环了一次,标志FIFO的存储器处于满状态。
以上空满状态的判断只能针对二进制指针是可行的,但是针对格雷码指针完全不能使用。
格雷码空满状态的判断
空状态:当读指针和写指针的值是一样时,则认为读指针与写指针经历了相同次数的循环,也就是说FIFO存储器处于空状态,与二进制空状态判断方法一直。
满状态:
1、读指针与写指针的最高位不一样;
2、读指针的前两位异或值与写指针前两位的异或值一致;
3、读指针与写指针的其余位置上的值完全一致。

接下来是异步FIFO的各个子模块的设计:
本次设计的FIFO划分为五个子模块,分别是:读指针控制模块、写指针控制模块、存储器RAM模块、读指针同步到写时钟域模块和写指针同步到读时钟域模块。

首先是读指针同步到写时钟域的子模块代码:

/* time:  20200828
 主要功能:fifo子模块,读指针同步到写时钟域模块代码
 端口定义:wrptr2: 写时域读指针
    rptr:  写指针
    wclock: 写时钟
    wreset: 写复位  
=========================================================*/
module sysc_r2w(wrptr2, rptr, wclock, wreset);
parameter ADDRSIZE = 4;
input [ADDRSIZE:0] rptr;        //同步前的读指针
input wclock, wreset;     
output [ADDRSIZE:0] wrptr2;     //同步后的读时针
reg [ADDRSIZE:0] wrptr2;
reg [ADDRSIZE:0] wrptr1;    //中间的寄存器
always@(posedge wclock, negedge wreset)begin
 if (!wreset)
 begin
  {wrptr2, wrptr1} <= 0;
 end
 else
 begin
  {wrptr2, wrptr1} <= {wrptr1, rptr} ;//两个寄存器之间进行连接
 end
end
endmodule

RTL电路如图所示:
在这里插入图片描述

接下来是写指针同步在读时钟域的子模块

/*time:   2020.8.28。
  主要功能: fifo子模块,写指针同步到读时钟域模块代码
  端口定义: rwptr2: 读时域写指针
    wptr:  写指针
    rclock: 读时钟
    rreset: 读复位
================================================================*/
module syse_w2r(rwptr2, wptr, rclock, rreset);
parameter ADDRSIZE = 4;
input [ADDRSIZE:0] wptr;        //同步前的写指针
input rclock, rreset;     
output [ADDRSIZE:0] rwptr2;     //同步后的写时针
reg [ADDRSIZE:0] rwptr2;
reg [ADDRSIZE:0] rwptr1;    //中间的寄存器
always@(posedge rclock, negedge rreset)begin
 if (!rreset)
 begin
  {rwptr2, rwptr1} <= 0;
 end
 else
 begin
  {rwptr2, rwptr1} <= {rwptr1, wptr} ; //两个寄存器之间进行连接
 end
end
endmodule

RTL电路如图所示:

在这里插入图片描述
接下来是空状态的判断:

/* time:  20200828
   主要功能:fifo子模块,读指针电路以及读空控制逻辑电路
 端口定义: rempty:读空标志
     raddr: 读地址
     rptr:  读指针
     rwptr2:读时域同步写指针 
     rinc:  读使能
     rclock:读时钟
     rreset:读复位
=========================================================================*/
module rptr_empty(rempty, raddr, rptr, rwptr2, rinc, rclock, rreset);
parameter ADDRSIZE = 4;
input rinc, rclock, rreset;
input [ADDRSIZE:0] rwptr2;
output [ADDRSIZE - 1:0] raddr;
output [ADDRSIZE:0] rptr;
output rempty;
reg rempty;
wire [ADDRSIZE - 1:0] raddr;
reg [ADDRSIZE:0] rptr;
reg [ADDRSIZE:0] rbin, rgnext, rbnext;
reg raddrmsb;
integer i;
always @(posedge rclock or negedge rreset)begin
 if(!rreset)
 begin
  rptr <= 0;
  raddrmsb <= 0;
 end
 else
 begin
  rptr <= rgnext;
  raddrmsb <= rgnext[ADDRSIZE]^rgnext[ADDRSIZE-1];
 end
end
always @(rptr or rinc)begin
 for (i = 1 ; i <= ADDRSIZE ; i = i + 1)
 begin
  rbin[i] =^(rptr >> i);
 end
 if(!rempty)
 begin
  rbnext = rbin + rinc;
 end
 else
 begin
  rbnext = rbin;
 end
 rgnext = (rbnext >> 1)^rbnext; 
end
always @(posedge rclock or negedge rreset)begin
 if(!rreset)
 begin
  rempty <= 1'b1;
 end
 else
 begin
  rempty <= (rgnext == rwptr2);
 end
end
assign raddr = {raddrmsb, rptr[ADDRSIZE - 2:0]};
endmodule

RTL电路如图所示
在这里插入图片描述

接下来是满状态判断:

/* time:     20200828
   主要功能: fifo子模块,写指针电路以及写满控制逻辑电路
 端口定义: wfull: 写满标志
     waddr: 写地址
     wptr:  写指针
     wrptr2:写时域同步读指针 
     winc:  写使能
     wclock:写时钟
     wreset:写复位
==============================================================================*/
module wptr_full(wfull, waddr, wptr, wrptr2, winc, wclock, wreset);
parameter ADDRSIZE = 4;
input winc, wclock, wreset;
input [ADDRSIZE:0] wrptr2;
output [ADDRSIZE - 1:0] waddr;
output [ADDRSIZE:0] wptr;
output wfull;
reg wfull;
wire [ADDRSIZE - 1:0] waddr;
reg [ADDRSIZE:0] wptr;
reg [ADDRSIZE:0] wbin, wgnext, wbnext;
reg waddrmsb;
wire w_2ndmsb, wr_2ndmsb;
integer i;
always @(posedge wclock or negedge wreset)begin
 if(!wreset)
 begin
  wptr <= 0;
  waddrmsb <= 0;
 end
 else
 begin
  wptr <= wgnext;
  waddrmsb <= wgnext[ADDRSIZE]^wgnext[ADDRSIZE-1];
 end
end
always @(wptr or winc)begin

 for (i = 1 ; i <= ADDRSIZE ; i = i + 1)
 begin
  wbin[i] =^(wptr>> i);
 end
 if(!wfull)
 begin
  wbnext = wbin + winc;
 end
 else
 begin
  wbnext = wbin;
 end
 wgnext = (wbnext >> 1)^wbnext; 
end
assign w_2ndmsb = wgnext[ADDRSIZE] ^ wgnext[ADDRSIZE - 1];
assign wr_2ndmsb = wrptr2[ADDRSIZE] ^ wrptr2[ADDRSIZE - 1];
always @(posedge wclock or negedge wreset)begin
 if(!wreset)
 begin
  wfull <= 1'b0;
 end
 else
 begin
  wfull <= ((wgnext[ADDRSIZE] !== wrptr2[ADDRSIZE]) && (w_2ndmsb == wr_2ndmsb) &&(wgnext[ADDRSIZE - 2 : 0] == wrptr2[ADDRSIZE - 2:0]));
 end
end
assign waddr = {waddrmsb, wptr[ADDRSIZE - 2:0]};
endmodule

RTL电路如图所示:
在这里插入图片描述

接下来是读取RAM存储器中的数据和向RAM存储器中写入数据:

/*time:  20200828。
  主要功能: fifo子模块存储模块
  端口定义: rdata:读出数据
    wdata:写入数据
    waddr:写地址
    raddr:读地址
    wclock:写时钟
    rclock:读时钟
    wclken:写使能
    rclken:读使能
=============================================================================*/
module fifomem(rdata, wdata, waddr, raddr, wclock, rclock, wclken, rclken);
parameter DATASIZE = 8;
parameter ADDRSIZE = 4;
input wclken, wclock, rclken, rclock;
input [DATASIZE - 1:0] wdata;
input [ADDRSIZE - 1:0] waddr, raddr;
output [DATASIZE - 1:0] rdata;
reg [DATASIZE - 1:0] rdata;
reg [DATASIZE - 1:0] MEM [0:(1<<ADDRSIZE) - 1];
always @(posedge rclock)begin
if (rclken)begin
 rdata = MEM[raddr];
end 
else ;
end
always @(posedge wclock)begin
if (wclken)begin
 MEM[waddr] = wdata ;
end
else ;
end
endmodule

RTL电路如图所示:
在这里插入图片描述
单独对读取数据和写入数据进行仿真,仿真电路图如下所示:
在这里插入图片描述最后是顶层模块:

/* time:  20200828
 主要功能:fifo顶层模块,实现异步fifo的通讯功能,主要通过输入指定内容给fpga,
      再通过读操作将输入值返回。
 端口定义:rdata:读出数据
    wfull:写满标志
    rempty:读空标志
    wdata:写入数据
    winc:写使能
    wclock:写时钟
    wreset:写复位
    rinc:读使能
    rclock:读时钟
    rreset:读复位    
===============================================================*/
module fifo_asyn(rdata, wfull, rempty, wdata, winc, wclock, wreset, rinc, rclock, rreset);
parameter ADDRSIZE = 4;
parameter DATASIZE = 8;
input wclock, winc, wreset;
input rclock, rinc, rreset;
input [DATASIZE - 1:0] wdata;
output [DATASIZE - 1:0] rdata;
output wfull, rempty;
wire [ADDRSIZE - 1:0] waddr,raddr;
wire [ADDRSIZE:0] wptr, wrptr2, rptr, wrptr2;
sysc_r2w sysc_r2w(.wrptr2(wrptr2), .rptr(rptr), .wclock(wclock), .wreset(wreset));
syse_w2r syse_w2r(.rwptr2(rwptr2), .wptr(wptr), .rclock(rclock), .rreset(rreset));
fifomem fifomem(.rdata(rdata), .wdata(wdata), .waddr(waddr), .raddr(raddr), .wclock(wclock), .rclock(rclock), .wclken(winc), .rclken(rinc));
rptr_empty rptr_empty(.rempty(rempty), .raddr(raddr), .rptr(rptr), .rwptr2(rwptr2), .rinc(rinc), .rclock(rclock), .rreset(rreset));
wptr_full wptr_full(.wfull(wfull), .waddr(waddr), .wptr(wptr), .wrptr2(wrptr2), .winc(winc), .wclock(wclock), .wreset(wreset));
endmodule

RTL电路如图所示:
在这里插入图片描述
仿真结果如下图所示:
在这里插入图片描述

例化过程一定看好端口的接口,

声明:该文只适用于学习,其内容包含来自书本的摘抄和总结,欢迎大家补充,共同学习进步。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值