一、慢时钟域到快时钟域
1、单bit 信号的跨时钟域同步
module single_bit_sync(
input sys_clk,
input sys_rst,
input Asynch_in,
output synch_out );
reg Asynch_in_1;
reg Asynch_in_2;
assign synch_out=Asynch_in_2;
always @(posedge sys_clk or posedge sys_rst) begin
if(sys_rst) begin
Asynch_in_1 <=1'b0;
Asynch_in_2<=1'b0;
else begin
Asynch_in_1 <=Asynch_in;
Asynch_in_2<=Asymch_in_1; //此处为二级同步器,可以解决跨时钟域的亚稳态问题;
end
end
2、多bit信号的跨时钟域同步(选择合适的时刻来采集信号,而不用同步多位宽的数据信号,以便节省硬件资源),具体分以下三种情况:
1)有input_en信号,且频率相差比较小
module delay_sample(
input rstn,
input clk1,
input [7:0] din,
input input_en,
input clk2,
output [7:0] dout,
output output_en);
reg input_en_r1, input_en_r2, input_en_r3;
always @(posedge clk2 or negedge rstn) begin
if() begin
input_en_r1<=0;
input_en_r2<=0;
input_en_r3<=0;
end else begin
input_en_r1<=input_en;
input_en_r2<=input_en_r1;
input_en_r3<=input_en_r2;
end
end
wire input_en_pos = input_en_rw && !input_en_r3;
reg [7:0] dout_r;
reg dout_en_r;
always @(posedge clk2 or negedge rstn) begin //dout_r 信号输出delay
if(!rstn) dout_r <='b0;
else if (input_en_pos)
dout_r <=din;
end
always @(posedge clk2 or negedge rstn) begin //dout_en_r 信号输出delay
if(!rstn) dout_en_r <='b0;
else
dout_en_r <=input_en_pos;
end
assign dout=dout_r;
assign dout_en= dout_en_r;
2) 有input_en信号,且频率相差较大
此时可以设置一个计数器,再input_en 的上升沿附近开始计数,根据频率比确定counter计数达到多少时,input_en有效期的中间时刻附近,并再此时对数据进行采样;
3)无input_en信号
此时可以在快时钟对慢时钟域clk的边沿进行检测,
--->若两个clk 相差比较小,需要对数据进行延迟缓存,以便保证采集到的时当拍时钟的数据(频率接近时,第一个clk边沿不容易被采集到,可能到下一个数据有效期间才能被采样到,所以应该将上一个数据延迟到此刻才能正确采样)
--->若两个clk相差比较大,数据采样时刻可以通过计数方式获取,而不用对数据及逆行缓存;
module test(
input rstn,
input clk1,
input [7:0] din,
//input input_en,
input clk2,
output [7:0] dout,
output dout_en);
reg clk1_r1 ;
reg clk1_r2 ;
reg clk1_r3 ;
//在clk2快时钟域内对clk1时钟边沿进行采样,会获取到同步到clk2快时钟域内的边沿信号clk1_pos;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) begin
clk1_r1 <= 0;
clk1_r2 <= 0;
clk1_r3 <= 0;
end
else begin
clk1_r1 <= clk1;
clk1_r2 <= clk1_r1;
clk1_r3 <= clk1_r2;
end
end
wire clk1_pos = clk1_r2 && !clk1_r3 ;
//delay counter
reg [5:0] cnt ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
cnt <= 0 ;
//每检测到新的慢时钟上升沿并且din_en有效,那么就预示着新的数据到来了,就要重新计数了
else if (clk1_pos)
cnt <= 0 ;
else if (cnt != 6'b111111)
cnt <= cnt + 1'b1 ;
else
cnt <= cnt ;
end
//sync data
reg [7:0] dout_r ;
reg dout_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_r <= 'b0 ;
else if ( cnt == 33)
dout_r <= din ;
end
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_en_r <= 1'b0 ;
else if ( cnt == 33)
dout_en_r <= 1'b1 ;
else
dout_en_r <= 1'b0 ;
end
assign dout = dout_r ;
assign dout_en = dout_en_r ;
endmodule
二、快时钟域到慢时钟域
1、single-bit 同步
module test ( input clka, input clkb, input rst, input d_in, output reg d_out ); reg req_a; reg req_a_1; reg req_a_2; reg req_a_3; reg ack_b; reg ack_b_1; reg ack_b_2; always @(posedge clkb or negedge rst) begin//将req_a同步到慢时钟域,告诉慢时钟域,数据已经准备好 if(!rst) begin req_a_1 <= 'b0; req_a_2 <= 'b0; req_a_3 <= 'b0; end else begin req_a_1 <= req_a; req_a_2 <= req_a_1; req_a_3 <= req_a_2; end end always @(posedge clkb or negedge rst) begin if(!rst) begin d_out <= 'b0; end else if (req_a_2 == 1'b1) begin //表示慢时钟域收到了这个信号 d_out <= req_a_2 & (~req_a_3); end else begin d_out <= d_out; end end always @(posedge clkb or negedge rst) begin//发送ack,告诉慢时钟域,数据已经读取结束 if(!rst) begin ack_b <= 'b0; end else if(req_a_2 == 1'b1) begin ack_b <= 'b1; end else begin ack_b <= 'b0; end end always @(posedge clka or negedge rst) begin//将慢时钟域发出的ack信号同步到快时钟域,表示读取结束 if(!rst) begin ack_b_1 <= 'b0; ack_b_2 <= 'b0; end else begin ack_b_1 <= ack_b; ack_b_2 <= ack_b_1; end end always @(posedge clka or negedge rst) begin//快时钟将数据放到总线上,然后发送req信号继续提醒 if(!rst) begin req_a <= 'b0; end else if(d_in == 1'b1)begin req_a <= 1'b1; end else begin req_a <= 'b0; end end always @(posedge clka or negedge rst) begin if(!rst) begin req_a <= 'b0; end else if (ack_b_2 == 1'b1) begin req_a <= 'b0; end else begin req_a <= req_a; end end endmodule
2、多bit 同步
module test ( input clka, input clkb, input rst, input [7:0]d_in, output reg [7:0]d_out ); reg req_a; reg req_a_1; reg req_a_2; reg ack_b; reg ack_b_1; reg ack_b_2; reg [7:0]d_in_line; always @(posedge clkb or negedge rst) begin//将req_a同步到慢时钟域,告诉慢时钟域,数据已经准备好 if(!rst) begin req_a_1 <= 'b0; req_a_2 <= 'b0; end else begin req_a_1 <= req_a; req_a_2 <= req_a_1; end end always @(posedge clkb or negedge rst) begin//检测到req_a,将读取需要传输的数据 if(!rst) begin d_out <= 'b0; end else if (req_a_2 == 1'b1) begin d_out <= d_in_line; end else begin d_out <= d_out; end end always @(posedge clkb or negedge rst) begin//发送ack,告诉慢时钟域,数据已经读取结束 if(!rst) begin ack_b <= 'b0; end else if(req_a_2 == 1'b1) begin ack_b <= 'b1; end else begin ack_b <= 'b0; end end always @(posedge clka or negedge rst) begin//将慢时钟域发出的ack信号同步到快时钟域,表示读取结束 if(!rst) begin ack_b_1 <= 'b0; ack_b_2 <= 'b0; end else begin ack_b_1 <= ack_b; ack_b_2 <= ack_b_1; end end always @(posedge clka or negedge rst) begin//快时钟将数据放到总线上,然后发送req信号继续提醒 if(!rst) begin req_a <= 'b0; end else if(d_in_line != 'b0)begin req_a <= 1'b1; end else begin req_a <= 'b0; end end always @(posedge clka or negedge rst) begin if(!rst) begin d_in_line <= 'b0; end else begin d_in_line <= d_in; end end always @(posedge clka or negedge rst) begin if(!rst) begin req_a <= 'b0; end else if (ack_b_2 == 1'b1) begin req_a <= 'b0; end else begin req_a <= req_a; end end endmodule