1 概述
本小节主要讲解 Verilog 语法的跨时钟域设计,需要掌握跨时钟域时快慢时钟之间信号是如何同步的。
2 跨时钟域慢速到快速时钟
由慢时钟到快时钟的信号传递,就传输的信号位宽一般分为两种,单比特信号和多比特信号。下面我们分开进行讨论。
2.1 单比特信号
上一节课同步异步设计有提到慢时钟到快时钟的设计方法,一般分析,快时钟域的信号总能采集到慢时钟域的信号,但是如果存在异步时钟域可能会导致采样数据出错,因此需要进行时钟同步处理。同步处理针对单比特信号采取打三拍进行同步。
异步时钟域的信号从一个时钟域进入另一个时钟域之前,将该信号用三级触发器缓存两次,可有效降低因为时序而导致的亚稳态问题。例:
module async_data(
input clk_1, //输入时钟 clk_1
input data_1, //输入数据 data_1
input clk_2, //输入时钟 clk_2
output data_2 //输出数据 data_2
);
reg [2:0] data_ff ;
//寄存器变量 data_ff
always @(posedge clk_2)
//clk_2 敏感 posedge,上升沿触发
begin
data_ff <= {data_ff[1:0], data_1};
//data_1 数据采样
End
//data_ff[0]表示最新采样的数据,!data_ff[1]为上一个时钟周期采样的数据
assign data_2 = !data_ff[2] && data_ff[1];
//最新的电平为 1,之前的电平为 0,判断为上升沿检测
| clk_1 为慢时钟,clk_2 为快时钟,将 data_1 在快时钟域进行三拍缓存,检测信号的上升沿,此时将信号进行了同步化。
2.2 多比特信号
对于多比特信号的传输,我们要考虑两种情况,即单时钟周期突发信号和多时钟周期连续信号的传输。
针对单时钟周期突发信号我们可以采用利用打拍法延迟采样。例:
module async_data_1(
input clk_1, //输入时钟 clk_1
input [7:0] data_in,
//输入数据 data_1
input din_en, //输入突发信号
input clk_2, //输入时钟 clk_2
output reg[7:0] data_out,
//输出数据 data
output reg dout_en
//输出突发数据有效信号
);
reg [2:0] din_en_ff ;
wire din_en_pos;
always @(posedge clk_2 )
begin
din_en_ff <= {din_en_ff[1:0], din_en} ;
//输入突发信号打拍缓存 2 拍
end
assign din_en_pos = !din_en_ff[2] && din_en_ff[1] ; //din_en_ff[1]为新缓存的信号,为 1 时有效,din_en_ff[2]为上一拍缓存的信号
//为 0 时有效,所以 din_en_pos 为捕捉 din_en 信号上升沿
always @(posedge clk_2)
begin
if (din_en_pos)
data_out <= din ;
//din_en_pos 信号拉高,说明数据有效,将输入数据输出
else
;
end
always @(posedge clk_2 )
begin
dout_en <= din_en_pos ;
//输出突发数据有效信号为 din_en_pos 信号
end
endmodule
| 针对多时钟周期连续信号我们可以采用检测慢时钟的边沿进行采样。如果两个时钟的频率相差较小,可能还需要对数据进行延迟缓存,以保证采集到的是当拍时钟的数据;如果两个时钟的频率相差较大,数据采样时刻可以通过计数的方法获得,而不用对数据进行缓存。例:
module async_data_2(
input clk_1,
input [7:0] data_in,
input din_en,
input clk_2,
output reg[7:0] data_out,
output reg dout_en
);
reg [3:0] din_en_ff ;
wire din_edge_pos;
reg [3:0] cnt ;
always @(posedge clk_2 )
begin
din_en_ff <= {din_en_ff[2:0], clk_1} ;
end
assign din_edge_pos = !din_en_ff[2] && din_en_ff[1] ;
always @(posedge clk_2)
//clk_2 为驱动时钟,进行计数
if (din_edge_pos & din_en)
//din_edge_pos 信号为捕捉到上升沿,且 din_en 信号持续拉高
cnt <= 4'h0 ;
else if (cnt != 4'hf)
//计数值为 4'hf
cnt <= cnt + 1'b1 ;
end
always @(posedge clk_2)
//clk_2 为驱动时钟
begin
if (din_en && cnt == 7)
//din_en 信号持续拉高并且完成 7 次计数时,确认数据有效
data_out
<= din ;
//数据完成赋值
else
;
end
always @(posedge clk_2 )
begin
if(din_en && cnt == 8)
//din_en 信号持续拉高并且完成 8 次计数时,确认数据有效
dout_en <= 1 ;
//输出突发数据有效信号进行拉高
else
dout_en <= 0;
end
endmodule
|
3 跨时钟域快速到慢速时钟
由快时钟到慢时钟的信号传递,就传输的信号位宽一般分为两种,单比特信号和多比特信号。下面我们分开进行讨论。
3.1 单比特信号
由快时钟到慢时钟的单比特信号传递,我们需要考虑信号本身是单时钟周期的脉冲信号还是连续多个周期的电平信号。针对单时钟周期的脉冲信号,最好的传送方法是握手法,假设单时钟周期脉冲信号的高电平为有效信号,其基本原理如下。
(1) 快时钟域对脉冲信号进行检测,检测有高电平时输出一个高电平信号。快时钟域输出高电平信号时,保持输出信号为高电平状态。
(2) 慢时钟域对快时钟域的检测信号进行延迟打拍采样。因为此时的脉冲信号被快时钟域保持拉高状态,延迟打拍会采集到该信号。
(3) 慢时钟域确认采样得到高电平检测信号后,再反馈信号给快时钟域。
(4) 快时钟域对反馈信号进行延迟打拍采样。如果检测到反馈信号为高电平,证明慢时钟域已经接收到有效的高电平信号。如果此时快时钟域自身逻辑不再要求脉冲信号为高电平状态,拉低快时钟域的脉冲信号即可。
此方法是通过相互握手的方式对窄脉冲信号进行脉宽扩展。例:
module pulse_fast_2_slow(
input
rst_n,
//复位信号
input
clk_fast,
//快时钟
input
pulse_fast,
//快时钟握手信号
input
clk_slow,
//慢时钟
output
pulse_slow
//慢时钟握手信号
);
wire
clear_up ;
reg
pulse_fast_detect ;
reg [1:0]
pulse_fast_detect_ff ;
reg [1:0]
pulse_slow_2_fast ;
always@(posedge clk_fast or negedge rst_n)
begin
if (!rst_n)
pulse_fast_detect <= 1'b0 ;
//pulse_fast_detect 拉低复位
else if (clear_up)
pulse_fast_detect <= 1'b0 ;
//pulse_fast_detect 拉低复位
else if (pulse_fast)
//在快时钟域中对快时钟握手信号进行抓取
pulse_fast_detect <= 1'b1 ;
//信号抓取后,将 pulse_fast_detect 持续拉高,可能为多个时钟周期,方便慢时钟域捕捉
end
always@(posedge clk_slow or negedge rst_n)
begin
if (!rst_n)
pulse_fast_detect_ff
<= 3'b0 ;
//缓存三拍清零
else
pulse_fast_detect_ff
<= {pulse_fast_detect_ff[0], pulse_fast_detect} ; //在慢时钟域中对 pulse_fast_detect 信号进行抓取
End
//不要求一翻转立刻捕捉到,只要求延时捕捉
assign pulse_slow = pulse_fast_detect_ff[1] ;
//取中间一拍进行输出给快时钟域
always@(posedge clk_fast or negedge rst_n)
begin
if (!rst_n)
pulse_slow_2_fast <= 1'b0 ;
else
pulse_slow_2_fast <= {pulse_slow_2_fast[0], pulse_slow};
//快时钟域对慢时钟握手信号进行捕捉
end
assign clear_up = !pulse_fast && pulse_slow_2_fast[1];
//捕捉到回应,将 pulse_fast_detect 拉低
endmodule
|
3.2 多比特信号
当多位宽数据进行同步时,如果数据变化速率过快,就不能再使用延迟打拍采样的方法。因为此时数据各 bit 位变化的时间参差不齐,用异步时钟进行打拍采样,可能会采集到因路径延迟不同而导致的错误数据。解决此类异步问题的常用方法是采用异步 FIFO (First In First Out)进行数据的交换。异步 FIFO 在后面的一节课专门介绍。
|