跨时钟域传递多比特信号的问题是,在同步多个信号到一个时钟域时将可能偶发数据变化歪斜(Skew),这种数据歪斜最终会在第二个时钟域的不同时钟上升沿上被采集。即便能够完美地控制和匹配这些多比特信号的走线长度,随着芯片衬底工艺不同,上升和下降的时间也会不一样,这些因素都会产生足够的歪斜导致在精心匹配的多条信号上采样失败。
为了避免这种多比特跨时钟域信号上的采样歪斜,需要掌握一些不一样的方法。这些方法大致可以分为以下几种:
一般适用于多个控制信号且控制信号之间有一定逻辑关系,可以合并成单比特信号。
例如接收域有一个寄存器,它需要一个加载(Load)信号和一个使能(Enable)信号来加载一个数值到寄存器。如果加载和使能信号在发送时钟域的同一个时钟沿被驱动有效(即两个控制信号需要同时有效),那么这两个控制信号之间就有可能存在产生一个小歪斜的机会,这就可能导致在接收时钟域中这两个信号被同步到不同的时钟周期。在这种情况下,数据是不能被加载到寄存器的。


解决上述问题的方法非常简单,就是将加载和使能两个控制信号融合成一个单比特控制信号(这两个控制信号本身相同,且同时有效),如图下图所示。


在发送时钟域先后有两个使能信号(注意不同于之前的同时有效),同步到接收时钟域用于控制两级流水寄存器寄存数据。问题是在第一个时钟域中,B_en1控制信号正好稍微在B_en2有效前结束有效,这就导致在接收时钟域时钟上升沿采集B_en1和B_en2脉冲时产生一个细微的缝隙。如图下图所示,

同步后两个使能控制信号间隔了两个时钟周期,而不是流水一个时钟周期。这样导致数据a2没有及时加载到第二个寄存器。这里需要注意的是,a3会在Aq2_en2有效的时候加载到第二个寄存器,但是在设计要求a1,a2,a3的更新符合一个周期的流水,所以设计出现了问题。
首先是在发送时钟域将两个使能控制信号融合为一个控制信号,其次是要增加一个额外的寄存器将同步后的使能控制信号寄存一拍,这样数据和控制信号形成匹配的流水,如下图所示:

MUX同步器为了同步数据,当数据在源触发器准备就绪时,在源时钟域中产生控制脉冲。然后,根据源域和目标域之间的时钟比,使用两个触发器同步器或脉冲同步器(触发或握手)来同步控制脉冲。同步控制脉冲用于在目的域内对总线上的数据进行采样。以下两种情况时可以使用MUX同步器:
以牛客网练习题为例:
描述:在data_en为高期间,data_in将保持不变,data_en为高至少保持3个B时钟周期。表明,当data_en为高时,可将数据进行同步。本题中data_in端数据变化频率很低,相邻两个数据间的变化,至少间隔10个B时钟周期。电路的接口如下图所示。端口说明如下表所示:

实现电路图如下:A时钟域异步复位和B时钟域异步复位未在图中标注

代码实现:
module mux_sync(clk_a, clk_b, arst_n, brst_n, data_in, data_out, data_en);
/********************参数定义********************/
/*********************IO 说明********************/
input wire clk_a ;//A时钟域时钟(发送域)
input wire clk_b ;//B时钟域时钟(接收域)
input wire arst_n ;//A时钟异步复位
input wire brst_n ;//B时钟异步复位
input wire data_en ;//A时钟域有效信号,高电平有效
input wire [3:0] data_in ;//A时钟域数据输入
output reg [3:0] data_out;//B时钟域数据输出
/********************** 内部信号声明 **********************/
reg [3:0] data_in_reg; //输入数据暂存寄存器
reg a_data_en;
reg b_data_en;
reg b_date_en_reg;
/*************************功能定义*************************/
/*数据暂存*/
always@(posedge clk_a or negedge arst_n)
begin
if(!arst_n)
data_in_reg <= 1'b0;
else
data_in_reg <= data_in;
end
//使能信号在A时钟域用一个D触发器暂存
always@(posedge clk_a or negedge arst_n)
begin
if(!arst_n)
a_data_en <= 1'b0;
else
a_data_en <= data_en;
end
//使能信号在B时钟域打两拍(两级电平同步器)
always@(posedge clk_b or negedge brst_n)
begin
if(!brst_n)
begin
b_date_en_reg<= 1'b0;
b_data_en <= 1'b0;
end
else
begin
b_date_en_reg <= a_data_en;
b_data_en <= b_date_en_reg;
end
end
/*根据同步到B时钟域的使能信号b_data_en,更新输出。*/
always@(posedge clk_b or negedge brst_n)
begin
if(!brst_n)
data_out <= 4'b0;
else
begin
data_out <= b_data_en ? data_in_reg : data_out;
end
end
endmodule
RTL视图:

若上题中为B时钟域(接收域)的时钟频率时A时钟域(发送域)的时钟频率的几十倍,甚至上百倍。且data_en为脉冲信号时,data_en在在快时钟域打完几拍的时间相对于慢时钟域是非常短暂的,此时慢时钟域中的多bit数据信号可能还处于冒险中间态,则此时选通进入快时钟域的数据就是“毛刺”。
可以在接收域使用边沿同步器,检测data_en的下降沿,以保证此时的多比特数据一定是稳定的。
实现电路图如下:A时钟域异步复位和B时钟域异步复位未在图中标注

代码实现
module mux_sync(clk_a, clk_b, arst_n, brst_n, data_in, data_out, data_en);
/********************参数定义********************/
/*********************IO 说明********************/
input wire clk_a ;//A时钟域时钟(发送域)
input wire clk_b ;//B时钟域时钟(接收域)
input wire arst_n ;//A时钟异步复位
input wire brst_n ;//B时钟异步复位
input wire data_en ;//A时钟域有效信号,高电平有效
input wire [3:0] data_in ;//A时钟域数据输入
output reg [3:0] data_out;//B时钟域数据输出
/********************** 内部信号声明 **********************/
reg [3:0] data_in_reg; //输入数据暂存寄存器
reg a_data_en;
reg b_data_en;
reg b_date_en_reg;
reg edge_reg;
wire edge_flag;
/*************************功能定义*************************/
/*数据暂存*/
always@(posedge clk_a or negedge arst_n)
begin
if(!arst_n)
data_in_reg <= 1'b0;
else
data_in_reg <= data_in;
end
//使能信号在A时钟域用一个D触发器暂存
always@(posedge clk_a or negedge arst_n)
begin
if(!arst_n)
a_data_en <= 1'b0;
else
a_data_en <= data_en;
end
//使能信号在B时钟域打两拍(两级电平同步器)
always@(posedge clk_b or negedge brst_n)
begin
if(!brst_n)
begin
b_date_en_reg<= 1'b0;
b_data_en <= 1'b0;
end
else
begin
b_date_en_reg <= a_data_en;
b_data_en <= b_date_en_reg;
end
end
//下降沿检测
always@(posedge clk_b or negedge brst_n)
begin
if(!brst_n)
edge_reg <= 1'b0;
else
edge_reg <= b_data_en;
end
assign edge_flag = ~b_data_en & edge_reg;
/*根据同步到B时钟域的使能信号b_data_en,更新输出。*/
always@(posedge clk_b or negedge brst_n)
begin
if(!brst_n)
data_out <= 4'b0;
else
data_out <= edge_flag ? data_in_reg : data_out;
end
endmodule
RTL视图:

多周期路径规划是一种通用的安全传递多比特跨时钟域信号技术。多周期路径规划是指在传输非同步数据到接收时钟域时配上一个同步的控制信号,数据和控制信号被同时发送到接收时钟域,同时控制信号在接收时钟域使用两级寄存器同步到接收时钟域,使用此同步后的控制信号来加载数据,这样数据就可以在目的寄存器被安全加载。 使用这种技术有以下两个好处:
个人感觉多周期路径同步法包含MUX同步器和握手同步方式。其中MUX同步器相当于开环的多周期路径同步。而完全握手方式相当于带确认反馈的多周期路径同步,部分握手方式相当于带反馈的闭环多周期路径同步。
解决总线同步问题最基本的方法是保持寄存器和握手信号。保持寄存器用于进行数据锁存和采样,握手信号指示接收域中的电路何时可以对总线进行采样,以及发送域电路何时可以更新保持寄存器当前的内容。握手协议是一种闭环的数据同步方法,更像是基于MUX同步器的应答机制的扩展,其主要结构如下图所示,

握手同步方式分为完全握手和部分握手两种基本类型。 每种类型的握手都使用上述的结构,但每种握手都有自己的设计权衡。握手信号指示接收域何时可以对总线数据进行采样,以及发送域何时可以更新当前数据锁存器中保存的内容。数据路径主要由发送时钟域的数据锁存模块以及接收时钟域的数据采集模块组成。
完全握手的特点是发送域和接收域两端在发送请求或者终止请求之前都需要等待对端的回应。完全握手在收发两侧都使用的是电平同步器。当接收域需要通知发送域它正在积极处理请求时,通常会采用完全握手。完全握手要求发送域推迟它的下一个请求,直到它检测到无效的确认信号。以下是完全握手的流程和时序:


用前缀t_表示发送域,用前缀r_表示接收域,发送时钟用t_clk表示,接收时钟用r_clk表示。数据由发送域向接收域传输;以下是完全握手的工作步骤:
部分握手协议则可以缩短握手的过程,提高数据传输的效率,但使用部分握手信号时,收发双方不等对方响应就中止各自的请求,并继续执行握手命令序列。部分握手类型比全握手类型在可靠性方面稍弱,因为握手信号并不指示各自电路的状态,收发侧都必须保存状态信息。但是,由于无需等待其他电路的响应,完整的时间周期花费时间较少。有两种部分握手方案,区别在于握手所采用的信号类型。一种所用的部分握手方法在发送侧使用电平信号来发送请求,在接收侧使用脉冲信号来发送响应;另一种所用的部分握手方法在发送侧与接收侧都使用脉冲信号。
在第一种部分握手方式中,接收域对请求信号使用电平同步器,发送域对确认信号使用脉冲同步器。在此握手协议中,应答脉冲仅在接收域检测到请求信号时出现。这允许发送域通过控制其请求信号的时序来控制脉冲进入同步器的间隔。为了确保部分握手同步方式成立,发送域中止请求信号至少持续发送域1个时钟周期长,否则接收域就不能区别前一个请求和新的请求。以下是部分握手1的流程和时序:


用前缀t_表示发送域,用前缀r_表示接收域,发送时钟用t_clk表示,接收时钟用r_clk表示。数据由发送域向接收域传输;以下是部分握手1的工作步骤:
完整的部分握手1在发送域中最多需要3个周期,在接收域中最多需要5个周期。部分握手1在发送域中使用的时钟周期比完全握手少2个时钟周期,在接收域中使用的时钟周期少1个时钟周期。
在第二种部分握手方式中,发送域用一个单时钟宽度脉冲发出它的请求,而接收域也用一个单时钟宽度脉冲响应这个请求。这种情况下,发送域和接受域都需要保存状态,以指示请求正待处理。这种握手类型使用的是脉冲同步器,但如果其中一个电路时钟比另一个电路时钟快两倍,则可以用边沿检测同步器来代替。完整的时序是:发送侧时钟域最多2个时钟周期,接收侧最多3个时钟周期。以下是部分握手2的流程和时序:


用前缀t_表示发送域,用前缀r_表示接收域,发送时钟用t_clk表示,接收时钟用r_clk表示。数据由发送域向接收域传输;以下是部分握手1的工作步骤:

分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加必要的握手信号,保证数据传输不丢失。
模块的接口信号图如下:
data_req和data_ack的作用说明:
data_req表示数据请求接受信号。当data_out发出时,该信号拉高,在确认数据被成功接收之前,保持为高,期间data应该保持不变,等待接收端接收数据。
当数据接收端检测到data_req为高,表示该时刻的信号data有效,保存数据,并拉高data_ack。
当数据发送端检测到data_ack,表示上一个发送的数据已经被接收。撤销data_req,然后可以改变数据data。等到下次发送时,再一次拉高data_req。
输入描述:
clk_a:发送端时钟信号
clk_b:接收端时钟信号
rst_n:复位信号,低电平有效
data_ack:数据接收确认信号
输出描述:
data:发送的数据
data_req:请求接收数据
数据发送模块:
module data_driver(
input clk_a, //发送端时钟信号
input rst_n, //复位信号,低电平有效
input data_ack, //数据接收确人信号
output reg [3:0] data, //发送数据
output reg data_req //请求接收信号
);
/********************参数定义********************/
/*********************IO 说明********************/
/********************** 内部信号声明 **********************/
reg [2:0] cnt_reg;
reg data_ack_sync1;
reg data_ack_sync2;
/*************************功能定义*************************/
//计数
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
cnt_reg <= 3'd0;
else if(data_ack_sync1 && !data_ack_sync2 == 1'b1)
cnt_reg <= 3'd0;
else if(data_req == 1'b1)
cnt_reg <= cnt_reg;
else
cnt_reg <= cnt_reg + 1'b1;
end
//data_ack两级同步
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
begin
data_ack_sync1 <= 1'b0;
data_ack_sync2 <= 1'b0;
end
else
begin
data_ack_sync1 <= data_ack;
data_ack_sync2 <= data_ack_sync1;
end
end
//请求接收信号
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
data_req <= 1'b0;
else if(cnt_reg == 3'd4)
data_req <= 1'b1;
else if(data_ack_sync2 == 1'b1)
data_req <= 1'b0;
else
data_req <= data_req;
end
//发送数据
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
data <= 4'd0;
else if(data == 4'd7 && data_ack_sync2 == 1'b1 && data_req == 1'b1 )
data <= 4'd0;
else
begin
if(data_ack_sync2 == 1'b1 && data_req == 1'b1 )
data <= data + 1'b1;
else
data <= data;
end
end
endmodule
数据接收模块
module data_receiver(
input clk_b, //接收端时钟信号
input rst_n, //复位信号,低电平有效
input [3:0] data, //接收数据
input data_req, //请求接收信号
output reg data_ack//数据接收确人信号
);
/********************参数定义********************/
/*********************IO 说明********************/
/********************** 内部信号声明 **********************/
reg data_req_sync1;
reg data_req_sync2;
/*************************功能定义*************************/
//data_req两级同步
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
begin
data_req_sync1 <= 1'b0;
data_req_sync2 <= 1'b0;
end
else
begin
data_req_sync1 <= data_req;
data_req_sync2 <= data_req_sync1;
end
end
//数据接收确人信号
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
data_ack <= 1'b0;
else if(data_req_sync2 == 1'b1)
data_ack <= 1'b1;
else
data_ack <= 1'b0;
end
endmodule
功能仿真

通过编码的方式将多位信号转化为每次只有一位变化的信号,将“多比特”的跨时钟域变换成“单比特”进行处理。格雷码的特点是相邻的两个编码之间只有1位不同,消除了在同一个时钟沿,多比特信号或者数据同时变化所带来的跨时钟域问题。
应用局限:只有在数据在相数值间连续变化的情况下才有用,不适用于大多数信号传输或者数据传输的情况。一般用于跨时钟域的计数器设计,如异步FIFO的读写地址,其他场合基本没有用到。
数据流的传输与指示信号不同在于:数据流大多具有连续性,及背靠背传输;数据流要求信号具有较快的传输速度。跨时钟域传输数据用得最多的方法就是使用先人先出(FIFO)结构。FIFO可以用于在两个异步时钟域之间传输多比特信号。通常看到的FIFO应用包括在两个标准总线之间传输数据,以及从可突发访问的存储器中读出数据或者对其写入数据。
异步FIFO有两个端口,一个端口写人输入数据,另一个端口读出数据。两个端口工作在相互独立的时钟域内,通过各自的指针(地址)来读写数据。由于每个端口工作在相互独立的时钟域内,因此读写操作可以独立实现并且不会出现任何差错。FIFO有满标志和空标志,当FIFO变满时,应停止写操作,直到FIFO中出现空闲空间;同样,当FIFO为空时,应停止读操作,直到有新的数据被写人FIFO中。异步FIFO结构如下图所示

1.Synchronizer techniques for multi-clock domain SoCs & FPGAs
2.Clock Domain Crossing Techniques for FPGA
3.Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog
4.2021年秋招面经分享·华为【海思·芯片与器件工程师】
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow