方法
使用握手信号是在两个不同域之间传输数据的有效方式,如下图所示:
使用握手信号xack和yreq,系统X发给系统Y,下面是使用握手信号传输数据的例子:
1)发送器系统X将数据放到数据总线上并发出xreq请求信号,表示有效数据已经发送到接收器系统Y的数据总线上
2)把xreq信号同步到接收器的时钟域yclk上。
3)接收器在识别xreq同步信号yreq2后,锁存数据总线上的信号
4)接收器发出确认信号yack,表示其已经接受了数据
5)接收器发出的yack信号同步到发送时钟xclk上
6)发送器在识别同步的ack信号后,将下一个数据放到数据总线上
握手信号的时序图如下所示:
握手信号的要求
数据应该在发送时钟域内稳定至少两个时钟上升沿,请求信号xreq的宽度应该超过两个上升沿时钟,否则从高速时钟的低速时钟域传递可能无法捕捉到该信号。缺点是是延迟太大(相比于FIFO)。
硬件实现
数据发送系统
模块框图如下
module tx_data(
input wire tx_clk ,
input wire xack2 ,
input wire rst_n ,
input wire en ,
output reg xreq ,
output wire [7:0] t_data
);
reg [3:0] data1 ;
reg [3:0] data2 ;
//在yack2有效时data1和data2更新
always @(posedge tx_clk or negedge rst_n) begin
if(!rst_n) begin
data1 <= 4'd1;
data2 <= 4'd1;
end
else if(xack2) begin
data1 <= data1 + 4'd1;
data2 <= data2 + 4'd2;
end
else begin
data1 <= data1;
data2 <= data2;
end
end
//xreq信号
always @(posedge tx_clk or negedge rst_n) begin
if(!rst_n) begin
xreq <= 1'b0;
end
else if(en) begin
xreq <= 1'b1;
end
else if(xack2) begin
xreq <= 1'b1;
end
else begin
xreq <= 1'b0;
end
end
assign t_data = {data1, data2};
endmodule
数据接收系统
模块框图如下:
module rv_data(
input wire rv_clk ,
input wire rst_n ,
input wire yreq2 ,
input wire [7:0] r_data ,
output reg yack ,
output wire [5:0] result
);
/*
接收到数据的第一周期进行数据计算
第二周期返回ack信号
*/
reg dly1 ;
reg [5:0] result_reg ;
always @(posedge rv_clk or negedge rst_n) begin
if(!rst_n) begin
yack <= 1'b0;
end
else if(dly1) begin
yack <= 1'b1;
end
else begin
yack <= 1'b0;
end
end
always @(posedge rv_clk or negedge rst_n) begin
if(!rst_n) begin
dly1 <= 1'b0;
end
else if(yreq2) begin
dly1 <= 1'b1;
end
else begin
dly1 <= 1'b0;
end
end
always @(posedge rv_clk or negedge rst_n) begin
if(!rst_n) begin
result_reg <= 'd0;
end
else if(yreq2) begin
result_reg <= r_data[3:0] + r_data[7:4];
end
else begin
result_reg <= result_reg;
end
end
assign result = dly1 ? result_reg : 6'd0;
endmodule
异步握手实现
模块框图如下:
module shake_hand_asy(
input wire tx_clk ,
input wire rv_clk ,
input wire rst_n ,
input wire en ,
output wire [5:0] result
);
wire xreq ;
wire yack ;
wire [7:0] data ;
reg yreq1 ;
reg yreq2 ;
reg xack1 ;
reg xack2 ;
//xreg的两级同步
always @(posedge rv_clk or negedge rst_n) begin
if(!rst_n) begin
yreq1 <= 1'b0;
yreq2 <= 1'b0;
end
else begin
yreq1 <= xreq;
yreq2 <= yreq1;
end
end
//yack的两级同步
always @(posedge tx_clk or rst_n) begin
if(!rst_n) begin
xack1 <= 1'b0;
xack2 <= 1'b0;
end
else begin
xack1 <= yack;
xack2 <= xack1;
end
end
tx_data u_tx_data(
. tx_clk (tx_clk) ,
. xack2 (xack2) ,
. rst_n (rst_n) ,
. en (en) ,
. xreq (xreq) ,
. t_data (data)
);
rv_data u_rv_data(
.rv_clk (rv_clk) ,
.rst_n (rst_n) ,
.yreq2 (yreq2) ,
.r_data (data) ,
.yack (yack) ,
.result (result)
);
endmodule
波形仿真
testbench代码如下:
`timescale 1ns/1ns
module tb_shake_hand_asy();
reg tx_clk ;
reg rv_clk ;
reg rst_n ;
reg en ;
wire [5:0] result ;
initial begin
tx_clk = 1'b0;
rv_clk = 1'b0;
rst_n = 1'b0;
en = 1'b0;
#30
rst_n = 1'b1;
@(posedge tx_clk);
en = 1'b1; //启动
@(posedge tx_clk);
en = 1'b0;
end
always #15 tx_clk = ~tx_clk;
always #10 rv_clk = ~rv_clk;
shake_hand_asy u_shake_hand_asy(
. tx_clk (tx_clk) ,
. rv_clk (rv_clk) ,
. rst_n (rst_n) ,
. en (en) ,
. result (result)
);
endmodule
波形如下所示:
可知仿真正确。
总结
自己基础还很薄弱,继续加油!!!!