这一篇老李终于要开始聊异步FIFO(Asynchronous FIFO)了。在知乎上曾经老李见过一个问题:

硕士生找工作的时候把异步fifo写成一个项目经历,是不是显得很low啊?

知乎:https://www.zhihu.com/question/38321271

下面回答很有意思,老李发现那些回答很low的以在校学生和刚毕业的应届生居多,大家都觉得简历里面一定要写上什么无线接收机,USB控制器等等才显得高大上。而几个来自真正工业界大佬的回答反而不说low,比如下面的这个回答来自海思的架构师说

我如果面试,不会介意。但会很乐意基于这个话题继续问你对异步原理的理解,例如格雷码的原理,两拍同步或者三拍同步的差异,或者FIFO内DATA如何保证绝对稳定等问题来进一步试探,这才是决定结果的。

夏晶晶 https://www.zhihu.com/question/38321271/answer/81593966

老李自己也基本认同这个观点,即异步FIFO里面其实需要了解的东西很多,几乎涵盖了CDC中所有相关的知识点,面试官可以就异步FIFO里面很多的点进行提问。老李当年面试硅谷各大芯片公司,几乎都被问到了异步FIFO。所以说,深刻理解异步FIFO,是一个合格的前端芯片设计工程师必须掌握的基本技能。

在开始讲异步FIFO之前,老李先带大家简单回顾一下同步FIFO。FIFO就是一个存储的管道,有进的口,有出的口。同步FIFO就是说进口(写入端)和出口(读出端)是同一个时钟域。FIFO一般深度多于1,就需要两个指针: write pointer和read pointer。

对于write pointer和read pointer我们一般用2进制,写入操作(Push)使得write pointer + 1,读出操作(Pop)使得read pointer + 1。这就像是两个人在一个环形跑道上赛跑。当write pointer领先了read pointer一圈之后,也就是说FIFO里面所有的存储单元都存了数据,FIFO没有空余的存储单元了,我们就说FIFO满了。反过来,当read pointer追上了write pointer,所有的存储单元都空闲了,我们就说FIFO空了。

对于异步FIFO来说,Push和Pop分别在不同的时钟域,那么最核心的问题就是空满的判断了。在Pop的这一侧,FIFO空不空是关键,因为空的时候不能Pop,满不满反而不重要。在Push的这一侧,反过来,满不满才关键,因为满的时候不能继续往进Push。因此,我们就要在读的这一侧判断FIFO是否空,在写的这一侧来判断FIFO是否满。当然我们还是要有read pointer和write pointer,在pop这一侧更新read pointer,在push这一侧更新write pointer。那么当我们要把pointer同步到另外的时钟域进而去比较的时候,我们就遇到了上一讲讨论的multi-bit 同步的问题,即binary counter不能直接利用double flop来同步。

那么我们上一篇讲到的带反馈的asynchronous load模块可以用来同步pointer吗?可以是可以,但是缺点也很明显,即反馈的话要跨两次时钟域,对于效率很有影响,比如说push这一侧要等到反馈信号回来之后才能继续下一个push,哪怕FIFO里面还有很多空闲的单元。pop的这一侧也是一样。这样对于FIFO的整体性能影响太大。

那有没有更快的办法呢?答案就是老李上一期最后埋的坑 -- 用格雷码Gray Code。

格雷码是以美国学者Frank Gray于1947年提出的一种二进制编码方式,后面这种编码方式就以他的名字命名。这种编码方式的特点老李以两句话概况

  1. 每相邻的两个编码之间有且只有一位不同

  2. 当第N位从0变到1的时候,之后的数的N-1位会关于前半段轴对称,而比N位高的位是相同的。

记住了以上两点,老李保证你可以现场推出来Gray code是什么样的。说实话,老李自己也记不住二进制到Gray code的转换公式,每次都是写出来现场推。(相信老李,面试官问你的时候除非他自己面你之前背了公式,否则他也得现推。)如果你只记得1,你现场可能推不出来,所以老李还是建议你把2也记住。下面这幅图就是用10进制,传统2进制以及Gray code来表示0-15个数。

老李所说的轴对称是什么意思呢?请看Gray code前两个数4'b0000, 4'b0001,它们俩之间可以画一条对称轴,第1-3位都是相同的。再看前4个数,在4‘b0001和4'b0011之间画一条对称轴,第2、3位是相同的,第0位则是轴对称的,从0-1到1-0。之后的规律老李也在图上标出来了,一看就懂。有了这两个规律,更多位数的Gray code老李相信你也可以现场直接写出来。

然后咱们就来推一推关系,你只要大概记住需要用到异或操作,就能推出来

Binary to Grayg(3) = b(3) ^ 0g(2) = b(3) ^ b(2)g(1) = b(2) ^ b(1)g(0) = b(1) ^ b(0)g(n) = b(n+1) ^ b(n)Gray to Binaryb(3) = g(3)b(2) = g(3) ^ g(2)b(1) = g(3) ^ g(2) ^ g(1)b(0) = g(3) ^ g(2) ^ g(1) ^ g(0)

Gray code有什么魔法之处,能够突破muti-bit不能用2flop synchronzier的限制呢?关键就在于它的第一个特点:相邻两个编码之间有且只有1位不同。我们说multi-bit如果在一个时钟沿有多个bit同时翻转,在另外一个时钟域采到的时候由于2flop 稳定需要1个或2个周期,所以可能会出现错误的值。Gray code这种编码,从根本上就没有这个问题,因为以Gray code编码作为计数器,每个时钟沿来的时候只会有1个bit发生了翻转,其余所有bit都是稳定的!这样即使这一个bit在用2flop synchronizer同步到另外一个时钟域时,可能需要1个周期发生变化,或者2个周期,在发生变化前,另一个域的值就是之前的稳定值,变化后就是新的值,而不会出现其他不该出现的值。

用了2flop synchronizer来同步,省去了反馈,把read pointer同步到write domain来判断满,把write pointer 同步到read domain来判断空,只需要跨一次domain,就可以判断,这样可以提高push和pop的效率。

而对于memory的取址还是得用2进制编码,我们需要做的就是在同步pointer的时候把binary pointer 转化为gray code pointer,然后用2flop synchronizer同步到对面时钟域之后,再来判断空满。这里有个问题,我们需不需要把gray code再转化为binary code之后再来比较空满呢?当然可以,我们学习新知识就是不断把问题转换为已经解决的问题,在同步FIFO的时候我们已经知道怎么用read pointer和write pointer判断空满,那么自然而然我们可以这样做。那么有没有更快的办法呢?我们能不能直接利用gray code来判断空满呢?

有!我们再仔细观察gray code。和同步FIFO一样,我们对于2^n个entry的FIFO, 需要N+1个bit来表示address和gray code。以下面的编码为例,假设FIFO有8个entry,我们用4位来表示。

FIFO空比较好判断,write pointer == read pointer,用binary或者gray code都行,要求每位都相同。

满稍微复杂一点,我们举例来说,假设FIFO一开始一直写,不读,写满8个entry后write pointer 的binary变成4’b1000, gray code是4‘b1100, 而read pointer的gray code是4’b0000,可以看到高两位是相反的,之后的低位是相同的。再举个例子,假设write pointer 的gray code到了4'b1011, 而这个时候read pointer如果是4'b0111,那么也是8个entry满了。

所以我们归纳出,利用gray code判断满的条件为:

assign full = (write_ptr_gray[N:N-1] == ~read_ptr_gray[N:N-1])            &&(write_ptr_gray[N-2:0] == read_ptr_gray[N-2:0]);

最后再说一个面试中的常见问题,这个问题知乎上也有人提

慢时钟域同步快时钟域格雷码时候,在慢时钟域的一个周期中,经历了两次或多次快时钟域的上升沿,那么对应的格雷码就会有两个或多个bits发生变化,这个不会产生多个bits同步的问题吗?

知乎:https://www.zhihu.com/question/290661321

说实话老李当年面试就被问过好几次这个问题。这个问题很有迷惑性,但是回答起来也很简单,其实老李上面已经回答了,这里再复习一下,继续搬出我们之前看过的图。我们说多个bit发生变化其实是针对source clock的每一个edge来说的,因为不同bit之间发生翻转的时间不能严格对齐,所以会导致destination clock可能看到不同的值,导致最后synchronizer输出会出现错误的值,从而影响FIFO的空满判断。而gray code在每个source clock的沿只会有一个bit发生翻转,其余bit保持稳定,这样每个destination clock edge来的时候最多也只可能碰到1bit在翻转,这个翻转的bit可能会给synchronizer的第一级引入metastable,但是最后synchronizer的输出无非就是保持前值或者是更新后的值,而这两个值都是合理的值,不会出现一个错误的值从而导致FIFO空满判断逻辑错误。虽然慢时钟域同步过来的值可能和之前的值相比有多个bit发生变化,但是这些bit的翻转不是同时发生的,这是回答这道题的关键。

这周老李工作较忙,上周的推送晚了几天,拖到了这周末。下周老李继续带大家了解异步FIFO里的其他细节内容。比如前面讲的判断空满是真满、真空还是假满、假空?如果FIFO的深度不是2^n,还能用gray code吗?

如果你觉得这篇文章对你有所帮助,不妨点个右下角的“在看”,最好能够分享到群里或者朋友圈,你的支持就是老李更新的动力!

异步fifo_面试必杀技:异步FIFO(上) CDC的那些事(5)相关推荐

  1. 异步fifo_数字IC校招基础知识点复习(五)——跨时钟域涉及part2(异步FIFO)

    1.跨时钟域设计(CDC)-part 2 在上一篇中我们主要关注的是单比特的控制信号,而对于多比特信号的跨时钟域传输则需要一些其他的手段. 首先需要明确的是,一般不采用对多比特信号中的每个比特单独使用 ...

  2. 异步fifo_正点原子开拓者FPGA开发板资料连载第十五章 IP核之FIFO实验

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  3. 同步(单时钟)、异步(双时钟)FIFO的Verilog HDL实现(含Testbench仿真代码)

    目录 一.FIFO的定义和应用场景 二.FIFO的结构 三.FIFO的应用场景 3.1 单时钟(同步)FIFO 3.2 双时钟(异步)FIFO 四.FIFO的结构 五.FIFO常见参数 六.实现 FI ...

  4. [进阶] --- Python3 异步编程详解(史上最全篇)

    [进阶] - Python3 异步编程详解:https://blog.csdn.net/lu8000/article/details/45025987 参考:http://aosabook.org/e ...

  5. (86)FPGA同步复位与异步复位-面试必问(十)(第18天)

    (86)FPGA同步复位与异步复位-面试必问(十)(第18天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)FPGA同步复位与异步复位-面试必问(十)(第18天 ...

  6. [进阶]-Python3 异步编程详解(史上最全篇)

    目录 1 什么是异步编程 1.1 阻塞 1.2 非阻塞 1.3 同步 1.4 异步 1.5 并发 1.6 并行 1.7 概念总结 1.8 异步编程 1.9 异步之难(nán) 2 苦心异步为哪般 2. ...

  7. java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...

    异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...

  8. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记

    Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记 点击上方"java进阶架构师",选择右上角"置顶公众号" 20大进阶架构专题每日送达 引 ...

  9. python异步编程视频_asyncio异步编程【含视频教程】

    Python Python开发 Python语言 asyncio异步编程[含视频教程] 不知道你是否发现,身边聊异步的人越来越多了,比如:FastAPI.Tornado.Sanic.Django 3. ...

  10. 浅谈.Net异步编程的前世今生----异步函数篇(完结)

    前言 上一篇我们着重讲解了TPL任务并行库,可以看出TPL已经很符合现代API的特性:简洁易用.但它的不足之处在于,使用者难以理解程序的实际执行顺序. 为了解决这些问题,在C# 5.0中,引入了新的语 ...

最新文章

  1. 2021-01-16交叉熵损失函数比均方误差损失函数优点在哪里
  2. stdout和stderr标准输出的区别
  3. Linux apache源码安装自定义Server
  4. Spring操作Redis
  5. 想学编程一定要看的文章,你真的了解编程吗?
  6. Ural_1003 Parity(并查集)
  7. Java讲课笔记08:数组
  8. 【CVPR 2019】Strong-Weak Distribution Alignment for Adaptive Object Detection
  9. 创业日记:进入电子商务领域,需未雨绸缪,更要步步谨慎
  10. Python 之父:移动设备中的 Python 应用“又大又慢”!
  11. openwrt路由器打印机服务器设置_OPENWRT网络打印机TCP/IP共享设置教程 以703N为例...
  12. 创业怎么写商业计划书?
  13. 为IT和互联网写历史
  14. 电话交换机tdmx2000dx硬件配置说明
  15. Python基于PHP+MySQL的个人网页设计与实现
  16. Ae 入门系列之五:效果和预设
  17. 月薪过2w的IT程序员都是怎么做到的?
  18. OpenCV笔记:模板匹配 cv2.matchTemplate()、cv2.minMaxLoc() 与 绘制矩形 cv2.rectangle() 方法介绍
  19. MongoDB——聚合操作
  20. javaweb记账本系统

热门文章

  1. 二分法其实很简单,为什么老是写不对!!!
  2. 449A - Jzzhu and Chocolate 贪心
  3. poj 1797 Heavy Transportation 本来以为floyd瞬秒,结果各种re,真无语,看网上别人的并查集了
  4. InDesign 2022 for mac排版布局和页面设计
  5. Aiseesoft Screen Recorder入门教程
  6. ZBrush for Mac的插图技巧
  7. iOS开发多线程篇—线程间的通信
  8. MySQL 8支持文档存储,并带来性能和安全方面的改进
  9. Atlassian发布Bamboo 6.0和Bitbucket Server 5.0
  10. 【Java】Java_16 控制循环结构Break、Continue、Return