ARM技术论坛
直播中

杨平

7年用户 1679经验值
私信 关注
[经验]

教你在SpinalHDL中如何快速地实现总线连接

》总线连接因小失大?

在系统设计上,往往会存在同一个总线接口在不同的模块里在进行例化时其配置略有差别。

例如当我们为整个系统不同模块分配地址范围时,不同模块的地址段大小各不相同,这从而也就使得不同模块总线配置其地址位宽略有不同。以AxiLite4总线为例,我们若例化不同的总线配置:

val input=slave(AxiLite4(32,32))

这里如果我们想用input驱动output,那么如何连线呢?

在SpinalHDL中,对于总线连接时位宽的检查是十分严格的,这里直接使用"<>"或者“>>”方法来进行连接显然是不行的。

严格的位宽检查显然能够避免 我们犯不该犯的错误,但如果这里你还是像Verilog那样一根根信号的手动连接时那么显然是要遭到鄙视的。

》removeAssignments

在最早接触SpinalHDL时,针对这种情况,最早全是自己DIY,在之前曾在一文中自己实现了一个同一总线即使配置不同也自动连接的一个connect方法来针对这种以及更多类似的场景。

然而虽然这种方式可用,但不同总线例化的差异在使用时虽然能够连接,但也破坏了SpinalHDL本身设计的严谨性。

** 而现在,针对这种场景,更推荐使用removeAssignments来解决类似的问题。**

顾名思义,removeAssignments会在elaboration阶段删除之前对信号所有的赋值,那么在这里,总线连接我们可以这么来写:

input<>output
output.ar.addr.removeAssignments()
output.aw.addr.removeAssignments()
output.ar.addr:=input.ar.addr.resized
output.aw.addr:=input.aw.addr.resized

在使用<>连接时最终实现时当处理到addr时会因位宽不匹配而报错,但这里我们在接下来对output.ar,output.aw的addr删除了赋值,从而也就避免了在elaboration阶段的错误赋值,而是执行下面新的带有位宽调节的赋值连线。

通过这种方式,我们可以在保持SpinalHDL的严谨性的同时也能以最少的代码量实现我们想要的效果。通过这种“先礼后兵”的方式,简洁而不失严谨的完成功能。

》隐式转换API封装

针对一些在自己系统设计里常用的功能,如果当用到SpinalHDL 中自带的库时,我们可以通过隐式转换封装成相应的API函数,从而避免一次次的来徒增代码。以上面的AXILite4总线为例,我们可以自定义隐式扩展类如下:

object AxiLite4Tools{
  implicit class AxiLite4Extend(bus:AxiLite4){
    def driveWithAddressAdjust(that:AxiLite4)={
      bus.aw>>that.aw
      bus.w>>that.w
      bus.ar>>that.ar
      bus.b<<that.b
      bus.r<<that.r
      that.aw.addr.removeAssignments()
      that.ar.addr.removeAssignments()
      that.aw.addr:=bus.aw.addr.resized
      that.ar.addr:=bus.ar.addr.resized
    }
  }
}

这里之所以不掉用<>方法主要在于<>需确保连接的两端必须有一个能推断的出方向,否则则无法实现连接。

而之所以不适用>>来连接则是在于AxiLite4总线的>>方法中有个config断言在这种场景下无法通过。

随后我们在使用时即可通过impot AxiLite4Tools._来实现掉用:

import AxiLite4Tools._
case class test() extends Component{
  val io=new Bundle{
    val input=slave(AxiLite4(32,32))
    val output=master(AxiLite4(16,32))
  }
  io.input.driveWithAddressAdjust(io.output)
}

原作者:玉骐

更多回帖

发帖
×
20
完善资料,
赚取积分