✎ 编 者 按
来看看Pipeline中的flush操作
flush
在Stage中,对于Flush有提供这两个函数:
def flushIt() : Unit = flushIt(ConditionalContext.isTrue)
def flushIt(cond : Bool, root : Boolean = true) : Unit = {
internals.request.flush += cond
if(root) internals.request.flushRoot += cond
}
可以看出,调用flush函数,会牵涉到internals.request中的fulsh以及flushRoot两个元素:
val flush = ArrayBuffer[Bool]()val flushRoot = ArrayBuffer[Bool]()
那么来分别看下这两个元素在Pipeline的build函数中都起了什么作用。
在build函数中,对于处于pipeline中没有驱动其他Stage的Stage,会调用propagateRequirements函数:
for(end <- connectionsWithoutSinks){ propagateRequirements(end)}
同时,build函数中有定义:
val clFlush = mutable.LinkedHashMap[ConnectionLogic, Bool]()
在函数propagateRequirements中:
var flush = stage.internals.request.flush.nonEmpty generate orR(stage.internals.request.flush)
也就意味着如果当前stage中若flush非空,则会将flush中的所有信号进行或操作得到一个flush信号。
(stage.internals.arbitration.isFlushed, flush) match { case (null, null) => case (x, null) => stage.isFlushed := False case (_, x) => stage.isFlushed := flush}
若flush非空,那么就会驱动赋值给stage中的internals.arbitration.
isFlushed。
def isFlushed : Bool = {
if(internals.arbitration.isFlushed == null) internals.arbitration.isFlushed = ContextSwapper.outsideCondScope(Bool())
internals.arbitration.isFlushed
}
对于驱动当前stage的Connection Logic,也会对flush有进行检测:
c.logics.reverseIterator.foreach{ l =>
clFlush(l) = flush
clFlushNext(l) = flushNext
clFlushNextHit(l) = null
if(flushNext != null){
clFlushNextHit(l) = Bool()
flush = flush match {
case null => clFlushNext(l) && clFlushNextHit(l)
case _ => flush || clFlushNext(l) && clFlushNextHit(l)
}
flushNext = l.alwasContainsSlaveToken match {
case true => null
case false => clFlushNext(l) && !clFlushNextHit(l)
}
}
if(flush != null) c.m.flushIt(flush, false)
if(flushNext != null) c.m.flushNext(flushNext)
clThrowOne(l) = throwOne
clThrowOneHit(l) = null
if(throwOne != null){
clThrowOneHit(l) = Bool()
throwOne = l.alwasContainsSlaveToken match {
case true => null
case false => clThrowOne(l) && !clThrowOneHit(l)
}
}
}
我们着重关注和flush相关的逻辑。首先会讲flush注册到驱动当前Stage的Conntection Logic中的clFlush中:
clFlush(l) = flush
此处flushNext我们先不用管,为null。而flush为非空,故对于驱动当前Stage的master侧Stage,会调用其flush函数以flush为变量为其注册flush动作(注意,root参数传输的为false)。也就具备了前向传播的特性:
c.m.flushIt(flush, false)
随后通过递归,flush动作也就回一直向前传播:
for(m <- stageMasters(stage)){ if(stage.internals.arbitration.propagateReady) m.internals.arbitration.propagateReady = true propagateRequirements(m)}
在每一级的Stage关系里,默认情况下:
s.output.valid := s.input.valid
而当flushRoot非空时,则会:
if(s.internals.request.flushRoot.nonEmpty) s.output.valid clearWhen(s.internals.arbitration.isFlushingRoot)
也就意味着output.valid会立即清空。
而在处理InterConnection时,会用到上面的clFlush:
val area = l.on(m, s, clFlush(l), clFlushNext(l), clFlushNextHit(l), clThrowOne(l), clThrowOneHit(l))
我们以M2S为例,其中定义了:
if (flush != null && !flushPreserveInput) s.valid clearWhen(flush)
s.valid为寄存器,也就意味着当flush为true时,s.valid将会在下一拍驱动为0,即下一级的Stage的input.valid将会在下一拍清零。
功能总结
结合Pipeline中的源代码,可以总结下flushIt的作用。
- 调用flushIt函数,会想request.flush中添加cond,若root 为true,则会向request.flushRoot中同时添加cond。
request.flush作用:
- 对StageN调用flushIt,在pipeline.build函数中会向其所有的前级Stage中同样添加调用相应的flushIt函数(root为false)
- request.flush主要在conntecion Logic中起作用,用于清除s.valid,即下一级的input.valid。
request.flushRoot的作用:
- request.flushRoot用于清除当前级的output.valid
- 默认情况下,每一级的output.valid:=input.valid
- 当flushRoot.orR为True时,会立即清除当前Stage的output.valid
- flushRoot中的元素并不会向前级传输
flushRoot的作用区分:
当在StageN调用flushIt时如果希望cond为true时下一拍不希望Stage(N+1)input.valid拉起,则需将root设置为true。否则因为output.valid:=input.valid,stageN的output.valid会驱动下一级的input.valid为True(之所以前向传输调用flushIt时root=false,原因在于flush会作用于Connection Logic确保下一拍valid清零)。
example
给一个简单的example:
case class Test5() extends Component{
val io=new Bundle{
val data_in=slave(Flow(UInt(8 bits)))
val data_out=master(Flow(UInt(8 bits)))
val cond=in Bool()
}
noIoPrefix()
val A=Stageable(UInt(8 bits))
val pip=new Pipeline{
val stage0=new Stage{
internals.input.valid:=io.data_in.valid
A:=io.data_in.payload
}
val stage1=new Stage(Connection.M2S()){}
val stage2=new Stage(Connection.M2S()){
io.data_out.valid:=internals.output.valid
io.data_out.payload:=A
this.flushIt(io.cond)
}
}
}
这里在stage2调用flushIt函数,当io.cond为true时,整个流水线都将会清空。而由于root默认为true,故io.cond为true时,io.data_out.valid会立即为0,即等效于:
io.data_out.valid:=internals.output.valid&(~io.cond)
如果root设置为false,那么io.cond为true时io.data_out.valid仍可能为True。
☆ END ☆
作者:玉骐
原文链接:Spinal FPGA
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。