十二 · 2023年11月23日

pipeline高端玩法(八)—FlushNext

✎ 编 者 按 

看完了flush,再看下flushNext的用法

flushNext

在Stage里,有关flushNext提供的API有:

def flushNext() : Unit = flushNext(ConditionalContext.isTrue)def flushNext(cond : Bool) : Unit = internals.request.flushNext += cond

    调用flushNext,最终会将flushNext的需求暂存到internals.request.flushNext中。

    在Pipeline中,propagateRequirements函数中对于每一级Stage的处理:

var flushNext = stage.internals.request.flushNext.nonEmpty generate orR(stage.internals.request.flushNext)

如果flushNext不为空,则将所有条件或后得到flushNext电路对象。

image.png

在上面的这段描述中,针对驱动当前Stage的Conntection处理,flushNext电路将会被存储在clFlushNext(l)中。如果flushNext不为空(line:5),可以看到在line7:9行处理时,不管flush是存在,都会创建一个flush电路对象,也就意味着一般情况下flush,flushNext不需要同时使用。而在line11:14中,以M2S为例,alwasContainsSlaveToken为True,会将flushNext清空。此时在line16:17时,仅会对驱动当前Stage的Stage Master调动flushIt函数,也就意味着flushNext将会向前传播,前级相当于执行flushIt。

clFlushNext的使用仅在Connection中使用到。还是以M2S为例,其处理逻辑为:

if(flushNext != null && !flushPreserveInput) s.valid clearWhen(flushNext && s.ready)

在这里,如果flushNext不为空(flushPreserveInput默认为true),s.valid仅会在slave端ready和flushNext同时为高时才会清零。对比flush操作:

if (flush != null && !flushPreserveInput) s.valid clearWhen(flush)

也就意味着flushNext存在ready的情况下才具有意义。

example

结合上面的分析,flushNext与flush的最大区别在于存在ready传播的情况。这里先给出一个flsuhIt的例子:

case class streamFlush() extends Component{  val io=new Bundle{    val data_in=slave(Stream(UInt(8 bits)))    val data_out=master(Stream(UInt(8 bits)))    val cond=in Bool()  }  noIoPrefix()  val A=Stageable(UInt(8 bits))  val pip=new Pipeline{    val stage0=new Stage{      this.driveFrom(io.data_in)      A:=io.data_in.payload    }    val stage1=new Stage(Connection.M2S()){    }    val stage2=new Stage(Connection.M2S()){      io.data_out.valid:=this.internals.output.valid      io.data_out.payload:=A      internals.output.ready=Bool()      this.flushIt(io.cond,true)      this.isReady      internals.output.ready:=io.data_out.ready      this.internals.arbitration.propagateReady=true    }  }  pip.build()}

这里是一个三级pipeline,最后一级调用flushIt操作,flushRoot参数传递为true。

采用下面的仿真代码:

object FlushSim extends App{  import spinal.core.sim._  SimConfig.withFstWave.compile(streamFlush()).doSim{dut=>    dut.io.data_in.valid#=false    dut.io.data_out.ready#=true    dut.io.cond#=false    dut.clockDomain.forkStimulus(10)    dut.clockDomain.waitSampling(10)    dut.io.data_in.valid#=true    for(index<- 1 until 10){      dut.io.data_in.payload#=index      if(index==5){        dut.io.cond#=true        dut.io.data_out.ready#=false      }else{        dut.io.cond#=false        dut.io.data_out.ready#=true      }      dut.clockDomain.waitSampling()    }    dut.io.data_in.valid#=false    dut.clockDomain.waitSampling(10)  }}

我们这里在index==5时将cond拉高一拍,data_out.ready拉低一拍。

仿真波形如下:

image.png

可以看到,由于这里流水线为3级,在index=5时执行flush数据3,4,5不会从data_out有效输出。

将flushRoot参数修改为false:

image.png

cond为高时data_out.valid仍然为高电平,下一个时钟周期拉低。虽然此时ready为低电平,这个数据没有被消耗,但其拉低时间不考虑ready信号的高低电平。

再将上面的代码换成flushNext:

image.png

可以看到,虽然cond为高,但其仍会坚持将此时已经传播到stage2的3给稳定传输出去,仅有4,5不会被data_out输出。

☆ END ☆

作者:玉骐
原文链接:Spinal FPGA
微信公众号:
 title=

推荐阅读

更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
1578
内容数
131
用SpinalHDL提升生产力
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息