文档章节

异步fifo控制器-从平行宇宙的追逐说起。

 阿昏豆
发布于 2015/11/08 22:46
字数 1871
阅读 12
收藏 0

ASIC 设计:异步fifo控制器-从平行宇宙的追逐说起。

   在逻辑设计中,通常会用到异步FIFO,可以通过designware可以例化相关设计,但是如何设计一个适合系统的异步fifo控制器,不依赖其他IP,是本问题所讨论的重点。

异步FIFO控制器是经典的异步信号传输的范例,通过FIFO控制器与异步双端口RAM的组合实现数据的从一个时钟域到另一个时钟域的传输。

 fifo_图.jpg

 

上图所示:FIFO由fifo控制器生成,FIFO控制器为逻辑生成,而RAM可使用memory complier生成。对于异步FIFO控制器,最重要控制的两个信号就是满与空,其他信号都产生机理都可以此类推,

     如何产生这两个信号,就是本文重点,最后给出一个FIFO控制器设计实例,供大家参考;

平行宇宙.jpg

 

     下面先讲一个故事,假设存在存在两个平行宇宙(PUSH和POP),平行宇宙上有两个人(W和R)沿着平行宇宙的圈在追逐,有一个规则,R不能超过W,而W不能超过R的对角线。这样两个人就开始了在两个平行宇宙中的追逐游戏。

但是存在一个问题,W和R在两个平行宇宙中,W不知道R现在的位置,R也不知道W现在的位置,因此W位置和R的位置需要通过一种机制传输到对面的宇宙上。这个位置分别就是W_b 和R_b; 因此,上述规则变成了,R不能超过W_b和W不能超过R_b的对角线。如果W跑到了R_b的对角线,则这种事件叫做满(FULL),而如果R跑到W_b的位置,则这种事件叫做空(empty)。W和R分别叫做读指针和写指针。因此,我把异步FIFO的机制就类似上述的平行宇宙的追逐关系。

空满信号.jpg

 

      上图所示:若W跑到R_b的对面,则为full,此时RAM中存储N的深度;若R赶上W_b,则RAM中存储深度为0.则为empty。

那如何在平行宇宙PUSH和POP之间传递W和R当前位置的信号,则需要将先讲W转换成格雷码W_gray=(W^(W>>1)),然后将将W_gray码寄存两拍,然后将寄存两拍后数据W­_gray_d2转换成二进制的(W_b)(具体装换方式见代码实现),通过上述操作,则我们得到的W_b,  此时能够保证 W_b ≤W ,同样以此类推得到R_b;此时能够保证在跨时钟域操作中,因为格雷码每次只变化1bit,因此跨时钟域采样时要么变化1bit,要么不变化。因此保证W在变化时(例如W+1),要么W_b的状态保持不变(为W),要么为(W+1),而不会是其他状态。

综上:我们就可以设计一个FIFO控制器,实现异步时钟域的数据的传输。如果需要数据在还没有POP读出时,就首先在dout端口上,在非空状态下,输出最早写入的值。则需要RAM的读数据提前读出值来,即在非空标示的前一拍,为空时,就提前将数据读出,并且在非空时,要将读地址+1;详细见代码所示;

另外:如果写数据位宽与读数据位宽不匹配,假如为整数关系,则也可以用上述模型来实现,只不过W和R的平行宇宙的大小按比例缩小或放大即可,其相位关系不变。

 

 附录:fifo控制器源码:

//=================================================================

//MODULE  NAME :async_fifo_ctrl

//FUNCTION     : fifo fucntion

//             : 1,transmit data cross asynchronouc clock domain

//             : 2,generate control signal, empty/full and so on;

//             : 3,support first word down through.

// AUTHOR      : lihui_140601 @163.com

// modify info : v1.0 build this module,10/30/15

//=================================================================

module async_fifo_ctrl(

           push_clk,

           push_rst_n,

           pop_clk,

           pop_rst_n,

           push,

           push_din,

           pop,

           pop_dout,

           push_full,

           pop_empty,

           push_almost_full,

           pop_almost_empty,

           push_progfull, 

           pop_progempty,

           push_cnt,

           pop_cnt,

           ram_din,

           ram_we,

           ram_we_addr,

           ram_dout,

           ram_rd,

           ram_rd_addr

       );

parameter    fifo_depth = 8;

parameter    wr_data_w  = 32;

parameter    rd_data_w  = 32;

parameter    wr_depth_add =  (wr_data_w/rd_data_w == 32)? 5:

                             (wr_data_w/rd_data_w == 16)? 4:

                             (wr_data_w/rd_data_w == 8)? 3:

                             (wr_data_w/rd_data_w == 4)? 2:

                             (wr_data_w/rd_data_w == 2)? 1:0;

parameter    rd_depth_add =  (rd_data_w/wr_data_w == 32)? 5:

                             (rd_data_w/wr_data_w == 16)? 4:

                             (rd_data_w/wr_data_w == 8)? 3:

                             (rd_data_w/wr_data_w == 4)? 2:

                             (rd_data_w/wr_data_w == 2)? 1:0;

parameter    wr_depth    = fifo_depth + wr_depth_add;

parameter    rd_depth    = fifo_depth + rd_depth_add;

parameter    fwdt        = 1;  //first word down through

 

input                      push_clk;

input                      push_rst_n;

input                      pop_clk;

input                      pop_rst_n;

input                      push;

input   [(wr_data_w-1):0]  push_din;

input                      pop;

output  [(rd_data_w-1):0]  pop_dout;

output                     push_full;

output                     pop_empty;

output                     push_almost_full;

output                     pop_almost_empty;

output                     push_progfull; 

output                     pop_progempty;

output [(wr_depth-1):0]    push_cnt;

output [(rd_depth-1):0]    pop_cnt;

output [(wr_data_w-1):0]   ram_din;

output                     ram_we;

output [(wr_depth-1):0]    ram_we_addr;

input  [(rd_data_w-1):0]   ram_dout;

output                     ram_rd;

output [(rd_depth-1):0]    ram_rd_addr;

//internal signal define

reg                        push_full;

reg                        pop_empty;

reg                        push_almost_full;

reg                        pop_almost_empty;

 

reg   [wr_depth:0] push_wr_cnt;

wire  [wr_depth:0] push_wr_cnt_tmp;

wire  [wr_depth:0] push_wr_cnt_gray;

reg   [wr_depth:0] push_wr_cnt_gray_d1;

reg   [wr_depth:0] push_wr_cnt_gray_d2;

wire  [wr_depth:0] push_wr_cnt_sync;

reg   [rd_depth:0] pop_rd_cnt;

wire  [rd_depth:0] pop_rd_cnt_tmp;

wire  [rd_depth:0] pop_rd_cnt_gray;

reg   [rd_depth:0] pop_rd_cnt_gray_d1;

reg   [rd_depth:0] pop_rd_cnt_gray_d2;

wire  [rd_depth:0] pop_rd_cnt_sync;

wire  [wr_depth+wr_depth_add:0] push_wr_cnt_tmp_cmp;

wire  [wr_depth+wr_depth_add:0] push_wr_cnt_sync_cmp;

wire  [rd_depth+rd_depth_add:0] pop_rd_cnt_tmp_cmp;

wire  [rd_depth+rd_depth_add:0] pop_rd_cnt_sync_cmp;

wire  [(wr_depth+wr_depth_add):0]    push_cnt_tmp;

wire  [(rd_depth+rd_depth_add):0]    pop_cnt_tmp;

//-----------------------------------------------------------------------------

//push clk  space //write to ram

//-----------------------------------------------------------------------------

assign push_wr_cnt_tmp = (push  && !push_full) ? push_wr_cnt + 1'b1 : push_wr_cnt;

always@(posedge push_clk or negedge push_rst_n)

  if(!push_rst_n)

    push_wr_cnt <= 0;

  else

    push_wr_cnt <= push_wr_cnt_tmp;

   

assign push_wr_cnt_gray = push_wr_cnt ^ (push_wr_cnt >> 1)  ;//

 

 

//-----------------------------------------------------------------------------

//pop clk  space // read from ram

//-----------------------------------------------------------------------------

assign pop_rd_cnt_tmp =  (pop  && !pop_empty)? pop_rd_cnt + 1 : pop_rd_cnt;

 

always@(posedge pop_clk or negedge pop_rst_n)

  if(!push_rst_n)

    pop_rd_cnt <= 0;

  else

    pop_rd_cnt <= pop_rd_cnt_tmp;

 

assign  pop_rd_cnt_gray = pop_rd_cnt ^ (pop_rd_cnt>>1) ;//

 

//-----------------------------------------------------------------------------

//binay to gray code ,

//-----------------------------------------------------------------------------

always@(posedge push_clk or negedge push_rst_n)

  if(!push_rst_n) begin

    pop_rd_cnt_gray_d1 <= 0;

    pop_rd_cnt_gray_d2 <= 0;

  end

  else begin

    pop_rd_cnt_gray_d1 <= pop_rd_cnt_gray;

    pop_rd_cnt_gray_d2 <= pop_rd_cnt_gray_d1;

  end

 

generate

  genvar i;

    assign pop_rd_cnt_sync[fifo_depth] =   pop_rd_cnt_gray_d2[fifo_depth];//

   for(i=fifo_depth-1;i>=0 ;i=i-1) begin: pop_gray_binay

     assign pop_rd_cnt_sync[i]  = pop_rd_cnt_sync[i+1] ^ pop_rd_cnt_gray_d2[i];//

   end

endgenerate

 

always@(posedge pop_clk or negedge pop_rst_n)

  if(!pop_rst_n) begin

    push_wr_cnt_gray_d1 <= 0;

    push_wr_cnt_gray_d2 <= 0;

  end

  else begin

    push_wr_cnt_gray_d1 <= push_wr_cnt_gray;

    push_wr_cnt_gray_d2 <= push_wr_cnt_gray_d1;

  end

 

generate

  genvar j;

   assign  push_wr_cnt_sync[fifo_depth]  =  push_wr_cnt_gray_d2[fifo_depth];//

   for(j=fifo_depth-1;j>=0 ;j=j-1) begin: push_gray2binay

     assign  push_wr_cnt_sync[j]  =  push_wr_cnt_sync[j+1] ^ push_wr_cnt_gray_d2[j];//

   end

endgenerate

 

//genrate fifo empty

always@(posedge pop_clk or negedge pop_rst_n)

  if(!pop_rst_n)

    pop_empty <= 1'b0;

  else if({pop_rd_cnt_tmp,{rd_depth_add{1'b0}}}  == {push_wr_cnt_sync,{wr_depth_add{1'b0}}})//pop read count and push write count  

    pop_empty <= 1'b1;

  else

    pop_empty <= 1'b0;

 

//generate push fifo full;

always@(posedge push_clk or negedge push_rst_n)

  if(!push_rst_n)

    push_full <= 1'b0;

  else if({push_wr_cnt_tmp,{wr_depth_add{1'b0}} } ==  {!pop_rd_cnt_sync[fifo_depth],  pop_rd_cnt_sync[(fifo_depth-1):0],{rd_depth_add{1'b0}}})

    push_full <= 1'b1;

  else

    push_full <= 1'b0;

 

//genrate fifo  almost empty

always@(posedge pop_clk or negedge pop_rst_n)

  if(!pop_rst_n)

    pop_almost_empty <= 1'b0;

  else if({(pop_rd_cnt_tmp + 1),{rd_depth_add{1'b0}}} == {push_wr_cnt_sync,{wr_depth_add{1'b0}}})

    pop_almost_empty <= 1'b1;

  else

    pop_almost_empty <= 1'b0;

 

//generate push almost full;

always@(posedge push_clk or negedge push_rst_n)

  if(!push_rst_n)

    push_almost_full <= 1'b0;

  else if({(push_wr_cnt_tmp + 1),{wr_depth_add{1'b0}}}  ==  {!pop_rd_cnt_sync[fifo_depth],  pop_rd_cnt_sync[(fifo_depth-1):0],{rd_depth_add{1'b0}}})

    push_almost_full <= 1'b1;

  else

    push_almost_full <= 1'b0;

 

 

//generate  push count for push clk and pop count for pop clk, used for generating progfull and other signal

assign  pop_rd_cnt_tmp_cmp    =  {pop_rd_cnt_tmp,{rd_depth_add{1'b0}}};//

assign  push_wr_cnt_sync_cmp  =  {push_wr_cnt_sync,{wr_depth_add{1'b0}}};//

assign  push_wr_cnt_tmp_cmp   =  {push_wr_cnt_tmp,{wr_depth_add{1'b0}}};//

assign  pop_rd_cnt_sync_cmp   =  {pop_rd_cnt_sync,{rd_depth_add{1'b0}}};//

assign  push_cnt_tmp  = (push_wr_cnt_tmp_cmp  > pop_rd_cnt_sync_cmp) ?

                         push_wr_cnt_tmp_cmp - pop_rd_cnt_sync_cmp :

                         {1'b1,push_wr_cnt_tmp_cmp} - pop_rd_cnt_sync_cmp;

assign  pop_cnt_tmp   = (push_wr_cnt_sync_cmp > pop_rd_cnt_tmp_cmp)  ? 

                        push_wr_cnt_sync_cmp - pop_rd_cnt_tmp_cmp :

                        {1'b1,push_wr_cnt_sync_cmp} - pop_rd_cnt_tmp_cmp ;

assign  push_cnt =  push_cnt_tmp[(wr_depth+wr_depth_add-1):wr_depth_add];   

assign  pop_cnt  =  pop_cnt_tmp[(rd_depth+rd_depth_add-1):rd_depth_add];   

 

 

//-----------------------------------------------------------------------------

//ram interface

//-----------------------------------------------------------------------------

assign ram_we_addr = push_wr_cnt[(wr_depth-1):0] ;//

assign ram_we      = push & !push_full ;//

assign ram_din     = push_din;

assign pop_dout    = ram_dout;

 

generate if(fwdt == 1) begin

assign ram_rd_addr = pop_rd_cnt[(rd_depth-1):0] + !pop_empty ;//

assign ram_rd      = pop & !pop_empty  | pop_empty;//

end

else begin

assign ram_rd_addr = pop_rd_cnt[(rd_depth-1):0] ;//

assign ram_rd      = pop & !pop_empty ;//

end

endgenerate

endmodule

 //以下为双端口RAM的仿真模型,将控制器与模型连接在一起,即可仿真。

module  dual_ram(

         we_clk,

         rd_clk,

         we,

         rd,

         we_data,

         rd_data,

         we_addr ,

         rd_addr

         );

 

parameter    ram_depth = 8;

parameter    wr_data_w  = 32;

parameter    rd_data_w  = 32;

parameter    wr_depth_add =  (wr_data_w/rd_data_w == 32)? 5:

                             (wr_data_w/rd_data_w == 16)? 4:

                             (wr_data_w/rd_data_w == 8)? 3:

                             (wr_data_w/rd_data_w == 4)? 2:

                             (wr_data_w/rd_data_w == 2)? 1:0;

parameter    rd_depth_add =  (rd_data_w/wr_data_w == 32)? 5:

                             (rd_data_w/wr_data_w == 16)? 4:

                             (rd_data_w/wr_data_w == 8)? 3:

                             (rd_data_w/wr_data_w == 4)? 2:

                             (rd_data_w/wr_data_w == 2)? 1:0;

parameter    wr_depth    = ram_depth + wr_depth_add;

parameter    rd_depth    = ram_depth + rd_depth_add;

 

 

input                     we_clk;

input                     rd_clk;

input                     we;

input                     rd;

input [(wr_data_w-1):0]   we_data;

input [(wr_depth-1):0]    we_addr ;//

input [(rd_depth-1):0]    rd_addr ;//

 

output [(rd_data_w-1):0]   rd_data;

 

reg    [(rd_data_w-1):0]   rd_data;

reg [(wr_data_w-1):0]  ram_reg[(1 << ram_depth) :0] ;

 

always@(posedge we_clk)

  if(we)

       ram_reg[we_addr] <= we_data;

always@(posedge rd_clk)

  if(rd)

       rd_data <= ram_reg[rd_addr];

endmodule

© 著作权归作者所有

粉丝 0
博文 1
码字总数 1871
作品 0
东城
私信 提问
在平行宇宙中,我们都是薛定谔的猫

有人不知道薛定谔的猫吗? 恐怕没有。这只猫甚至比薛定谔本谔还要出名。 当你随机地对一个人说起“薛定谔”的时候,他(她)首先想到的恐怕是一只喵星人,而不是那个奔儿头锃亮的奥地利大叔。...

图灵教育
01/14
0
0
我们的宇宙是数学计算出来的?——《穿越平行宇宙》读书笔记

万维钢《精英日课》076讲开始讲《穿越平行宇宙-Our Mathematical Universe》这本书,脑洞开得好大呀,好不容易接受量子力学的随机性了,忽然发现又不随机了! 这本书从宏观尺度宇宙讲起,我们...

梅拉
2018/01/24
0
0
day185-2018-12-22-英语流利阅读-待学习

《蜘蛛侠:平行宇宙》上映:这是你从没看过的全新版本 雪梨 2018-12-22 1.今日导读 《蜘蛛侠:平行宇宙》于 12 月 21 日在大陆上映,来自平行宇宙的六位蜘蛛侠首次在电影中同框。影片在美国上...

飞鱼说编程
2018/12/22
51
0
量子平行宇宙理论:你的身边存在着无数的“异世界”!

  有一种类型的平行宇宙,它使爱因斯坦头痛不已,如今仍不断折磨着物理学家们,那就是由普通量子力学预测的量子宇宙。这一问题最具代表的实例就是著名的“薛定谔猫”问题,简单来说薛定谔猫...

历史坎坷路
2017/12/26
0
0
Git入门之形象化理解checkout

实际上其实是个平行宇宙时光机,可以带你穿梭到任意一个平行宇宙中,还可以带你穿梭回过去的任意一个时间点。在过去的那个点上,你可以各种观察、修改、删除等,而不对原本时间点产生任何影响...

solomonxie
2018/06/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

手写RPC框架指北另送贴心注释代码一套

Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的。所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架。 完整代码...

全菜工程师小辉
10分钟前
2
0
【Java】开发收货

简介 谨以此篇,记载开发过程中的一些tips。 编译器 【Shift + F6】可实现变量的联动修改。

Areya
27分钟前
5
0
DOM官方定义

DOM Document Object Model 文档对象模型 DOM的官方定义:W3C的DOM,可以使程序或者脚本(JS或AS\JScript),动态的访问或者操作文档的内容、结构、样式。 DOM只是一个标准,操作网页的标准。...

前端老手
32分钟前
6
0
IT兄弟连 HTML5教程 HTML5的学习线路图 第一阶段学习网页制作

学习HTML5技术可并不是简单学会几个新增的标签而已,HTML5现在可以说是前端所有技术的代名词。需要学习的语言和工具不仅多,对于刚接触他们的新人会感觉很乱。另外,前端开发也会细分很多个开...

老码农的一亩三分地
34分钟前
6
0
可见性有序性,Happens-before来搞定

写在前面 上一篇文章并发 Bug 之源有三,请睁大眼睛看清它们 谈到了可见性/原子性/有序性三个问题,这些问题通常违背我们的直觉和思考模式,也就导致了很多并发 Bug 为了解决 CPU,内存,IO ...

tan日拱一兵
49分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部