从处理单bit跨时钟域信号同步问题来入手

在数字电路中,跨时钟域处理是个很庞大的问题,因此将会作为一个专题来陆续分享。今天先来从处理单bit跨时钟域信号同步问题来入手。
01
握手(handshake)是用来处理单bit信号的跨时钟域传递的一个有效的方法。在从快时钟向慢时钟传递时,由于输入信号变化较快,输出一侧可能跟不上输入的变化,从而导致“漏采“现象。
图1 “漏采”现象演示
在图1中,由于两个时钟的速度差距,来自快时钟域的脉冲信号还未到达慢时钟的采样边沿便消失了,导致了“漏采”。
在这种情况下,如何让脉冲信号准确无误地传递过去呢?一个方法是将脉冲信号展宽,待输出一侧检测到信号并将其解析为脉冲信号后,再向输入一侧发送应答信号,表明接收到信号并且传输完成。这个过程被称之为“握手”。
02
先来看一个最基本的握手协议,下边是它的电路图。
图2 “握手”电路演示
在上图中 src_clk和dst_clk分别为输入和输出侧的时钟,src_pulse为输入的脉冲信号,dst_pulse为同步到输出端的脉冲信号。可以看到,脉冲信号被同步到输出端后,输出端便立即向输入端发送了应答信号,表示收到信号。而当输出端的应答信号同步到输入端后,输入端才可以同步下一个信号。以下为这个电路的verilog描述。
module sync_pulse ( input wire src_clk, input wire dst_clk, input wire rst_n, input wire src_pulse, output wire dst_pulse ); reg req_state_dly1, req_state_dly2,dst_req_state,src_sync_req; reg ack_state_dly1,src_sync_ack; wire dst_sync_ack; always @ (posedge src_clk or negedge rst_n) begin if (rst_n == 1'b0) src_sync_req <= 1'b0; else if (src_pulse) src_sync_req <= 1'b1; else if (src_sync_ack) src_sync_req <= 1'b0; else; end always @ (posedge dst_clk or negedge rst_n) begin if (rst_n == 1'b0) begin req_state_dly1 <= 1'b0; req_state_dly2 <= 1'b0; dst_req_state <= 1'b0; end else begin req_state_dly1 <= src_sync_req; req_state_dly2 <= req_state_dly1; dst_req_state <= req_state_dly2; end end assign dst_sync_ack = req_state_dly2; always @ (posedge src_clk or negedge rst_n) begin if (rst_n == 1'b0) begin ack_state_dly1 <= 1'b0; src_sync_ack <= 1'b0; end else begin ack_state_dly1 <= dst_sync_ack; src_sync_ack <= ack_state_dly1; end end assign dst_pulse = dst_req_state & (~req_state_dly2);endmodule03
上述电路虽然可以完整的同步信号,但是若在同步一个脉冲的过程中,输入端又接收到一个输入进来的脉冲,那么此时刚刚输入进来的脉冲将会同步失败。更糟糕的是该电路没有同步失败的反馈,导致使用者误以为正确同步了信号。鉴于此,将上述电路进行改进,当同步失败后将输出src_sync_fail信号来指示同步失败。以下为该电路的verilog描述(此代码来源于网络):
module handshake_pulse_sync( src_clk , //source clock src_rst_n , //source clock reset (0: reset) src_pulse , //source clock pulse in src_sync_fail , //source clock sync state: 1 clock pulse if sync fail. dst_clk , //destination clock dst_rst_n , //destination clock reset (0:reset) dst_pulse //destination pulse out); //para declaration //input declaration input src_clk ; //source clock input src_rst_n ; //source clock reset (0: reset) input src_pulse ; //source clock pulse in input dst_clk ; //destination clock input dst_rst_n ; //destination clock reset (0:reset) //output declaration output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail. output dst_pulse ; //destination pulse out //inter declaration wire dst_pulse ; wire src_sync_idle ; reg src_sync_fail ; reg src_sync_req ; reg src_sync_ack ; reg ack_state_dly1 ; reg ack_state_dly2 ; reg req_state_dly1 ; reg req_state_dly2 ; reg dst_req_state ; reg dst_sync_ack ; //--========================module source code==========================-- //--=========================================-- // dst clock : // 1. generate src_sync_fail; // 2. generate sync req // 3. sync dst_sync_ack //--=========================================-- assign src_sync_idle = ~(src_sync_req | src_sync_ack ); //report an error if src_pulse when sync busy ; always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) src_sync_fail <= 1'b0 ; else if (src_pulse & (~src_sync_idle)) src_sync_fail <= 1'b1 ; else src_sync_fail <= 1'b0 ; end //set sync req if src_pulse when sync idle ; always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) src_sync_req <= 1'b0 ; else if (src_pulse & src_sync_idle) src_sync_req <= 1'b1 ; else if (src_sync_ack) src_sync_req <= 1'b0 ; end always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) begin ack_state_dly1 <= 1'b0 ; ack_state_dly2 <= 1'b0 ; src_sync_ack <= 1'b0 ; end else begin ack_state_dly1 <= dst_sync_ack ; ack_state_dly2 <= ack_state_dly1 ; src_sync_ack <= ack_state_dly2 ; end end //--=========================================-- // dst clock : // 1. sync src sync req // 2. generate dst pulse // 3. generate sync ack //--=========================================-- always @(posedge dst_clk or negedge dst_rst_n) begin if(dst_rst_n == 1'b0) begin req_state_dly1 <= 1'b0 ; req_state_dly2 <= 1'b0 ; dst_req_state <= 1'b0 ; end else begin req_state_dly1 <= src_sync_req ; req_state_dly2 <= req_state_dly1 ; dst_req_state <= req_state_dly2 ; end end //rising edge of dst_state generate a dst_pulse; assign dst_pulse = (~dst_req_state) & req_state_dly2 ; //set sync ack when src_req = 1 , clear it when src_req = 0 ; always @(posedge dst_clk or negedge dst_rst_n) begin if(dst_rst_n == 1'b0) dst_sync_ack <= 1'b0; else if (req_state_dly2) dst_sync_ack <= 1'b1; else dst_sync_ack <= 1'b0; endendmodule从代码可以得知,在同步信号的过程中,src_sync_idle会拉低,此时若再有输入脉冲,则会输出同步失败信号。
仿真结果如下
图3 电路仿真结果
从仿真结果可以看到,该设计达到了同步信号要求。

日产发布新一代纯电动车型,重振品牌形象在此一搏
元宇宙时代,企业展销这么办可节约办展成本80%
汽车总装中螺栓拧紧的常见问题
红米Note7和魅族Note9哪个好
汉和独有的电“冰箱”技术解决无人机电池散热难题
从处理单bit跨时钟域信号同步问题来入手
IoTEE项目推出实现太空物联网的设备
什么是高压脉冲电网 高压脉冲电网工作原理
一种基于互调失真形式的射频连接器
认真对待功能安全和EMC
AI来帮忙:互联互通疏通诊疗链路,落实医疗健康服务
SAP助力丰宝电子,在信息化管理及数字化转型上加强合作
LED驱动电路的浪涌保护设计
惊现黑科技 这个台灯有点意思
TOPPERS主动降噪耳机E2体验 足够满足日常听歌需求
微软Copilot已在全球7500万设备上运行,效率提升达7成
RFID技术如何让饮水更健康
关于AR导航技术的介绍和应用
OpenHarmony开源开发者成长计划寻找改变世界的开源新生力
华为p50pro价格官网价