目录
一、简介
- 在不同时钟域之间进行数据传输时,可以考虑使用握手同步机制。
- 握手同步机制分为半握手和全握手。当从低频时钟域向高频时钟域传输数据时,半握手机制比较适用,这是由于接收端可以更快地完成操作。但是当从高频时钟向低频时钟传输数据时,则需要全握手机制。
(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
- 设计全握手同步机制,传输数据位9bits。
- 一个发送端口,一个接收端口
(2)Interface description
tranmit module
Signal Name | Width | Type Direction | Description |
---|---|---|---|
t_clk | 1 | input | System clk signal, 50Mhz |
t_ rstn | 1 | input | System reset signal,negedge |
data_in | 9 | input | data 9 bits |
data_valid | 1 | input | 信号valid |
t_data | 9 | output | data to recevier |
t_ready | 1 | output | ready signal |
r_ack | 1 | input | 接收完成信号 |
recevier module
Signal Name | Width | Type Direction | Description |
---|---|---|---|
r_clk | 1 | input | System clk signal, 50Mhz |
r_ rstn | 1 | input | System reset signal,negedge |
r_data | 9 | input | data to recevier |
t_ready | 1 | input | ready signal |
r_ack | 1 | output | 接收完成信号 |
(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