现在的芯片(比如SOC,片上系统)集成度和复杂度越来越高,通常一颗芯片上会有许多不同的信号工作在不同的时钟频率下。比如SOC芯片中的CPU通常会工作在一个频率上,总线信号(比如DRAM BUS)会工作在另一个时钟频率下,而普通的信号又会工作在另外的时钟频率下。这3个不同时钟频率下工作的信号往往需要相互沟通和传递信号。不同时钟域下的信号传递就涉及到跨时钟域信号处理。
亚稳态:数据无法在规定的时间段内达到一个稳定的状态
亚稳态的发生原因:
- 1、数据:数据传输中不满足D触发器的
建立时间Tsu
和保持时间Th
要求- 2、复位:复位过程中复位信号的释放相对于有效时钟沿的
恢复时间(recovery time)
和移除时间(removal time)
不满足
亚稳态
主要发生在异步信号采集、跨时钟域信号传输以及异步复位电路等常用设计中
亚稳态输出不确定,但是会传给后一级触发器,这会导致后级电路出错,所以亚稳态危害很大
1、有些情况下,一个信号在跨越时钟域之后又分为了多个分支。比如:一个使能的控制信号分别使能后续的多个模块
恢复recovery时间
和移除removal时间
要求,会导致亚稳态
//Synchronized Asynchronous Reset
//异步复位、同步释放:只适用于没有PLL的系统复位信号的设置
module sync_async_reset(clock,reset_n,rst_n);
input clock, reset_n;
output rst_n;
reg rst_nr1;//打一拍
reg rst_nr2;//打两拍
always @(posedge clock or negedge reset_n) begin
if(!reset_n) begin
rst_nr1 <= 1'b0;
rst_nr2 <= 1'b0; //异步复位(一旦复位信号有效,就拉低)
end
else begin
rst_nr1 <= 1'b1;
rst_nr2 <= rst_nr1; //同步释放(打两拍,再拉高(释放))
end
end
assign rst_n = rst_nr2; //新的系统复位信号rst_n
//信号rst_n作为新的系统复位信号,后续可以用来直接“异步复位”
endmodule // sync_async_reset
//单比特电平信号
module single_cdc(
input clk1,
input clk2,
input rst_n,
input signal_in,
output signal_out
);
reg signal_out_r; //打一拍
reg signal_out_rr; //打两拍
always @(posedge clk2 or negedge rst_n) begin
if(!rst_n) begin
signal_out_r <= 1'b0;
signal_out_rr <= 1'b0;
end
else if(signal_in == 1'b1) begin
signal_out_r <= signal_in;
signal_out_rr <= signal_out_r;
end
else begin
signal_out_r <= 1'b0;
signal_out_rr <= 1'b0;
end
end
assign signal_out = signal_out_rr;
endmodule
//单比特脉冲信号:慢到快
module single_cdc(
input clk1,
input clk2,
input rst_n,
input signal_in,
output reg signal_out
);
reg signal_out_r; //打一拍
reg signal_out_rr; //打两拍
reg signal_out_rrr; //边沿检测电路
always @(posedge clk2 or negedge rst_n) begin
if(!rst_n) begin
signal_out_r <= 1'b0;
signal_out_rr <= 1'b0;
end
else if(signal_in == 1'b1) begin
signal_out_r <= signal_in;
signal_out_rr <= signal_out_r;
signal_out_rrr <= signal_out_rr;
end
else begin
signal_out_r <= 1'b0;
signal_out_rr <= 1'b0;
signal_out_rrr <= 1'b0;
end
end
//组合逻辑(与逻辑)输出脉冲
assign signal_out = signal_out_rr && !signal_out_rrr;
endmodule
这里有一个平均失效间隔时间MTBF(Mean Time Between Failure)的考虑。 MTBF即触发器采样失败的时间间隔,MTBF时间越长,出现亚稳态的概率就越小,但是也不能完全避免亚稳态。过一级DFF,相当于乘了一个MTBF,也就是说,每过一级DFF,MTBF就会变大,亚稳态概率就会变小。
1、从慢时钟域到快时钟域的单比特数据传输,用两级D触发器同步的方法就可以解决; 但从快时钟域到慢时钟域呢?用两级DFF同步的方法已经无法满足了(慢时钟域仍然无法采集到信号),此时我们就引入了脉冲展宽信号,把快时钟域的信号多稳定一段时间,等到慢时钟域采到了,再拉低,如此便能保证数据的跨时钟域传输。
2、具体实现:
3、在快时钟域的一个单脉冲信号signal_in
,当快时钟域时钟采集到该信号为1
时拉高singal_a
,以展宽该脉冲,方便慢时钟域采样;展宽后的脉冲通过慢时钟域进行采集(过两级DFF)得到singal_b_r
。紧接着利用singal_b_r
进行两个操作:①、拉低快时钟域的展宽信号singal_a
,表示慢时钟域已经采集到该脉冲;②、再次打拍后进行边沿检测,在慢时钟域输出该单脉冲。
4、代码:
module led(
input clk_fast,
input clk_slow,
input rst_n,
input signal_in,
output signal_out
);
//快时钟域脉冲展宽
reg signal_a;
always @(posedge clk_fast or negedge rst_n) begin
if(!rst_n)
signal_a <= 1'b0;
else if(signal_in == 1'b1)//拉高
signal_a <= signal_in;
else if(signal_a_rr == 1'b1)//拉低
signal_a <= 1'b0;
//其它情况,保持上一时刻的值,这里可以省略
end
//慢时钟域采集脉冲展宽信号
//信号同步不用加判断条件
reg signal_b; //打一拍
reg signal_b_r; //打两拍
always @(posedge clk_slow or negedge rst_n) begin
if(!rst_n) begin
signal_b <= 1'b0;
signal_b_r <= 1'b0;
end
else begin
signal_b <= signal_a;
signal_b_r <= signal_b;
end
end
//快时钟域采集慢时钟域返回信息:signal_b_r
reg signal_a_r; //打一拍
reg signal_a_rr; //打两拍
always @(posedge clk_fast or negedge rst_n) begin
if(!rst_n)
{signal_a_rr,signal_a_r} <= {2{1'b0}};
else
{signal_a_rr,signal_a_r} <= {signal_a_r,signal_b_r};//左边给左边,右边给右边
end
//慢时钟域边沿检测,得到脉冲信号
//与逻辑检测上升沿:signal_b_r为1,且signal_b_rr为0
reg signal_b_rr; //上升沿检测,将signal_b_r打一拍
always @(posedge clk_slow or negedge rst_n) begin
if(!rst_n)
signal_b_rr <= 1'b0;
else
signal_b_rr <= signal_b_r;
end
assign signal_out = signal_b_r && (!signal_b_rr);
endmodule
`timescale 1ns/1ns
module tb_led();
reg rst_n;
reg clk_fast;
reg clk_slow;
reg pulse_in;
wire pulse_out;
led u1
(
.rst_n (rst_n ),
.clk_fast (clk_fast ),
.clk_slow (clk_slow ),
.signal_in (pulse_in ),
.signal_out(pulse_out)
);
initial
begin
clk_fast = 1;
clk_slow = 0;
rst_n = 0;
pulse_in = 0;
repeat(5)@(posedge clk_slow);
rst_n = 1;
repeat(5)@(posedge clk_slow);
pulse_gen();
end
task pulse_gen;
begin
pulse_in <= 1;
@(posedge clk_slow);
pulse_in <= 0;
end
endtask
always #10 clk_slow = ~clk_slow;
always #5 clk_fast = ~clk_fast;
endmodule
常用于异步FIFO中读写地址的跨时钟域传递!
0111变成1000
,直接打拍,如果发生亚稳态,则可能变成1111、1001、1100
等中间态(因为每个比特都会发生改变),这些中间态,很有可能造成已经写满、读空的状态下没有及时判断成写满、读空,发生数据的覆盖或者重复读出;0111变成1000
,转换成格雷码是从0100变成1100(注意只有最高位发生了改变)
,如果发生亚稳态,也只会变成0111
(因为每次只改变一位信号:本例中是最高位发生变化(可以变成0或1)),这只会导致在写满、读空之前判断成写满、读空,而不会造成该空满而未报空满的情况。从0100变成1100
,如果写时钟域采到的是0100
,则1100
是实际的读指针,0100
是同步到写时钟域的读指针,数据安全写入的标准是:同步到写时钟域的读指针应该小于等于实际的读指针(这样当写指针追了一圈,又追上读指针时,写满标志才是安全的),而此时0100
显然比1100
要小,因此数据写入是安全的;从0100变成1100
,如果读时钟域采到的是0100
,则1100
是实际的写指针,0100
是同步到读时钟域的写指针,数据安全读出的标准是:同步到读时钟域的写指针应该小于等于实际的写指针(这样当读指针追上写指针时,读空标志才是安全的),而此时0100
显然比1100
要小,因此数据读出是安全的。2^n
,否则就不能用格雷码加双DFF的方式跨时钟域传输
0000、0001、0011、0010
,则首0000
和尾0010
之间,只有1bit不同;0000、0001、0011
,则首0000
和尾0011
之间,有2bit不同,因此不能用双DFF方式1、握手协议将多比特数据的传输问题转换成单个信号的跨时钟域问题(只对请求信号REQ和应答信号ACK进行同步);
2、握手协议可以只对请求信号REQ和应答信号ACK进行同步,在请求信号REQ有效期间,发送端的数据保持不变,因此握手协议可以满足并行数据传输安全的需要假设发送端:clk_a;接收端:clk_b
3、握手机制相关信号:
代码:
module led(
input clk_a,
input clk_b,
input rst_n,
input a_en,
input [3:0] data_in,
output b_en,
output reg [3:0] data_out
);
//a_en下降沿检测(与逻辑)
reg a_en_d1;
wire a_en_neg;
always @(posedge clk_a or negedge rst_n) begin
if(!rst_n)
{a_en_neg,a_en_d1} <= {2{1'b0}};
else
a_en_d1 <= a_en;
end
assign a_en_neg = a_en_d1 && !a_en;
//a时钟域发出请求信号
reg req_a;
//ack信号打两拍同步到a时钟域
reg ack_a_r;
reg ack_a_rr;
always @(posedge clk_a or negedge rst_n) begin
if(!rst_n)
req_a <= 1'b0;
else if(a_en_neg)
req_a <= 1'b1;
else if(ack_a_rr)//拉低
req_a <= 1'b0;
//其它情况,保持上一时刻的值,这里可以省略
end
//请求信号打两拍同步到b时钟域
reg req_b_r;
reg req_b_rr;
always @(posedge clk_b or negedge rst_n) begin
if(!rst_n)
{req_b_rr,req_b_r} <= {2{1'b0}};
else
{req_b_rr,req_b_r} <= {req_b_r,req_a};
end
//检测到a时钟域发出的请求信号的上升沿,则b时钟域可以接收数据
assign b_en = req_b_r && !req_b_rr;
//b时钟域接收数据
always @(posedge clk_b or negedge rst_n) begin
if(!rst_n)
data_out <= 'b0;
else if(b_en)
data_out <= data_in;
end
//b时钟域接收完数据,发送ack信号,打两拍同步到a时钟域
always @(posedge clk_a or negedge rst_n) begin
if(!rst_n)
{ack_a_rr,ack_a_r} <= {2{1'b0}};
else
{ack_a_rr,ack_a_r} <= {ack_a_r , req_b_rr};
end
endmodule
//DMUX
module led(
input clk_a,
input clk_b,
input rst_n,
input a_en,
input [3:0] data_in,
output reg [3:0] data_out
);
//a时钟域使能信号同步到b时钟域,作为MUX的sel
reg a_en_r;
reg a_en_rr;
always @(posedge clk_b or negedge rst_n) begin
if(!rst_n)
{a_en_rr,a_en_r} <= {2{1'b0}};
else
{a_en_rr,a_en_r} <= {a_en_r,a_en};
end
//二选一MUX
always @(posedge clk_b or negedge rst_n) begin
if(!rst_n)
data_out <= 'b0;
else if(a_en_rr == 1'b1)//如果使能信号有效
data_out <= data_in;
else //如果使能信号无效
data_out <= data_out;
end
endmodule