十二 · 2023年11月16日

pipeline高端玩法(七)—flush

✎ 编 者 按 

来看看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
微信公众号:
 title=

推荐阅读

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