原创 异步 FIFO based on SRAM

2014-5-29 21:21 2033 11 11 分类: FPGA/CPLD 文集: FPGA

异步FIFO  用于不同时钟域数据的交换。

设计难点: 在不同时钟域里,如何比较读写指针 ?

为了比较不同时钟产生的指针,需要把不同时钟域的信号同步到本时钟域中来,也就是,读指针同步到写时钟,写指针同步到读时钟。这里会带来一个问题: 亚稳态。即写时钟采样读指针时,读指针可能正在变化;读时钟采样写指针时,写指针也可能在变化。

如何减少亚稳态 ? 完全消除是不可能的,读写指针计数器可以采用著名的 格雷码 ,每次变化一位,把亚稳态影响降到最低。

保守的设计:

将(格雷码的)读指针与写时钟同步。因为每当同步读指针的时候,实际的读指针可能会变为不同的值。这意味着读指针可能会是一个失效的值(实际读指针变到了N,而在写时钟读成了N-1)。如果是这样,从写操作的角度考虑会发生少读现象(相比实际情况,认为读指针在N-1),如果条件吻合,判定FIFO为满。实际上,FIFO可能未满(还有1个位置),因为有可能读操作发生了,而从写操作的角度是“看不到”的。然而,我们只要阻止额外的写操作就OK了,如果当FIFO真的满了时我们不去阻止写操作将会出现错误。这样肯定不会溢出。

同样的从读操作的角度看——实际上当FIFO 中还有一些数据时,可能会认为FIFO为空。这种情况读操作被阻止,直到写操作“变得可被读操作一方所看见”,它将不允许进一步的读操作。

上述被称为保守的报告。简而言之,当FIFO未满时,对于写操作一方报告称FIFO已满,当FIFO未空时,报告对读操作一方称FIFO已空。这种现象好比FIFO动态的缩小了一点,这毫无坏处。

 

直接上代码 : 环境 QII

/////////////////////////////////////////////////////
//    Function :   asynchronous fifo
//    Structure:   SRAM
// 读写fifo注意:  满时,只能读,不能写;空时,只能写,不能读
//        Date :   2014.05.29
/////////////////////////////////////////////////////
 
 module asyn_fifo(wr_clk,rd_clk,rst_n,we,re,din,dout,full,empty);

parameter dw=8;  //  数据位宽
parameter aw=5;  //  RAM地址线宽度 fifo深度 2^5=32

input wr_clk;   // 写时钟
input rd_clk;   // 读使能
input rst_n;
input we;  // 写使能
input re;  // 读使能
input  [dw-1:0] din;   // 输入数据
output [dw-1:0] dout;  // 输出数据
output reg full;   // 满标志
output reg empty;  // 空标志

////////////////////////////////////////////////////
// SRAM 例化 , SRAM 使用 QII IP核
wire aclr;
assign aclr=~rst_n;
reg  [aw-1:0] wr_ptr;         // 写指针
reg  [aw-1:0] wr_ptr_gray;
reg  [aw-1:0] rd_ptr;         // 读指针
reg  [aw-1:0] rd_ptr_gray;
wire [aw-1:0] wr_ptr_next;
wire  [aw-1:0] wr_ptr_gray_next;
wire [aw-1:0] rd_ptr_next;
wire  [aw-1:0] rd_ptr_gray_next;
ram U1(
    .data(din),
    .rd_aclr(aclr),
    .rdaddress(rd_ptr),
    .rdclock(rd_clk),
    .rden(re),
    .wraddress(wr_ptr),
    .wrclock(wr_clk),
    .wren(we),
    .q(dout)
    );
   
//////////////////////////////////////////////////// 
//    读写地址(指针变化)   
always@(posedge wr_clk or negedge rst_n)
if(!rst_n)
 wr_ptr<={aw{1'b0}};
else if(we)
 wr_ptr<=wr_ptr_next;     //  二进制
 
always@(posedge wr_clk or negedge rst_n)
if(!rst_n)
 wr_ptr_gray<={aw{1'b0}};
else
 wr_ptr_gray<=wr_ptr_gray_next;   // 格雷码

assign wr_ptr_next=wr_ptr+{{(aw-1){1'b0}},1'b1};     // 指向下一个地址    二进制
assign wr_ptr_gray_next=wr_ptr_next^{1'b0,wr_ptr_next[aw-1:1]};   // 指向下一个地址    自然二进制转格雷码
 
always@(posedge rd_clk or negedge rst_n)
if(!rst_n)
 rd_ptr<={aw{1'b0}};
else if(re)
 rd_ptr<=rd_ptr_next;   //  二进制

always@(posedge rd_clk or negedge rst_n)
if(!rst_n)
 rd_ptr_gray<={aw{1'b0}};
else if(re)
 rd_ptr_gray<=rd_ptr_gray_next;   // 格雷码
 
assign rd_ptr_next=rd_ptr+{{(aw-1){1'b0}},1'b1};    // 指向下一个地址    二进制
assign rd_ptr_gray_next=rd_ptr_next^{1'b0,rd_ptr_next[aw-1:1]};  // 指向下一个地址    自然二进制转格雷码

////////////////////////////////////////////////////////////////
//读指针同步到写时钟下,写指针同步到读时钟下
reg [aw-1:0]rp;
reg [aw-1:0]wp;
always@(posedge wr_clk)
 rp<=rd_ptr_gray;
 
always@(posedge rd_clk)
 wp<=wr_ptr_gray;


////////////////////////////////////////////////////
//  空满标志
always@(posedge wr_clk or negedge rst_n)
if(!rst_n)
 full<=1'b0;
else if((wr_ptr_gray_next==rp)&(we))    //  写赶上读 ,满
 full<=1'b1;
else if(wr_ptr_gray!=rp)
 full<=1'b0;
 
always@(posedge rd_clk or negedge rst_n)
if(!rst_n)
 empty<=1'b1;
else if((rd_ptr_gray_next==wp)&(re))  //  读赶上写, 空
 empty<=1'b1;
else if(rd_ptr_gray!=wp)
 empty<=1'b0;
 
 
endmodule

仿真:

20140529201432775.jpg

 

影响此设计最高时钟速率的因素: 存储器SRAM读写速率和格雷码计数器运行速率。

文章评论0条评论)

登录后参与讨论
我要评论
0
11
关闭 站长推荐上一条 /2 下一条