跨时钟域同步方法
对于1bit信号,一般来说都是控制信号,方法有
a) 如果是慢时钟域到快时钟域,采用两级触发器同步来抑制亚稳态传播,如果快时钟域只要求输出一个时钟周期的有效信号,那么可以采用边沿检测电路;
b) 如果是快时钟域到慢时钟域,需要将快时钟域的信号展宽保证慢时钟可以才到,可以采用计数、状态机、握手协议来完成,但这些的前提都是快时钟域的控制信号是不连续的,一般都能满足(如果不行就采用FIFO);也可以采用窄脉冲检测电路或脉冲同步器。
对于多比特的信号,一般来说是数据信号,方法有
a) 如果是非连续信号(数据变化速率低于接收时钟),可以采用握手协议或者DMUX
b) 如果是连续信号,则必须采用FIFO
1、基础知识
传播延迟(Tpd)
从第一个触发器的输出,传播到第二个触发器的输入所花费的时间;
解决高传播延迟的方法:
1、降低时钟频率
2、将逻辑分解为多个阶段(流水线)
建立时间(Tsu,setup):在时钟上升沿到达之前,输入到触发器稳定所需的时间。
保持时间(Th,hold):在时钟沿之后,输入到触发器稳定所需的最短时间。
假设Tco表示触发器时钟有效到数据输出的时间;Tpd表示传播延迟,Tsu表示建立时间,不考虑时钟偏斜,那么最小时钟周期为:
Tmin = Tco + Tpd + Tsu;
2、单比特跨时钟域传输
- 慢时钟 -> 快时钟
!!! 在快时钟域中“打两拍”
- 快时钟 -> 慢时钟
!!!展宽信号
为了正确保证满足建立时间和保持时间,延长脉冲使之在慢速时钟域中至少占用2个时钟周期。因此应该将100 MHz脉冲扩展到至少8个时钟周期;
module Sync_Pulse(
input clka,
input clkb,
input rst_n,
input pulse_ina,
output pulse_outb,
output signal_outb
);
//-------------------------------------------------------
reg signal_a;
reg signal_b;
reg signal_b_r;
reg signal_b_rr;
reg signal_a_r;
reg signal_a_rr;
//-------------------------------------------------------
//在clka下,生成展宽信号signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a <= 1'b0;
end
else if(pulse_ina == 1'b1)begin
signal_a <= 1'b1;
end
else if(signal_a_rr == 1'b1)
signal_a <= 1'b0;
else
signal_a <= signal_a;
end
//-------------------------------------------------------
//在clkb下同步signal_a
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b <= 1'b0;
end
else begin
signal_b <= signal_a;
end
end
//-------------------------------------------------------
//在clkb下生成脉冲信号和输出信号
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b_r <= 'b0;
signal_b_rr <= 'b0;
end
else begin
signal_b_rr <= signal_b_r;
signal_b_r <= signal_b;
end
end
assign pulse_outb = ~signal_b_rr & signal_b_r;
assign signal_outb = signal_b_rr;
//-------------------------------------------------------
//在clka下采集signal_b_rr,生成signal_a_rr用于反馈拉低signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a_r <= 'b0;
signal_a_rr <= 'b0;
end
else begin
signal_a_rr <= signal_a_r;
signal_a_r <= signal_b_rr;
end
end
endmodule
3、多比特跨时钟域传输(握手同步)
module test(
input clka,
input clkb,
input rst_n,
input [3:0] data_in,
input req,
output reg [3:0] data_out,
output data_vld
);
reg en_a;
reg en_a_r,en_a_rr;
reg en_b;
reg en_b_r,en_b_rr;
always @(posedge clka or negedge rst_n)begin
if(~rst_n)begin
en_a <= 1'd0;
end
else if(req)
en_a <= 1'd1;
else if(en_a_rr)
en_a <= 1'd0;
end
always @(posedge clkb or negedge rst_n)begin
if(~rst_n)begin
en_b <= 1'd0;
end
else
en_b <= en_a;
end
always @(posedge clkb or negedge rst_n)begin
if(~rst_n)begin
en_b_r <= 1'd0;
en_b_rr <= 1'd0;
end
else begin
en_b_r <= en_b;
en_b_rr <= en_b_r;
end
end
assign data_vld = en_b_r && ~en_b_rr;
always @(posedge clkb or negedge rst_n)begin
if(~rst_n)begin
data_out <= 4'd0;
end
else if(data_vld)
data_out <= data_in;
end
always @(posedge clka or negedge rst_n)begin
if(~rst_n)begin
en_a_r <= 1'd0;
en_a_rr <= 1'd0;
end
else begin
en_a_r <= en_b;
en_a_rr <= en_a_r;
end
end
endmodule
仿真
module tb(
);
reg clka ;
reg clkb ;
reg rst_n ;
reg en ; //来自于外部的使能信号,脉冲持续一个时钟周期
reg [3:0] data_in ; //外部输入信号
wire [3:0] data_out ;
wire data_vld ;
initial begin
clka = 1'd0;
forever begin
#2 clka = ~clka;
end
end
initial begin
clkb = 1'd0;
forever begin
#3 clkb = ~clkb;
end
end
initial begin
rst_n = 1'd0;
data_in = 4'd0;
en = 0;
@(negedge clka);
rst_n = 1'd1;
@(posedge clka);
en = 1;
@(posedge clka);
en = 0;
end
always@(posedge clka or posedge rst_n) begin
if(~rst_n) begin
data_in <= 4'd0;
end
else if(en) begin
data_in <= $random;
end
end
test test(
.clka (clka ) ,
.clkb (clkb ),
.rst_n (rst_n ) ,
.data_in (data_in ),
.req (en ) ,
.data_out (data_out ) ,
. data_vld( data_vld)
);
endmodule
学习链接 https://cloud.tencent.com/developer/article/1801288