题解 | #使用握手信号实现跨时钟域数据传输#
使用握手信号实现跨时钟域数据传输
https://www.nowcoder.com/practice/2bf1b28a4e634d1ba447d3495134baac
main.v+tb.v
main分为两个模块:发送机和接收机
发送机和接收机分别又有几个模块在always作用下同时工作。
发送机:
1.发送数据0-7,重要的是改变数据的时间节点:当上次发送的数据已被成功接收,数据就加一,当数据等于7时,数据归零。那么数据何时被成功接收:当ack信号变为1时,代表数据被成功接收。
2.发送信号req,当数据被接收之后 req=0,等待五个周期的时间内 req=0,五个周期结束后开始发送数据 req=1,且在ack拉高之前req一直=1。
3.等待五个时钟周期,rst时 cnt=0,数据刚被接收后 ack拉高后,表示数据被接收,重新开始计数 置cnt=0,且在传输数据(req=1)期间cnt不变。
4.接受数据ack,打两拍 信号变化表示数据已经被接收(上升沿,但是用来做if判断的话 不能用上升沿这个语句,就用两拍之间数据不一样作为判断)
(整个过程就是等待五个周期后,req=1开始传数据,传数据期间cnt不变,数据传输完毕ack=1,req=0,cnt=0)
接收机:
1.接收数据data 打两拍,重要的是什么时候接收数据,当等待完五个周期 且req=1时接收数据
2.发送数据 ack 当接收到数据后ack被拉高 ,何以见得数据被接收,参见上一条。
module data_driver(
input clk_a,
input rst_n,
input data_ack,
output reg [3:0]data,
output reg data_req
);
reg data_ack_d0_r;
reg data_ack_d1_r;
reg data_ack_d2_r;
always @(posedge clk_a or negedge rst_n) begin
if (!rst_n) begin
data_ack_d0_r <= 'd0;
data_ack_d1_r <= 'd0;
data_ack_d2_r <= 'd0;
end
else begin
data_ack_d0_r <= data_ack;
data_ack_d1_r <= data_ack_d0_r;
data_ack_d2_r <= data_ack_d1_r;
end
end
//data
always @(posedge clk_a or negedge rst_n) begin
if (!rst_n) begin
data <= 'd0;
end
else if (data_ack_d1_r & !data_ack_d2_r) begin
data <= data == 'd7 ? 'd0 : data + 'd1;
end
end
//cnt
reg [3-1:0] cnt_r;
always @(posedge clk_a or negedge rst_n) begin
if (!rst_n) begin
cnt_r <= 'd0;
end
else if (data_ack_d1_r & !data_ack_d2_r) begin
cnt_r <= 'd0;
end
else if (data_req) begin
cnt_r <= cnt_r;
end
else begin
cnt_r <= cnt_r + 'd1;
end
end
//req
always @(posedge clk_a or negedge rst_n) begin
if (!rst_n) begin
data_req <= 'd0;
end
else if (data_ack_d1_r & !data_ack_d2_r) begin
data_req <= 'd0;
end
else if (cnt_r == 'd4) begin
data_req <= 'd1;
end
end
endmodule
module data_receiver(
input clk_b,
input rst_n,
input [3:0]data,
input data_req,
output reg data_ack
);
reg data_req_d0_r;
reg data_req_d1_r;
reg data_r;
always @(posedge clk_b or negedge rst_n) begin
if (!rst_n) begin
data_req_d0_r <= 'd0;
data_req_d1_r <= 'd0;
end
else begin
data_req_d0_r <= data_req;
data_req_d1_r <= data_req_d0_r;
end
end
always @(posedge clk_b or negedge rst_n) begin
if (!rst_n) begin
data_r <= 'd0;
data_ack <= 'd0;
end
else if (data_req_d1_r) begin
data_r <= data;
data_ack <= 'd1;
end
else begin
data_ack <= 'd0;
end
end
endmodule
`timescale 1ns/1ns
module testbench;
reg clk_a,clk_b,rst_n;
wire data_req,data_ack;
wire [3:0]data;
initial begin
clk_a=1;
clk_b=1;
end
always #10 clk_a=~clk_a;
always #15 clk_b=~clk_b;
initial begin
rst_n=0;
#30
rst_n=1;
end
data_driver u_1(
clk_a,
rst_n,
data_ack,
data,
data_req);
data_receiver u_2(
clk_b,
rst_n,
data,
data_req,
data_ack);
endmodule