✎ 编 者 按
看完了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电路对象。
在上面的这段描述中,针对驱动当前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拉低一拍。
仿真波形如下:
可以看到,由于这里流水线为3级,在index=5时执行flush数据3,4,5不会从data_out有效输出。
将flushRoot参数修改为false:
cond为高时data_out.valid仍然为高电平,下一个时钟周期拉低。虽然此时ready为低电平,这个数据没有被消耗,但其拉低时间不考虑ready信号的高低电平。
再将上面的代码换成flushNext:
可以看到,虽然cond为高,但其仍会坚持将此时已经传播到stage2的3给稳定传输出去,仅有4,5不会被data_out输出。
☆ END ☆
作者:玉骐
原文链接:Spinal FPGA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。