使用握手信号实现跨时钟域数据传输(牛客题霸_VL60)

一、题目说明——牛客题霸_VL60

        分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加必要的握手信号,保证数据传输不丢失。
        模块的接口信号图如下:

        data_req表示数据请求接受信号。当data_out发出时,该信号拉高,在确认数据被成功接收之前,保持为高,期间data应该保持不变,等待接收端接收数据。
        当数据接收端检测到data_req为高,表示该时刻的信号data有效,保存数据,并拉高data_ack
        当数据发送端检测到data_ack为高,表示上一个发送的数据已经被接收。撤销data_req,然后可以改变数据data。等到下次发送时,再一次拉高data_req

网站:使用握手信号实现跨时钟域数据传输_牛客题霸_牛客网

二、题目分析

分析题目可知,我们需要设计的电路应满足以下特征:

1)功能:实现跨时钟域数据传输,传输内容是循环发送0-7.

2)发端端口:发端时钟clk_a、共用复位信号rst_n、发送数据data,发送成功应答data_ack、发送请求data_req。

     收端端口:收端时钟clk_b、共用复位信号rst_n、接收数据data、接收成功应答data_ack、发送请求data_req。

3)触发方式:时钟上升沿触发,rst_n同步且低电平有效。

4)时序解释说明

        复位后,两个模块将初始化。

        在发端的时钟上升沿到来时,发端先将data_req置1,随后等待收端的应答data_ack。直到收到应答之前,发端不进行其他任何操作。

        收端初始化后,将data_ack置0,若收到了发送请求,则将data_ack置1,并等待data,收到data后将data_ack置0,以告知发端成功接收了data。

三、实现与总结

顶层模块

module top(input clk_a,clk_b,
           input rst_n,
           output wire [3:0] data_receive);

wire [3:0] data_driver;
wire data_req,data_ack;
data_driver m1(.clk_a(clk_a),
               .rst_n(rst_n),
               .data(data_driver),
               .data_ack(data_ack),
               .data_req(data_req));
data_receiver m2(.clk_b(clk_b),
               .rst_n(rst_n),
               .data(data_driver),
               .data_ack(data_ack),
               .data_req(data_req),
               .data_receive(data_receive));
endmodule

发端模块

module data_driver(
	input clk_a,
	input rst_n,
	input data_ack,
	output reg [3:0]data,
	output reg data_req
	);

reg flag_transimision;

always @(posedge clk_a or negedge rst_n)begin 
    if(~rst_n)begin
	    data <= 4'b0;
	    data_req <= 1'b1;
	    flag_transimision <= 1;
	end
    else begin
        if(~data_ack)
            flag_transimision <= 1'b1; 
	    if(data_ack && flag_transimision)begin
		    data_req <= 1'b0;
		    flag_transimision <= 1'b0;
		    repeat(5)@(posedge clk_a);
		    data <= data + 1'b1;
		    if(data == 4'b0111)begin
			    data <= 4'b0;
            end
	    end
	if(flag_transimision)
	   data_req <= 1'b1;
    end
end

endmodule

收端模块

module data_receiver(
	input clk_b,
	input rst_n,
	input [3:0] data,
	input data_req,
	output reg data_ack,
	output reg [3:0] data_receive
	);

reg flag_receive;

always @(posedge clk_b or negedge rst_n)begin   
    if(~rst_n)begin
	    data_ack <= 1'b0;
	    flag_receive <= 1'b1;
    end
    else begin
        if(~data_req)
           flag_receive <= 1'b1;
        if(data_req && flag_receive)begin
	       data_ack <= 1'b1;
	       data_receive <= data;
	       flag_receive <= 1'b0;
	    end
        if(~data_req && (~flag_receive))
            data_ack <= 1'b0;
    end
end

endmodule

测试台

module simulation;
    reg clk_a,clk_b,rst_n;
    wire [3:0] data_receive;

top m0(.clk_a(clk_a),
       .clk_b(clk_b),
       .rst_n(rst_n),
       .data_receive(data_receive));

initial begin
    clk_a = 1'b0;clk_b = 1'b0;
    #2 rst_n = 1'b0;#2 rst_n = ~rst_n;
end

initial begin
    forever # 1 clk_a = ~clk_a;
end

initial begin
    forever # 2000 clk_b = ~clk_b;
end

//initial begin
//$monitor($time,"data_receive = %b",data_receive);
//end
initial begin
    repeat(100)@(posedge clk_b);
    $stop;
end
endmodule

仿真波形图:

情况一:收发端相同时钟

 情况二:收端时钟远远快于发端时钟

 情况三:发端时钟远远快于收端时钟

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值