单bit信号跨时钟域处理

    跨时钟域在FPGA开发中是比较常见的问题,常见的跨时钟域有单bit信号及多bit信号。笔者这里讲的是单bit信号的跨时钟域处理方法,其实单bit与多bit信号的处理方式有些是通用的,只是资源消耗问题。比如都可以使用异步FIFO或者ram进行异步转换,但是对于单bit信号则显得有些浪费资源。

  对于快转慢信号:可通过扩展信号时间长度来实现,其实就是要保证快时钟域的信号在慢时钟域的边沿能够检测到完整的信号,最好信号长度拓展超过慢时钟周期的一半以上,这样才能确保信号被慢时钟边沿检测到。前提是知道慢时钟大小才能确定要拓展多少的长度。

  对于慢转快信号:慢信号的长度直接超过了快时钟的周期,所以可以直接打两拍进行时钟域同步。

  以上都是最常见而且最简单的跨时钟域处理方式,笔者这里再提下使用原语进行跨时钟域处理的方法。可以根据需要到xilinx官网下载相关的原语资料,这里不对原语进行一一说明。以下是以UltraScale的原语为例,如图目录下红框CDC部分是跨时钟域处理的原语模块。

  根据实际需要使用情况选择相应的原语模块,笔者这里演示的是单bit信号,因此只需使用xpm_cdc_single模块,文档中也有相关的Testbanch File,以下是笔者写的仿真代码及仿真结果。

  1 `timescale    1ns/1ps
  2 module CDC_tb;
  3 
  4     reg            clk1;        //125MHz
  5     reg            clk2;        //100MHz
  6     reg            rstn;
  7     
  8     reg            signal_in1;
  9     reg            signal_in2;
 10     reg    [15:0]  cnt1;
 11     reg    [15:0]  cnt2;
 12     
 13     wire           signal_out1;
 14     wire           signal_out2;
 15     
 16     
 17     initial
 18     begin
 19         clk1 = 0;
 20         clk2 = 0;
 21         rstn = 0;
 22         #200;
 23         rstn = 1;
 24     end
 25     
 26     always #4 clk1 = ~clk1;
 27     always #5 clk2 = ~clk2;
 28 //fast to slow
 29     xpm_cdc_single 
 30     #(
 31         .DEST_SYNC_FF    (2               ),     // DECIMAL; range: 2-10
 32         .INIT_SYNC_FF    (0               ),     // DECIMAL; 0=disable simulation init values, 1=enable simulation init values
 33         .SIM_ASSERT_CHK  (0               ),     // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
 34         .SRC_INPUT_REG   (0               )      // DECIMAL; 0=do not register input, 1=register input
 35     )
 36     u_xpm_cdc_single1 
 37     (
 38         .dest_out        (signal_out1     ),     // 1-bit output: src_in synchronized to the destination clock domain. This output is
 39                                                  // registered.
 40         .dest_clk        (clk2            ),     // 1-bit input: Clock signal for the destination clock domain.
 41         .src_clk         (clk1            ),     // 1-bit input: optional; required when SRC_INPUT_REG = 1
 42         .src_in          (signal_in1      )      // 1-bit input: Input signal to be synchronized to dest_clk domain.
 43     );
 44 //slow to fast    
 45     xpm_cdc_single 
 46     #(
 47         .DEST_SYNC_FF    (2               ),     // DECIMAL; range: 2-10
 48         .INIT_SYNC_FF    (0               ),     // DECIMAL; 0=disable simulation init values, 1=enable simulation init values
 49         .SIM_ASSERT_CHK  (0               ),     // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
 50         .SRC_INPUT_REG   (1               )      // DECIMAL; 0=do not register input, 1=register input
 51     )
 52     u_xpm_cdc_single2 
 53     (
 54         .dest_out        (signal_out2     ),     // 1-bit output: src_in synchronized to the destination clock domain. This output is
 55                                                  // registered.
 56         .dest_clk        (clk1            ),     // 1-bit input: Clock signal for the destination clock domain.
 57         .src_clk         (clk2            ),     // 1-bit input: optional; required when SRC_INPUT_REG = 1
 58         .src_in          (signal_in2      )      // 1-bit input: Input signal to be synchronized to dest_clk domain.
 59     );
 60     
 61     always@(posedge clk1 or negedge rstn)
 62     begin
 63         if(~rstn)
 64         begin
 65             cnt1 <= 'd0;
 66         end
 67         else if(cnt1[15])
 68         begin
 69             cnt1 <= cnt1;
 70         end
 71         else
 72         begin
 73             cnt1 <= cnt1 + 1'b1;
 74         end
 75     end
 76     
 77     always@(posedge clk1 or negedge rstn)
 78     begin
 79         if(~rstn)
 80         begin
 81             signal_in1 <= 'd0;
 82         end
 83         else if(&cnt1[5:0])
 84         begin
 85             signal_in1 <= 1'b1;
 86         end
 87         else
 88         begin
 89             signal_in1 <= 'd0;
 90         end
 91     end
 92     
 93     
 94     always@(posedge clk2 or negedge rstn)
 95     begin
 96         if(~rstn)
 97         begin
 98             cnt2 <= 'd0;
 99         end
100         else if(cnt2[15])
101         begin
102             cnt2 <= cnt2;
103         end
104         else
105         begin
106             cnt2 <= cnt2 + 1'b1;
107         end
108     end
109     
110     always@(posedge clk2 or negedge rstn)
111     begin
112         if(~rstn)
113         begin
114             signal_in2 <= 'd0;
115         end
116         else if(&cnt2[5:0])
117         begin
118             signal_in2 <= 1'b1;
119         end
120         else
121         begin
122             signal_in2 <= 'd0;
123         end
124     end
125     
126 endmodule

   也可以自己写代码实现,为了方便使用,不管快到慢还是慢到快都能兼容,这里使用结绳法也叫握手法。原理就是信号的握手,代码如下:

 1 //**************************************************************************
 2 // *** file name      : Handshake_CDC.v
 3 // *** version        : 1.0
 4 // *** Description    : Handshake_CDC
 5 // *** Blogs          : https://www.cnblogs.com/WenGalois123/
 6 // *** Author         : Galois_V
 7 // *** Date           : 2022.7.11
 8 // *** Changes        : Initial
 9 //**************************************************************************
10 `timescale    1ns/1ps
11 module Handshake_CDC
12 (
13     input                        i_clk1                ,
14     input                        i_rstn                ,
15     input                        i_clk2                ,
16     input                        i_clk1_signal         ,
17     output                       o_clk2_siganl         ,
18     output                       o_clk2_signal_pos    
19 );
20     reg        [2:0]        r_clk1_signal;
21     reg        [2:0]        r_clk2_signal;
22 
23     always@(posedge i_clk1)
24     begin
25         if(~i_rstn)
26         begin
27             r_clk1_signal[0] <= 'd0;
28         end
29         else if(i_clk1_signal)
30         begin
31             r_clk1_signal[0] <= 1'b1;
32         end
33         else if(r_clk1_signal[2])
34         begin
35             r_clk1_signal[0] <= 'd0;
36         end
37     end
38     
39     always@(posedge i_clk1)
40     begin
41         if(~i_rstn)
42         begin
43             r_clk1_signal[2:1] <= 'd0;
44         end
45         else
46         begin
47             r_clk1_signal[2:1] <= {r_clk1_signal[1],r_clk2_signal[1]};
48         end
49     end
50     
51     always@(posedge i_clk2)
52     begin
53         if(~i_rstn)
54         begin
55             r_clk2_signal <= 'd0;
56         end
57         else
58         begin
59             r_clk2_signal <= {r_clk2_signal[1:0],r_clk1_signal[0]};
60         end
61     end
62     assign o_clk2_siganl =  r_clk2_signal[1];
63     assign o_clk2_signal_pos = ~r_clk2_signal[2] & r_clk2_signal[1];
64 endmodule

  虽说写的代码输出信号没原语输出的那么好,但实际运用使用上下边沿同步信号即可。

posted on 2022-07-11 11:34  Galois_V  阅读(746)  评论(0编辑  收藏  举报