之前有系列文章介绍了SpinalHDL中Pipeline的使用,最近在一个功能模块中真实的使用了这个lib。虽然在使用上基于Flow/Stream的抽象已基本满足大多数使用场景,但在FPGA端有时为了优化时序往往不得不做一些逻辑打散,此时发现Pipeline中的标准化组件就有点难以满足需求了。
》举个例子
考虑下面的一个使用pipeline简单的逻辑代码:
这是一个很简单的逻辑,port_in打两拍输出至port_out。逻辑里有这样一个动作需求:
当port_out.valid为高且port_out.data1为true时,checked为真
这里checked输出为一个组合逻辑,如果外部在使用时还需要其他许多类似的条件那么就有可能会对时序收敛带来困难了(这里仅用来举个例子阐述这种类似的需求)。
那么也许想当然的将checked的判断条件声明一个Stageable变量在stage1中完成判断,stage2中直接使用,就像这么来做:
如此,我们将组合逻辑前移,checked输出为时序逻辑。看似完美是吧~
然而,如果你在VCS仿真器里仿真你会发现,checked可能刚一上电就是高电平导致后续逻辑异常。其原因就是cond_matched没有赋初值。
在pipeline的架构里,在Connection中实现了不同Stage之间的连接,其中也包含了时序协议的实现。以这里我们调用的M2S为例:
其将valid声明为Reg并赋给初始值False,然而对于payload仅声明为寄存器并未赋初值。
由于有valid信号指示,paylaod不赋初值无可厚非,奔着控制路径添加复位,数据路径不添加复位的原则,这里并没有问题。然而我们在针对一些时序优化的场景需要将部分paylaod赋初值,这里就不太符合我们的需求了。
》M2SExt
既然满足不了需求,那就扩展。这里的实现可能略显丑陋,但能解决问题。Stageable类型在Pipeline中例化为对象时会以我们声明的Stageable变量名作为结尾,我们只需在M2S的on实现基础上添加匹配规则即可。由于M2S是Class不可继承,这里重新定义了一个M2SExt来实现:
case class M2SExt(collapse: Boolean = true, holdPayload: Boolean = false, flushPreserveInput: Boolean = false) extends ConnectionLogic { val initMap = LinkedHashMap[String, Data]() def addInitValue[T <: Data](target: Stageable[T], initValue: T) = { initMap.update(target.getName(), initValue) } def on(m: ConnectionPoint, s: ConnectionPoint, flush: Bool, flushNext: Bool, flushNextHit: Bool, throwHead: Bool, throwHeadHit: Bool) = new Area { s.valid.setAsReg() init (False) s.payload.foreach(_.setAsReg()) m.ready match { case null => s.valid := m.valid (s.payload, m.payload).zipped.foreach(_ := _) case r => { if (flush != null && flushPreserveInput) s.valid clearWhen (flush) if (throwHead != null) s.valid clearWhen (throwHead) when(r) { s.valid := m.valid } when(if (holdPayload) m.valid && r else r) { (s.payload, m.payload).zipped.foreach(_ := _) } } } if (flush != null && !flushPreserveInput) s.valid clearWhen (flush) if (flushNext != null && !flushPreserveInput) s.valid clearWhen (flushNext && s.ready) if (flushNextHit != null) flushNextHit := True // assert(!(flushNext != null && flushPreserveInput)) if (m.ready != null) { m.ready := s.ready if (collapse) m.ready setWhen (!s.valid) } Component.current.addPrePopTask(() => { s.payload.foreach(paylaod => { initMap.foreach { case (signalEndName, initValue) => { if (paylaod.getName().endsWith(signalEndName)) { paylaod.init(initValue) } } } }) }) }
这里为M2S定义了一个addInitValue方法,从而能使得我们能够为某个ConnectionLogic中制定的Stageable映射电路对象添加复位值。在on实现函数最后通过添加PrePopTask来遍历搜索当前ConnectionLogic中对应的payload并赋初始值。
最终我们可以在实现里如此:
stage1To2Connection通过调用addInitValue来为cond_matched添加复位值,以此满足需求:
pipe_stage2_cond_matched添加复位控制。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !