咱们FPGA里面开发遇到FIFO一般分单时钟和双时钟FIFO,双时钟FIFO是读写各自不同的时钟。由于时钟相位频率关系不可预知,所有也有人叫做异步FIFO。真正的异步FIFO不是这样,是没有时钟的,写入信号通过写信号的上升边缘实现,类似早期的asram。

双时钟FIFO的实现有两大重点:使用格雷码,跨域时钟域要打两个拍子。这其实不就是CDC的基本原则嘛。格雷码能保证多位中每次只有一个BIT发生变化,打两个拍子能防止亚稳态。这样能将跨越时钟的数据出错概率降到最低。这两个重点主要是用在信号传递部分,而具体的数值保存部分也有专门的硬件保证,就是双时钟RAM。读写双时钟的RAM如下实现:

module generic_dpram  #(
parameter aw = 4,
parameter dw= 8
) (input        rclk,input       rd_rst,input       rce,oe,input [aw-1:0]raddr,waddr, output reg [dw-1:0] do,input wclk,wrst,wce,we,rrst,input [dw-1:0]di );localparam DEPTH = 1<<aw-1 ;reg [7:0] mem[0:DEPTH-1] ; integer i;initial begin for (i=0;i<DEPTH;i=i+1) mem[i] =0; end always @ ( posedge wclk ) if (we ) mem[waddr]<=di;
always @ ( posedge rclk ) if (rce) do <= mem[raddr];endmodule 

www.opencore.org上有一个FIFO的项目,内有多个FIFO实现,其中一个使用格雷码的双时钟FIFO,这篇文章咱们就仿真。

下面是这个双时钟FIFO的代码。

上图是此FIFO实例化双时钟RAM,我们没有在项目里面找到具体的实现,所以写了我这篇文字开头的那个generic_dpram 。

其实输入应该没有什么可以看时序的,主要看输出的时序,也主要是看看re和dout的时序对应关系。

写一下测试平台,给两个不一样的时钟,设置FIFO的地址是4,位宽是8位,也就说FIFO有16个单元。写入5个数据,之后再读出5个数据。


`timescale 1ns/1nsmodule tb ;reg rst,clr,wr_clk,rd_clk,we,re;
initial  { rst,clr,wr_clk,rd_clk,we,re }= 0 ;always #4 wr_clk = ~wr_clk ;
always #5 rd_clk = ~rd_clk ;integer i ;initial begin $dumpfile("tb_dc_fifo.vcd");
$dumpvars;#10;
rst=1;clr=1;#10;
rst=0;clr=0;#10;@(posedge wr_clk);
for(i=0;i<5;i=i+1) begin
we=1;@(posedge wr_clk);
end
we=0;  //write 5 data to fifo #200 ;
@(posedge rd_clk);
for(i=0;i<5;i=i+1) begin
re=1;@(posedge rd_clk);
end
re=0;  //read 5 data from fifo #200 ;
$finish ;end
wire [7:0]dout;
reg [7:0] din = 0 ;
always @(posedge wr_clk) if (we)din<=din+1;//generic_fifo_dc   #(.aw(4),.dw(8)) generic_fifo_dc_gray (    generic_fifo_dc_gray #(.aw(4),.dw(8)) generic_fifo_dc_gray (    .rd_clk(rd_clk) , .wr_clk(wr_clk) , .rst(~rst) , .clr(clr) , .din(din ) , .we(we) ,.dout(dout ) , .re(re ) , .full(full) , .empty(empty )// , //.wr_level(wr_level) , //.rd_level(rd_level)  );endmodule 

这里我再给出这个dcfifo的代码:

/Universal FIFO Dual Clock, gray encoded                    Author: Rudolf Usselmann                                   rudi@asics.ws                                      D/L from: http://www.opencores.org/cores/generic_fifos/    /Copyright (C) 2000-2002 Rudolf Usselmann                    www.asics.ws                        rudi@asics.ws                       This source file may be used and distributed without        restriction provided that this copyright statement is not   removed from the file and that any derivative work contains the original copyright notice and the associated disclaimer.THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         POSSIBILITY OF SUCH DAMAGE.                                 ///  CVS Log
//
//  $Id: generic_fifo_dc_gray.v,v 1.2 2004-01-13 09:11:55 rudi Exp $
//
//  $Date: 2004-01-13 09:11:55 $
//  $Revision: 1.2 $
//  $Author: rudi $
//  $Locker:  $
//  $State: Exp $
//
// Change History:
//               $Log: not supported by cvs2svn $
//               Revision 1.1  2003/10/14 09:34:41  rudi
//               Dual clock FIFO Gray Code encoded version.
//
//
//
//
////`include "timescale.v"/*Description
===========I/Os
----
rd_clk  Read Port Clock
wr_clk  Write Port Clock
rst low active, either sync. or async. master reset (see below how to select)
clr synchronous clear (just like reset but always synchronous), high active
re  read enable, synchronous, high active
we  read enable, synchronous, high active
din Data Input
dout    Data Outputfull Indicates the FIFO is full (driven at the rising edge of wr_clk)
empty   Indicates the FIFO is empty (driven at the rising edge of rd_clk)wr_level   indicates the FIFO level:2'b00 0-25%    full2'b01 25-50%   full2'b10 50-75%   full2'b11 %75-100% fullrd_level   indicates the FIFO level:2'b00 0-25%    empty2'b01    25-50%   empty2'b10    50-75%   empty2'b11    %75-100% emptyStatus Timing
-------------
All status outputs are registered. They are asserted immediately
as the full/empty condition occurs, however, there is a 2 cycle
delay before they are de-asserted once the condition is not true
anymore.Parameters
----------
The FIFO takes 2 parameters:
dw  Data bus width
aw  Address bus width (Determines the FIFO size by evaluating 2^aw)Synthesis Results
-----------------
In a Spartan 2e a 8 bit wide, 8 entries deep FIFO, takes 97 LUTs and runs
at about 113 MHz (IO insertion disabled). Misc
----
This design assumes you will do appropriate status checking externally.IMPORTANT ! writing while the FIFO is full or reading while the FIFO is
empty will place the FIFO in an undefined state.*/module generic_fifo_dc_gray(  rd_clk, wr_clk, rst, clr, din, we,dout, re, full, empty, wr_level, rd_level );parameter dw=8;
parameter aw=4;input           rd_clk, wr_clk, rst, clr;
input   [dw-1:0]    din;
input           we;
output  [dw-1:0]    dout;
input           re;
output          full;
output          empty;
output  [1:0]       wr_level;
output  [1:0]       rd_level;//
// Local Wires
//reg   [aw:0]      wp_bin, wp_gray;
reg [aw:0]      rp_bin, rp_gray;
reg [aw:0]      wp_s, rp_s;
reg         full, empty;wire    [aw:0]      wp_bin_next, wp_gray_next;
wire    [aw:0]      rp_bin_next, rp_gray_next;wire  [aw:0]      wp_bin_x, rp_bin_x;
reg [aw-1:0]    d1, d2;reg          rd_rst, wr_rst;
reg         rd_rst_r, wr_rst_r;
reg         rd_clr, wr_clr;
reg         rd_clr_r, wr_clr_r;//
// Reset Logic
//always @(posedge rd_clk or negedge rst)if(!rst)  rd_rst <= 1'b0;elseif(rd_rst_r)    rd_rst <= 1'b1;        // Release Resetalways @(posedge rd_clk or negedge rst)if(!rst)    rd_rst_r <= 1'b0;else      rd_rst_r <= 1'b1;always @(posedge wr_clk or negedge rst)if(!rst)  wr_rst <= 1'b0;elseif(wr_rst_r)    wr_rst <= 1'b1;        // Release Resetalways @(posedge wr_clk or negedge rst)if(!rst)    wr_rst_r <= 1'b0;else      wr_rst_r <= 1'b1;always @(posedge rd_clk or posedge clr)if(clr)       rd_clr <= 1'b1;elseif(!rd_clr_r)   rd_clr <= 1'b0;        // Release Clearalways @(posedge rd_clk or posedge clr)if(clr)     rd_clr_r <= 1'b1;else      rd_clr_r <= 1'b0;always @(posedge wr_clk or posedge clr)if(clr)       wr_clr <= 1'b1;elseif(!wr_clr_r)   wr_clr <= 1'b0;        // Release Clearalways @(posedge wr_clk or posedge clr)if(clr)     wr_clr_r <= 1'b1;else      wr_clr_r <= 1'b0;//
// Memory Block
//generic_dpram  #(.aw(aw),.dw(dw)) u0(.rclk(       rd_clk      ),.rrst(        !rd_rst     ),.rce(     1'b1       ),.oe(      1'b1       ),.raddr(       rp_bin[aw-1:0]  ),.do(      dout        ),.wclk(        wr_clk      ),.wrst(        !wr_rst     ),.wce(     1'b1       ),.we(      we      ),.waddr(       wp_bin[aw-1:0]  ),.di(      din     ));//
// Read/Write Pointers Logic
//always @(posedge wr_clk)if(!wr_rst)  wp_bin <= {aw+1{1'b0}};elseif(wr_clr) wp_bin <= {aw+1{1'b0}};elseif(we)     wp_bin <= wp_bin_next;always @(posedge wr_clk)if(!wr_rst)  wp_gray <= {aw+1{1'b0}};elseif(wr_clr)    wp_gray <= {aw+1{1'b0}};elseif(we)        wp_gray <= wp_gray_next;assign wp_bin_next  = wp_bin + {{aw{1'b0}},1'b1};
assign wp_gray_next = wp_bin_next ^ {1'b0, wp_bin_next[aw:1]};always @(posedge rd_clk)if(!rd_rst)    rp_bin <= {aw+1{1'b0}};elseif(rd_clr) rp_bin <= {aw+1{1'b0}};elseif(re)     rp_bin <= rp_bin_next;always @(posedge rd_clk)if(!rd_rst)  rp_gray <= {aw+1{1'b0}};elseif(rd_clr)    rp_gray <= {aw+1{1'b0}};elseif(re)        rp_gray <= rp_gray_next;assign rp_bin_next  = rp_bin + {{aw{1'b0}},1'b1};
assign rp_gray_next = rp_bin_next ^ {1'b0, rp_bin_next[aw:1]};//
// Synchronization Logic
//// write pointer
always @(posedge rd_clk)   wp_s <= wp_gray;// read pointer
always @(posedge wr_clk)   rp_s <= rp_gray;//
// Registered Full & Empty Flags
//assign wp_bin_x = wp_s ^ {1'b0, wp_bin_x[aw:1]};    // convert gray to binary
assign rp_bin_x = rp_s ^ {1'b0, rp_bin_x[aw:1]};  // convert gray to binaryalways @(posedge rd_clk)empty <= (wp_s == rp_gray) | (re & (wp_s == rp_gray_next));always @(posedge wr_clk)full <= ((wp_bin[aw-1:0] == rp_bin_x[aw-1:0]) & (wp_bin[aw] != rp_bin_x[aw])) |(we & (wp_bin_next[aw-1:0] == rp_bin_x[aw-1:0]) & (wp_bin_next[aw] != rp_bin_x[aw]));//
// Registered Level Indicators
//
reg [1:0]       wr_level;
reg [1:0]       rd_level;
reg [aw-1:0]    wp_bin_xr, rp_bin_xr;
reg         full_rc;
reg         full_wc;always @(posedge wr_clk)   full_wc <= full;
always @(posedge wr_clk)   rp_bin_xr <=  ~rp_bin_x[aw-1:0] + {{aw-1{1'b0}}, 1'b1};
always @(posedge wr_clk)   d1 <= wp_bin[aw-1:0] + rp_bin_xr[aw-1:0];always @(posedge wr_clk) wr_level <= {d1[aw-1] | full | full_wc, d1[aw-2] | full | full_wc};always @(posedge rd_clk)    wp_bin_xr <=  ~wp_bin_x[aw-1:0];
always @(posedge rd_clk)   d2 <= rp_bin[aw-1:0] + wp_bin_xr[aw-1:0];always @(posedge rd_clk) full_rc <= full;
always @(posedge rd_clk)   rd_level <= full_rc ? 2'h0 : {d2[aw-1] | empty, d2[aw-2] | empty};//
// Sanity Check
//// synopsys translate_off
always @(posedge wr_clk)if(we && full)$display("%m WARNING: Writing while fifo is FULL (%t)",$time);always @(posedge rd_clk)if(re && empty)$display("%m WARNING: Reading while fifo is EMPTY (%t)",$time);
// synopsys translate_onendmodulemodule generic_dpram  #(
parameter aw = 4,
parameter dw= 8
) (input        rclk,input       rd_rst,input       rce,oe,input [aw-1:0]raddr,waddr, output reg [dw-1:0] do,input wclk,wrst,wce,we,rrst,input [dw-1:0]di );localparam DEPTH = 1<<aw-1 ;reg [7:0] mem[0:DEPTH-1] ; integer i;initial begin for (i=0;i<DEPTH;i=i+1) mem[i] =0; end always @ ( posedge wclk ) if (we ) mem[waddr]<=di;
always @ ( posedge rclk ) if (rce) do <= mem[raddr];endmodule 

之后在iverilog下仿真,并gtkwave 查看波形:

这里看到写的状况,也可以看到wr_level和rd_level的变化。

wr_level indicates the FIFO level:2'b00 0-25%    full2'b01 25-50%   full2'b10 50-75%   full2'b11 %75-100% fullrd_level   indicates the FIFO level:2'b00 0-25%    empty2'b01    25-50%   empty2'b10    50-75%   empty2'b11    %75-100% empty

empty和rd_level是rd_clk时钟域的信号,所以看上去没有和di对齐。

再看一下读FIFO的时序:

这里看到最关键的一点就是数据出现在FIFO读时钟之后一周期。empty伴随着最用一个输出出现。

这样一来时序就非常明确了,我们可以用在自己的代码里。

{{aAxvOXMOIvVUoXMxvoxiowMwWV8xxWTxoxOIOVIUUOvwVOUiIoUvvTMMVMwovWHWX8vOUOWOVOOTxiToVwToVO8UOMOxxUMiMoIiivmXIVWxix8vHOMoiHIoVU8VvmvIWXTvvOvv8xvMovOWMOOWUoMm8UiUZz}}

一个DC FIFO的仿真实验相关推荐

  1. matlab画一个电动机系统图,基于MATLABGUI的电机学仿真实验系统设计

    38 2009年第17期(总第87期) E-mail:cmee@http://www.doczj.com/doc/ee4e2bdfb7360b4c2f3f6479.html 基于MATLAB GUI的 ...

  2. 基于virtuoso IC 618的LDO仿真实验

    前言: 这里是我的LDO仿真记录帖. 往后各种结构的LDO仿真记录就存放在这个帖子里了. 不定期更新. 基于virtuoso IC 618的LDO仿真实验 目录 序    LDO学习 0.1 仿真参数 ...

  3. verilog 自动售货机状态机实现_Verilog HDL自动售货机系统设计与仿真实验报告

    前言 随着电子技术和计算机技术的飞速发展,电子线路的设计工作也日益显得重要.经过人工设计.制作实验板.调试再修改的多次循环才定型的传统产品设计方法必然被计算机辅助设计所取代,因为这种费时费力又费资源的 ...

  4. 【ZYNQ Ultrascale+ MPSOC FPGA教程】第八章FPGA片内FIFO读写测试实验

    原创声明: 本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处. 适用于板卡型号: AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E ...

  5. matlab最小二乘法参数辨识,基于最小二乘法的MATLAB辨识仿真实验

    <工业控制计算机>2013 年第 26 卷第 7 期基于最小二乘法的 MATLAB 辨识仿真实验 崔秀美 刘文杰 陈 澄 (苏州大学机电工程学院,江苏 苏州 215021) Experim ...

  6. 物理实验计算机仿真单摆实验,大学物理实验仿真实验实验报告.doc

    仿真实验 (单摆测重力加速度和单透镜焦距的测定) 引言 随着计算机应用的普及,在各个应用领域都采用计算机设计和仿真,在大学物理实验课教学中,除了实际操作外还可以进行计算机仿真实验,对有些内容采用仿真实 ...

  7. matlab搭建sdn,软件定义网络SDN简介和简单仿真实验

    阅读: 11,620 SDN是在2009年左右出现的一种新型网络结构,它将数据层面与控制层面分离,使用中央控制器完成网络的操作和管理,并通常采用OpenFlow协议作为其核心通信协议,拥有着集中式控制 ...

  8. 系统仿真基础与计算机实现,计算机综合仿真实验系统的研究与开发

    摘要: 计算机仿真实验系统是实验教学的理想平台.目前,我国高校因扩招而出现实验教学与理论教学比例失衡的问题,且现有自控原理实验设备已不能满足过程控制仿真要求.针对这一现状,本文在对国内外仿真实验室建设 ...

  9. matlab概率论实验 分别掷硬币1,基于Matlab的概率论仿真实验

    -61- 基于Matlab 的概率论仿真实验 可以看到,当z 在区间度是线性减函数,仿真结果与之吻合很好.再考虑一个离散的例子:抛掷两个均匀的骰子,考虑最小点数的分布.在等可能的点. 采用Matlab ...

最新文章

  1. [JAVA EE]Spring Boot 控制层:参数传递方法
  2. php 读取stdclass,json - 在php中读取stdclass的问题?
  3. 做 AI 大咖在顶级单位之间随兴漂移,好开心!
  4. u盘可以安装java吗_java下载安装 (三)Java 开发环境配置
  5. [转]php socket编程通信
  6. python openpyxl读写xlsx_python高阶教程-python操作xlsx文件(openpyxl)
  7. 洛谷 P3384 【模板】树链剖分
  8. 在.NET Core 3.0中发布单个Exe文件(PublishSingleFile)
  9. 提高计算机水平的小技巧,五大电脑小技巧
  10. Git——版本管理工具(一)
  11. html video 笔记
  12. 关于ping是用的TCP还是UDP的争论
  13. libgdx之瓦片地图(TiledMap)
  14. 计算机中级职称考试答题卡,2016年软考网络工程师考试填涂答题卡(纸)须知...
  15. 倍福PLC通过CANOpen通信控制伺服
  16. unity中显示fps
  17. 在Excel中如何实现快速互换两列内容
  18. 企业微信微信社群运营该怎么做?
  19. 内网渗透(五十三)之域控安全和跨域攻击-利用域信任密钥获取目标域控
  20. 生活-啤酒鸡 鸡啤酒 酒啤鸡 啤鸡酒- 做法

热门文章

  1. ARMv8 Linux内核异常处理过程分析
  2. Oracle调用带有out参数的存储过程
  3. 在xsl中插入有大于、小于符号JavaScript,CSS代码的方法
  4. 如何在虚拟机中安装winXP
  5. Linux jq 、vim以及LInux集群安装miniconda并配置虚拟环境(笔记)
  6. RoboMaster 2020机甲大师对抗赛飞手理论测评通过名单
  7. 抽象代数——代数结构
  8. Python学习(三十八)—— Djago之Ajax
  9. UVM:寄存器模型 构建
  10. 重根的二阶迭代法matlab,计算方程重根的一个高阶迭代程序