当前位置:   article > 正文

全握手机制verilog实现_握手verilog实现

握手verilog实现

全握手机制verilog实现

PART1:基本原理
在不同时钟域之间进行数据传输时,可以考虑使用握手同步机制。
握手同步机制分为半握手和全握手。当从低频时钟域向高频时钟域传输数据时,半握手机制比较适用,这是由于接收端可以更快地完成操作。但是当从高频时钟向低频时钟传输数据时,则需要全握手机制。
握手同步机制的工作步骤:
(1)发送端在t_clk时钟域下将需要发送的数据准备好后,将t_rdy信号置为有效,该信号必须在tclk下降沿输出。接收端在rclk时钟域下同步r_rdy信号,同步后的信号命名为t_rdy_rclk。
(2)接收端在t_rdy_rclk有效期间,对t_data进行采样,得到t_data_rclk。
(3)接收端将r_ack信号置为1,信号必须在rclk下降沿输出。发送端将r_ack同步为r_ack_tclk。
至此,已经完成“半握手”,发送端在输出下一数据前,不会等到r_ack_tclk被置为0。半握手机制工作速度快,但是使用不当有可能会导致操作错误。然而,如果要从高频时钟向低频时钟传输数据,则需要采用全握手机制。
(4)当r_ack_tclk为高电平时,发送端将t_rdy置为0。
(5)当t_rdy_rclk为低电平时,接收端将r_ack置为0。
(6)当r_ack_tclk为低电平时,发送端将t_rdy重新置为1发送端可以发送新的数据。
至此,全握手完成。显然,全握手过程耗时较长,数据传输较慢。但是全握手机制稳定可靠,可以在两个任意频率的时钟域中安全地进行数据传输。
上面过程比较绕口,因此我制作了一张流程图,如下所示:
在这里插入图片描述

PART2:verilog代码实现
发送端:使用三个状态的状态机,其跳转关系如下:
在这里插入图片描述

代码如下

module transmit(tclk,reset_tclk,t_rdy,data_avail,transmit_data,t_data,r_ack);
input tclk;
input reset_tclk;
input data_avail;
input [31:0]transmit_data;
input r_ack;
output t_rdy;
output t_data;

localparam IDLE_T = 2'd0,
    ASSERT_T_RDY = 2'd1,
    DEASSERT_T_RDY = 2'd2;
    
reg [1:0] t_hndshk_state,t_hndshk_state_nxt;
reg t_rdy,t_rdy_nxt;
reg [31:0] t_data,t_data_nxt;
reg r_ack_tclk;

always@(*)begin

 t_hndshk_state_nxt = t_hndshk_state;
 t_rdy_nxt = 1'b0;
 t_data_nxt = t_data;
 
 case(t_hndshk_state)
  IDLE_T:begin
   if(data_avail) begin
    t_rdy_nxt = 1'b1;
    t_hndshk_state_nxt = ASSERT_T_RDY;
    t_data_nxt = transmit_data;
   end
  end
  
  ASSERT_T_RDY:begin
   if(r_ack_tclk)begin
    t_rdy_nxt = 1'b0;
    t_hndshk_state_nxt = DEASSERT_T_RDY;
    t_data_nxt = 'd0;
   end
   else begin
    t_rdy_nxt = 1'b1;
    t_data_nxt = transmit_data;
   end
  end
  
  DEASSERT_T_RDY:begin
   if(!r_ack_tclk)begin
    if(data_avail)begin
     t_rdy_nxt = 1'b1;
     t_hndshk_state_nxt = ASSERT_T_RDY;
     t_data_nxt = transmit_data;
    end
    else begin
     t_hndshk_state_nxt = IDLE_T;
    end
   end
  end
  
 endcase
end
always@(posedge tclk or negedge reset_tclk)begin
 if(!reset_tclk)begin
  t_rdy <= 1'b0;
  t_hndshk_state <= IDLE_T;
  t_data <= 32'h00000000;
  r_ack_tclk <= 1'b0;
 end
 
 else begin
  t_rdy <= t_rdy_nxt;
  t_hndshk_state <= t_hndshk_state_nxt;
  t_data <= t_data_nxt;
  r_ack_tclk <= r_ack;
 end
 
end
endmodule 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

接收端:使用两个状态的状态机,跳转关系如下:

在这里插入图片描述
代码如下:

module receiver(rclk,reset_rclk,t_rdy,t_data,r_ack);
input rclk,reset_rclk;
input t_rdy;
input[31:0] t_data;
output r_ack;

reg r_hndshk_state,r_hndshk_state_nxt;
reg t_rdy_rclk;
reg[31:0] t_data_rclk,t_data_rclk_nxt;
reg r_ack,r_ack_nxt;

localparam IDLE_R = 1'b0,
    ASSERT_ACK = 1'b1;
    
always@(*)begin
 r_hndshk_state_nxt = r_hndshk_state;
 r_ack_nxt = 1'b0;
 t_data_rclk_nxt = t_data_rclk;
 case(r_hndshk_state)
  IDLE_R:begin
   if(t_rdy_rclk)begin
    r_hndshk_state_nxt = ASSERT_ACK;
    t_data_rclk_nxt = t_data;
    r_ack_nxt = 1'b1;
   end
  end
  
  ASSERT_ACK:begin
   if(!t_rdy_rclk)begin
    r_hndshk_state_nxt = IDLE_R;
    r_ack_nxt = 1'b0;
   end
   else begin
    r_ack_nxt = 1'b1;
   end
  end
  
 endcase
end

always@(posedge rclk or negedge reset_rclk)begin
 if(!reset_rclk)begin
  r_hndshk_state <= IDLE_R;
  t_data_rclk <= 1'b0;
  t_rdy_rclk <= 1'b0;
  r_ack <= 1'b0;
 end
 
 else begin
  r_hndshk_state <= r_hndshk_state_nxt;
  t_data_rclk <= t_data_rclk_nxt;
  t_rdy_rclk <= t_rdy;
  r_ack <= r_ack_nxt;
 end
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

测试代码

`timescale 1ns/1ns
module testbench;
reg tclk_tb,rclk_tb;
reg [31:0] transmit_data_tb;
reg reset_tclk_tb,reset_rclk_tb;
reg data_avail_tb;
wire t_rdy_tb;
wire [31:0] t_data_tb;
wire r_ack_tb;

parameter CLK_HALF_PERIOD1 = 5;
parameter CLK_HALF_PERIOD2 = 8;
parameter RESET_DELAY = 100;

initial begin
 tclk_tb = 0;
 rclk_tb = 0;
end

always #CLK_HALF_PERIOD1 tclk_tb = ~tclk_tb;
always #CLK_HALF_PERIOD2 rclk_tb = ~rclk_tb;

initial begin
 reset_rclk_tb = 0;
 reset_tclk_tb = 0;
 #RESET_DELAY reset_rclk_tb = 1;
 reset_tclk_tb = 1;
end

initial begin
 #500;
 data_avail_tb = 1;
 transmit_data_tb = 32'h96431346;
end

transmit transmit_test
 (.tclk(tclk_tb),
 .reset_tclk(reset_tclk_tb),
 .t_rdy(t_rdy_tb),
 .data_avail(data_avail_tb),
 .transmit_data(transmit_data_tb),
 .t_data(t_data_tb),
 .r_ack(r_ack_tb));
 
receiver receiver_test
 (.rclk(rclk_tb),
 .reset_rclk(reset_rclk_tb),
 .t_rdy(t_rdy_tb),
 .t_data(t_data_tb),
 .r_ack(r_ack_tb));
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

PART3:v仿真结果及其分析
在这里插入图片描述

由波形图我们可以发现,在t_rdy有效期间,t_data=t_transmit,将数据发送出去(为了简便,这里把要发送的数据都设为一个数值),在t_rdy无效期间,t_data输出32’h00000000。并且t_rdy会带动后面的t_rdy_rclk、r_ack、r_ack_tclk的一连串变化。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/206859
推荐阅读
相关标签
  

闽ICP备14008679号