十二 · 4月29日

上帝视角—mem.streamReadSync冗余

✎ 编 者 按 

最近收集一些代码覆盖率,其中牵涉到关于mem中streamReadSync的使用,在覆盖率收集过程中留意到其中具备的一些不可达的状态。

1 Mem的模型

提起Mem(RAM),以常用的简单双端口为例,也许一个初学FPGA没多久的人都会觉得没啥,不就是一些wen、ren、waddr、raddr、wdata、rdata信号的控制么,so easy。曾经我也这么是这么想。直到我刚开始学习SpinalHDL时看到了这个例子《见微知著——从example感受Lib 的强大》,才真正意识到在逻辑编程里逻辑解耦与模型抽象的重要性。现在去看Mem,无外乎这两种模型:

image.png

写接口无需论述。对于读接口,通过模型抽象将读指令与读数据解耦成两条外部看来独立的数据流,其形态无非就是两种模型:

  • Flow数据流:不带反压的数据流。
  • Stream数据流: 带反压的数据流。 

对于Stream数据流,从外部使用上来看,你rdata的反压与rcmd的反压不再存在“直接关系”。诸如此类通过这些基础组件的模型抽象,能够在实现业务逻辑时能够简洁明了地实现功能。

对于Mem的模型抽象,其基本都逃不过这两类模型,具体进一步细化无非就是一些write first、ram read latency>1,读指令需要附带一些数据信息随着rdata输出等,这些在之前的文章中也都曾提到过,不再涉及。

就个人来看,SpinalHDL在描述电路时诚然是非常便捷的,但真正收获颇深的还是这些编程模型的抽象。诸如此类在SpinalHDL中可谓不胜枚举。

而今天主要看下在mem.streamReadSync中一些冗余的地方。

2 streamReadSync之不足

streamReadSync主要用于实现Mem的Stream模型。其源代码如下:

def streamReadSync[T2 <: Data](cmd: Stream[UInt], linkedData: T2, crossClock:Boolean = false) : Stream[ReadRetLinked[T,T2]] = {
    val ret = Stream(new ReadRetLinked(mem.wordType, linkedData))

    val retValid = RegInit(False)
    val retData = mem.readSync(cmd.payload, cmd.fire, clockCrossing = crossClock)
    val retLinked = RegNextWhen(linkedData, cmd.ready)

    when(ret.ready) {
      retValid := Bool(false)
    }
    when(cmd.ready) {
      retValid := cmd.valid
    }

    cmd.ready := ret.isFree

    ret.valid := retValid
    ret.value := retData
    ret.linked := retLinked
    ret
  }

其实就本质而言,这里的实现思路和Steram中的m2sPipe没有什么差异,然而在这里的代码实现对于retValid的处理略显冗余。在代码中为了方便收集覆盖率,按照last win原则我将这里对retValid的处理做了些调整:

def streamReadSync[T2 <: Data](cmd: Stream[UInt], linkedData: T2, crossClock:Boolean = false) : Stream[ReadRetLinked[T,T2]] = {
    val ret = Stream(new ReadRetLinked(mem.wordType, linkedData))

    val retValid = RegInit(False)
    val retData = mem.readSync(cmd.payload, cmd.fire, clockCrossing = crossClock)
    val retLinked = RegNextWhen(linkedData, cmd.ready)

    when(cmd.ready) {
      retValid := cmd.valid
    }elsewhen(ret.ready) {
      retValid := Bool(false)
    }

    cmd.ready := ret.isFree

    ret.valid := retValid
    ret.value := retData
    ret.linked := retLinked
    ret
  }

在收集覆盖率时会发现elsewhen(ret.ready)无法覆盖。那这里就要深究下了。

来看cmd.ready的赋值条件:ret.isFree,即(!ret.valid)||ret.ready。若想实现cmd.ready为False,那么也就必然意味着 ret.valid=1,ret.ready=0,自然无论如何elsewhen(ret.ready)是无法覆盖的,所以这里的elsewhen可删除:

def streamReadSync[T2 <: Data](cmd: Stream[UInt], linkedData: T2, crossClock:Boolean = false) : Stream[ReadRetLinked[T,T2]] = {
    val ret = Stream(new ReadRetLinked(mem.wordType, linkedData))

    val retValid = RegInit(False)
    val retData = mem.readSync(cmd.payload, cmd.fire, clockCrossing = crossClock)
    val retLinked = RegNextWhen(linkedData, cmd.ready)

    when(cmd.ready) {
      retValid := cmd.valid
    }
    cmd.ready := ret.isFree

    ret.valid := retValid
    ret.value := retData
    ret.linked := retLinked
    ret
  }

☆ END ☆

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

推荐阅读

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