解题思路:
在两个不同时钟域之间进行数据传输,根据两个时钟域的频率快慢,可以分为多种情况,分别由对应的解决方法。而最通用的方法是使用握手信号,所谓握手信号即加入一些指示信号,在两个模块间确认数据已经被接受之后再进行下一个数据的传输。一般来说,发送端随数据发出一个数据有效信号,或者称为数据请求接受信号data_req,接收端在data_req有效时,采集数据data,进行缓存或者其他处理,所以要保证在data_req有效期间,也就是还没收到data_ack确认信号之前,传输的数据data不能发生变化。然后接收端发送一个数据确认信号data_ack,告知发送端,数据已经接受,可以开始下一个数据的传输,发送端在接收到data_ack之后,撤销data_req,并可以改变数据,为下一步传输做准备。
解题过程:
首先是数据发送端,发送数据的标志是data_req拉高,在data_req拉高期间,data需要保持不变,一直到接收端完成数据的接收,即接收端发送data_ack确认信号。所以取data_ack的上升沿信号作为data_req撤销和data_out改变的指示信号。
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
begin
data_ack_reg_1 <= 0;
data_ack_reg_2 <= 0;
end
else
begin
data_ack_reg_1 <= data_ack;
data_ack_reg_2 <= data_ack_reg_1;
end
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
begin
data <= 0;
end
else if(data_ack_reg_1 && !data_ack_reg_2)
begin
data <= data +1;
end
else begin
data <= data;
end
同时在data_ack有效之后,开始计数五个时钟,之后发送新的数据,也就是再一次拉高data_req.
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
cnt <= 0;
else if (data_ack_reg_1 && !data_ack_reg_2)
cnt <= 0;
else if (data_req)
cnt <= cnt;
else
cnt <= cnt+1;
always @ (posedge clk_a or negedge rst_n)
if (!rst_n)
data_req <= 0;
else if (cnt == 3'd4)
data_req <= 1'b1;
else if (data_ack_reg_1 && !data_ack_reg_2)
data_req <= 1'b0;
else
data_req <= data_req;
接收端的逻辑较为简单,首先是探测data_req的电平,如果data_req为高,表示有数据正在传输,则保存该时刻的数据,然后拉高data_ack告知发送端数据已经接收,直到发送端撤销data_req。
reg data_req_reg_1;
reg data_req_reg_2;
reg [2:0]data_in_reg;
always @ (posedge clk_b or negedge rst_n)
if (!rst_n)
begin
data_req_reg_1 <= 0;
data_req_reg_2 <= 0;
end
else
begin
data_req_reg_1 <= data_req;
data_req_reg_2 <= data_req_reg_1;
end
always @ (posedge clk_b or negedge rst_n)
if (!rst_n)
data_ack <= 0;
else if (data_req_reg_1)
data_ack <= 1;
else data_ack <=0 ;
always @ (posedge clk_b or negedge rst_n)
if (!rst_n)
data_in_reg <= 0;
else if (data_req_reg_1 && !data_req_reg_2)
data_in_reg <= data;
else data_in_reg <= data_in_reg ;
仿真结果:
在仿真平台中,将两个模块分别例化并连接。得到如下结果:
如图所示,每次发送端的data_req拉高,接收端采集到data_req的上升沿后,将输出数据data同步到接收端,同时接收端发送data_ack信号告知发送端,数据已接受。在数据被成功接受之后,发送端才改变数值,同时拉低data_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_reg_1; reg data_ack_reg_2; reg [9:0] cnt; always @ (posedge clk_a or negedge rst_n) if (!rst_n) begin data_ack_reg_1 <= 0; data_ack_reg_2 <= 0; end else begin data_ack_reg_1 <= data_ack; data_ack_reg_2 <= data_ack_reg_1; end always @ (posedge clk_a or negedge rst_n) if (!rst_n) begin data <= 0; end else if(data_ack_reg_1 && !data_ack_reg_2) begin data <= data+1; end else begin data <= data; end //同时在data_ack有效之后,开始计数五个时钟,之后发送新的数据,也就是再一次拉高data_req. always @ (posedge clk_a or negedge rst_n) if (!rst_n) cnt <= 0; else if (data_ack_reg_1 && !data_ack_reg_2) cnt <= 0; else if (data_req) cnt <= cnt; else cnt <= cnt+1; always @ (posedge clk_a or negedge rst_n) if (!rst_n) data_req <= 0; else if (cnt == 3'd4) data_req <= 1'b1; else if (data_ack_reg_1 && !data_ack_reg_2) data_req <= 1'b0; else data_req <= data_req; endmodule module data_receiver( input clk_b, input rst_n, output reg data_ack, input [3:0]data, input data_req ); reg [3:0]data_in_reg; reg data_req_reg_1; reg data_req_reg_2; always @ (posedge clk_b or negedge rst_n) if (!rst_n) begin data_req_reg_1 <= 0; data_req_reg_2 <= 0; end else begin data_req_reg_1 <= data_req; data_req_reg_2 <= data_req_reg_1; end always @ (posedge clk_b or negedge rst_n) if (!rst_n) data_ack <= 0; else if (data_req_reg_1) data_ack <= 1; else data_ack <=0 ; always @ (posedge clk_b or negedge rst_n) if (!rst_n) data_in_reg <= 0; else if (data_req_reg_1 && !data_req_reg_2) data_in_reg <= data; else data_in_reg <= data_in_reg ; endmodule
如果关于此次题单有问题或者反馈意见,欢迎加入牛客用户反馈群沟通~