文章主要是基于学习后的总结。

1. 时钟域

假如设计中所有的触发器都使用一个全局网络,比如FPGA的主时钟输入,那么我们说这个设计只有一个时钟域。假如设计有两个输入时钟,如图1所示,一个时钟给接口1使用,另一给接口2使用,那么我们说这个设计中有两个时钟域。

2. 亚稳态

触发器的建立时间和保持时间在时钟上升沿左右定义了一个时间窗口,如果触发器的数据输入端口上数据在这个时间窗口内发生变化(或者数据更新),那么就会产生时序违规。存在这个时序违规是因为建立时间要求和保持时间要求被违反了,此时触发器内部的一个节点(或者要输出到外部的节点)可能会在一个电压范围内浮动,无法稳定在逻辑0或者逻辑1状态。换句话说,如果数据在上述窗口中被采集,触发器中的晶体管不能可靠地设置为逻辑0或者逻辑1对应的电平上。所以此时的晶体管并未处于饱和区对应的高或者低电平,而是在稳定到一个确定电平之前,徘徊在一个中间电平状态(这个中间电平或许是一个正确值,也许不是)。如图2所示,这就是所谓的亚稳态。

一般解决信号亚稳态有三种方法:

  1. 相位控制

    相位控制技术可以在一个时钟频率是另外一个时钟的数倍,并且其中一个时钟可以由FPGA 内部PLL 或者DLL 控制时使用。
  2. 多级寄存器

    一般针对单bit控制信号跨越两个异步时钟域传输,可以采用多级寄存器,俗称多打拍。同步电路中的第一拍后也许会产生亚稳态,但是信号有机会在其被第二级寄存以及被其它逻辑看到之前稳定下来。常用的就是对单bit信号打两拍,这也是最简单、最常见的处理方式。
  3. 异步FIFO缓存

    一般用于跨时钟域传输数据,写端和读端分别对应两个时钟域,由空/满信号控制着读写过程,实现数据的跨域传输。

每种方法应对的情况不同,下面着重介绍最常用的单bit信号消除亚稳态的方法:多级触发器法。


3. 多级寄存器处理

在全同步设计中,如果信号来自同一时钟域,各模块的输入不需要使用寄存器来寄存。只要满足建立时间和保持时间的约束,可以保证在时钟上升沿到来时,输入信号已经稳定,可以采样得到正确的值。但是如果要采用输入信号的边沿来触发某一过程,则需要寄存来检测上升沿,这是另外一个范畴的问题。

一般而言单bit信号就是我们所用到的脉冲信号或者电平信号。假设A和B是两个时钟域,各自的频率是clk_a和clk_b,clk_a的频率高于clk_b(同频相位差稳定的,不在讨论范围内),那么单bit信号传输分为两种情况。

3.1 信号从B到A(慢到快)

在时钟域B下的脉冲信号pulse_b在时钟域A看来,是一个很宽的“电平”信号会,保持多个clk_a的时钟周期,所以一定能被clk_a采到。经验设计采集过程必须寄存两拍。第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少亚稳态带来的影响。一般来说两级是最基本要求,如果是高频率设计,则需要增加寄存级数来大幅降低系统的不稳定性。也就是说采用多级触发器来采样来自异步时钟域的信号,级数越多,同步过来的信号越稳定。

特别需要强调的是,此时pulse_b必须是clk_b下的寄存器信号,如果pulse_b是clk_b下的组合逻辑信号,一定要先在clk_b先用D触发器(DFF)抓一拍,再使用两级DFF向clk_a传递。这是因为clk_b下的组合逻辑信号会有毛刺,在clk_b下使用时会由setup/hold时间保证毛刺不会被clk_b采到,但由于异步相位不确定,组合逻辑的毛刺却极有可能被clk_a采到。一般代码设计如下:

always @ (posedge clk_a or negedge rst_n)
begin
if (rst_n == 1'b0)
begin
pules_a_r1 <= 1'b0;
pules_a_r2 <= 1'b0;
pules_a_r3 <= 1'b0;
end
else
begin //打3拍
pules_a_r1 <= pulse_b;
pules_a_r2 <= pules_a_r1;
pules_a_r3 <= pules_a_r2;
end
end assign pulse_a_pos = pules_a_r2 & (~pules_a_r3); //上升沿检测
assign pulse_a_neg = pules_a_r3 & (~pules_a_r2); //下降沿检测
assign pulse_a = pules_a_r2;

实际上,具体打几拍背后是有时序收敛的理论作支撑的,对于一般的设计而言,打两三拍就已经足够了。

3.2 信号从A到B(快到慢)

如果单bit信号从时钟域A到时钟域B,那么存在两种不同的情况,传输脉冲信号pulse_a或传输电平信号level_a。实际上,在一般情况下只有电平信号level_a的宽度能被clk_b采集到才可以保证系统正常工作。那么对于脉冲信号pulse_a采取怎样的处理方法呢?可以用一个展宽信号来替代pulse_a实现垮时钟域的握手。

主要原理就是先把脉冲信号在clk_a下展宽,变成电平信号signal_a,再向clk_b传递,当确认clk_b已经“看见”信号同步过去之后,再清掉signal_a。代码通用框架如下:

module Sync_Pulse (
clk_a,
clk_b,
rst_n,
pulse_a_in, pulse_b_out,
b_out
);
/****************************************************/ input clk_a;
input clk_b;
input rst_n;
input pulse_a; output pulse_b_out;
output b_out; /****************************************************/ reg signal_a;
reg signal_b;
reg signal_b_r1;
reg signal_b_r2;
reg signal_b_a1;
reg signal_b_a2; /****************************************************/
//在时钟域clk_a下,生成展宽信号signal_a
always @ (posedge clk_a or negedge rst_n)
begin
if (rst_n == 1'b0)
signal_a <= 1'b0;
else if (pulse_a_in) //检测到到输入信号pulse_a_in被拉高,则拉高signal_a
signal_a <= 1'b1;
else if (signal_b_a2) //检测到signal_b1_a2被拉高,则拉低signal_a
signal_a <= 1'b0;
else;
end //在时钟域clk_b下,采集signal_a,生成signal_b
always @ (posedge clk_b or negedge rst_n)
begin
if (rst_n == 1'b0)
signal_b <= 1'b0;
else
signal_b <= signal_a;
end
//多级触发器处理
always @ (posedge clk_b or negedge rst_n)
begin
if (rst_n == 1'b0)
begin
signal_b_r1 <= 1'b0;
signal_b_r2 <= 1'b0;
end
else
begin
signal_b_r1 <= signal_b; //对signal_b打两拍
signal_b_r2 <= signal_b_r1;
end
end
//在时钟域clk_a下,采集signal_b_r1,用于反馈来拉低展宽信号signal_a
always @ (posedge clk_a or negedge rst_n)
begin
if (rst_n == 1'b0)
begin
signal_b_a1 <= 1'b0;
signal_b_a2 <= 1'b0;
end
else
begin
signal_b_a1 <= signal_b_r1; //对signal_b_r1打两拍,因为同样涉及到跨时钟域
signal_b_a2 <= signal_b_a1;
end
end assign pulse_b_out = signal_b_r1 & (~signal_b_r2);
assign b_out = signal_b_r1; endmodule

这样一来,实际上clk_a下的脉冲信号“作用”到了clk_b时钟域下,它对于clk_a与clk_b的时钟频率关系没有任何限制,快到慢,慢到快就都没问题了。

总而言之,在设计中可以简单的牢记以下五条原则:

1. 再全局时钟的跳变沿最可靠。

2. 来自异步时钟域的输入需要寄存一次以同步化,再寄存一次以减少亚稳态带来的影响。

3. 不需要用到跳变沿的来自同一时钟域的输入,没有必要对信号进行寄存。

4. 需要用到跳变沿的来自同一时钟域的输入,寄存一次即可。

5. 需要用到跳变沿的来自不同时钟域的输入,需要用到3个触发器,前两个用以同步,第3个触发器的输出和第2个的输出经过逻辑门来判断跳变沿。

3.3 设计分区同步器模块

在顶层为设计分区是一个好的设计实践行为,这样任何功能模块外面都包含一个独立的同步器模块。这样有利于在划分模块的基础上实现所谓的理想时钟域情况(即整个设计模块只有一个时钟),如下图所示:

对设计进行分区有很多理由。首先,对每个独立的功能模块进行时序分析变得简易,因为模块都是完全的同步设计。其次,整个同步模块中的时序例外也很容易得到定义。再次,底层模块的同步器加时序例外在代入到设计顶层时,大大降低了由于人为失误造成的疏漏。所以,同步寄存器应该在功能模块外单独分区。


参考:

Verilog基本电路设计之一(单bit跨时钟域同步)

异步时钟的同步化,俗称“慢打一拍",寄存一拍

高级FPGA设计:第六章 时钟域(译文)

FPGA跨时钟域处理方法的更多相关文章

  1. FPGA基础学习(3) -- 跨时钟域处理方法

    文章主要是基于学习后的总结. 1. 时钟域 假如设计中所有的触发器都使用一个全局网络,比如FPGA的主时钟输入,那么我们说这个设计只有一个时钟域.假如设计有两个输入时钟,如图1所示,一个时钟给接口1使 ...

  2. FPGA跨时钟域握手信号的结构

    FPGA跨时钟数据传输,是我们经常遇到的问题的,下面给出一种跨时钟握手操作的电路结构.先上图 先对与其他人的结构,这个结构最大的特点是使用 req 从低到高或者高到低的变化 来表示DIN数据有效并开始 ...

  3. 基于FPGA的跨时钟域信号处理——专用握手信号

    在逻辑设计领域,只涉及单个时钟域的设计并不多.尤其对于一些复杂的应用,FPGA往往需要和多个时钟域的信号进行通信.异步时钟域所涉及的两个时钟之间可能存在相位差,也可能没有任何频率关系,即通常所说的不同 ...

  4. FPGA中亚稳态相关问题及跨时钟域处理

    前言 触发器输入端口的数据在时间窗口内发生变化,会导致时序违例.触发器的输出在一段时间内徘徊在一个中间电平,既不是0也不是1.这段时间称为决断时间(resolution time).经过resolut ...

  5. 异步FIFO跨时钟域亚稳态如何解决?

    跨时钟域的问题:前一篇已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域 ...

  6. cdc跨时钟域处理-结绳握手法

    参考文档 https://blog.csdn.net/u011412586/article/details/10009761 前言 对于信号需要跨时钟域处理而言,最重要的就是确保数据能稳定的传送到采样 ...

  7. 跨时钟域设计【一】——Slow to fast clock domain

    跨时钟域设计是FPGA设计中经常遇到的问题,特别是对Trigger信号进行同步设计,往往需要把慢时钟域的Trigger信号同步到快时钟域下,下面是我工作中用到的慢时钟域到快时钟域的Verilog HD ...

  8. 跨时钟域设计【二】——Fast to slow clock domain

    跨时钟域设计中,对快时钟域的Trigger信号同步到慢时钟域,可以采用上面的电路实现,Verilog HDL设计如下:   // Trigger signal sync, Fast clock dom ...

  9. 【iCore、iCore2、iBoard例程】【异步FIFO跨时钟域通信(通过ARM 读FPGA FIFO)】

    欢迎访问电子工程师学堂,以便了解更多内容:http://www.eeschool.org 一.本实验基于iCore2 完成,通过简单改动,即可用在 iCore 核心板.iBoard 电子学堂上. iC ...

随机推荐

  1. slf4j+log4j在Java中实现日志记录

    小Alan今天来跟大家聊聊开发中既简单又常用但必不可少的一样东西,那是什么呢?那就是日志记录,日志输出,日志保存. 后面就统一用日志记录四个字来形容啦. 日志记录是项目的开发中必不可少的一个环节,特别 ...

  2. AmCharts 对数据排序后展示

    在官网看到的例子 给chart添加一个排序功能的handler AmCharts.addInitHandler( function(chart){ if (chart.orderByField === ...

  3. Unity编辑器下重启

    我们项目AssetBundle打包走的是全自动化流程,打包之前要进行各种资源检测,如果检测顺利通过,则进入打包,否则提示错误资源名称及路径,打包中断!有时候即使资源检测通过也会打包崩溃,初步断定是Un ...

  4. NodeJS 初学之安装配置环境

    [TOC] 1.环境安装 操作系统: Ubuntu 16.04.2 LTS 1.1安装nvm ryan@ryan-900X5L:~/temp$ curl https://raw.githubuserc ...

  5. Android 发展史

    1. 概述 http://zh.wikipedia.org/wiki/Android# 2. Android版本发布表 Time Code name Version API level  2014年1 ...

  6. Java集合源码分析(一)ArrayList

    前言 在前面的学习集合中只是介绍了集合的相关用法,我们想要更深入的去了解集合那就要通过我们去分析它的源码来了解它.希望对集合有一个更进一步的理解! 既然是看源码那我们要怎么看一个类的源码呢?这里我推荐 ...

  7. JavaScript命令模式

    第一,命令模式: (1)用于消除调用者和接收者之间直接的耦合的模式,并且可以对(调用这个过程进行留痕操作) (2)真的不要乱用这个模式,以为他使你简单调用写法变得非常的复杂和有些难以理解. (3)你的 ...

  8. 细品 - 逻辑回归(LR)

    1. LR的直观表述 1.1 直观表述 今天我们来深入了解一个人见人爱,花见花开,工业界为之疯狂,学术界..额,好像学术界用的不多哎.不过没关系,就算学术界用的不多也遮不住它NB的光芒,它就是LR模型 ...

  9. LeetCode 205. Isomorphic Strings (同构字符串)

    Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...

  10. Call From master/192.168.128.135 to master:8485 failed on connection exception: java.net.ConnectException: Connection refused

    hadoop集群搭建了ha,初次启动正常,最近几天启动时偶尔发现,namenode1节点启动后一段时间(大约10几秒-半分钟左右),namenode1上namenode进程停掉,查看日志: -- :: ...