【Verilog】valid-ready双向握手机制 ——很绕但是很有意思

题干

描述
实现串行输入数据累加输出,输入端输入8bit数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-ready双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。
电路的接口如下图所示。valid_a用来指示数据输入data_in的有效性,valid_b用来指示数据输出data_out的有效性;ready_a用来指示本模块是否准备好接收上游数据,ready_b表示下游是否准备好接收本模块的输出数据;clk是时钟信号;rst_n是异步复位信号。


接口时序示意图
输入描述:
input clk ,
input rst_n ,
input [7:0] data_in ,
input valid_a ,
input ready_b
输出描述:
output ready_a ,
output reg valid_b ,
output reg [9:0] data_out

代码框架:

module valid_ready(
	input 				clk 		,   
	input 				rst_n		,
	input		[7:0]	data_in		,
	input				valid_a		,
	input	 			ready_b		,
 
 	output		 		ready_a		,
 	output	reg			valid_b		,
	output  reg [9:0] 	data_out
);

endmodule

时序解释和“双向握手机制”的理解


【总时序】
本模块在valid_a和ready_a握手(都拉高)后,开始接收上游下行的数据;(ready_a是输出,受制于其他变量)
本模块接受数据时,valid_b始终保持低。(并且ready_a也拉低,不允许输入)
接收完四个数据并累加后,valid_b拉高(表示data_out有效),等待ready_b握手。
ready_b握手(拉高)后,data_out被赋予值。此时ready_a应立刻拉高,进行下一轮读取和累加。
【两次握手】
1.输入握手:valid_a和ready_a。(ready_a等待valid_a)
2.输出握手:valid_b和ready_b。(valid_b等待ready_b)
【握手间的相互制约】
输出握手后,ready_a立刻拉高。


代码分割理解

  1. 计数器:计四拍,表示接受四个数。【计数器开始计数的标志是:输入握手】
//计数器控制块。计四拍。00 01 10 11 -> 00 01 10 11 -> ......
reg [1:0] cnt;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	cnt <= 2'b00;
	else if (valid_a && ready_a)	cnt <= cnt + 1'b1;	//接受数据
	else	cnt <= cnt;		//不接受数据
end
  1. OUTPUT控制:即书写控制ready_a 和 valid_b的时序块。
/* 
	ready_a 和 valid_b:
	1. 他们是 OUTPUT,需要我们手动去拉高拉低。
	2. 他们控制时序,互为判断依据。
*/
//ready_a拉高的情况:1.ready_b拉高时,本模块数据下行,此时立刻允许新的data输入,立刻拉高ready_a。(保证无气泡传输)
//                  2.valid_b拉低时,本模块还在接受数据,ready_a持续高。
assign ready_a = (ready_b || !valid_b) ? 1 : 0;

//valid_b拉高拉低的情况: 1.本模块接收完数据时:valid_b拉高。(此时即在等待ready_b握手,进行下行输出)
//						 2.valid_b和ready_b握手后,打一拍,valid_b再次拉低,准备接受新的数据。
//【valid_b:指示data_out数据是否有效,即指示“输入是否完成”和“输出是否完成”】
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	valid_b <= 1'b0;
	else if(valid_a && ready_a && (cnt == 2'b11))  //1.输入结束,等待握手
		valid_b <= 1'b1;
	else if(valid_b && ready_b)	//2.输出握手后
		valid_b <= 1'b0;
end
  1. 数据写入和写出:
//数据写入和写出
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	
		data_out <= 0;
	else if((cnt == 2'b00) && valid_a && ready_a && ready_b)
		//接收完毕,等待ready_b拉高。ready_b一旦拉高,数据写出到data_out。
		//同时开始下一周期的写入。
		data_out <= data_in;
	else if(valid_a && ready_a)	//输入握手:正在接收上游数据,累加
		data_out <= data_out + data_in;
	//注意:这两个else if不能调换位置!!!【md看了半天】
end

【注意:else if从上到下进行顺序判断,如果不进行嵌套,条件多的必须写在上面。否则用else if嵌套。】


完整代码

`timescale 1ns/1ns

module valid_ready(
	input 				clk 		,   
	input 				rst_n		,
	input		[7:0]	data_in		,
	input				valid_a		,
	input	 			ready_b		,
 
 	output		 		ready_a		,
 	output	reg			valid_b		,
	output  reg [9:0] 	data_out
);


//计数器控制块。计四拍。00 01 10 11 -> 00 01 10 11 -> ......
reg [1:0] cnt;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	cnt <= 2'b00;
	else if (valid_a && ready_a)	cnt <= cnt + 1'b1;	//接受数据
	else	cnt <= cnt;		//不接受数据
end

/*
	【总时序】
	本模块在valid_a和ready_a握手(都拉高)后,开始接收上游下行的数据;(ready_a是输出,受制于其他变量)
	本模块接受数据时,valid_b始终保持低。(并且ready_a也拉低,不允许输入)
	接收完四个数据并累加后,valid_b拉高(表示data_out有效),等待ready_b握手。
	ready_b握手(拉高)后,data_out被赋予值。此时ready_a应立刻拉高,进行下一轮读取和累加。
*/

/*
	【两次握手】
	1.输入握手:valid_a和ready_a。(ready_a等待valid_a)
	2.输出握手:valid_b和ready_b。(valid_b等待ready_b)
	【握手间的相互制约】
	输出握手后,ready_a立刻拉高。
*/

/* 
	ready_a 和 valid_b:
	1. 他们是 OUTPUT,需要我们手动去拉高拉低。
	2. 他们控制时序,互为判断依据。
*/
//ready_a拉高的情况:1.ready_b拉高时,本模块数据下行,此时立刻允许新的data输入,立刻拉高ready_a。(保证无气泡传输)
//                  2.valid_b拉低时,本模块还在接受数据,ready_a持续高。
assign ready_a = (ready_b || !valid_b) ? 1 : 0;

//valid_b拉高拉低的情况: 1.本模块接收完数据时:valid_b拉高。(此时即在等待ready_b握手,进行下行输出)
//						 2.valid_b和ready_b握手后,打一拍,valid_b再次拉低,准备接受新的数据。
//【valid_b:指示data_out数据是否有效,即指示“输入是否完成”和“输出是否完成”】
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	valid_b <= 1'b0;
	else if(valid_a && ready_a && (cnt == 2'b11))  //1.输入结束,等待握手
		valid_b <= 1'b1;
	else if(valid_b && ready_b)	//2.输出握手后
		valid_b <= 1'b0;
end

//数据写入和写出
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)	
		data_out <= 0;
	else if((cnt == 2'b00) && valid_a && ready_a && ready_b)
		//接收完毕,等待ready_b拉高。ready_b一旦拉高,数据写出到data_out。
		//同时开始下一周期的写入。
		data_out <= data_in;
	else if(valid_a && ready_a)	//输入握手:正在接收上游数据,累加
		data_out <= data_out + data_in;
	//注意:这两个else if不能调换位置!!!【md看了半天】
end

endmodule
物联沃分享整理
物联沃-IOTWORD物联网 » 【Verilog】valid-ready双向握手机制 ——很绕但是很有意思

发表评论