1. MUX完成多bit信号跨时钟域传输的原理
这里简单的说明一下,因为网上这方面资料比较多。简单来说,对于多bit信号,不能直接使用打两拍的方式来进行跨时钟域传输,因为各个信号的延时可能不同,打两拍可能会因为延时不同造成错误。
而使用MUX来进行跨时钟域传输,实际是通过一个使能信号去选通数据传输的通路,然后该选通信号是通过跨时钟域单bit信号处理方式进行处理的,首先对使能信号data enable和数据信号data bus在原时钟域打一拍,这是为了保持数据和使能信号的时序尽量一致。再在新时钟域对选通信号打两拍,这一方面是为了完成选通信号的同步,另一方面个人认为是完成使能信号的两拍延迟,从而保证使能信号完成同步后,数据信号已处于稳定,因而能保证数据传输的正确。
个人认为,MUX同步方式更适用于慢时钟域到快时钟域的传输,且数据信号最好持续几个时钟。因为使能信号需在新时钟域打两拍,如果新的时钟域更慢的话,这个两拍的时间对数据的影响可能太大了,甚至使能信号同步完成时数据已经结束了。
2.Verilog实现
时钟:1MHz和10MHz(系统时钟sys_clk为50MHz)。为了更好的仿真,将clk_cnt_1初始值设定为20,来实现时钟相位的不同。
module clk_gen(
input sys_clk,
input sys_rst_n,
output reg clk_1, //1MHz
output reg clk_2 //10MHz
);
reg [5:0] clk_cnt_1;
reg [5:0] clk_cnt_2;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n==1'b0)begin
clk_cnt_1<=5'd20;
clk_cnt_2<=5'd0;
clk_1<=1'b1;
clk_2<=1'b0;
end
else begin
clk_cnt_1<=(clk_cnt_1==6'd50)?6'd0:clk_cnt_1+1'b1;
clk_cnt_2<=(clk_cnt_2==6'd5)?6'd0:clk_cnt_2+1'b1;
clk_1<=(clk_cnt_1==6'd50)?~clk_1:clk_1;
clk_2<=(clk_cnt_2==6'd5)?~clk_2:clk_2;
end
end
endmodule
数据和使能信号的产生,数据每次加111(随便定的数字),使能信号在数据的第一个时钟产生:
module data_gen(
input clk_1,
input rst_n,
output reg [15:0] data,
output valid
);
reg [3:0] cnt;
assign valid=(cnt==4'd0)?1'b1:1'b0;
always@(posedge clk_1 or negedge rst_n)begin
if(!rst_n)begin
data<=16'd0;
cnt<=4'd0;
end
else begin
cnt<=(cnt==4'd10)?4'd0:cnt+1'b1;
data<=(cnt==4'd10)?data+16'd111:data;
end
end
endmodule
跨时钟域实现代码:
module cdc(
input clk_2,
input clk_1,
input [15:0] data,
input valid,
input rst_n,
output reg [15:0] data_out
);
reg valid_reg0,valid_reg1,valid_reg;
reg [15:0] data_reg;
always@(posedge clk_1 or negedge rst_n)begin
if(!rst_n)begin
data_reg<=16'd0;
valid_reg<=1'b0;
end
else begin
data_reg<=data;
valid_reg<=valid;
end
end
always@(posedge clk_2 or negedge rst_n)begin
if(!rst_n)
{valid_reg1,valid_reg0}<=2'd0;
else
{valid_reg1,valid_reg0}<={valid_reg0,valid_reg};
end
always@(posedge clk_2 or negedge rst_n)begin
if(!rst_n)
data_out<=16'd0;
else
data_out<=valid_reg1?data_reg:data_out;
end
endmodule
顶层例化:
module top(
input sys_clk,
input rst_n
);
wire clk_1,clk_2;
wire [15:0] data;
wire valid;
wire [15:0] cdc_data;
cdc u1(
.clk_2(clk_2),
.clk_1(clk_1),
.data(data),
.valid(valid),
.rst_n(rst_n),
.data_out(cdc_data)
);
data_gen u2(
.clk_1(clk_1),
.rst_n(rst_n),
.data(data),
.valid(valid)
);
clk_gen u3(
.sys_clk(sys_clk),
.sys_rst_n(rst_n),
.clk_1(clk_1),
.clk_2(clk_2)
);
endmodule
3.仿真
testbench:
module tb(
);
reg rst_n;
reg sys_clk;
initial begin
rst_n=1'b0;
sys_clk=1'b0;
#100
rst_n=1'b1;
end
always #10 sys_clk=~sys_clk;
top u(
.sys_clk(sys_clk),
.rst_n(rst_n)
);
endmodule
仿真结果,数据被同步到clk_2时钟域: