单比特处理
跨时钟域的信号分为两类,一类是单比特的信号,一类是多比特的信号。
对于单比特信号,可以分为两种情况:
- 慢时钟到快时钟:一般通过加两级触发器的方式处理,即所说的打两拍,另外若多比特数据每次只有一个比特发生变化,也可以采用多级寄存器同步的方法,例如采用格雷码。
- 快时钟到慢时钟:快时钟域的信号脉宽较窄,慢时钟域不一定能采到,可以通过握手机制让窄脉冲展宽,慢时钟域采集到信号后再“告诉”快时钟域已经采集到信号,确保能采集到。
多比特信号处理
对于多比特信号:一般采用异步FIFO、异步双口RAM、握手、格雷码方式处理。
该题为笔试遇到的遇到真题,该题主要考察了多比特信号的处理,且为慢时钟域到快时钟域。
思路:将计数结果换成格雷码,并在快时钟域下打两拍处理。
1.代码实现:
/*
* @Author: yaohao
* @Date: 2022-06-18 10:12:34
* @Last Modified by: yaohao
* @Last Modified time: 2022-06-18 11:03:13
*/
module cross_clock_domain(
input clk0,
input clk1,
input rst1,
input rst0,
output reg [3:0]cnt_o
);
wire [3:0]sum;
reg [3:0]cnt;
assign sum = cnt + 1'd1;
always @(posedge clk0 or negedge rst0) begin //产生一个4bit的计数器
if(!rst0)
begin
cnt <= 4'd0;
end
else begin
cnt <= sum;
end
end
wire [3:0]cnt_g; //定义格雷码形式的cnt
assign cnt_g = cnt ^ (cnt>>1);
reg [3:0] cnt_g1, cnt_g2; //打拍处理cdc问题
always @(posedge clk1 ) begin //将计数结果从clk0同步到clk1
if(!rst1)
begin
cnt_g1 <= 4'd0;
cnt_g2 <= 4'd0;
end
else begin
cnt_g1 <= cnt_g;
cnt_g2 <= cnt_g1;
end
end
always @(posedge clk1 ) begin
if(!rst1)
begin
cnt_o <= 4'd0;
end
else begin
cnt_o <= cnt_g2;
end
end
endmodule
`timescale 1ns/1ns
module tb ();
reg clk0, rst0;
reg clk1, rst1;
wire [3:0]cnt_o;
cross_clock_domain dut(
.clk0(clk0),
.clk1(clk1),
.rst0(rst0),
.rst1(rst1),
.cnt_o(cnt_o)
);
//时钟
initial begin
clk0 = 0;
forever begin
#25 clk0 = ~clk0;
end
end
initial begin
clk1 = 0;
forever #15 clk1 = ~clk1;
end
initial begin
rst0 = 0;
rst1 = 0;
#25;
rst0 = 1;
rst1 = 1;
#500;
$finish();
end
endmodule //cross_clock_domain
2.仿真结果如下:
当计数步进为2时,格雷码每次变化的数不为1,不满足单比特变化,在跨时钟域时容易出问题,使得采样有误。
步进为2时的仿真结果:
单比特信号处理(快到慢)
思路:将脉冲信号进行展宽,然后同步到慢时钟域,再将反馈回快时钟域的信号用来拉低展宽信号。
1.代码实现
/*
* @Author: yaohao
* @Date: 2022-06-18 14:26:23
* @Last Modified by: yaohao
* @Last Modified time: 2022-06-18 15:16:05
*/
module cdc(
input wire clka, //快时钟域
input wire rst_n,
input wire pulse_a,//a 时钟域的脉冲信号
input wire clkb,
output wire out_pulse,//b时钟域检测到的脉冲输出
output wire singal_out//表示展宽以后的脉冲信号
);
reg ext_pulse_a; // 展宽信号a
reg ext_pulse_b;
reg [1:0] ext_pulse_r; //从b反馈回的信号
reg [1:0] pos_pulse_b;//检测同步过来的pulse_b的上升沿
assign singal_out = pos_pulse_b[1];
//在a时钟域下对脉冲信号进行展宽
always @(posedge clka or negedge rst_n)
if(!rst_n)
ext_pulse_a <= 1'b0;
else if(pulse_a)
ext_pulse_a <= 1'b1;
else if(ext_pulse_r[1])// 反馈信号,表明已经成功展宽,则拉低展宽脉冲信号
ext_pulse_a <= 1'b0;
else
ext_pulse_a <= ext_pulse_a;
//将展宽信号同步到b时钟域
always @(posedge clkb or negedge rst_n) begin
if (!rst_n)
ext_pulse_b <= 1'b0;
else
ext_pulse_b <= ext_pulse_a;
end
//将ext_pulse_b同步回a
always @(posedge clka or negedge rst_n) begin
if (!rst_n)
ext_pulse_r <= 2'b0;
else
ext_pulse_r <= {ext_pulse_r[0],ext_pulse_b}; //将ext_pulse_b信号传回a时钟域
end
//检测ext_pulse_b上升沿
always @(posedge clkb or negedge rst_n) begin
if (!rst_n)
pos_pulse_b <= 2'b0;
else
pos_pulse_b <= {pos_pulse_b[0],ext_pulse_b};
end
assign out_pulse = (pos_pulse_b[0] & ~pos_pulse_b[1]);
endmodule
`timescale 1ns/1ps
module tb_cdc();
reg clka;
reg clkb;
reg pulse_a;
wire out_pulse;
reg rst_n;
wire singal_out;
initial begin
clka =0;
clkb =0;
rst_n =0;
pulse_a =0;
#100
rst_n =1;
# 100;
pulse_a =1'b1;
#10;
pulse_a = 1'b0;
#100;
pulse_a =1'b1;
#10;
pulse_a = 1'b0;
end
always #5 clka =~clka;
always #10 clkb = ~clkb;
cdc cdc_inst(
.clka(clka),
.rst_n(rst_n),
.pulse_a(pulse_a),//a 时钟域的脉冲信号
.clkb(clkb),
.out_pulse(out_pulse),//b时钟域检测到的脉冲输出
.singal_out(singal_out)
);
endmodule