本文版权归本公众号所有。

 

 hi大家好,最近工作实在是太忙了,今天终于有时间更新一下公众号。虽然现在上海仍处于疫情的中心,但是对于芯片设计行业影响并不大,项目一个接一个。同时希望大家在出行的时候保护好自己,注意安全,千万别“中招”哦。

 

什么是握手协议

 

说起握手,首先查了一下百度百科。握手是一种礼仪,起源于中世纪的欧洲,顺序为长幼有序,女士优先。(PS:所以握手的时候,男士记得要绅士一点哦)。    

 

在芯片中,握手是最古老的跨时钟域之间传输数据的方式。握手机制通过将脉冲信号展宽,待输出一侧检测到信号并将其解析为脉冲信号后,再向输入一侧发送应答信号,表明接收到信号并且传输完成。

 

为什么要握手

 

在人类的进化史中,握手作为一种善意的表达方式,可以增进人与人之间的和谐。言归正传,那么数字电路中为什么也需要握手机制呢?这是因为在数字电路中,跨时钟域处理是个较为常见的问题。关于跨时钟域,我们公众号之前有介绍过,想复习一下的同学可以查看一下之前写的文章。

 

在从快时钟向慢时钟传递时,由于输入信号变化较快,输出一侧可能跟不上输入的变化,从而导致“漏采“现象。由于两个时钟之间的频率不同,来自快时钟域的脉冲信号,还没来得及被慢时钟的采到,便转瞬即逝,从而导致信号被漏采。此时,握手机制便可以大显神通。

 

握手机制的原理

 

 

(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发送端可以发送新的数据。

 

至此,全握手完成。显然,全握手过程耗时较长,数据传输较慢。但是全握手机制稳定可靠,可以在两个任意频率的时钟域中安全地进行数据传输。需要注意一点的是,数据应该在发送时钟域内稳定至少两个时钟上升沿,请求信号req的宽度应该超过两个时钟周期,否则从高速时钟向低速时钟传递可能无法捕捉到该信号,也就是信号“失联”了。

 

握手机制的代码实现

 

发送端状态机:

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   endcaseendalways@(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 endendmodule 

 

接收端状态机:

 

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   endcaseend
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; endend
endmodule

 

握手机制的缺点

 

 一个字:慢。

 

好了,希望本文对大家有所帮助。疫情期间,请多注意防护~

 

参考文献:https://blog.csdn.net/qq_39814612/article/details/105795252