verilog实例-fullshakehand握手同步

一、简介

  • 在不同时钟域之间进行数据传输时,可以考虑使用握手同步机制。
  • 握手同步机制分为半握手全握手。当从频时钟域向频时钟域传输数据时,半握手机制比较适用,这是由于接收端可以更快地完成操作。但是当从频时钟向频时钟传输数据时,则需要全握手机制

(1) 半握手

●用后缀_t 表示发送端,用后缀 _r表示接收端。发送时钟用t_clk表示,接收时钟用r_clk表示。数据从t_clk域向r_clk域传输;
●当需要发送的数据准备好后,发送端将t_ready信号置为有效
●在t_ready有效期间,t_data必须保持稳定;
●接收端在r_clk域中采用双同步器同步t_ready控制信号,并把同步后的信号命名为t_rdy_rclk;接收端在发现t_rdy_rclk信号有效时,t_data已经安全地进入了r_clk域,使用r_clk对其进行采样,可以得到t_data_rclk。由于数据已经在r_clk域进行了正确采样,所以此后在r_clk域使用该数据是安全的:接收端将r_ack信号置为1,(信号必须在r_clk下降沿输出);
●发送端通过双同步器在t_clk域内同步r_ack信号,同步后的信号称为r_ack tclk;
●以上所有步骤称为“半握手”。这是因为发送端在输出下一数据之前,不会等到r_ack_tclk被置为0:半握手机制工作速度快,但是,使用半握手机制时需要谨慎,一旦使用不当,会导致操作错误;

(2)全握手

●用后缀_t 表示发送端,用后缀 _r表示接收端。发送时钟用t_clk表示,接收时钟用r_clk表示。数据从t_clk域向r_clk域传输;
●当需要发送的数据准备好后,发送端将t_ready信号置为有效
●在t_ready有效期间,t_data必须保持稳定;
●接收端在r_clk域中采用双同步器同步t_ready控制信号,并把同步后的信号命名为t_rdy_rclk;接收端在发现t_rdy_rclk信号有效时,t_data已经安全地进入了r_clk域,使用r_clk对其进行采样,可以得到t_data_rclk。由于数据已经在r_clk域进行了正确采样,所以此后在r_clk域使用该数据是安全的:接收端将r_ack信号置为1,(信号必须在r_clk下降沿输出);
●发送端通过双同步器在t_clk域内同步r_ack信号,同步后的信号称为r_ack tclk;
●以上所有步骤称为“半握手”。这是因为发送端在输出下一数据之前,不会等到r_ack_tclk被置为0:半握手机制工作速度快,但是,使用半握手机制时需要谨慎,一旦使用不当,会导致操作错误;
●从低频时钟域向高频时钟域传输数据时,半握手机制较为适用,这是由于接收端可以更快地完成操作。然而,如果从高频时钟域向低频时钟域传输数据,则需要采用全握手机制;
●当r_ack_tclk为高电平时,发送端接收到“接收完成信号”,发送端将t_ready置为0;
●当t_ready_rclk为低电平时,接收端将r_ack置为0;
●当发送端发现r_ack_tclk为低电平后,全握手过程结束,传输端可以发送新的数据;
●显然,全握手过程耗时较长,数据传输速度较慢。然而,全握手机制稳定可靠,可以在两个任意频率的时钟域内安全地进行数据传输。

二、Spec

(1)Function descripton

  1. 设计全握手同步机制,传输数据位9bits。
  2. 一个发送端口,一个接收端口

(2)Interface description

tranmit module

Signal NameWidthType DirectionDescription
t_clk1inputSystem clk signal, 50Mhz
t_ rstn1inputSystem reset signal,negedge
data_in9inputdata 9 bits
data_valid1input信号valid
t_data9outputdata to recevier
t_ready1outputready signal
r_ack1input接收完成信号

recevier module

Signal NameWidthType DirectionDescription
r_clk1inputSystem clk signal, 50Mhz
r_ rstn1inputSystem reset signal,negedge
r_data9inputdata to recevier
t_ready1inputready signal
r_ack1output接收完成信号

(3)Block Diagram

在这里插入图片描述
在这里插入图片描述

(4)Design detil

●用后缀_t 表示发送端,用后缀 _r表示接收端。发送时钟用t_clk表示,接收时钟用r_clk表示。数据从t_clk域向r_clk域传输;
●当需要发送的数据准备好后,发送端将t_ready信号置为有效
●在t_ready有效期间,t_data必须保持稳定;
●接收端在r_clk域中采用双同步器同步t_ready控制信号,并把同步后的信号命名为t_rdy_rclk;接收端在发现t_rdy_rclk信号有效时,t_data已经安全地进入了r_clk域,使用r_clk对其进行采样,可以得到t_data_rclk。由于数据已经在r_clk域进行了正确采样,所以此后在r_clk域使用该数据是安全的:接收端将r_ack信号置为1,(信号必须在r_clk下降沿输出);
●发送端通过双同步器在t_clk域内同步r_ack信号,同步后的信号称为r_ack tclk;
●以上所有步骤称为“半握手”。这是因为发送端在输出下一数据之前,不会等到r_ack_tclk被置为0:半握手机制工作速度快,但是,使用半握手机制时需要谨慎,一旦使用不当,会导致操作错误;
●从低频时钟域向高频时钟域传输数据时,半握手机制较为适用,这是由于接收端可以更快地完成操作。然而,如果从高频时钟域向低频时钟域传输数据,则需要采用全握手机制;
●当r_ack_tclk为高电平时,发送端接收到“接收完成信号”,发送端将t_ready置为0;
●当t_ready_rclk为低电平时,接收端将r_ack置为0;
●当发送端发现r_ack_tclk为低电平后,全握手过程结束,传输端可以发送新的数据;
●显然,全握手过程耗时较长,数据传输速度较慢。然而,全握手机制稳定可靠,可以在两个任意频率的时钟域内安全地进行数据传输。

(5)Timing

在这里插入图片描述

三、Design and Verification

- RTL

transmit

module transmit
#(
    parameter              DATA_WITCH  =   9
)
(
    input                           t_clk       ,
    input                           t_rstn      ,
    input [DATA_WITCH-1:0]          data_in     ,
    input                           data_valid  ,
    input                           r_ack       ,
    output[DATA_WITCH-1:0]          t_data      ,
    output                          t_ready     
);

   //wire [DATA_WITCH-1:0]            t_data      ;


    reg [1:0]                       t_state     ;
    reg [1:0]                       t_next_state;
    reg [DATA_WITCH-1:0]           data_in_d    ;

    //delay singal
    reg                             r_ack_tclk1    ;
    reg                             r_ack_tclk2    ;

    //delay
    always @(posedge t_clk or negedge t_rstn)begin
        if(!t_rstn)begin
            r_ack_tclk1 <= 1'b0 ;
            r_ack_tclk2 <= 1'b0 ;
        end
        else 
            r_ack_tclk1 <= r_ack       ;
            r_ack_tclk2 <= r_ack_tclk1 ;
    end

    //fsm
    localparam          T_IDLE      = 0    ,    //空闲状态。ready=0。
                        T_ASSERT    = 1    ,    //传输阶段。ready为1,如果ack为1,说明已经完成接收,进入下一阶段。
                        T_DEASSERT  = 2    ;    //发送结束阶段。等待发送一次数据结束。如果ack=0,则传输一次完成,考虑data_valid,转换为什么状态
    always @(*)begin
        case(t_state)
            T_IDLE:
            begin
                    if(data_valid)begin
                        t_next_state = T_ASSERT ;
                    end
                    else
                        t_next_state = T_IDLE   ;
            end
            T_ASSERT:
            begin
                    if(r_ack_tclk2)
                        t_next_state = T_DEASSERT   ;
                    else
                        t_next_state = T_ASSERT     ;
            end
            T_DEASSERT:
            begin
                    if(!r_ack_tclk2)begin //此次传输完成,可以准备进行下一次传输
                        if(data_valid)begin
                            t_next_state = T_ASSERT ;
                        end
                        else
                            t_next_state = T_IDLE     ;
                    end
                    else
                        t_next_state =  T_DEASSERT  ;
            end
            default: t_next_state = T_IDLE ;
        endcase
    end

    always @(posedge t_clk or negedge t_rstn)begin
        if(!t_rstn)
            t_state = T_IDLE    ;
        else
            t_state = t_next_state  ;
    end

    //output 

    always @(posedge t_clk or negedge t_rstn)begin
        if(!t_rstn)
            data_in_d <= 0;
        else if(data_valid)
            data_in_d <= data_in    ;
    end
    assign t_ready = (t_state == T_ASSERT)  ;
    assign t_data  = (t_state == T_ASSERT) ? data_in_d : 0  ;

endmodule

receiver

module receiver
#(
    parameter           DATA_WITCH  =   9
)
(
    input                   r_clk       ,
    input                   r_rstn      ,
    input                   t_ready     ,
    input [DATA_WITCH-1:0]  r_data     ,
    output                  r_ack       
);

    reg                     t_ready_rclk1   ;
    reg                     t_ready_rclk2   ;

    reg [1:0]                       r_state     ;
    reg [1:0]                       r_next_state;

    //buf
    always @(posedge r_clk or negedge r_rstn)begin
        if(!r_rstn)begin
            t_ready_rclk1 <= 1'b0   ;
            t_ready_rclk2 <= 1'b0   ;
        end
        else
            t_ready_rclk1 <= t_ready            ;
            t_ready_rclk2 <= t_ready_rclk1      ;
    end    
    
    //fsm
    localparam          R_IDLE      = 0    ,    //空闲状态。
                        R_ASSERT    = 1    ;    //接收阶段。
                    
    always @(*)begin
        case(r_state)
        R_IDLE:
        begin
            if(t_ready_rclk2)
                r_next_state = R_ASSERT ;
            else
                r_next_state = R_IDLE   ;
        end
        R_ASSERT:
        begin
            if(!t_ready_rclk2)
                r_next_state = R_IDLE   ;
            else
                r_next_state = R_ASSERT ;
        end
        endcase
    end


    always @(posedge r_clk or negedge r_rstn)begin
        if(!r_rstn)
            r_state = R_IDLE    ;
        else
            r_state = r_next_state  ;
    end

    //output
    assign r_ack = (r_state == R_ASSERT) ;
endmodule

top

module top
#(
    parameter       DATA_WITCH  =   9   
)
(   
    input                       t_clk       ,
    input                       t_rstn      ,
    input [DATA_WITCH-1:0]      data_in     ,
    input                       data_valid  ,

    input                       r_clk       ,
    input                       r_rstn      

);
    //port defined
    wire [DATA_WITCH-1:0]       t_data      ;
    wire                        t_ready     ;
    wire                        r_ack       ;

    //instance
    transmit
    #(
        .DATA_WITCH(DATA_WITCH)
    )
    u_transmit
    (
        .t_clk      (t_clk     ), 
        .t_rstn     (t_rstn    ), 
        .data_in    (data_in   ), 
        .data_valid (data_valid),
        .r_ack      (r_ack     ),   
        .t_data     (t_data    ), 
        .t_ready    (t_ready   )
    );


    receiver
    #(
        .DATA_WITCH(DATA_WITCH)
    )
    u_receiver
    (
        .r_clk      (r_clk    ),  
        .r_rstn     (r_rstn   ),  
        .t_ready    (t_ready  ),  
        .r_data     (t_data   ),  
        .r_ack      (r_ack    )          
    );

endmodule

- Test bench

`timescale 1ns/1ps
module test
#(
    parameter       DATA_WITCH = 9
);
    reg                     t_clk       ;   //200MHZ = 5ns
    reg                     t_rstn      ;
    reg [DATA_WITCH-1:0]    data_in     ;
    reg                     data_valid  ;

    reg                     r_clk       ;   //100MHZ = 10ns
    reg                     r_rstn      ;

    //initial
    initial begin
        #0;
        t_clk = 0   ;
        t_rstn = 0  ;
        data_in = 0 ;
        data_valid = 0;

        r_clk = 0   ;
        r_rstn = 0  ; 

        #50;
        t_rstn = 1  ;
        data_in = 9'b1_0101_0111    ;
        data_valid = 1  ;
        r_rstn = 1  ;

        #20;
        data_valid = 0;

        #200;
        data_in = 9'b1_1111_1111    ;
        data_valid = 0  ;

        #200;
        data_in = 9'b1_1111_1111    ;
        data_valid = 1;

        #20;
        data_valid = 0  ;

        #200;
        data_in = 9'h4f             ;
        data_valid = 1;

    end

    //clk generate
    always #2.5 t_clk = ~t_clk  ;
    always #5   r_clk = ~r_clk  ;

    //instance
    top
    #(
        .DATA_WITCH(DATA_WITCH)
    )
    u_top
    (
        .t_clk      (t_clk      ), 
        .t_rstn     (t_rstn     ), 
        .data_in    (data_in    ), 
        .data_valid (data_valid ), 
        .r_clk      (r_clk      ), 
        .r_rstn     (r_rstn     )
    );

    //stop
    initial begin
        forever begin
            #100;
            if($time >= 1000)   $finish   ;
        end
    end
endmodule

四、Graph

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Valid-Ready握手协议在Verilog设计中用于数据传输的同步,确保数据的有效性和可靠性。它通常用于两个模块之间的数据交换,并且在发送数据之前,接收方必须准备好接收数据。 下面是一个简单的Verilog代码示例,演示了Valid-Ready握手协议的基本原理: ```verilog module ValidReadyHandshake ( input wire clk, input wire reset, input wire data_valid, output wire data_ready, input wire [DATA_WIDTH-1:0] data_in, output wire [DATA_WIDTH-1:0] data_out ); // 内部状态定义 reg [DATA_WIDTH-1:0] internal_data; reg internal_valid; reg internal_ready; // 同步时钟 always @(posedge clk) begin if (reset) begin internal_ready <= 1'b0; internal_data <= {DATA_WIDTH{1'b0}}; internal_valid <= 1'b0; end else begin internal_ready <= data_ready; internal_data <= data_in; internal_valid <= data_valid; end end // 数据输出逻辑 assign data_out = internal_data; // Ready信号逻辑 assign data_ready = internal_ready && !internal_valid; endmodule ``` 在这个示例中,Valid-Ready握手协议的发送方将数据放入`data_in`端口,并设置`data_valid`信号为高电平。接收方通过`data_ready`信号表示它已经准备好接收数据。当接收方准备好时,它将`data_ready`信号设置为高电平,发送方将通过`data_ready`信号的状态来判断是否可以发送数据。 值得注意的是,`clk`和`reset`信号是必需的,用于同步时钟和复位。 这只是一个基本示例,你可以根据具体的设计需求对握手协议进行扩展和修改。希望这个示例对你有所帮助!如果你有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值