跨时钟域信号传输问题之握手同步
所谓握手,即通信双方使用了专用控制信号进行状态指示,这个控制信号既有发送域给接受域的也有接收域给控制域的,有别于单向控制信号方式。
使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req 和 ack)分别使用脉冲检测方法进行同步,在具体实现中,假设req ,ack, data,总线在初始化时都处于无效状态,发送域先把数据放入总线,随后发送有效的req信号给接收域;接收域在检测到有效的req信号后锁存数据总线,然后会送一个有效的ack信号表示读取完成应答;发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常握手通信,此后,发送域可以继续开始下一次握手通信,如此循环,该方式能够使接收到的数据稳定可靠,有效的避免了亚稳态的出现,但是控制信号握手检测会消耗通信双方较多的时间。
上段内容参考:跨时钟域处理——专用握手信号
如下图为握手通信示意图:
//2019年8月17日更新
发送域的Verilog设计:
-
module handshake_tclk(
-
input tclk, //发送域的时钟和复位
-
input resetb_tclk,
-
input r_ack, //接收到的响应信号
-
input data_avail, //从其他模块接收到的数据有效信号
-
input [31:0] transmit_data, //需要发送出去的信号
-
output t_rdy, //对于发送时钟域,需要输出数据准备好了的信号,以便在接收时钟域接收此信号,用来提示接收信号
-
output [31:0] t_data //需要发送出去的信号
-
);
-
localparam IDLE = 3'b001; //空闲状态,判断数据是否有效,如果有效就输出t_rdy有效,表示数据准备好了
-
localparam ASSERT_TRDY = 3'b010; //到了这个状态,表明t_rdy已经有效了,这时我们需要判断响应r_ack_tclk是否有效,如果有效则使t_rdy无效,否则保持不变
-
localparam DEASSERT_TRDY = 3'b100; //到了这个t_rdy无效状态,需要考虑下一次的数据传输了,如果data_avail有效,则下一个状态进入ASSERT_TRDY,且t_rdy有效,否则进入空闲状态
-
-
reg t_cur_state, t_nxt_state;
-
reg t_rdy, t_rdy_nxt;
-
reg [31:0] t_data, t_data_nxt;
-
reg r_ack_d1, r_ack_tclk; //对于接收到的响应信号,需要进行时钟域同步,同步到发送时钟域
-
-
-
always@(posedge tclk or negedge resetb_tclk) begin
-
if(resetb_tclk) begin
-
t_cur_state <= IDLE;
-
t_rdy <= 0;
-
-
//同步接收域的响应信号ack
-
r_ack_d1 <= 0;
-
r_ack_tclk <= 0;
-
end
-
else begin
-
t_cur_state <= t_nxt_state;
-
t_rdy <= t_rdy_nxt;
-
-
r_ack_d1 <= r_ack;
-
r_ack_tclk <= r_ack_d1;
-
-
end
-
-
-
end
-
-
always@(*) begin
-
t_nxt_state = t_cur_state;
-
t_rdy_nxt = 0;
-
t_data_nxt = t_data;
-
case(t_cur_state)
-
IDLE: begin
-
if(data_avail) begin
-
t_nxt_state = ASSERT_TRDY;
-
t_rdy_nxt = 1'b1;
-
t_data_nxt = transmit_data;
-
end
-
else ;
-
end
-
ASSERT_TRDY: begin
-
if(r_ack_tclk) begin
-
t_rdy_nxt = 1'b0;
-
t_nxt_state = DEASSERT_TRDY;
-
t_data_nxt = 'd0;
-
end
-
else begin
-
t_rdy_nxt = 1'b1;
-
t_data_nxt = t_data;
-
end
-
end
-
DEASSERT_TRDY: begin
-
if(!r_ack_tclk) begin
-
if(data_avail) begin
-
t_nxt_state = ASSERT_TRDY;
-
t_rdy_nxt = 1'b1;
-
t_data_nxt = transmit_data;
-
end
-
else t_nxt_state = IDLE;
-
end
-
else;
-
end
-
default: ;
-
-
endcase
-
-
end
-
-
-
-
endmodule
更新于2019年8月18日:
接收域设计代码:
-
module handshake_rclk(
-
input rclk,
-
input resetb_rclk,
-
input t_rdy,
-
input [31:0] t_data,
-
output r_ack
-
);
-
-
localparam IDLE = 2'b01;
-
localparam ASSERT_ACK = 2'b10;
-
-
reg r_cur_state, r_nxt_state;
-
reg r_ack, r_ack_nxt;
-
reg [31:0] t_data_rclk, t_data_rclk_nxt;
-
reg t_rdy_d1, t_rdy_rclk;
-
-
always@(posedge clk or negedge resetb_rclk) begin
-
if(~resetb_rclk) begin
-
r_cur_state <= IDLE;
-
r_ack <= 0;
-
t_data_rclk <= 0;
-
t_rdy_d1 <= 0;
-
t_rdy_rclk <= 0;
-
end
-
else begin
-
r_cur_state <= r_nxt_state;
-
r_ack <= r_ack_nxt;
-
t_data_rclk <= t_data_rclk_nxt;
-
t_rdy_d1 <= t_rdy;
-
t_rdy_rclk <= t_rdy_d1;
-
end
-
end
-
-
always@(*) begin
-
r_nxt_state = r_cur_state;
-
r_ack_nxt = 1'b0;
-
t_data_rclk_nxt = t_data_rclk;
-
case(r_nxt_state)
-
IDLE: begin
-
if(t_rdy_rclk) begin
-
r_nxt_state = ASSERT_ACK;
-
r_ack_nxt = 1'b1;
-
t_data_rclk_nxt = t_data;
-
end
-
else;
-
end
-
-
-
ASSERT_ACK: begin
-
if(~t_rdy_rclk) begin
-
r_ack_nxt = 1'b0;
-
r_nxt_state = IDLE;
-
end
-
else r_ack_nxt = 1'b1;
-
end
-
-
endcase
-
-
end
综合上述两段设计程序,来大致总结下发送域的控制信号t_rdy和接收域的响应信号r_ack之间的关系;
最先是t_rdy有效,当接收域检测到t_rdy信号后,r_ack有效;
让发送域检测到r_ack后,t_rdy无效;
让接收域检测到t_rdy无效后,使r_ack无效,此时,一次完整的握手成功完成。
无效素材:
下面的设计是参考互联网上的,不得不说真是良莠不齐,这个设计既然是跨时钟域设计的握手信号,你都没有两套时钟,怎么能叫跨时钟域呢?
下面给出Verilog代码及测试文件:
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 2019/04/28 21:00:41
-
// Design Name:
-
// Module Name: handshack
-
// Project Name:
-
// Target Devices:
-
// Tool Versions:
-
// Description:
-
//
-
// Dependencies:
-
//
-
// Revision:
-
// Revision 0.01 - File Created
-
// Additional Comments:
-
//
-
//
-
-
-
module handshack(
-
input clk, //系统时钟
-
input rst_n, //低电平复位信号
-
input req, //请求信号,高电平有效
-
input [7:0] datain, //输入数据
-
output ack, //应答信号,高电平有效
-
output [7:0] dataout //输出数据,主要用于观察是否和输入一致
-
-
);
-
-
//req上升沿检测
-
reg reqr1, reqr2, reqr3; //定义reqrx代表延迟x拍
-
//--------------------------------------------------------
-
//第一种方法
-
always @ (posedge clk or negedge rst_n) begin
-
if(!rst_n) begin
-
reqr1 <= 1'b0;
-
reqr2 <= 1'b0;
-
reqr3 <= 1'b0;
-
end
-
else begin
-
reqr1 <= req;
-
reqr2 <= reqr1;
-
reqr3 <= reqr2;
-
end
-
-
end
-
-
//pos_req3比pos_req2延后一拍,确保数据被稳定锁存
-
wire pos_req1, pos_req2, pos_req3;
-
-
assign pos_req1 = ( { req, reqr1 } == 2'b10 ) ? 1'b1 : 1'b0;
-
assign pos_req2 = ( { reqr1, reqr2 } == 2'b10 ) ? 1'b1 : 1'b0;
-
assign pos_req3 = ( { reqr2, reqr3 } == 2'b10 ) ? 1'b1 : 1'b0;
-
//------------------------------------------------------------------
-
// 检测上升沿的第二种方法
-
// assign pos_req1 = ~reqr1 & req;
-
// assign pos_req2 = ~reqr2 & reqr1;
-
// assign pos_req3 = ~reqr3 & reqr2;
-
-
//----------------------------------------------------------------
-
//数据锁存
-
reg [7:0] dataoutr;
-
-
always @ (posedge clk or negedge rst_n) begin
-
if(!rst_n) begin
-
dataoutr <= 8'h00;
-
end
-
else if(pos_req2) begin
-
dataoutr <= datain; //检测到req有效后的第二拍,锁存输入数据;
-
end
-
-
end
-
assign dataout = dataoutr;
-
-
//-------------------------------------------------------------------
-
//产生应答信号
-
reg ackr;
-
always @ (posedge clk or negedge rst_n) begin
-
if(!rst_n) begin
-
ackr <= 1'b0;
-
end
-
else if(pos_req3) begin //锁存输入数据后的一拍,产生应答信号
-
ackr <= 1'b1;
-
end
-
else if(!req) begin
-
ackr <= 1'b0;
-
end
-
end
-
assign ack = ackr;
-
-
-
endmodule
-
`timescale 1ns / 1ps
-
//
-
// Company:
-
// Engineer:
-
//
-
// Create Date: 2019/04/28 22:12:00
-
// Design Name:
-
// Module Name: handshack_tb
-
// Project Name:
-
// Target Devices:
-
// Tool Versions:
-
// Description:
-
//
-
// Dependencies:
-
//
-
// Revision:
-
// Revision 0.01 - File Created
-
// Additional Comments:
-
//
-
//
-
-
-
module handshack_tb(
-
-
);
-
-
reg clk, rst_n;
-
reg req;
-
reg [7:0] datain;
-
wire ack;
-
wire [7:0] dataout;
-
-
//--------------------------------------------------
-
//产生时钟
-
initial clk = 0;
-
-
always begin
-
#1 clk = ~clk;
-
end
-
-
//------------------------------------------------------
-
//初始化
-
initial begin
-
rst_n = 0;
-
req = 0;
-
-
#4
-
rst_n = 1;
-
#4
-
datain = 8'h55;
-
#2
-
req = 1;
-
#10
-
req = 0;
-
datain = 8'haa;
-
#4
-
req = 1;
-
-
#10
-
req = 0;
-
-
end
-
-
//------------------------------------------------------
-
//调用模块
-
handshack u_handshack(
-
.clk(clk),
-
.rst_n(rst_n),
-
.req(req),
-
.datain(datain),
-
.ack(ack),
-
.dataout(dataout)
-
);
-
-
-
-
endmodule
仿真波形:
RTL原理图:
主要观察pos_reqx是由多路选择器构成,但如果将上升沿检测部分用与门方式实现,我们看看RTL图是什么样的:
文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。
原文链接:reborn.blog.csdn.net/article/details/89647526
- 点赞
- 收藏
- 关注作者
评论(0)