分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加必要的握手信号,保证数据传输不丢失。
握手信号的要求
数据应该在发送时钟域内稳定至少两个时钟上升沿。
请求信号”xreq"的宽度应该超过两个上升沿时钟,否则从高速时钟域向低速时钟域传递可能无法捕捉到该信号。
握手信号的特点
跨时钟域传输单个数据的延迟比使用FIFO传输相同的数据要大得多。
发送的数据不能连续,也不能太快,必须在req拉低的时候才能开始一个新数据的发送。如果在req拉高的时候又来了新的数据,这个数据就会被丢掉。所以对于连续数据的跨时钟域同步一般是采用异步缓存的方式
`timescale 1ns/1ns
module data_driver(
input clk_a,
input rst_n,
input data_ack,
output reg [3:0]data,
output reg data_req
);
reg data_ack_reg1;
reg data_ack_reg2;
reg [3:0] cnt;
//收到的ack打两拍 降低亚稳态
always@(posedge clk_a or negedge rst_n)
if(!rst_n)begin
data_ack_reg1 <= 1'b0;
data_ack_reg2 <= 1'b0;
end else begin
data_ack_reg1 <= data_ack;
data_ack_reg2 <= data_ack_reg1;
end
//收到ack之后 可以在总线上放下一个数据
always@(posedge clk_a or negedge rst_n)
if(!rst_n)
data <= 'd0;
else if(data_ack_reg1 & ~data_ack_reg2)
data <= data + 1;
//收到ack 间隔五个时钟周期 发送下一个数据
always@(posedge clk_a or negedge rst_n)
if(!rst_n)
cnt <= 'd0;
else if(data_ack_reg1 & ~data_ack_reg2)
cnt <= 'd0;
else if(data_req)
cnt <= cnt;
else
cnt <= cnt + 1'b1;
//计数到4 则拉高req代表输入数据有效,收到ack之后 req即可拉低
always@(posedge clk_a or negedge rst_n)
if(!rst_n)
data_req <= 1'b0;
else if(cnt == 3'd4)
data_req <= 1'b1;
else if(data_ack_reg1 & ~data_ack_reg2)
data_req <= 1'b0;
endmodule
module data_receiver(
input clk_b,
input rst_n,
output reg data_ack,
input [3:0]data,
input data_req
);
reg data_req_reg1;
reg data_req_reg2;
//req打拍 降低亚稳态
always@(posedge clk_b or negedge rst_n)
if(!rst_n)begin
data_req_reg1 <= 1'b0;
data_req_reg2 <= 1'b0;
end else begin
data_req_reg1 <= data_req;
data_req_reg2 <= data_req_reg1;
end
//收到req 就可以将ack拉高
always@(posedge clk_b or negedge rst_n)
if(!rst_n)
data_ack <= 1'b0;
else if(data_req_reg2)
data_ack <= 1'b1;
else
data_ack <= 1'b0;
endmodule