电子说
。
说起握手,首先查了一下百度百科。握手是一种礼仪,起源于中世纪的欧洲,顺序为长幼有序,女士优先。(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
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
接收端状态机:
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
一个字:慢。
好了,希望本文对大家有所帮助。
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !