跨时钟域数据传输

本文

主要讲解跨时钟域的数据传输处理,重点是异步fifo。

版本 说明
0.1 初版发布
0.2 添加格雷码转换逻辑
0.3 补充问题
0.4 补充问题:非2次幂fifo深度的处理

参考

名称 作者 来源
各种网络资源 网络

专业术语与缩略语

缩写 全称 说明
CDC clock domain crossing 跨时钟域

基础概念

异步时序

异步时序指的是在设计中有两个或以上的时钟,且时钟之间是同频不同相或不同频的关系。而异步时序设计的关键就是把数据或控制信号正确地进行跨时钟域传输。

亚稳态

每一个触发器都有其规定的建立(setup)和保持(hold)时间参数,在这个时间参数内,输入信号在时钟的上升沿是不允许发生变的。如果在信号的建立时间中对其进行采样,得到的结果将是不可预知的,即亚稳态。

格雷码

格雷码就是一种可靠性编码。在一组数的编码中,若 任意两个相邻的码只有一位二进制数不同 ,则称这种编码为格雷码,另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码。

举例:

  1. 两位数据格雷码:00 01 11 10
  2. 三位数据格雷码:000 001 011 010 110 111 101 100

转换逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Binary to Gray
g[0] = b[1] ^ b[0];
g[1] = b[2] ^ b[1];
g[2] = b[3] ^ b[2];
g[3] = b[4] ^ b[3];
...
g[n-1] = b[n] ^ b[n-1]; //b[n-1:0]为常规二进制数,这里b[n]设置为0。g[n-1:0]为格雷码。

// Gray to Binary
b[0] = g[0] ^ g[1] ^ g[2] ^ g[3] ^ ... ^ g[n-1]
b[1] =        g[1] ^ g[2] ^ g[3] ^ ... ^ g[n-1]
b[2] =               g[2] ^ g[3] ^ ... ^ g[n-1]
b[3] =                      g[3] ^ ... ^ g[n-1]
...
b[n-1] =                                 g[n-1]

数据同步

如果一个时钟域直接去采样另一个时钟域的数据,可能会采样到数据的亚稳态,采样到的亚稳态数据,随着时间延长,亚稳态数据可能逐渐稳定为高电平,也可能逐渐稳定为低电平,也有可能依旧处于亚稳态。这时候的采样数据是无效的,直到下一次采样才能拿到有效数据。如下图:

首先,跨时钟域采样, 采样到亚稳态的情况是无法避免的 ,亚稳态数据是无法做逻辑运算的,为了防止亚稳态的传播,采用了两级同步电路,也就是两个寄存器级联,可以有效的防止亚稳态的传播。如下图红色框内的两级同步电路,rd_clk域的done0信号, 经过wr_clk域的两级寄存器同步,done2数据为亚稳态的概率几乎为零

上面的两级同步电路,只是 解决了亚稳态的传播,但是无法保证同步数据的正确性 。如下图,在a_clk时钟上升沿的驱动下,a_data电平拉低,同时b_clk上升沿进行采样,此时采样到数据处于亚稳态,经过第二级同步寄存器,数据处于稳定状态,但是为高电平,所以同步数据错误,直到下一拍才获取正确值低电平。

跨时钟域数据传输

上面解决了亚稳态的传播,但是无法保证同步数据的正确性,所以需要特有的跨时钟域数据传输电路。如下图:data由wr传向rd,蓝色为wr时钟域,橙色为rd时钟域,中间的read和done信号需要跨时钟域的同步。操作流程:

  1. 当外部wr和wr_data到来时,更新寄存器read0和data,这些是在wr时钟域完成的。
  2. read0经过rd时钟域的两级同步,得到read2。
  3. read2有效,则等待外部rd请求,当外部rd请求有效时,更新寄存器done0。
  4. done0经过wr时钟域的两级同步,得到done2。
  5. done2有效,则等待外部wr请求,进行下一次数据传输。

上文已经说过,两级同步电路,只是解决了亚稳态的传播,但是无法保证同步数据的正确性。那么read2或done2数据错误会发生什么?首先read和done都是单比特信号,如果发生错误,也就是导致有效高电平此时为无效低电平,无法进行wr或rd。但是在下一拍会将正确的有效高电平传输到read2或done2,可进行正常的wr或rd。总结, 由于两级同步电路不能保证ready和done传输的正确性,数据data可能会迟到一拍传输到另一个时钟域,但是数据data不会发生错误

跨时钟域数据传输的解耦

异步fifo

上文介绍的跨时钟域数据传输电路是根据ready和done信号完成的握手协议,可以避免在数据未读走时再次写入。这样电路结构简单,但是对于快时钟域向慢时钟域的数据连续传输,会严重影响传输性能,为了解决此问题,引入fifo,对读写时钟域进行解耦。如下图:

写时钟域

  1. 起始写指针指向0,wr时钟域的读指针也指向0,fifo非full,可接收写请求。
  2. wr有效,根据写指针将数据写入fifo,同时更新写指针(加1)。
  3. 将rd时钟域的读指针同步到wr时钟域。
  4. 根据写指针和同步到wr时钟域的读指针,判断fifo是否full。
  5. 如果full,反馈给外部电路,不再发送写请求,如果非full,等待下一次写请求。

读时钟域

  1. 起始读指针指向0,rd时钟域的写指针也指向0,fifo empty,不可接收读请求,等待fifo写入数据。
  2. 将wr时钟域的读指针同步到rd时钟域。
  3. 根据读指针和同步到rd时钟域的写指针,判断fifo是否empty。
  4. 如果empty,反馈给外部电路,不再发送写请求,如果非empty,等待写请求。
  5. rd有效,根据读指针从fifo读出数据,同时更新读指针(加1)。
  6. 根据读指针和同步到rd时钟域的写指针,判断fifo是否empty。
  7. 如果empty,反馈给外部电路,不再发送写请求,如果非empty,等待写请求。

指针编码

根据上文描述,读写指针在进行跨时钟域同步,这里读写指针的功能类似之前的done和ready。我们知道,两级同步电路,只是解决了亚稳态的传播,但是无法保证同步数据的正确性,如果像done和ready单比特数据的同步,发生错误的代价是延迟一拍传输,但不会造成功能性错误。显然指针不是单比特数据,例如地址从11跳转到00时,经过同步电路后的数据可能是最新值00,可能是原始值11,也有可能发生错误10或01,这对wr时钟域的full判断和rd时钟域的empty判断,会造成灾难性错误。那么如何保证在发生同步数据错误时不导致功能性错误?答案是采用格雷码。

格雷码的特点是相邻两个数据之间只要一比特变化(00 01 11 10),也就是指针的加一,只会改变一个比特,这在经过同步电路后的数据无非两个结果,一是最新值,二是原始值。例如地址从11跳转到10时,只有低位比特发生跳变,有可能采样到亚稳态并最终发生同步错误。那么同步后的值可能为最新值10,或原始值11。这对wr时钟域的full判断和rd时钟域的empty判断,其代价就是延迟一拍。

总结

通过在跨时钟域数据传输电路中添加fifo结构,可以实现两个时钟域的解耦,也就是wr时钟域只负责数据写入fifo,rd时钟域只负责从fifo读出数据, fifo深度越大,两个时钟域相关性越小 。(异步fifo的使用是解决跨时钟域问题最典型的方法,对于由高频向低频进行连续数据传输时,可显著提高传输性能)

fifo的真实读写指针是rd时钟域的rd_ptr0和wr时钟域的wr_ptr0,两者代表作fifo真实的空满。但是wr时钟域在做full判断时,需要将rd_ptr0进行两级同步到rd_ptr2,rd时钟域在做empty判断时与其类似。由于两级同步电路的存在,以及同步错误(延迟一拍)的存在,wr时钟域的rd_ptr2可能不等于读时钟域的rd_ptr0,这时候根据rd_ptr2和wr_ptr0做出的full信号,可能是 虚满虚空 与其类似。但是 虚空虚满只会延迟传输时间,并不影响传输功能

补充问题

同步器之前的组合逻辑

如果在两级同步电路之前添加组合逻辑,会产生额外的毛刺或增加不稳定性,影响一个时钟域的同步寄存器对另一个时钟域的采样。如下图:

异步复位同步释放

跨时钟域数据传输中的异步复位问题,坚持一个原则,同一个时钟域的触发器中D和reset来自同一个时钟域(每个时钟域有自己的reset信号,这涉及异步复位同步释放问题)。

  • 异步复位:复位信号可以理解为一个普通的数据信号,它只有在时钟的跳变沿才会其作用,一般只要复位信号持续时间大于一个时钟周期,就可以保证正确复位。
  • 同步复位:复位可以在任何时候发生,表面上看跟时钟没有关系,但真实情况是异步复位也需考虑时钟跳变沿,因为时钟沿变化和异步复位都可以引起Q端数据变化。如果时钟跳变沿期间异步复位信号发生变化,Q端可能发生亚稳态现象。

同步复位虽然解决了当时钟的有效沿来临的时候rst_n的边沿也正好来临所出现的冒险与竞争。但是从综合的电路上可以看出,多了一个组合逻辑MUX。如果设计中所有的复位都是这样的,那会增加很多的资源,导致芯片面积很大。那么有没有更好的解决办法呢?答案是有,那就是异步复位同步释放机制。

  • 异步复位同步释放:异步复位,同步释放就是指在复位信号到来的时候不受时钟信号的同步,而是在复位信号释放的时候受到时钟信号的同步。

如下图,单独看System框的复位策略,是一个异步复位电路,即复位信号有效时不管时钟信号是否处于有效沿,输出都会被复位。但是如果复位信号在时钟信号的上升沿发生时,这时候的输出就是亚稳态。reset_gen框中电路图是实现异步复位同步释放的关键(两级同步电路)。

关于毛刺

电路中的毛刺是无法避免的,原因是一个组合逻辑的数据结果,可能与多个数据源以及多个操作路径有关,数据源到来时间不一致,或者操作路径间的长短不一样,都会使组合逻辑的数据结果产生毛刺,同步电路会保证这些毛刺产生在时钟周期内,换句话说,在时钟沿采样时,组合逻辑的数据结果是稳定的最终结果。

而异步时序中,采样时机无法保证,这时毛刺就会造成采样数据错误。解决方法还是通过格雷码,即使发生采样错误不会导致功能错误。

异步fifo中读写地址多bit跳变

  • 问题说明:慢时钟域同步快时钟域时候,在慢时钟域的一个周期内,经历多次快时钟域的地址更新,那么对应的格雷码就会有多个bit发生变化,此场景会不会影响地址同步的正确性?

  • 答案:不会! 这里的多bit跳变是在偷换概念,问题中的多bit跳变指的是快时钟域地址同步到慢时钟域时,地址的多次加一。这不会影响电路功能,格雷码解决的问题是慢时钟域向快时钟域采样时刻,保证地址单bit跳变,这里快时钟域的地址也是在快时钟的沿触发下依次加一的,所以采样时不会发生多bit跳变。(换句话说,多bit跳变是指,读时钟域的读地址或写时钟域的写地址,在沿触发前后的地址变化)

非2次幂fifo深度的处理

前面所说的fifo深度都是2次幂,在wrap around时能够保持格雷码特性,也就是单bit跳变。那么如何实现任意深度的异步fifo呢?

这时候我们利用格雷码的另一个特性,那就是对称性。如下图4 bit格雷码为例,以中间为对称的两个数,也保持了单bit跳变的特性,所以我们可以利用这个特性来实现任意深度的异步fifo。但是对于地址更新逻辑和空满判断逻辑需要另外处理。

先来看常规深度为8的异步fifo,地址更新采用+1即可,空满判断逻辑使用高位[3]和低位地址是否相同来实现:

再来看深度为7的异步fifo,地址更新依然采用+1,但是要注意从1开始到14结束,对14加1要跳变为1,空判断逻辑与深度为8时逻辑相同,但是满判断逻辑需要通过地址差是否为7来实现:


文章原创,可能存在部分错误,欢迎指正,联系邮箱 cao_arvin@163.com。