✎ 编 者 按
最近收集一些代码覆盖率,其中牵涉到关于mem中streamReadSync的使用,在覆盖率收集过程中留意到其中具备的一些不可达的状态。
1 Mem的模型
提起Mem(RAM),以常用的简单双端口为例,也许一个初学FPGA没多久的人都会觉得没啥,不就是一些wen、ren、waddr、raddr、wdata、rdata信号的控制么,so easy。曾经我也这么是这么想。直到我刚开始学习SpinalHDL时看到了这个例子《见微知著——从example感受Lib 的强大》,才真正意识到在逻辑编程里逻辑解耦与模型抽象的重要性。现在去看Mem,无外乎这两种模型:
写接口无需论述。对于读接口,通过模型抽象将读指令与读数据解耦成两条外部看来独立的数据流,其形态无非就是两种模型:
- 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
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注[Spinal FPGA]欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。