多比特跨时钟域的处理,并不能像单比特跨时钟域的处理那样,简单的使用打拍处理。根本原因在于:每个寄存器的位置不同,布局布线和逻辑的不同会导致每比特数据到达下一级寄存器的延时不同,而且延时会随着打拍数的增加、数据位宽的增加、时钟频率的增大而变得更加恶劣,所导致时钟采样的数据不对,从而导致系统出错。
下面看一下多比特跨时钟域的处理流程:
根据图中可以看出,针对不同的应用场景,有不同的处理方法:
(1)如果多比特信号为静态数据,可以不用跨时钟域处理。这里的静态数据,是指某些场景下,数据传输的实时性并不是非常苛刻,则可以认为是静态数据。比方说,一个可编程时钟分频电路,由分频系数和使能信号这两个跨时钟域信号控制,系统在时钟域 A 下配置分频系数和使能信号,时钟域 B 作为时钟源获取分频系数和使能信号,这时,可以通过系统先配置分频系数再配置使能信号,控制分频电路的输出,此时分频系数这个多比特信号就不需要作跨时钟域处理。当然,还要很多类似的应用,可以根据实际情况分析评判。
(2)如果每个时钟都有数据,或者传输的数据需要缓存,则使用异步 FIFO(源语或者 GUI 生成 FIFO);
(3)如果传输的数据是计数器(递增或者递减),则可以通过格雷码来处理;
(4)如果多比特的数据不必在同一个周期接收,则可以使用 XPM_CDC_ARRAY_SINGLE,例如一个电路的使能信号和选择信号组成的多比特数据,二者的有效电平并不会同时出现,这时可以使用该源语;反之,则使用握手信号处理多比特信号的跨时钟域。
下面分别介绍上述几种多比特跨时钟域处理的方法。
一、格雷码转换
使用场景:连续变化的多 bit 信号。
通过编码的方式将多位信号转化为每次只有一位变化的信号,将“多比特”的跨时钟域变换成“单比特”进行处理。例如,将 CLKA 的数据转为格雷码,然后再将此格雷码进行打两拍,之后在 CLKB 将格雷码恢复成原始数据。
assign Gray = (Bin >> 1) ^ Bin; always @ (*)begin Bin[length-1] = Gry[length-1]; for(i = length - 2; i >= 0; i = i-1) Bin[i] = Bin[i+1]^Gry[i]; //感觉这样的组合逻辑很长,有没有优化的方法? end
二、格雷码+异步双口 RAM
使用场景:无限制。尤其在有大量的数据需要进行跨时钟域传输, 并且对数据传输速度要求比较高的场合 。
假设我们现在有一个信号采集平台,ADC 芯片提供源同步时钟 60MHz,而 FPGA 内部需要使用 100MHz 的时钟来处理 ADC 采集到的数据。需要 100MHz 的时钟对 RAM 的写地址进行判断,当写地址大于某个值之后再去读取 RAM。
将 RAM 的写地址转为格雷码,然后再将写地址的格雷码进行打两拍,之后再在 RAM 的读时钟域将格雷码恢复成原始地址。
三、异步 FIFO
使用场景:无限制。
一个异步 FIFO 一般由如下部分组成:
- Memory, 作为数据的存储器;
- 写逻辑部分,主要负责产生写信号和地址;
- 读逻辑部分,主要负责产生读信号和地址;
- 地址比较部分,主要负责产生 FIFO 空、满的标志。
异步 FIFO 融合了寄存器同步,格雷码编码,握手控制等处理方式,是处理跨时钟域数据传输最常用的方式。
四、握手处理
使用场景:带 en 脉冲信号的多比特数据做跨时钟域。
所谓握手,是指通信双方使用了专用控制信号进行数据收发的状态指示。这个控制信号既有发送域给接收域的,也有接收域给发送域的,有别于前面的单向控制信号检测方式。类似于 DMA 与外设模块的通信,会用到 dma_req,dma_ack。
本质上和单 bit 握手同步一样,A_EN 和 B_EN 就是单 bit 信号。当 B_EN 同步之后,Data 也就同步了。不同之处在于 CLKB 的 ACK 信号由 B2_q 换成了下图中的 ACK,显然是因为收到了 Data_B 才能反馈。
缺点:实现较为复杂,效率不高,在对设计性能要求较高的场合应该慎用。
五、MUX 同步器
使用场景:带数据有效标志信号的多比特数据做跨时钟域。
(1) 对单比特的数据有效标志信号在 clkB 时钟域打两拍;
(2) 将同步后的数据有效标志信号作为多路选择器的选通信号,由于 data enable A 的时序等同于 data bus,也就保证了 data bus 已经对齐。
//时钟域 a 下同步本地数据及其有效标志信号,改善时序 always@(posedge clka or posedge rst) begin if(rst) begin reg_data_enable_a <= 1'b0 ; reg1_data_bus_a <= 4'd0 ; end else begin reg_data_enable_a <= data_enable_a ; reg1_data_bus_a <= data_bus ; end end //将数据有效标志信号同步到 b 时钟域,两级同步器 always@(posedge clkb or posedge rst) begin if(rst) begin data_enable_b_mid <= 1'b0 ; data_enable_b <= 1'b0; end else begin data_enable_b_mid <= reg_data_enable_a; data_enable_b <= data_enable_b_mid; end end assign data_bus_mux = data_enable_b ? reg1_data_bus_a : data_bus_b; always@(posedge clkb or posedge rst) begin if(rst) begin data_bus_b <= 4'b0; end else begin data_bus_b <= data_bus_mux; end end
其实 MUX 充当的作用就是触发器的一个使能信号。判断源时钟域的单比特信号是否在目的时钟域成功同步,如果是,那么这个时间长度下多比特信号也可以同步过来,而不违背建立时间。
六、总结
多比特数据跨时钟域的本质,最终也要转化成单比特跨时钟域来处理,对于 MUX/DUMX 我们知道通过对数据有效标志信号的同步,来间接实现数据的同步,那双口 RAM 和异步 FIFO 呢?对于双口 RAM 来说,通过读地址实现数据的同步,而读地址什么时候有效、什么时候自加需要有控制信号,这个控制信号就作为两边时钟域同步的桥梁,它往往是单比特的,判断写的状态,控制读的开始。而异步 FIFO 也是如此,异步 FIFO 通过另一种巧妙的设计,虽然数据是随机的,但是地址却是连续的,此时可以借助格雷码的特点通过同步 2^n 的地址来实现数据的同步,将地址转成成格雷码后就转换为仅变化一位的单比特跨时钟域情况,地址同步后,数据也就跟着同步了。
扫码关注尚为网微信公众号
原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/1274.html