[X]关闭

15 Verilog语法_跨时钟域设计

文档创建者:uisrc
浏览次数:1459
最后更新:2024-01-07
FPGA基础知识
FPGA基础: FPGA编程语言 » Verilog编程入门
软件版本:无
操作系统:WIN10 64bit
硬件平台:适用所有系列FPGA
登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
本小节主要讲解Verilog语法的跨时钟域设计,需要掌握跨时钟域时快慢时钟之间信号是如何同步的。
2跨时钟域慢速到快速时钟
由慢时钟到快时钟的信号传递,就传输的信号位宽一般分为两种,单比特信号和多比特信号。下面我们分开进行讨论。
2.1 单比特信号
上一节课同步异步设计有提到慢时钟到快时钟的设计方法,一般分析,快时钟域的信号总能采集到慢时钟域的信号,但是如果存在异步时钟域可能会导致采样数据出错,因此需要进行时钟同步处理。同步处理针对单比特信号采取打三拍进行同步。
异步时钟域的信号从一个时钟域进入另一个时钟域之前,将该信号用三级触发器缓存两次,可有效降低因为时序而导致的亚稳态问题。例:
module async_data(
input       clk_1, //输入时钟clk_1
input       data_1,   //输入数据data_1
input       clk_2, //输入时钟clk_2
output      data_2 //输出数据data_2
);

reg [2:0]    data_ff ;   //寄存器变量data_ff
always @(posedge clk_2) //clk_2敏感posedge,上升沿触发
begin  
    data_ff  <= {data_ff[1:0], data_1}; //data_1数据采样
End //data_ff[0]表示最新采样的数据,!data_ff[1]为上一个时钟周期采样的数据
assign data_2 = !data_ff[2] && data_ff[1]; //最新的电平为1,之前的电平为0,判断为上升沿检测


clk_1为慢时钟,clk_2为快时钟,将data_1在快时钟域进行三拍缓存,检测信号的上升沿,此时将信号进行了同步化。
2.2多比特信号
对于多比特信号的传输,我们要考虑两种情况,即单时钟周期突发信号和多时钟周期连续信号的传输。
针对单时钟周期突发信号我们可以采用利用打拍法延迟采样。例:
module async_data_1(
    input               clk_1, //输入时钟clk_1
    input [7:0]         data_in, //输入数据data_1
    input               din_en, //输入突发信号
    input               clk_2, //输入时钟clk_2
    output  reg[7:0]    data_out, //输出数据data
    output  reg         dout_en //输出突发数据有效信号
);

reg [2:0]    din_en_ff ;
wire din_en_pos;

always @(posedge clk_2 )
begin
    din_en_ff  <= {din_en_ff[1:0], din_en} ; //输入突发信号打拍缓存2拍
end

assign din_en_pos = !din_en_ff[2] && din_en_ff[1] ; //din_en_ff[1]为新缓存的信号,为1时有效,din_en_ff[2]为上一拍缓存的信号
//为0时有效,所以din_en_pos为捕捉 din_en信号上升沿
always @(posedge clk_2)
begin
    if (din_en_pos)
        data_out   <= din ; //din_en_pos信号拉高,说明数据有效,将输入数据输出
    else
    ;
end

always @(posedge clk_2 )
begin
    dout_en <= din_en_pos ; //输出突发数据有效信号为din_en_pos信号
end

endmodule


针对多时钟周期连续信号我们可以采用检测慢时钟的边沿进行采样。如果两个时钟的频率相差较小,可能还需要对数据进行延迟缓存,以保证采集到的是当拍时钟的数据;如果两个时钟的频率相差较大,数据采样时刻可以通过计数的方法获得,而不用对数据进行缓存。例:
module async_data_2(
    input               clk_1,
    input [7:0]         data_in,
    input               din_en,
    input               clk_2,
    output  reg[7:0]    data_out,
    output  reg         dout_en
);

reg [3:0]    din_en_ff ;
wire din_edge_pos;
reg [3:0] cnt ;

always @(posedge clk_2 )
begin
    din_en_ff  <= {din_en_ff[2:0], clk_1} ;
end
assign din_edge_pos = !din_en_ff[2] && din_en_ff[1] ;

always @(posedge clk_2) //clk_2为驱动时钟,进行计数
    if (din_edge_pos & din_en) //din_edge_pos信号为捕捉到上升沿,且din_en信号持续拉高
        cnt <= 4'h0 ;
    else if (cnt != 4'hf)     //计数值为4'hf
        cnt <= cnt + 1'b1 ;
end

always @(posedge clk_2) //clk_2为驱动时钟
begin
    if (din_en && cnt == 7) //din_en信号持续拉高并且完成7次计数时,确认数据有效
        data_out   <= din ; //数据完成赋值
    else
    ;
end

always @(posedge clk_2 )
begin
    if(din_en && cnt == 8) //din_en信号持续拉高并且完成8次计数时,确认数据有效
        dout_en <= 1 ; //输出突发数据有效信号进行拉高
    else
        dout_en <= 0;
end
endmodule


3跨时钟域快速到慢速时钟
由快时钟到慢时钟的信号传递,就传输的信号位宽一般分为两种,单比特信号和多比特信号。下面我们分开进行讨论。
3.1 单比特信号
由快时钟到慢时钟的单比特信号传递,我们需要考虑信号本身是单时钟周期的脉冲信号还是连续多个周期的电平信号。针对单时钟周期的脉冲信号,最好的传送方法是握手法,假设单时钟周期脉冲信号的高电平为有效信号,其基本原理如下。
(1) 快时钟域对脉冲信号进行检测,检测有高电平时输出一个高电平信号。快时钟域输出高电平信号时,保持输出信号为高电平状态。
(2) 慢时钟域对快时钟域的检测信号进行延迟打拍采样。因为此时的脉冲信号被快时钟域保持拉高状态,延迟打拍会采集到该信号。
(3) 慢时钟域确认采样得到高电平检测信号后,再反馈信号给快时钟域。
(4) 快时钟域对反馈信号进行延迟打拍采样。如果检测到反馈信号为高电平,证明慢时钟域已经接收到有效的高电平信号。如果此时快时钟域自身逻辑不再要求脉冲信号为高电平状态,拉低快时钟域的脉冲信号即可。
此方法是通过相互握手的方式对窄脉冲信号进行脉宽扩展。例:
module pulse_fast_2_slow(
input                rst_n, //复位信号
input                clk_fast, //快时钟
input                pulse_fast, //快时钟握手信号
input                clk_slow, //慢时钟
output               pulse_slow //慢时钟握手信号
);

wire                 clear_up ;
reg                  pulse_fast_detect ;
reg  [1:0]           pulse_fast_detect_ff ;
reg  [1:0]           pulse_slow_2_fast ;

always@(posedge clk_fast or negedge rst_n)
begin
    if (!rst_n)
       pulse_fast_detect  <= 1'b0 ; //pulse_fast_detect拉低复位
    else if (clear_up)
       pulse_fast_detect  <= 1'b0 ; //pulse_fast_detect拉低复位
    else if (pulse_fast) //在快时钟域中对快时钟握手信号进行抓取
       pulse_fast_detect  <= 1'b1 ; //信号抓取后,将 pulse_fast_detect持续拉高,可能为多个时钟周期,方便慢时钟域捕捉
end

always@(posedge clk_slow or negedge rst_n)
begin
  if (!rst_n)
    pulse_fast_detect_ff     <= 3'b0 ; //缓存三拍清零
  else
    pulse_fast_detect_ff     <= {pulse_fast_detect_ff[0], pulse_fast_detect} ; //在慢时钟域中对pulse_fast_detect信号进行抓取
End //不要求一翻转立刻捕捉到,只要求延时捕捉

assign pulse_slow = pulse_fast_detect_ff[1] ; //取中间一拍进行输出给快时钟域

always@(posedge clk_fast or negedge rst_n)
begin
  if (!rst_n)
    pulse_slow_2_fast  <= 1'b0 ;
  else
    pulse_slow_2_fast  <= {pulse_slow_2_fast[0], pulse_slow}; //快时钟域对慢时钟握手信号进行捕捉
end

assign clear_up = !pulse_fast && pulse_slow_2_fast[1]; //捕捉到回应,将 pulse_fast_detect拉低
endmodule


3.2 多比特信号
当多位宽数据进行同步时,如果数据变化速率过快,就不能再使用延迟打拍采样的方法。因为此时数据各 bit 位变化的时间参差不齐,用异步时钟进行打拍采样,可能会采集到因路径延迟不同而导致的错误数据。解决此类异步问题的常用方法是采用异步 FIFO (First In First Out)进行数据的交换。异步FIFO在后面的一节课专门介绍。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则