校招Verilog——单bit跨时钟域(脉冲展宽法-握手法)

如何处理跨时钟域信号?

  单bit:

  • (1)双D触发器打拍同步;(只能慢到快)
  • (2)脉冲展宽处理(握手法);

  多bit:

  • (1)格雷码+双D触发器打拍;(不推荐)
  • (2)DMUX数据使能选通设计;(FIFO设计)
  • (3)异步握手协议;(效率较低)

这里写一下单bit进行跨时钟域的代码设计,如果设计时知道一定是慢时钟域到快时钟域,那么该信号到快时钟域信号打两拍,检测上升沿就行。如果是快时钟域到慢时钟域,或者不知道两边时钟域情况,那么可以用【脉冲展宽处理】,如果脉冲展宽是有限的,就是说拉高的展宽信号后面还得拉低,那就是【握手法】。

基本思想是:

  1. 快时钟域对脉冲信号进行检测,检测为高电平时输出高电平信号 req。

  2. 慢时钟域对快时钟域的信号 req 进行延迟打拍采样。因为此时的脉冲信号被快时钟域保持拉高状态,延迟打拍肯定会采集到该信号。

  3. 慢时钟域确认采样得到高电平信号 req_r1 后,拉高反馈信号 ack 再反馈给快时钟域。

  4. 快时钟域对反馈信号进行延迟打拍采样得到 ack_r1。如果检测到反馈信号为高电平,证明慢时钟域已经接收到有效的高电平信号,信号恢复原来状态。

 

1、代码设计

module Sync_Pulse
(
    input   clka,
    input   clkb,
    input   rst_n,
    input   bit_a,
    output  bit_b
);
//--------------------------------------------------------
reg         req_a;
reg         ack_a;
reg         ack_a_r1;
reg         req_b;
reg         req_b_r1;
//--------------------------------------------------------
//--    a时钟域生成展宽信号
//--------------------------------------------------------
always @(posedge clka or negedge rst_n)begin
    if(!rst_n)begin
        req_a <= 1'b0;
    end
    else if(bit_a) begin         //检测到脉冲
        req_a <= 1'b1;           //进行展宽
    end
    else if(ack_a_r1) begin      //同步到b时钟域后得到应答
        req_a <= 1'b0;           //展宽使命完成
    end
end
//--------------------------------------------------------
//--    展宽信号同步到b时钟域
//--------------------------------------------------------
always @(posedge clkb or negedge rst_n)begin
    if(!rst_n)begin
        req_b    <= 1'b0;
        req_b_r1 <= 1'b0;
    end
    else begin
        req_b    <= req_a;
        req_b_r1 <= req_b;
    end
end
//--------------------------------------------------------
//--    展宽信号同步回b时钟域,作为应答
//--------------------------------------------------------
always @(posedge clka or negedge rst_n)begin
    if(!rst_n)begin
        ack_a    <= 1'b0;
        ack_a_r1 <= 1'b0;
    end
    else begin
        ack_a    <= req_b_r1;
        ack_a_r1 <= ack_a;
    end
end
//--------------------------------------------------------
//--    脉冲信号输出,上升沿检测
//--------------------------------------------------------
assign bit_b = ~req_b_r1 & req_b;


endmodule

2、Testbench

`timescale 1ns/1ps      //时间精度
`define    Clock_a 10   //时钟周期,数字越小时钟越快
`define    Clock_b 100  //时钟周期,数字越大时钟越慢

module Sync_Pulse_tb;

//========================< 端口 >==========================================
reg                         clka                    ;
reg                         clkb                    ;
reg                         rst_n                   ;
reg                         bit_a                   ;
//==========================================================================
//==    模块例化
//==========================================================================
Sync_Pulse u_Sync_Pulse
(
    .clka                   (clka                   ),
    .clkb                   (clkb                   ),
    .rst_n                  (rst_n                  ),
    .bit_a                  (bit_a                  ),
    .bit_b                  (bit_b                  )
);
//==========================================================================
//==    时钟信号和复位信号
//==========================================================================
initial begin
    clka = 1;
    forever
        #(`Clock_a/2) clka = ~clka;
end

initial begin
    clkb = 1;
    forever
        #(`Clock_b/2) clkb = ~clkb;
end

initial begin
    rst_n = 0; #(`Clock_a*2+1);
    rst_n = 1;
end

//==========================================================================
//==    设计输入信号
//==========================================================================
initial begin
    bit_a = 0;
    #(`Clock_a*2+1); //初始化完成
    bit_a = 1;
    #(`Clock_a);
    bit_a = 0;
    #(`Clock_b*100);
    $stop;
end



endmodule

3、波形展示

(1)快 -> 慢

(2)慢 -> 快

这个代码建议背出来,看不太懂就自己仿真看看吧!

如果是多bit跨时钟域处理,可以用参见异步FIFO的代码设计。

posted @ 2020-09-09 20:42  咸鱼IC  阅读(4402)  评论(0编辑  收藏  举报