前言

异步FIFO是面试中常提的问题

正文

一、什么是异步FIFO

1.1 什么是FIFO

FIFO可以被看做成一个水池:

  1. 只有写通道打开,表示往水池加水
  2. 只有读通道打开,表示放水
  3. 写速度>读速度,可能会写满
  4. 写速度<读速度,可能会读空

那如何理解先入先出呢?
同样用水池作比喻,我们只考虑理想情况下,先放到水池里的水在池子最下面,那么也先被放出水池

1.2 异步FIFO

同步FIFO:读、写时钟域相同
异步FIFO:读、写时钟域不同,读、写的位宽也可以不同

1.3 异步FIFO的重要概念

FIFO的作用:缓存数据
FIFO的原则:满不写、空不读(否则会造成数据丢失、读取无效数据)
FIFO的关键:设计产生full满信号、empty空信号

二、如何建立FIFO

2.1 FIFO的核心:双端口RAM

双端口:输入端口和输出端口独立,因此可以同时进行读、写;在RAM中,使能信号、数据、数据地址这三个数据是对应的,

2.1.1 SDPRAM:Simple Dual Port RAM

首先明确该模块中可以将读、写端的信号分为:

  1. 时钟
  2. 使能信号
  3. 数据
  4. 地址
  5. 读FIFO独有的的复位信号

其次,设定好RAM的宽度和深度
数据宽度:8bit,RAM中存储的数据位宽
地址深度:16bit,RAM中能存多少个位宽为8bit的数据
地址宽度:4bit(2^4=16,使用4位二进制数就能表示16个地址)
写法:16×8
该FIFO能存放:16个8bit的数据

双端口RAM设计文件

实现RAM的读、写操作

module SDPRAM_ly (input                   w_en,input                   w_clk,input       [7:0]      w_data,input       [3:0]      w_addr,input                   r_en,input                   r_clk,input                   r_rst_n,input       [3:0]      r_addr,output  reg [7:0]      r_data);// 首先声明一个内存用来放数据和地址
reg     [7:0]  SAVE    [15:0];// 往SAVE写数据
always @(posedge w_clk) beginif(w_en)SAVE[w_addr] <= w_data;end// 从SAVE里读数据
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)r_data <= 16'd0;else if(r_en)r_data <= SAVE[r_addr];elser_data <= r_data;endendmodule

2.2 RAM外围模块的核心:设计写满、读空信号

这部分的重点是处理跨时钟域的问题:
单比特:

2.2.1二进制地址自增

在有了双端口RAM后,我们要设计外围的模块去控制RAM正确进行读写操作,下面的核心是如何设计出写满和读空信号


地址自增条件:使能有效+可写/可读

reg [4:0]   w_bin;// 对地址扩充一位
reg [4:0]   r_bin;// ===========================  写  ===============================// 写地址扩充一位后,地址自加1
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)w_bin <= 5'd0;else if(w_en && !w_full) // && 逻辑与,得到一位;&按位与,保持相与元素位宽w_bin <= w_bin + 5'd1;else   w_bin <= w_bin;
end// ===========================  读  ===============================
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)r_bin <= 5'd0;else if(r_en && !r_empty) // && 逻辑与,得到一位;&按位与,保持相与元素位宽r_bin <= r_bin + 5'd1;else   r_bin <= r_bin;
end

2.2.2 二进制转格雷码:减小跨时钟域下发生亚稳态的概率

这样做的目的:当信号发生变化时,二进制的变化位数远远多于格雷码,因此发生亚稳态的几率就更大,而格雷码只有一位变化
转化方法:
二进制最高位不变,直接复制到格雷码最高位,其余位两两之间异或

二进制转格雷码本身是组合逻辑,但是为了避免产生竞争冒险,于是将组合逻辑的输出打一拍
如果不打拍,可以参照这里的写法:异步FIFO的Verilg实现方法-孤独的单刀

//地址指针从二进制转换成格雷码
assign  wr_ptr_g = wr_ptr ^ (wr_ptr >> 1);
assign  rd_ptr_g = rd_ptr ^ (rd_ptr >> 1);

打一拍的写法:

// 二进制转格雷码// ===========================  写  ===============================// 方法:最高位不变,按位异或
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)w_gay <= 5'd0;else w_gay <= {w_bin[4],(w_bin[4:1]^w_bin[3:0])};
end// ===========================  读  ===============================
// 二进制转格雷码
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)r_gay <= 5'd0;else r_gay <= {r_bin[4],(r_bin[4:1]^r_bin[3:0])};
end

2.2.3 将格雷码打2拍:多比特信号的跨时钟域

先将二进制编码为格雷码,然后对格雷码打2拍(同步器),将格雷码同步到另一个时钟域下

// ===========================  写  ===============================
// 将读部分得到的格雷码打2拍后,同步到写时钟下
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)r_gay_dff1 <= 5'd0;r_gay_dff2 <= 5'd0;else r_gay_dff1 <= r_gay;r_gay_dff2 <= r_gay_dff1;
end// ===========================  读  ===============================
// 将写部分得到的格雷码打2拍后,同步到读时钟下
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)w_gay_dff1 <= 5'd0;w_gay_dff2 <= 5'd0;else w_gay_dff1 <= w_gay;w_gay_dff2 <= w_gay_dff1;
end

2.2.4 将打2拍后的格雷码解码为二进制:组合逻辑

// ===========================  写  ===============================
// 将同步过来的读格雷码解码为读二进制,方便和写二进制地址比较
always @(*) beginfor(i=4;i>=0;i=i-1)beginif(i==4)r_gay_2_bin[i] <= r_gay_dff2[i];elser_gay_2_bin[i] <= r_gay_dff2[i] ^ r_gay_2_bin[i+1]      end
end
// ===========================  读  ===============================
// 格雷码解码为二进制
integer j;
always @(*) beginfor(j=4;j>=0;j=j-1)beginif(j==4)w_gay_2_bin[j] <= w_gay_dff2[j];elsew_gay_2_bin[j] <= w_gay_dff2[j] ^ w_gay_2_bin[j+1]      end
end

2.2.5 得到写满、读空信号:组合逻辑

// ===========================  写  ===============================
// 方法一:写满信号:最高位不同,其余位相同——用格雷码
assign w_full = ((w_bin[4] != r_gay_dff2[4]) && (w_bin[3] != r_gay_dff2[3]) && (w_bin[2:0] == r_gay_dff2[2:0]))?1'b1:1'b0;
// 方法二:写满信号:最高位、次高位不同,其余位相同——用格雷码解码后的二进制
assign w_full = ((w_bin[4] != r_gay_2_bin[4]) && (w_bin[3:0] != r_gay_2_bin[3:0]))?1'b1:1'b0;
// ===========================  读  ===============================
// 读空信号:所有位相同
assign r_empty = (r_bin == w_gay_2_bin)?1'b1:1'b0;

2.2.6 汇总

module top_FIFO_ly (//写input           w_en,input           w_clk,input           w_rst_n,input   [7:0]  w_data,output          w_full,//读input           r_en,input           r_clk,input           r_rst_n,output          r_empty,output  [7:0]  r_data
);reg [4:0]   w_bin;// 对地址扩充一位
reg [4:0]   r_bin;
reg [4:0]   w_gay;
reg [4:0]   r_gay;
reg [4:0]   r_gay_dff1;
reg [4:0]   r_gay_dff2;
reg [4:0]   w_gay_dff1;
reg [4:0]   w_gay_dff2;
reg [4:0]   r_gay_2_bin;
reg [4:0]   w_gay_2_bin;// ===========================  写  ===============================
// 写地址扩充一位后,地址自加1
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)w_bin <= 5'd0;else if(w_en && !w_full) // && 逻辑与,得到一位;&按位与,保持相与元素位宽w_bin <= w_bin + 4'd1;else   w_bin <= w_bin;
end// 二进制转格雷码
// 方法:最高位不变,按位异或
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)w_gay <= 5'd0;else w_gay <= {w_bin[4],(w_bin[4:1]^w_bin[3:0])};
end// 将读部分得到的格雷码打2拍后,同步到写时钟下
always @(posedge w_clk or negedge w_rst_n) beginif(!w_rst_n)beginr_gay_dff1 <= 5'd0;r_gay_dff2 <= 5'd0;endelse beginr_gay_dff1 <= r_gay;r_gay_dff2 <= r_gay_dff1;    endendinteger i;
// 将同步过来的读格雷码解码为读二进制,方便和写二进制地址比较
always @(*) beginfor(i=4;i>=0;i=i-1)beginif(i==4)r_gay_2_bin[i] <= r_gay_dff2[i];elser_gay_2_bin[i] <= r_gay_dff2[i] ^ r_gay_2_bin[i+1];  end
end// 方法一:写满信号:最高位不同,其余位相同——用格雷码
//assign w_full = ((w_bin[4] != r_gay_dff2[4]) && (w_bin[3] != r_gay_dff2[3]) && (w_bin[2:0] == r_gay_dff2[2:0]))?1'b1:1'b0;
// 方法二:写满信号:最高位、次高位不同,其余位相同——用格雷码解码后的二进制
assign w_full = ((w_bin[4] != r_gay_2_bin[4]) && (w_bin[3:0] == r_gay_2_bin[3:0]))?1'b1:1'b0;// ===========================  读  ===============================
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)r_bin <= 5'd0;else if(r_en && !r_empty) // && 逻辑与,得到一位;&按位与,保持相与元素位宽r_bin <= r_bin + 4'd1;else   r_bin <= r_bin;
end// 二进制转格雷码
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)r_gay <= 5'd0;else r_gay <= {r_bin[4],(r_bin[4:1]^r_bin[3:0])};
end// 将写部分得到的格雷码打2拍后,同步到读时钟下
always @(posedge r_clk or negedge r_rst_n) beginif(!r_rst_n)beginw_gay_dff1 <= 5'd0;w_gay_dff2 <= 5'd0;        endelse beginw_gay_dff1 <= w_gay;w_gay_dff2 <= w_gay_dff1;           endend
// 格雷码解码为二进制
integer j;
always @(*) beginfor(j=4;j>=0;j=j-1)beginif(j==4)w_gay_2_bin[j] <= w_gay_dff2[j];elsew_gay_2_bin[j] <= w_gay_dff2[j] ^ w_gay_2_bin[j+1];   end
end// 读空信号:所有位相同
assign r_empty = (r_bin == w_gay_2_bin)?1'b1:1'b0;SDPRAM_ly SDPRAM_inst(.w_en(w_en_RAM),.w_clk(w_clk),.w_data(w_data),.w_addr(w_bin[3:0]),.r_en(r_en_RAM),.r_clk(r_clk),.r_rst_n(r_rst_n),.r_addr(r_bin[3:0]),.r_data(r_data));assign w_en_RAM = (w_en && (!w_full))?1'b1:1'b0;
assign r_en_RAM = (r_en && (!r_empty))?1'b1:1'b0;
endmodule

三、如何使用FIFO

这部分内容以后遇到了再进行补充

3.1 数据跨时钟域

3.2 缓存写速度快的数据

3.3 读写位宽不同

FPGA:异步FIFO相关推荐

  1. FPGA逻辑设计回顾(6)多比特信号的CDC处理方式之异步FIFO

    文章目录 前言 异步FIFO的概念 异步FIFO为什么可以解决CDC问题? 异步FIFO的RTL实现 参考资料 前言 异步FIFO是处理多比特信号跨时钟域的最常用方法,简单来说,异步FIFO是双口RA ...

  2. (87)FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计

    1.1 FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-同步FIFO与异步FIFO区 ...

  3. 基于FPGA的目标颜色识别追踪三——FIFO(同/异步FIFO)、DDR3

    FIFO在数据处理过程中是十分重要的. 同步FIFO比较简单,面试过程中手撕代码可能会用到. module sFIFO #(parameter DATA_WIDTH = 8,ADDR_WIDTH = ...

  4. FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】

    牛客 Verilog 刷题入门篇1~24 + 进阶篇1~34 题解代码,所有代码均能通过测试,配合视频讲解效果更佳.为避免内容冗余,本文只给出代码,部分题目给出必要说明. 很多题目本身出题有些问题,着 ...

  5. 【FPGA学习记录1】异步FIFO的介绍

    文章目录 写在前面 1.FIFO的定义以及为什么要用FIFO.FIFO的分类 1.1什么是FIFO 1.2为什么要用FIFO 1.3FIFO的分类 2.异步FIFO的工作原理 2.1FIFO的结构 2 ...

  6. Xilinx 异步FIFO核实现FPGA与DSP通过UPP(通用并口)进行数据传输

    Xilinx 异步FIFO核实现FPGA与DSP通过UPP(通用并口)进行数据传输 一.Xilinx FIFO IP核介绍 二.UPP 仿真效果 总结 一.Xilinx FIFO IP核介绍 1. 因 ...

  7. FPGA基础知识极简教程(4)从FIFO设计讲起之异步FIFO篇

    博文目录 写在前面 正文 同步FIFO回顾 $clog2()系统函数使用 综合属性控制资源使用 异步FIFO设计 FIFO用途回顾 异步FIFO原理回顾 异步FIFO设计 异步FIFO仿真 参考资料 ...

  8. 异步fifo的设计(FPGA)

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) fifo位宽(每个 ...

  9. 异步FIFO的FPGA实现

    本文大部分内容来自Clifford E. Cummings的<Simulation and Synthesis Techniques for Asynchronous FIFO Design&g ...

  10. 基于FPGA的异步FIFO设计

    今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域.由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出).这里的读写指针是异步的, ...

最新文章

  1. 基于Python的完全监督机器学习教程 Complete Supervised Machine Learning With Python
  2. python中的字典推导式_python 字典推导式(经典代码)(22)
  3. 原博客文章(Apache初配2008/4/8)
  4. 驱动中的资源共享和临界代码保护
  5. Maven配置文件无法被导出或者生效的问题【已解决】
  6. mysql-front5.1的注册码
  7. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(八):MyBatis分页功能实现
  8. 华硕主板破linux密码破解,华硕P8B75-M-LE老主板加持NVMe SSD bios(刷新软件和bios)...
  9. 即席查询—— Kylin使用
  10. ROS激光SLAM导航理解
  11. 弘辽科技:拼多多商品搜索热度如何提升?技巧分享
  12. Java线程池工作原理
  13. spyder顺眼配色方案记录
  14. 居然打不开msi文件?
  15. NetSuite Account Register报表详解
  16. 高中信息技术项目化教学开题报告
  17. 平面设计素材免费下载,无套路
  18. 警惕感冒不仅会头痛脑热,还可能引发致盲性眼病
  19. STM32移植uC/OSIII
  20. 计算机网络-关于信号的调制

热门文章

  1. MFC调用RDP实现远程桌面共享实例
  2. 录像机人机界面蓝屏怎么处理
  3. python识别图片中的文字、数值并转文档
  4. 识别图片验证码内容 -- ddddocr识别
  5. JS 正则表达式 验证中国大陆身份证号码
  6. python表示倍数_python 求倍数
  7. 第三届全国平面公益广告大赛暨全国大学生公益广告征集活动
  8. Python 生成微信头图
  9. 已解决-Windows10没有windows照片查看器-Windows10打开照片是黑底的
  10. Java关于数字的正则校验