解题思路:

       在两个不同时钟域之间进行数据传输,根据两个时钟域的频率快慢,可以分为多种情况,分别由对应的解决方法。而最通用的方法是使用握手信号,所谓握手信号即加入一些指示信号,在两个模块间确认数据已经被接受之后再进行下一个数据的传输。一般来说,发送端随数据发出一个数据有效信号,或者称为数据请求接受信号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。保证了数据传输的有效性。

注意到每次数据传输完成之后五个时钟,才再一次拉高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			


如果关于此次题单有问题或者反馈意见,欢迎加入牛客用户反馈群沟通~