流水线中的握手信号笔记

1.《握手信号的打拍(一)》

解释了,为什么在流水线中,握手信号不能简单得加一级寄存器

 

业界关于流水线级握手信号的标准答案是 skid buffer,此外还有人提到了 Register slice

 

2.《握手信号的打拍(二)》

为什么简单加一级寄存器会握手失败 ?

从图1可以看出,加入 Handshake Pipe Module 之后,在 Sender 端有一组握手信号,在 Receiver 也有一组握手信号。也就是说,我们必须同时满足 Sender 端和 Receiver 端的握手,才能保证握手成功。

但是简单加一级寄存器只是满足一边的,图2是对 valid+data 打一拍,可以看出只能满足 Sender 端的握手时序。

如图,对 valid + data 打一拍,只满足了 Sender 端的握手时序

 

 

握手信号 valid 打拍的改进

观察图2,我们可以看到,Receiver 端不能满足握手时序的原因是 Sender 端认为数据已经被接收,从而拉低 valid,导致 valid_pipe 也被拉低。那么我们解决的办法应该是在 pipe 模块中保持住 valid 直到 receiver 端也握手成功,如图3所示。

图3 - handshake valid pipe example1

为了提高效率,我们可以在 IDLE 状态(即没有保持数据)时让 pipe 模块输出 ready_o 高电平,直到 Sender 端握手成功。接着保持住 valid 和 data,直到 Receiver 端也握手成功。如图4所示。

图4 - handshake valid pipe example2

图5是 Sender 端连续发送数据,但是 Receiver 端 ready 不连续的例子。可以看到 Sender 端有三次握手,Receiver 端也有三次握手。

图5 - handshake valid pipe example3

图6是 Sender 端连续发送数据,Receiver 端 ready 也连续的例子。

图6 - handshake valid pipe example4

 

后面有对 ready 信号打拍的内容,不明白为什么要打拍,我觉得现在这样就挺好,skip

 

 

3. 芯片设计-握手与反压(valid&ready)

 

从工厂流水线说起

本来一众打工人在流水线上"快快乐乐"地搬砖,突然receiver处理不过来了,拉低了data_o_ready。为避免数据丢失,得立刻启动应急方案。

应急方案1 (receiver 直接告知 sender 不接受新数据,同时使用 FIFO 接收流水线中的数据) (缺点:成本高)

receiver立刻通知sender,拉低data_i_ready(assign data_i_ready = data_o_ready),不允许再进新数据了。

(注:本章所有图示均用红色表示该pipe处于工作状态,无色表示该pipe处于空闲状态)

此时虽然没有新输入了,但是流水线中的搬砖人没有得到通知,还在继续工作。意味着流水线还会输出3个数据,但是receiver已经拒绝接收数据了。没办法,这时我们得放个fifo在流水线末尾,fifo深度等于流水线级数。

虽然这个方案解决了问题,但增加了fifo,提升了工厂成本。老板不允许,想想其他方案吧。

应急方案2 (reciver 暂停整个 sender 和所有流水级)     (缺点:流水级中没有被塞满的部分也会空转,每次流水线复位都会有气泡在悬停)

在这个方案中,receiver不仅通知sender暂停,还会通知整个pipe暂停。此时不会有数据输出,也不需要加fifo了。

但从上图我们可以看到,PIPE2是空闲的,这意味着虽然此时receiver拒绝接收,但其实没必要立刻停掉流水线的,完全可以再跑一拍流水的。可能打工人觉得没啥,等一下就好,但工厂老板可不是这样想的。

首先,出现pipe空闲的情况,都是在流水初始阶段,这时候后级流水需要等若干拍才能拿到数据来干活。而我们往往会在完成一次任务后复位流水,而每次复位都会存在PIPE空闲的情况。

我们假设一次任务处理16个数据,流水线为3级,处理完后需要复位流水线。

(注:在ISP中,一般是一帧为一次任务,在vblank期间复位流水线。这里为描述方便简化处理)

理想情况下,需要16+3拍才能完成任务,这里的3拍我们称之为气泡。这意味着对于receiver来说,他需要等待。老板不希望有气泡时间,希望的是他的receiver能够满负荷工作,即一旦receiver准备好了(data_o_ready为高),就能立刻接收到数据。

所以,方案还得改善……

 

 

应急方案3

我们得想办法挤掉气泡,别data_o_ready一拉低,大家就摸鱼,可以干的得继续干。

如方案2所提到的,气泡仅出现在流水线初始阶段,且持续拍数=流水级数。也就是说,在前几拍的时候,不用管data_o_ready什么状态,大家努力干活就是。

因此,我们添加一个计数器,当data_i_ready & data_i_valid有效时,计数器+1。当计数器小于流水级数时,流水线保持工作;当计数器≥流水级数时,流水线工作与否取决于data_o_ready.

 

 

本方案虽然把气泡挤掉了,但是控制会麻烦许多,因此:

  • 如果你的应用不在意这点气泡,比如一帧4K图像仅有几十拍的气泡,那用方案2也没啥问题。
  • 如果你的应用不会出现起始阶段,receiver不ready的情况,那也没必要用本方案

老板对本方案表示很满意,但我们可以继续想想有没有其他方案。

 

 

 

 

 

 

 目前看到应急方案4

 

应急方案4

逐级反压,只要我的后级空闲或ready, 我就传数据给他。

以下是代码。注意:以下代码要配合下面的图,才比较好看懂

 

用代码表示:

module pipe_ctrl #(
	parameter DW = 8
)(
	input clk,
	input rst_n,

	output          data_i_ready,
	input           data_i_valid,
	input  [DW-1:0] data_i,

	input           data_o_ready,
	output          data_o_valid,
	output [DW-1:0] data_o
);

// -------------------
// signals def
// -------------------
reg           valid_l1;
wire          ready_l1;
reg  [DW-1:0] data_l1;

reg           valid_l2;
wire          ready_l2;
reg  [DW-1:0] data_l2;

reg           valid_l3;
wire          ready_l3;
reg  [DW-1:0] data_l3;

// -------------------
// pipe logic
// -------------------
// l0
assign data_i_ready = ~valid_l1 || ready_l1;

// l1
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n)
		valid_l1 <= 0;
	else if(data_i_ready)
		valid_l1 <= data_i_valid;
end

assign ready_l1 = ~valid_l2 || ready_l2;

always @ (posedge clk) begin
	if(data_i_ready && data_i_valid)
		data_l1 <= data_i + 1;
end

// l2
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n)
		valid_l2 <= 0;
	else if(ready_l1)
		valid_l2 <= valid_l1;
end

assign ready_l2 = ~valid_l3 || ready_l3;

always @ (posedge clk) begin
	if(ready_l1 && valid_l1)
		data_l2 <= data_l1 * 2;
end

// l3
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n)
		valid_l3 <= 0;
	else if(ready_l2)
		valid_l3 <= valid_l2;
end

assign ready_l3 = data_o_ready;

always @ (posedge clk) begin
	if(ready_l2 && valid_l2)
		data_l3 <= data_l2 + 3;
end

// -------------------
// output
// -------------------
assign data_o_valid = valid_l3;
assign data_o       = data_l3;

endmodule 

对比方案3:

  • 方案3需要知道一次任务中需要处理的数据量,但是在写pipe控制代码时更加简单,只涉及输入和输出的valid,ready信号。对于乘法器等内部有多级流水线的pipe,且你一般拿不到它内部每级的valid,ready,这时候就只能用方案3了。
  • 方案4不需要知道一次任务中需要处理的数据量,但是写代码时要麻烦些,需要对每一级的valid,ready进行控制(当然你也可以用generate实现)。

 

 

 

 

 

 

 

 

 

 

 

posted @ 2023-04-17 14:39  yinhuachen  阅读(395)  评论(0编辑  收藏  举报