如何去实现一种跨时钟域电路的设计?

电子说

1.3w人已加入

描述

时钟域

在一个复杂的SoC(System on Chip)系统中,不可能只有一个时钟。我们一般认为,一个时钟控制的所有寄存器集合处于该时钟的时钟域中。两个时钟域之间是可能有信号交互的,而且两个时钟域的时钟信号都可能有源头、相位以及频率的区别,如果不是同源、同相位以及同频率时钟管理的时钟域,交互信号是不能直接被使用的。主要面临以下几种情况:

时钟的源头不同

比方说时钟域A是工作在振荡器OSC1的输出时钟ClkOsc1下,而时钟域B是工作在振荡器OSC2的输出时钟ClkOsc2下。由于OSC1和OSC2的输出时钟完全不相关,因此无论是频率还是相位都不同,这叫做不同源不同频不同相,如下图所示,DFF1处于ClkOsc1的时钟域A,而DFF2处于ClkOsc2的时钟域B,那么DFF2输入信号如果有来自于DFF1的输出,是不能直接使用的,针对这种跨时钟域的电路设计方法,本节后续部分会做详细描述。

振荡器

时钟源头相同

如下图所示,DFF1、DFF2与DFF3的时钟同样来源于OSC1的输出ClkOsc1,但DFF2的时钟是ClkOsc1经过某时序元件(电路)SEQ1(时序元件可以是寄存器、锁存器、锁相环等)处理后的输出ClkSEQ1。ClkSEQ1可能是ClkOsc1的分频、倍频、门控时钟,在相位和频率上与ClkOsc1都会有区别。

振荡器

因此,DFF1、DFF3工作在ClkOsc1的时钟域A,而DFF2工作在ClkSEQ1的时钟域B,至于其间交互数据,从时钟域A去往时钟域B的LOG12,以及从时钟域B去往时钟A的LOG21,需要根据ClkOsc1和ClkSEQ1之间相位和频率的关系进行相应的设计。针对这种跨时钟域的电路设计方法,本节后续部分会做详细描述。

电平信号转脉冲信号处理电路(边沿检测电路)

电平信号与脉冲信号之间的转换过程,是数字集成电路设计中最基本的信号转换操作。

我们可以将电平信号看成一种状态(status),一种标志(flag),表示当前电路所处于的一个稳定态。比如说busy信号为高,表示该电路处于工作状态。比如说test_mode信号为高,表示要求当前系统工作在测试模式下。

而脉冲信号可以看做是一种触发要求(trigger),一种状态变化的通知(message)。比方说要启动电路工作的状态,给电路一个脉冲信号run。比如某个模块工作完成了,给个脉冲信号finished。

假设某个模块对外只有一个busy状态标志,那么我们怎么知道电路开始工作了,还是工作结束了呢?这就需要将状态标志busy,分别转换成电路启动信号work,以及电路工作完成信号finished,以便告诉系统进行下一步的工作。

下图所示就是产生worked脉冲信号的电路:

振荡器

振荡器

可以看出,busy标志为高的时候,表示电路正在工作,为低的时候,表示电路没有在工作。那么如果busy从低电平转换成高电平,表示电路启动工作了,产生的worked脉冲信号,只有当这种变化出现时才会为高,否则一直为低。这就将电平信号变化转换成了脉冲信号。

Workded表示busy从低到高的一种变化,所以这种电平转脉冲电路我们也可以称之为上升沿采集电路。

以上上升沿采集电路可以用以下Verilog HDL描述:

振荡器

那么finished信号,表示busy从高变成低的状态,电路工作结束,可以采用以下结构实现,当然,也被称之为 下降沿采集电路

振荡器

振荡器

以上下降沿采集电路可以用以下Verilog HDL描述:

振荡器

可能会有同学问,既然我们会选取带异步复位或异步置位(低电平有效)的上升沿触发寄存器,为什么画图的时候,看起来只是一个最简单的D触发器元件。

这主要是因为RTL设计时默认采用同步设计方法,在同一个模块中,所有寄存器都是用同一个时钟源及复位源输入,因此画图的时候做了简化处理,时钟和复位在没有特殊处理要求时都不做描述了。

本人所有电路在没有特别说明的情况下,都会按照该原则进行描述。

脉冲信号转电平信号处理电路

既然电平信号可以转成脉冲信号,表示在某种状态变化的时候要求做一个动作。那么需要因为某种动作的需要,改变一种状态。这就是脉冲转电平信号处理电路。

假设有两个输入信号,一个是开始信号start,一个是停止信号stop,都是脉冲信号。其中start脉冲来了后,要求电路开始工作,run状态寄存器从低电平变为高电平。当stop脉冲来了后,要求电路停止工作,run状态寄存器从高电平变为低电平。

则电路可以按照以下方式设计,当stop为高电平时,把run拉低,当start为高电平时,把run置高,否则run保持:

振荡器

振荡器

以上脉冲转电平电路可以用以下Verilog HDL描述:

振荡器

相信还有很多同学会有一些不同的电路实现方式,但在RTL设计方法学中,有一种通用的数据路径设计方式,用多路选择器(Multiplexer),通过不同的控制信号,选择寄存器下一个周期数据的来源,相对来说,会更切合人类思考的方式,同时对于硬件描述语言来说也更加友好。

至于最终如何实现电路,可以交给逻辑综合工具来处理。后续我们会重点介绍如何利用选择器进行通用电路设计,这也是RTL设计的一种常见方法。

时钟域的概念、电平信号和脉冲信号转换电路,是作为跨时钟信号采集电路设计的基础。跨时钟域的信号采集,一般分为三种情况,慢时钟域到快时钟的单位信号采集、快时钟域到慢时钟域的单位信号采集、跨时钟域多信号采集。

***慢时钟域到快时钟域的单位信号采集电路

慢时钟域到快时钟域的单位信号采集电路,一般用于控制类信号的传递。数据的源寄存器驱动时钟频率低于驱动采集信号寄存器的时钟频率。如下图所示,A时钟域寄存器DFFA,其输出信号I是由ClkA驱动输出的,该信号需要由B时钟域的寄存器采集使用,此时ClkB的频率大于等于ClkA的频率,则可以使用以下电路图来实现采集:

振荡器

如果在B时钟域直接使用信号I,则可能因为ClkA与ClkB的相位不同,由于亚稳态原因,造成B时钟域信号混乱,只需要使用去除亚稳态的电路结构,确保在B时钟域的信号完整可靠即可。以上电路的工作时序图如下所示:

振荡器

这里面Out1和Out2是电平信号输出,通过去除亚稳态电路处理,确保信号在ClkB的完整性,就可以在B时钟域使用了。而Out3是该信号上升沿标志位,也已经在B时钟域同步,也可根据需要选择使用。

以上信号处理电路可以用以下Verilog HDL描述(此处不描述DFFA的电路,因为来自于A时钟域的信号并不一定就是DFFA的输出,也可能是DFFA经过组合逻辑的输出,电路中只是一个参考):

振荡器

同样的电路,用Chisel描述如下:

振荡器

快时钟域到慢时钟域的单位信号采集电路

同样的,快时钟域到慢时钟域的单位信号采集电路,一般也只能用于控制类信号的传递。数据的源寄存器驱动时钟频率高于驱动采集信号寄存器的时钟频率,如果仅仅只是考虑去除亚稳态采集,如果跨时钟域输出信号的有效时间小于一个慢时钟域的时钟周期,就可能根本踩不到。要处理该信号,则同步处理电路相对较为复杂,需要有一个握手的过程:

如下图所示,信号I是由ClkA驱动输出的,该信号需要由B时钟域的寄存器采集使用,此时ClkB的频率大于等于ClkA的频率,可以采用下图所示电路做信号同步采集:

振荡器

整个电路的工作时序如下图所示:

振荡器

需要通过以下几步,确保信号的完整传输:

A. I通过脉冲转电平信号处理电路,转换成电平信号Ilevel_A,并传输出给B时钟域使用,这样做无论I信号是脉冲信号还是电平信号,只要ClrI_A这个清除信号没有到来,则一直会处于有效状态,确保B时钟域能够采集完成。

B. B时钟域将A时钟域传输过来的电平信号Ilevel_A做去除亚稳态处理,并采集使用(Out1,Out2,Out3根据实际需要使用)。在B时钟域采集到有效信号,并使用的同时,通知A时钟域清除Ilevel_A。

C. A时钟域采集到B时钟域清除信号的需求,做亚稳态处理后,清除Ilevel_A。

D. Ilevel_A被清除后,被B时钟域采集,并清除采集到的信号Out1以及Out2。同时通知A时钟域恢复ClrI同步电路的状态。

E. ClrI同步电路恢复状态。

以上信号处理电路可以用以下Verilog HDL描述(此处不描述DFFA的电路,因为来自于A时钟域的信号并不一定就是DFFA的输出,也可能是DFFA经过组合逻辑的输出,电路中只是一个参考):

振荡器

振荡器

同样的电路,用Chisel描述如下:

振荡器

跨时钟域总线数据处理电路

前面介绍了单bit控制数据跨时钟域处理的电路,这种电路的特点就是确保单根线可以在某个时刻稳定传递到另一个时钟域中,虽然电路图中都是打两个时钟周期,但因为可能遇到亚稳态传递的情况,实际传递的过程并不是真的2个时钟周期采集到,可能2个,也可能大于或小于2个。

假设参考慢时钟域到快时钟域传输采样电路,只是简单去亚稳态的,传递多bit数据,则可能造成采集数据在某几个时钟周期发生多次跳动的情况,并不会稳定传递。如下图所示,数据在传输过程中会因为亚稳态传递出一个变化过程,同时传输的bit位宽越大,则越不稳定(如果采用格雷码编码方式传递,在某种应用场景下,看起来是可以使用的):

振荡器

而慢时钟采快时钟的电路就更不能这样设计了。

这样看来,握手的过程仍然是需要的。那么如何握手呢?这个见仁见智,一般有2种基本的原则,一个是时间上的握手,一个是事件上的握手。

时间上的握手,相对比较容易理解,我们知道从A时钟域到达B时钟域数据传递需要一个时间,而这个信号稳定的时间是可控的,那么只要超过这个最大时间,再行采集就可以了。这种电路常见于采集从模拟异步数据处理电路中输出的数据,比如说下图所示的嵌入式Flash这种非时钟控制类元件的输出。

振荡器

A时钟域产生对E-Flash输出控制的使能信号OE,当E-Flash的OE信号被拉高后,需要一个时间,才能从DataOut上稳定输出数据。因此,将OE作为驱动信号实现一个B时钟计数器的使能信号TimerTrigger,使B时钟域的计数器开始计数,比如说B时钟的200个时钟周期,这个时间需要大于E-Flash从OE到DataOut的延时。

计数完成前,B时钟域的采集电路一直保持,不会将数据送出去使用,确保了电路的稳定性,直到计数完成,TimerFinished脉冲有效,才将数据传输出去。同时TimerFinshed脉冲有效又会反馈会A时钟域,以便把OE拉低,准备下一次传输。

因为A时钟域与B时钟域不是同一个时钟域(并不绝对,只是举例),因此OE转换成TimerTrigger的电路以及TimerFinished转换成OE的电路需要做时钟域同步处理,只是一个单bit控制信号的传递,相对来说就比较好设计了。

其实事件信号的握手与时间的握手,从机制上是一致的,主要是考虑一是什么时候数据被送出来了,二是数据是否已经稳定,三是数据是不是已经被采集好,四是数据采集完成后需要恢复初始状态准备下一次数据发送。

第二点,数据是否已经稳定,在时间握手的电路中,就体现在利用计数器实现判断。因此时间的握手,其实也可以看成是一种特殊的事件握手。

如果是判断数据是否稳定采集,原理上可以通过多次比较,比如说如果发生传输,且连续几次收到的数据都是一致的,同时又与发送端数据一致,则认为数据稳定采集。电路的实现方式还是比较灵活的,有兴趣的小伙伴可以自己动手设计一下。

因此如果我们把同步电路分成数据路径和控制路径,那么就不难理解如何进行同步了。数据不能同步,但控制信号是可以同步的。就像下面这幅框图一样:

振荡器

异步FIFO

对于同步电路来说,稳定的传输数据,一定是最重要的。但分析上面提到的同步电路,小伙伴们应该不难发现,如果一次同步工作没有完成,第二次同步需求是会被忽略掉的。那么我们可以采用异步FIFO的电路设计方法,来规避这个缺点。

FIFO,即First In First Out的缩写,意味着先被写进去的,会先被输出来,就像是火车钻山洞那样,火车头先进的山洞,那么也是火车头先出的山洞。

异步FIFO的意思就是FIFO输入和FIFO输出是不同的两个时钟域。一个简单的异步FIFO电路结构可以看下面这幅图:

振荡器

利用异步FIFO可以让时钟较连续的从一个时钟域传输到另一个时钟域。

常见的异步FIFO结构一般有2种,一种是如下读指针和写指针控制同一块存储区域,这种操作,数据就像流水一样,只要写进去,就可以读出来:

振荡器

另外一种是如下读指针和写指针控制不同的存储区域,写数据量较大,且连续的情况下,可以根据一次传输数据总量,先把数据存储在一个区域,下次又来数据的时候,存入另一个区域。而读的区域与写的区域直接分开,完全不受干扰。这种操作,又称为乒乓操作。

振荡器

而FIFO设计中最重要的就是指针的设计,指针类似于一个计数器,随着写或读的次数发生递增或递减,而指针作为存储块的地址信号,即可对存储块(一般是RAM或Register File)不同地址进行操作了:

振荡器

一般来说我们建议使用二进制计数做地址指针,而将其转换成格雷码用于做空满控制,这是因为数据信号直接传输,但控制信号需要同步,转换成为格雷码后,可以确保每次指针增减,都只有1bit数据在发生变换且用于同步,不会在同步过程中产生毛刺。

网上有不少异步FIFO的开源代码,有兴趣的小伙伴可以下载下来进行学习和使用。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分