题解 | #使用握手信号实现跨时钟域数据传输# VCS
使用握手信号实现跨时钟域数据传输
https://www.nowcoder.com/practice/2bf1b28a4e634d1ba447d3495134baac
使用vcs verdi环境,包含testbench和rtl.
处理跨时钟域信号打两拍同步,第三拍用于取上升沿产生控制信号,
当时钟频率相差过大,ack信号可能采不到,所以不能只产生一个clk的pulse,这里我做了一个可扩展长度的ack,req只有当检测到ack才会拉低所以不需要做这种处理.
`timescale 1ns/10ps
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;
rst_n = 1;
$fsdbDumpfile("out.fsdb");
$fsdbDumpvars(0,testbench);
#30
rst_n = 0;
#100
rst_n = 1;
#500_00 $finish();
end
always #15 clk_a = ~clk_a;
always #10 clk_b = ~clk_b;
data_driver dut_1
( .clk_a(clk_a),
.rst_n(rst_n),
.data(data),
.data_ack(data_ack),
.data_req(data_req)
);
data_receiver #(1) dut_2
( .clk_b(clk_b),
.rst_n(rst_n),
.data(data),
.data_ack(data_ack),
.data_req(data_req)
);
endmodule
module data_driver
(
input clk_a,
input rst_n,
input data_ack,
output reg [3:0] data,
output reg data_req
);
reg data_ack_dff1;
reg data_ack_dff2;
reg data_ack_dff3;
always @(posedge clk_a or negedge rst_n)begin
if(!rst_n)begin
data_ack_dff1 <= 1'b0;
data_ack_dff2 <= 1'b0;
data_ack_dff3 <= 1'b0;
end
else begin
data_ack_dff1 <= data_ack;
data_ack_dff2 <= data_ack_dff1;
data_ack_dff3 <= data_ack_dff2;
end
end
wire data_ack_rp = data_ack_dff2 & ~data_ack_dff3;
reg [2:0] data_cnt;
wire data_cnt5 = data_cnt==3'd4;
always @(posedge clk_a or negedge rst_n)begin
if(!rst_n)
data_cnt <= 3'b0;
else if(data_ack_rp)
data_cnt <= 3'b0;
else if(data_req)
data_cnt <= data_cnt;
else
data_cnt <= data_cnt + 1'b1;
end
always @(posedge clk_a or negedge rst_n)begin
if(!rst_n)
data <= 4'b0;
else if(data_ack_rp)
data <= {1'b0,(data[2:0] + 1'b1)};
else
data <= data;
end
always @(posedge clk_a or negedge rst_n)begin
if(!rst_n)
data_req <= 1'b0;
else if(data_ack_rp)
data_req <= 1'b0;
else if(data_cnt5)
data_req <= 1'b1;
end
endmodule
module data_receiver
#(
parameter DATA_ACK_EXT_LEN = 1
)
(
input clk_b,
input rst_n,
input [3:0] data,
input data_req,
output reg data_ack
);
reg data_req_dff1;
reg data_req_dff2;
reg data_req_dff3;
always @(posedge clk_b or negedge rst_n)begin
if(!rst_n)begin
data_req_dff1 <= 1'b0;
data_req_dff2 <= 1'b0;
data_req_dff3 <= 1'b0;
end
else begin
data_req_dff1 <= data_req;
data_req_dff2 <= data_req_dff1;
data_req_dff3 <= data_req_dff2;
end
end
wire data_req_rp = data_req_dff2 & ~data_req_dff3;
reg [3:0] data_rec;
always @(posedge clk_b or negedge rst_n)begin
if(!rst_n)
data_rec <= 4'b0;
else if(data_req_rp)
data_rec <= data;
else
data_rec <= data_rec;
end
reg [15:0] data_ack_cnt;
always @(posedge clk_b or negedge rst_n)begin
if(!rst_n)
data_ack_cnt <= 16'b0;
else if(data_req_rp)
data_ack_cnt <= 16'b0;
else if(data_req_dff3)
data_ack_cnt <= data_ack_cnt + 1'b1;
else
data_ack_cnt <= data_ack_cnt;
end
wire data_ack_ext = data_req_dff3 & (data_ack_cnt < DATA_ACK_EXT_LEN);
always @(posedge clk_b or negedge rst_n)begin
if(!rst_n)
data_ack <= 1'b0;
else if(data_req_rp | data_ack_ext)
data_ack <= 1'b1;
else
data_ack <= 1'b0;
end
endmodule
#练习时长两年半#