使用握手信号实现跨时钟域数据传输(verilog)

大家好,最近汇总了2021年oppo哲库招聘手撕代码题目,本文章一共含有以下几个题目:

一,使用握手信号实现跨时钟域数据传输(verilog)

二,自动售卖机(verilog)

三,序列发生器(verilog)

四,根据RTL图编写Verilog程序(verilog)

下面对各自题目分别进行解析:

一,使用握手信号实现跨时钟域数据传输(verilog)

1)题目

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

使用握手信号实现跨时钟域数据传输(verilog)_第1张图片 图1:接口图

 

 

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

其中:

clk_a:发送端时钟信号

clk_b:接收端时钟信号

rst_n:复位信号,低电平有效

data_ack:数据接收确认信号

data:发送的数据

data_req:请求接收数据

2)解题逻辑

所谓握手信号即加入一些指示信号,在两个模块间确认数据已经被接受之后再进行下一个数据的传输。一般来说,发送端随数据发出一个数据有效信号,或者称为数据请求接受信号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改变的指示信号。

②同时在data_ack有效之后,开始计数五个时钟,之后发送新的数据,也就是再一次拉高data_req.

③接收端的逻辑较为简单,首先是探测data_req的电平,如果data_req为高,表示有数据正在传输,则保存该时刻的数据,然后拉高data_ack告知发送端数据已经接收,直到发送端撤销data_req。

3)代码:

其中:

shakehand_tx是发送模块; 

shakehand_rx是发送模块; 

shakehand_top是顶层模块,负责将上述两个模块端口连接; 

shakehand_top_TB是shakehand_top的仿真文件;

`timescale 1ns / 1ps
/
module shakehand_tx(
	input clk_a,
	input rst_n,
	input data_ack,
	output reg [3:0]data,
	output reg data_req,
	output [3:0] cnt
    );
	 
reg data_ack_r1,data_ack_r2;
always@(posedge clk_a or negedge rst_n)
begin
	if(rst_n==0)
		begin
			data_ack_r1 <= 1'b0;
			data_ack_r2 <= 1'b0;
		end
	else
		begin
			data_ack_r1 <= data_ack;
			data_ack_r2 <= data_ack_r1;
		end	
end

always@(posedge clk_a or negedge rst_n)
begin
	if(rst_n==0 || (data == 4'd8))
		begin
			data <= 4'b0;			
		end
	else
		if(data_ack_r1 && !data_ack_r2 )
			begin
				data<= data +1'b1 ;
			end	
		else
			begin
				data<= data   ;
			end
end

reg [3:0] cnt=4'd0 ;
always@(posedge clk_a or negedge rst_n)
begin
	if(rst_n==0 || (data_ack_r1 && !data_ack_r2) )
					cnt <= 4'b0;
	else	if( data_req==1'b1) //捕捉上升沿				
					cnt<= cnt   ;				
	else				
					cnt<= cnt + 1'b1;
				
end

always@(posedge clk_a or negedge rst_n)
begin
	if(rst_n==0 ||(data_ack_r1 && !data_ack_r2)  )		
			data_req <= 1'b0;			
	else	if(cnt ==4'd4)			
			data_req<= 1'b1  ;				
	else			
			data_req<= data_req ;		
			
end




endmodule

下面是rx:

`timescale 1ns / 1ps
///
module shakehand_rx(
	 input clk_b,
    input rst_n,
    output reg data_ack,
    input [3:0]data,
    input data_req,
	 output [3:0] data_reg
    );
reg [3:0] data_reg=4'd0;
reg data_req_r1,data_req_r2;
always@(posedge clk_b or negedge rst_n)
begin
	if(rst_n==0)
		begin
			data_req_r1 <= 1'b0;
			data_req_r2 <= 1'b0;
		end
	else
		begin
			data_req_r1 <= data_req;
			data_req_r2 <= data_req_r1;
		end	
end

always@(posedge clk_b or negedge rst_n)
begin
	if(rst_n==0)
		begin
			data_ack <= 1'b0;
			data_reg <= 4'd0;
		end
	else if(data_req_r1 && !data_req_r2)
		begin
			data_ack <= 1'b1;
			data_reg <= data;
		end	
	else
		begin
			data_ack <= 1'b0;
			data_reg <= data_reg;
		end
end

endmodule

下面是top:

`timescale 1ns / 1ps
/
module shakehand_top(
input sys_clk,
input rst_n,
output [3:0] data,
output data_ack,
output data_req,
output [3:0] cnt,
output [3:0] data_reg,
output clk_a,
output clk_b
    );
reg [15:0] cnt_clk=16'd0;
always@(posedge sys_clk or negedge rst_n)
begin
	if(rst_n==0 || cnt_clk ==16'b0000_0000_1111_1111) cnt_clk <= 16'd0;
	else cnt_clk <= cnt_clk + 1'b1;	 
end

reg  clk_a = 1'd0;
reg  clk_b = 1'd0;
always@(posedge sys_clk or negedge rst_n)
begin
	if(rst_n==0 || cnt_clk[2:0] ==3'd4) 
			clk_a <= 1'd0;
	else if(cnt_clk[2:0] == 3'd0) 
			clk_a <= 1'b1;	 
	else 
			clk_a <= clk_a;
end

always@(posedge sys_clk or negedge rst_n)
begin
	if(rst_n==0 || cnt_clk[3:0] ==4'd8) 
			clk_b <= 1'd0;
	else if(cnt_clk[3:0] == 4'd0)
			clk_b <= 1'b1;	
	else
			clk_b <= clk_b;
end	  
	 
	 
	 
	 
wire [3:0] data;
wire data_req;
wire data_ack;
wire [3:0] cnt;
wire [3:0] data_reg;
shakehand_tx myshakehand_tx 
(
    .clk_a(clk_a),			//input
    .rst_n(rst_n),			//input
    .data_ack(data_ack),	//input
    .data(data),			//output
	 .data_req(data_req),	//output
	 .cnt(cnt)
  );
  
  

shakehand_rx myshakehand_rx 
(
    .clk_b(clk_b),			//input
    .rst_n(rst_n),			//input
    .data_ack(data_ack),//output
    .data(data),				//input
	 .data_req(data_req),		//input
	 .data_reg(data_reg)
  );
  
  
endmodule

 下面是TB:

`timescale 1ns / 1ps



module shakehand_top_TB;

	// Inputs
	reg sys_clk;
	reg rst_n;

	// Outputs
	wire [3:0] data;
	wire data_ack;
	wire data_req;
	wire [3:0] cnt;
	wire [3:0] data_reg;
	wire clk_a;
	wire clk_b;
	
	// Instantiate the Unit Under Test (UUT)
	shakehand_top uut (
		.sys_clk(sys_clk), 
		.rst_n(rst_n), 
		.data(data), 
		.data_ack(data_ack), 
		.data_req(data_req), 
		.cnt(cnt), 
		.data_reg(data_reg),
		.clk_a(clk_a),
		.clk_b(clk_b)
	);

	initial begin
		// Initialize Inputs
		sys_clk = 0;
		 
		rst_n = 0;

		// Wait 100 ns for global reset to finish
		#100;rst_n = 1;
        forever 
		  begin
			#500 sys_clk = ~sys_clk;
			 
		   end
		// Add stimulus here

	end
      
endmodule

4)仿真结果:

使用握手信号实现跨时钟域数据传输(verilog)_第2张图片 图3:仿真结果

 为了说明结果,对图3进行放大得到图4:

使用握手信号实现跨时钟域数据传输(verilog)_第3张图片 图4:放大图

 下面对图4进行解释:

①在A时刻,clk_a下cnt计数到4开始传输数据,data_req变1;

②在B时刻,clk_b下的data_ack捕捉到data_req则data_ack变1,开始接受数据,所以data_reg变1;

③在C时刻,clk_a下的data_req变0则表示结束本次传输,cnt开始从0计数准备下一次传输。同时,clk_b下的data_ack变0,表示传输结束。

你可能感兴趣的:(verilog,fpga开发)