【芯片前端】“异步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的跳转为:

高比特低比特指针数值
000000
000011
.........
010019
10000(套圈)0
10001(套圈)1
......
11001(套圈)9
000000

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

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

BUG修复

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

十进制二进制格雷码
000000000
100010001
200100011
300110010
401000110
501010111
601100101
701110100
810001100
910011101
1010101111
1110111110
1211001010
1311011011
1411101001
1511111000

显而易见的一条规律是,相邻两个数只有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为例,看一下这种策略下的指针跳转:

高比特低比特数值(二进制)数值(格雷码)实际指针数值
0000000_00000
0000110_00011
...............
01010100_111110
10101211_11111_0101 - 101('d5) = 1_0000,0
10110221_11011_0110 - 101 = 1_0001,1
...............
11111311_00001_1111 - 101 = 1_1010,10
0000000_00000

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

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尼德兰的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值