所谓握手,即通信双方使用了专用控制信号进行状态指示,这个控制信号既有发送域给接受域的也有接收域给控制域的,有别于单向控制信号方式。
使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req 和 ack)分别使用脉冲检测方法进行同步,在具体实现中,假设req ,ack, data,总线在初始化时都处于无效状态,发送域先把数据放入总线,随后发送有效的req信号给接收域;接收域在检测到有效的req信号后锁存数据总线,然后会送一个有效的ack信号表示读取完成应答;发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销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图是什么样的:
看出不一样了吧。