【芯片前端】“异步FIFO全解析”的BUG——格雷码连续性

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【芯片前端】“异步FIFO全解析”的BUG——格雷码连续性

前言

在前几天写完【芯片前端】保持代码手感——异步FIFO全解析之后自我感觉非常良好,觉得异步fifo的问题我已经全部拿捏了,没想到今天突然想到一个我自己代码里的bug。


代码中的BUG

事情的源头在同步fifo里,深度任意可配的同步fifo里使用了两个非饱和的cnt计数器记录读写地址指针,waddr和raddr均比实际地址多一位,最高位用来指示套圈情况。当waddr和raddr的最高位相同时,fifo_cnt = waddr-raddr;当waddr和raddr的最高位相反时,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0]   - raddr[ADDR_WIDTH-1:0]。最高位作为标志位,当低位计数到DEPTH-1时,高位翻转,代码大意如下:

assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
  if(~rst_n)    waddr <= 0;
  else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end


假设fifo深度设为10,那么waddr的跳转为:

高比特

低比特

指针数值

0

0000

0

0

0001

1

...

...

...

0

1001

9

1

0000

(套圈)0

1

0001

(套圈)1

...

...


1

1001

(套圈)9

0

0000

0


在同步fifo里这样做是没有问题的,但是问题出现在,把这个代码代入到异步fifo中的时候。



异步fifo中使用格雷码对addr进行跨异步,关键处理是二进制 - 格雷码 - 跨异步 - 二进制处理路径 。而之所以能够使用格雷码跨异步是因为格雷码相邻的两个数只相差1bit,而深度为10的异步fifo,存在'b01001('d9) -> 'b10000('d16)的跳转,这个就不是相邻跳转了,相应的格雷码变化也不是单比特的跳转:01101 -> 11000,显然的这会导致空满判断出现逻辑错误。

BUG修复

这个问题的关键就在于不饱和计数在边界的跳变导致的格雷码不连续,所以先观察格雷码。十进制0~15对应的二进制和格雷码如下表:

十进制

二进制

格雷码

0

0000

0000

1

0001

0001

2

0010

0011

3

0011

0010

4

0100

0110

5

0101

0111

6

0110

0101

7

0111

0100

8

1000

1100

9

1001

1101

10

1010

1111

11

1011

1110

12

1100

1010

13

1101

1011

14

1110

1001

15

1111

1000


显而易见的一条规律是,相邻两个数只有1bit不一样。进一步可以发现,3->0,7->0,15->0的编码也是“连续的”,即只有1bit不同。为了更好地观察规律,我们做出格雷码的码盘来观察,码盘如下图,红色为1白色为0,同心圆内为高比特,外部为低比特,如十进制2的格雷码为0011,对应的图就是“白白红红”。



盯一会码盘就会发现码盘的“对称性”和“连续性”,比如说1和14、2和13、6和9、7和8在码盘上处于轴对称的位置,而格雷码编码值恰好相差1bit。


那么也就是说,如果当前的值是5,那么下一个数想要保持格雷码连续性,就只能跳4、6或者10:

十进制5=格雷码:0111

十进制4=格雷码:0110


十进制6=格雷码:0101


十进制10=格雷码;1111


显然,此时只能跳10才能满足最高位作为标志位,当低位计数到DEPTH-1时,高位翻转的条件,也就是说我们实际要由5('b0101) -> 8('b1000)变成了由5('b0101) -> 10('b1010),所以说实际多条了两个数,因此实际cnt的值是10 - 偏移量,此时的偏移量是2,fifo的深度为6(所以低位计数计到5)。


那么继续推导,当深度配置为5,那么低位满4(格雷码:0110)跳转为11(格雷码:1110),实际的cnt值是11 - 3 = 8('d1000),偏移量为3。进一步的我就不推了,根据码盘也可以发现,偏移量 = 2^N - 配置深度,低位计满跳转值 = 2^N + 偏移量。用代码表示这几个参数:

parameter DEPTH = 11;
parameter WIDTH = $clog2(DEPTH);//4
parameter DEPTH_TO2 = 2 ^ WIDTH;//16
parameter SHIFT = DEPTH_TO2 - DEPTH;//16-11=5


还是以深度11为例,看一下这种策略下的指针跳转:

高比特

低比特

数值(二进制)

数值(格雷码)

实际指针数值

0

0000

0

0_0000

0

0

0001

1

0_0001

1

...

...

...

...

...

0

1010

10

0_1111

10

1

0101

21

1_1111

1_0101-101('d5)=1_0000,0

1

0110

22

1_1101

1_0110-101=1_0001,1

...

...

...

...

...

1

1111

31

1_0000

1_1111-101=1_1010,10

0

0000

0

0_0000

0


OK,策略上没有问题,进一步的异步FIFO代码组织放到下次,今天太晚了。  


相关文章
|
9天前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
12 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
2月前
|
开发者 图形学 C#
深度解密:Unity游戏开发中的动画艺术——Mecanim状态机如何让游戏角色栩栩如生:从基础设置到高级状态切换的全面指南,助你打造流畅自然的游戏动画体验
【8月更文挑战第31天】Unity动画系统是游戏开发的关键部分,尤其适用于复杂角色动画。本文通过具体案例讲解Mecanim动画状态机的使用方法及原理。我们创建一个游戏角色并设计行走、奔跑和攻击动画,详细介绍动画状态机设置及脚本控制。首先导入动画资源并添加Animator组件,然后创建Animator Controller并设置状态间的转换条件。通过编写C#脚本(如PlayerMovement)控制动画状态切换,实现基于玩家输入的动画过渡。此方法不仅适用于游戏角色,还可用于任何需动态动画响应的对象,增强游戏的真实感与互动性。
58 0
|
2月前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
45 0
|
2月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
23 0
|
2月前
|
开发者 Java
JSF EL 表达式:乘技术潮流之风,筑简洁开发之梦,触动开发者心弦的强大语言
【8月更文挑战第31天】JavaServer Faces (JSF) 的表达式语言 (EL) 是一种强大的工具,允许开发者在 JSF 页面和后台 bean 间进行简洁高效的数据绑定。本文介绍了 JSF EL 的基本概念及使用技巧,包括访问 bean 属性和方法、数据绑定、内置对象使用、条件判断和循环等,并分享了最佳实践建议,帮助提升开发效率和代码质量。
28 0
|
2月前
|
前端开发 开发者 Apache
揭秘Apache Wicket项目结构:如何打造Web应用的钢铁长城,告别混乱代码!
【8月更文挑战第31天】Apache Wicket凭借其组件化设计深受Java Web开发者青睐。本文详细解析了Wicket项目结构,帮助你构建可维护的大型Web应用。通过示例展示了如何使用Maven管理依赖,并组织页面、组件及业务逻辑,确保代码清晰易懂。Wicket提供的页面继承、组件重用等功能进一步增强了项目的可维护性和扩展性。掌握这些技巧,能够显著提升开发效率,构建更稳定的Web应用。
76 0
|
2月前
|
前端开发 程序员 API
从后端到前端的无缝切换:一名C#程序员如何借助Blazor技术实现全栈开发的梦想——深入解析Blazor框架下的Web应用构建之旅,附带实战代码示例与项目配置技巧揭露
【8月更文挑战第31天】本文通过详细步骤和代码示例,介绍了如何利用 Blazor 构建全栈 Web 应用。从创建新的 Blazor WebAssembly 项目开始,逐步演示了前后端分离的服务架构设计,包括 REST API 的设置及 Blazor 组件的数据展示。通过整合前后端逻辑,C# 开发者能够在统一环境中实现高效且一致的全栈开发。Blazor 的引入不仅简化了 Web 应用开发流程,还为习惯于后端开发的程序员提供了进入前端世界的桥梁。
52 0
|
2月前
|
前端开发 UED 开发者
React组件优化全攻略:深度解析让你的前端应用飞速运行的秘诀——从PureComponent到React.memo的彻底性能比较
【8月更文挑战第31天】在构建现代Web应用时,性能是提升用户体验的关键因素。React作为主流前端库,其组件优化尤为重要。本文深入探讨了React组件优化策略,包括使用`PureComponent`、`React.memo`及避免不必要的渲染等方法,帮助开发者显著提升应用性能。通过实践案例对比优化前后效果,不仅提高了页面渲染速度,还增强了用户体验。优化React组件是每个开发者必须关注的重点。
49 0
|
2月前
|
前端开发 API 开发者
【前端数据革命】React与GraphQL协同工作:从理论到实践全面解析现代前端数据获取的新范式,开启高效开发之旅!
【8月更文挑战第31天】本文通过具体代码示例,介绍了如何利用 GraphQL 和 React 搭建高效的前端数据获取系统。GraphQL 作为一种新型数据查询语言,能精准获取所需数据、提供强大的类型系统、统一的 API 入口及实时数据订阅功能,有效解决了 RESTful API 在复杂前端应用中遇到的问题。通过集成 Apollo Client,React 应用能轻松实现数据查询与实时更新,大幅提升性能与用户体验。文章详细讲解了从安装配置到查询订阅的全过程,并分享了实践心得,适合各层次前端开发者学习参考。
30 0
|
2月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
22 0

热门文章

最新文章

推荐镜像

更多
下一篇
无影云桌面