十二 · 2023年12月18日

逼死强迫症—优化Stream/Flow代码生成

✎ 编 者 按 

在SpinalHDL里在顶层一键优化代码中Stream/Flow代码生成的payload,fragment。

难看的代码

来看一段代码:

import spinal.core._
import spinal.lib._
case class DataPort() extends Bundle{
  val data0=UInt(8 bits)
  val data1=UInt(8 bits)
}

case class Demo() extends Component{
  val io=new Bundle{
    val sink=slave(Stream(Fragment(DataPort())))
    val source=master(Stream(Fragment(DataPort())))
  }
  noIoPrefix()
  io.source<<io.sink.pipelined(true,true)

}

很简单的功能,一个Stream接口的Pipeline打拍。在生成RTL代码时会看到下面这种有点儿“不太舒服”的结构命名:

  wire                sink_s2mPipe_payload_last;
  wire       [7:0] sink_s2mPipe_payload_fragment_data0;
  wire       [7:0] sink_s2mPipe_payload_fragment_data1;
  reg                 sink_rValidN;
  reg                 sink_rData_last;
  reg        [7:0] sink_rData_fragment_data0;
  reg        [7:0] sink_rData_fragment_data1;
  wire                sink_s2mPipe_m2sPipe_valid;
  wire                sink_s2mPipe_m2sPipe_ready;
  wire                sink_s2mPipe_m2sPipe_payload_last;
  wire       [7:0] sink_s2mPipe_m2sPipe_payload_fragment_data0;
  wire       [7:0] sink_s2mPipe_m2sPipe_payload_fragment_data1;

虽然说不怎么看生成的代码,但有时候别人看这里信号命名中间夹杂了一堆_payload_fragment_的信号还是略觉有点儿啰嗦。

尤其在用一些Axi/AxiLite总线时,当使用cloneOf时,会发现大量的信号名中间夹着一些paylaod字段,略觉不雅~

虽然这是Stream类的定义所导致,但如果去修改设计中的每一处总归还是比较麻烦的~

StreamRenameUtil

这里提供一个DIY的工具StreamRenameUtil,用于在设计的顶层一键让这种场景下的代码生成稍微优雅一些:

object StreamRenameUtil {
  def apply(topLevel:Component) = {
    Rename(topLevel,true)
  }

  def Rename(toplevel:Component,isCurrentComponent:Boolean):Boolean={
    //current component process
    if(!isCurrentComponent){
      toplevel.dslBody.foreachStatements{
        case bt:BaseType if bt.parent.isInstanceOf[Stream[_]] => streamRename( bt.parent.asInstanceOf[Stream[_]])
        case bt:BaseType if bt.parent.isInstanceOf[Flow[_]] => flowRename( bt.parent.asInstanceOf[Flow[_]])
        case _ =>
      }
    }else{
      toplevel.dslBody.foreachStatements{
        case bt:BaseType if bt.parent.isInstanceOf[Stream[_]] => toplevel.addPrePopTask(()=>{streamRename( bt.parent.asInstanceOf[Stream[_]])})
        case bt:BaseType if bt.parent.isInstanceOf[Flow[_]] => toplevel.addPrePopTask(()=>{flowRename( bt.parent.asInstanceOf[Flow[_]])})
        case _ =>
      }
    }

    for(child<-toplevel.children){
      Rename(child,false)
    }
    true
  }

  def streamRename(streamPort:Stream[_])={
    streamPort.flatten.foreach((bt)=>{
      val signalName=bt.getName()
      if(signalName.contains("fragment")){
        bt.setName(signalName.replace("_payload_fragment_","_"))
      }else{
        bt.setName(signalName.replace("_payload_","_"))
      }
    })
  }

  def flowRename(flowPort:Flow[_])={
    flowPort.flatten.foreach((bt)=>{
      val signalName=bt.getName()
      if(signalName.contains("fragment")){
        bt.setName(signalName.replace("_payload_fragment_","_"))
      }else{
        bt.setName(signalName.replace("_payload_","_"))
      }
    })
  }

}

使用时仅需在顶层调用该方法,其会遍历设计中各模块的Stream、Flow类变量定义统一做修改:

case class Demo() extends Component{
  val io=new Bundle{
    val sink=slave(Stream(Fragment(DataPort())))
    val source=master(Stream(Fragment(DataPort())))
  }
  noIoPrefix()
  io.source<<io.sink.pipelined(true,true)

  StreamRenameUtil(this)

}

最终代码生成会优雅一些:

  wire                sink_s2mPipe_valid;
  reg                 sink_s2mPipe_ready;
  wire                sink_s2mPipe_last;
  wire       [7:0] sink_s2mPipe_data0;
  wire       [7:0] sink_s2mPipe_data1;
  reg                 sink_rValidN;
  reg                 sink_rData_last;
  reg        [7:0] sink_rData_fragment_data0;
  reg        [7:0] sink_rData_fragment_data1;
  wire                sink_s2mPipe_m2sPipe_valid;
  wire                sink_s2mPipe_m2sPipe_ready;
  wire                sink_s2mPipe_m2sPipe_last;
  wire       [7:0] sink_s2mPipe_m2sPipe_data0;
  wire       [7:0] sink_s2mPipe_m2sPipe_data1;
  reg                 sink_s2mPipe_rValid;
  reg                 sink_s2mPipe_rData_last;
  reg        [7:0] sink_s2mPipe_rData_fragment_data0;
  reg        [7:0] sink_s2mPipe_rData_fragment_data1;
  wire                when_Stream_l369;

这里的sink_s2mPipe_rData_fragment_data0、sink_s2mPipe_rData_fragment_data1为在打拍时生命的Fragment类型,非Stream类型,如果你实在看不惯也可以依样画葫芦添加一个对Fragment类型的Rename~

☆ END ☆

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

推荐阅读

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