编 者 按
在逻辑设计中,基于Stream握手这种形式的总线行为是很常见的,在自定义总线时Stream这类总线代码写起来千篇一律,完全可以利用IDEA的live template来自动生成。
常用的Stream总线
最近做系统总线的定义,模块之间存在着大量的握手交互,在SpinalHDL中这类总线往往继承于SpinalHDL中的Stream。以下面的总线定义为例:
先来说说自己为什么这么来定义总线:
Bus继承于Stream,如此Stream所定义的所有信号均可以使用。
单独定义BusConfig,将总线的各种参数归到一个类中,如此在做修改调整时整个系统不至于从头改到尾。
定义objectBus主要原因在于在SpinalHDL中,Stream/Flow这种类型在映射生成Verilog代码时例如上面的data0,data1会带有xxx_payload_data0,xxx_payload_data1,为了生成的代码更美观在object中定义rename函数来对生成Verilog中的变量做一个命名上的调整。同时也可以避免在例化时写“new”。
嗯,出发点还不错吧,但在为整个系统定义总线时当我写到第三个第四个时就累了,因为这里面的大量代码完全是重复性的代码,陷入了赋值—>粘贴—>修改的怪圈~
Live template使用
像上面的代码完全是具有模版范式的代码。个人在总线定义时,习惯将每个总线单独定义成一个文件,那么上面的代码完全可以定制为一个Live template。
首先在打开你IDEA的Live Templates界面,点击图中“+”号按钮创建一个名称为“SpinalHDL”的Template Group(SpinalHDL的模版都可以放在这里面):
然后在SpinalHDL下创建Live Template:
这里在模版中定义了变量Bus,parameter,payload。在Live Template中可做变量的定义:
(上面红圈1的位置可指定在哪些语言中支持,在2中编辑变量)
(由于个人习惯每个总线单独一个文件,所以这里Bus变量指定表达式,如果想自己写这里可不指定表达式)
而之所以定义后面两个变量主要用于在调用该模版时光标自动跳转到对应的待编辑处。
由于这里我将模版起名为streamext,故可以在定义总线时敲该关键字自动实现总线模版定义:
自己仅需定义总线中的信号即可~
最后贴上这个模版的可copy代码:
import spinal.core._
import spinal.lib._
case class $Bus$Config($parameter$) {
}
case class $Bus$Payload(config: $Bus$Config) extends Bundle {
$payload$
}
class $Bus$(config: $Bus$Config) extends Stream($Bus$Payload(config)) {
}
object $Bus$ {
def rename(bus: $Bus$) = {
bus.flatten.foreach((bt) => {
bt.setName(bt.getName().replace("payload_", ""))
if (bt.getName().startsWith("io_")) bt.setName(bt.getName().replaceFirst("io_", ""))
})
}
def apply(config: $Bus$Config): Bus = {
val bus = new $Bus$(config)
if (Component.current == bus.component)
bus.component.addPrePopTask(() => {
rename(bus)
})
else
rename(bus)
bus
}
}
原作者:Spinal FPGA 玉骐