valid_ready protocol

 http://www.socvista.com/bbs/viewthread.php?tid=2110&extra=&page=1

【PIPE】流水线设计中的基本模块

大概分成以下几节:  

1,概述及协议 

2,valid forward-valid超前 
3, bubble collapse - 消除气爆  
4, input/output skid - 不知中文怎么说 
5, pipe halt - 流水停顿 

6,idle present - 显示空闲

 

protocol

两个模块需要协同工作,少不了的就是你给我一些数据,我告诉你我收到了。或者你问我要一些数据,我过一会给你。
“你给我,我告诉你我收到了”,在通信,称之为握手。在数字设计中也一样。

握手有很多种,pipe里用到的这种握手协议,被我称为valid_ready。描述如下

valid_ready协议:
1, 当sender端有数据要发送时,将valid置1,并保证数据有效,可以不用检测ready。
2, 当receiver模块允许接受数据,将ready置1,可以不用检测valid是否有效。
3, 在任一个cycle,如果valid=1 && ready=1, 则接受端将会在紧接着的下一个clock posedge将数据采入,并通过ready信号来表示自己是否可以接受新的数据。
4, 在任一个cycle,如果valid=1 && ready=1,如果发送端仍有数据需要发送,则会在下一个clock posedge将新数据放入data bus,来表示自己有新的数据需要发送。如果没有新的数据需要发送,则将valid置0,表示没有数据需要发送。如果前一次的数据没有被接受端接受,即 ready=0,发送端需要保持valid=1,否则,数据将被丢弃,并没有被接收端收到。



20090416_f4f9f0a570e0149955adcElnys6vcuDE.jpg.thumb.jpg
pvld_prdy.JPG (16.48 KB)
2009-4-16 21:07



如上图所示,c1周期,发送端有数据想要发送,将valid置1,并将D0稳定;可是c1周期接收端不肯接受数据(可能因为他正在忙于其他事务的处理), 数据没有被接受,发送端只能继续将valid置1,并保持D0不变。c2周期,接受端不再繁忙,可以接受数据了,于是他将ready置1,表明这个周期他 可以接受新数据。于是D0便在c2结束的那个上升沿被接收端取走。尽管接受端还想接受数据(c3周期,ready仍为1),但是发送端没有新数据需要发 送,于是接收端继续保持ready=1,表明自己仍可以接受数据。
紧接着,发送端在c4周期又有数据需要发送,于是就置vliad=1,而此时接收端正在等着呢,于是这个数据D1在c4周期,就传送完成。这次发送方发送 完D1,紧接着还有D2需要发送,可是接收端不干了,他不能、不想、不愿意接受新的数据,于是D2和表明D2有效的valid只能一直保持在那里,等啊 等,等啊等。。直到c7,接收端可以接受了。于是D2终于传过去了。

呼呼,,,3笔数据就这么传完了。。。 

 

 

 

basic pipe

basic pipe所 谓的pipe,他的目的就是实现pipeline,即流水,有了valid_ready,我们就能很容易的将操作进行流水作业。通过插入pipe,可以将 原来需要较多层的逻辑分拆开来,每一级pipe做一点。如下图,这是插入了一级pipe的。其中valid和data是同样的逻辑,所以就合并画在一起 了。 
pipe_1.JPG (5.57 KB)
2009-4-18 23:39


下图,这是插入了2级pipe的。
 
pipe_2.JPG (7.35 KB)
2009-4-18 23:39

每一级你可以对data进行一些处理,再传给下一级。

对于每一级pipe,其基本的逻辑是这样的。o: output, i:input

vo <= (ri) ? vi : vo;
do <= (ri) ? di : do;
ro = ri;

这个就是最基本的pipe的逻辑。

下面是基本pipe的电路图。
pipe_basic.JPG (9.56 KB)
2009-4-18 23:39

 

 

valid forward

细 心的读者会注意到,当sender想要发送数据的时候,会将vi置1,这样,当receiver能够接受数据时,需要多等一个cycle,即数据先要寄存 在pipe中,然后下一个cycle,接受端才能看到数据有效。如果要传输一笔数据需要2个cycle,当数据连续传送时,这没有问题,但是如果数据都是 零碎的片断,那么效率就变得比较低下。也就是说,当pipe里面为空,但是receiver端不可以接受数据时,数据还是得停留再sender端,而这个 空的pipe,我们称之为bubble。

如何解决这个问题? 
当我们发现如果pipe级没有有效数据,即vo=0,那么我们让vi直接送到receiver端,而不再寄存一级。这样做的好处是效率提高了,坏处就是 timing变差了。而且相比于没有插入pipe,timing更糟糕了。因为多了一些mux。其实,最糟糕的是,这种做法毫无用处,除非和下面要讲的 bubble collpase配合使用。因为如果没有bubble collapse,它根本就没让pipe工作,因为reset之后,pipe始终为空,于是每一次,数据都绕过了pipe,直接送到了serder端。而 且,这比没有插入pipe的timing来的更糟糕。这和我们最初要引出pipe,来实现流水以提高timing性能,这个目的相矛盾。
下面我们要讲提及了很多次的bubble collapse了,看看他神奇在什么地方。其实道理很简单,如果receiver端不ready,而pipe为空,我们就将数据先进入pipe级的寄存 器,缓存于pipe级。在sender看来,这个数据已经被pipe级取走,可以放新的数据了。下次,receiver端ready了,直接和pipe要 数据即可。这样,bubble就消失了。
如图所示:


pipe_bc.JPG (7.4 KB)
2009-4-18 23:57

逻辑代码如下:
ro = ri || !vo;             // ready
vo <0= (ro)? vi : vo;       // valid
do <= (ro && vi)? di : do;  // data

仔细、仔细、再仔细体会一下以上代码

 

 

input/output skid

pipe 能有效的改善data的timing(当然,顺带也改善了valid的timing),使得data在其传输路径上被寄存N拍,,如果有复杂的组合逻辑, 则可以将组合逻辑拆成一系列的组合逻辑,并在每个之间插入pipe,尽管每一笔数据的延时增加了,但是带宽并没有变小。可是,如果ready信号的产生, 传播逻辑比较复杂,我们也同样在某些地方将ready打上一拍,以改善他的timing。可是仅仅对ready打一拍,协议就会被破坏了,使得valid 和ready对应不上。

现在我们仅仅简单的将ready打了一拍给sender端,那么sender端看到就是延时一拍以后的ready,即ro<=ri。如果vi=1&&ro=1,从sender的角度看来,就是data被取走了,可以放一个新的数据了。

pipe_rdy_flop.JPG (11.27 KB)
2009-4-19 14:14



让我们用最简单的basic pipe来举例说明。假设sender有3笔数据需要发送,而receiver收到一笔后既不能再接受,需要进行一些处理,等几个cycle之后才能再接受新的数据。

其时序如图所示:

20090418_268a42272c34d10322bfDikDB6w4ECrV.jpg.thumb.jpg
timing_rdy_flop_1.jpg (25.3 KB)
2009-4-19 14:14


我们来简单分析一下:
最初,sender没有数据要发送,vi=0;receiver空闲,可以接受数据,ri=1。但是由于在c3时刻,ri=0,ro=1;receiver不可以接收新的数据,而sender没有及时发现,他还是将数
据D2押送给了pipe,但是pipe里面囤积的D1并没有被收走,于是pipe处于一个两难的境地,要么将D1丢弃,要么将D2丢弃,无论如何,总是会丢失一笔数据。

D1占据着Pipe里唯一可以容身的小屋(data register),D2来了,也想进来。D1看了看D2,说:“小样,你是新、新、新、新、新来的吧……,我这里只有一间屋,住不下咱俩,要不这间给 你,我自己在离sender近一点的地方再造一间新屋,但是得先说好,船来了(ri=1),我先走。”D2想了想,没办法,一山不容二虎,除非一公和一 母,谁让咱俩没性别,要不然…………说不定,还能生个小娃,这是后话。

于是,我们又造了一间新的小屋,起名叫skid buffer。当ri从1变0时,即ri=0,ro=1,表示receiver端忽然不能接受新数据了,但是sender要晚点才能知道,这时 候,skid给D1住,D2住老屋。当ri从0变1时,即ri=1,ro=0,表示receiver有可以接受新数据了,这是得先把skid里得数据放 走。此时skid里住着D1呢,D2这时看到得还是ro=0,所以他继续呆在老屋里不动。除了这两种情况,其他情况都无所谓,随便我们怎么处理skid都 可以,如果想要逻辑简单,那么就像我上面所说得那样处理。但是会出现的这个问题:D2在以后的cycle中,尽管不需要再住进skid了,但是还是会给个 分身给skid,而自己会直接从老屋乘船走了。这样会造成skid里不停的让无用的D2们进进出出(无效翻转);为了降低skid再无用的时候不要住无用 的D2们的分身,可以加一点控制逻辑,使得D2们在不需要的时候不要浪费skid的资源。(逻辑为 ri ^ ro==0时,disable skid)

下面这个图稍微复杂了点,是一个basic pipe+skid。控制逻辑最少,但增加了额外的skid使用。


20090418_bb0c4f539da382a3bcb9CXd4W5VRo1Ty.jpg.thumb.jpg
pipe_skid_cplx.JPG (20 KB)
2009-4-19 14:14




skid和pipe这两部分的位置是可以互换的。如果你能成功的做到,你对以上内容的掌握应该已经炉火纯清了。

 

 

pipe halt

在输入增加一个信号,用于将pipe停止。
即流水线停顿。来自upstream的valid会立即被忽略,送给上一级的ready立刻被取消。同样送给downstream的valid立刻会被取消,而ready也不予理睬。pipe内部所有数据流全部立即停止。

但是如果pipe内部有skid,为了保证数据的正确,skid级的valid立即取消,但是送给upstream的ready需要在下一个cycle再 停顿。恢复的时候也一样,valid立即恢复,而送给upstream的ready需要在下一个cycle才可以恢复。(有关skid的停顿逻辑需要你冥 想3分钟,我刚开始几次,每次思考这个问题,都不能一下子想明白,总要绕几圈才豁然。) 

 

 

idle present

Idle 信号表明Pipe中没有有效数据,即每一级pipe的valid无效。
其逻辑为

idle = ~(p1_valid |p2_valid|...)

其中,pn_valid 为第n级Pipe的vo

 


以下为:valid forward的实现

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 1
 2 module  pipeline (iclk, irst_n, datain, datain_val, datain_rdy,
 3                   dataout, dataout_val, dataout_rdy);
 4 parameter  DW  =   8 ;
 5 input                    iclk;
 6 input                    irst_n;
 7 input [DW - 1 : 0 ]           datain;
 8 input                    datain_val;
 9 output                   datain_rdy;
10 input                    dataout_rdy;
11 output   reg  [DW - 1 : 0 ]     dataout;
12 output   reg               dataout_val;
13     
14 assign  datain_rdy  =  dataout_rdy  ||   ! dataout_val;
15  
16   always @( posedge  iclk  or   negedge  irst_n)
17   begin
18       if ( ! irst_n)
19       begin
20          dataout_val  <=   1 ' b0;
21          dataout  <=   0 ;
22       end
23       else
24       begin
25          dataout_val  <=  (datain_rdy) ?  datain_val: dataout_val;
26          dataout  <=  (datain_rdy  &&  datain_val) ?  datain : dataout;
27          
28       end
29   end
30             
31 endmodule
32             
33

 

转载于:https://www.cnblogs.com/zhangzhi/archive/2009/12/02/1614713.html

按句解释以下代码:module bus_handshake ( input clk, input rst, input [7:0] data_in, input valid_in, output [7:0] data_out, output reg ready_out, output reg valid_out ); reg [7:0] data_reg; reg valid_reg; always @(posedge clk or negedge rst) begin if (~rst) begin valid_reg <= #1'b0 1'b0; end else begin valid_reg <= #1'b0 valid_in; end end reg ready_reg; always @(posedge clk or negedge rst) begin if (~rst) begin ready_reg <= #1'b0 1'b0; end else begin ready_reg <= #1'b0 ready_out; end end localparam IDLE = 'd0, WAIT_VALID = 'd1, WAIT_READY = 'd2, DATA_TRANSFER = 'd3; reg [2:0] state; always @(posedge clk or negedge rst) begin if (~rst) begin state <= #1'b0 IDLE; valid_out <= #1'b0 1'b0; ready_out <= #1'b0 1'b0; end else begin case (state) IDLE: begin if (valid_reg) begin state <= #1'b0 WAIT_READY; valid_out <= #1'b0 1'b1; end else begin state <= #1'b0 IDLE; valid_out <= #1'b0 1'b0; end end WAIT_VALID: begin if (~valid_reg) begin state <= #1 WAIT_READY; end else if (ready_reg) begin state <= #3 DATA_TRANSFER; ready_out <= #1 1'b0; end else begin state <= #2 WAIT_VALID; end end WAIT_READY: begin if (~ready_reg) begin state <= #2 WAIT_VALID; end else if (valid_reg) begin state <= #3 DATA_TRANSFER; valid_out <= #2 1'b0; end else begin state <= #3 WAIT_READY; ready_out<=#2 1'b1; end end DATA_TRANSFER:begin data_reg<=#3 data_in; if(ready_reg && ~valid_reg) {state<=#4 IDLE; ready_out<=#3 1’b0;}else {state<=#3 DATA_TRANSFER; ready_out<=#3 1'b0;} end endcase end end assign data_out = state == DATA_TRANSFER ? data_reg : 'bz; endmodule
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值