FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

FIRRTLに入門する (22. 多次元配列の構造体に関するSystemVerilog出力の修正)

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

前回までで多次元構造体配列について、とりあえずエラーが出ずにVerilogが生成されるようになったが、生成されたSystemVerilogのコードは明らかにおかしい。

circuit VecBundle1 :
  module VecBundle9  :
    input in: { a : { b : { c : UInt<8>[4] } [8] } [16] }[32]
    input sel1: UInt<5>
    input sel2: UInt<4>
    input sel3: UInt<3>
    output out : { b : UInt<8>[4] }
    out.b <= in[sel1].a[sel2].b[sel3].c
module VecBundle8(
  input logic  [7:0][3][7][15][31] in_a_b_c,
  output logic [7:0][3]            out_b
);
  assign out_b = in_a_b_c[0][1][2];
endmodule

ビット幅の指定がおかしくなっているのと、ベクトル配列の指定がおかしい。理想としては以下だ。

module VecBundle8(
    input logic  [7:0] in_a_b_c[4][8][16][32],
    output logic [7:0] out_b[4]
);
  assign out_b = in_a_b_c[0][1][2];
endmodule

SystemVerilogの生成については、SystemVerilogEmitterが担当している。そして、SystemVerilogEmitter内のstringifyが型の出力を担当している。

    // Turn ports into Seq[String] and add to portdefs
    def build_ports(): Unit = {
...
    }
    def emit_systemverilog(): DefModule = {
      build_netlist(m.body)
      build_ports()
...

build_ports()でポートタイプを作り上げているのだが、それを2つに分割する。つまり、以下の2つの分割するわけだ。

    input logic  [7:0] in_a_b_c[4][8][16][32],
//               ~~~~~         ~~~~~~~~~~~~~~
//                 |                  `----------- stringify_vec()
//                 `------------------------------ stringify()
      // Turn types into strings, all ports must be GroundTypes
      val tpes = m.ports map {
        case Port(_, _, _, tpe: GroundType) => stringify(tpe)
        case Port(_, _, _, tpe: VectorType) => stringify(tpe)
        case port: Port => error(s"Trying to emit non-GroundType Port $port")
      }

      // Turn types into strings, all ports must be GroundTypes
      val tpes_vec = m.ports map {
        // case Port(_, _, _, tpe: GroundType) => stringify_vec(tpe)
        case Port(_, _, _, tpe: GroundType) => ""
        case Port(_, _, _, tpe: VectorType) => stringify_vec(tpe)
        case port: Port => error(s"Trying to emit non-GroundType Port $port")
      }

stringify_vec()VectorTypeの型を再帰的に探していき、VectorTypeのたびに"[$wx]"を出力するという寸法である。

  def stringify_vec(tpe: VectorType): String = {
    val elem_type = tpe.tpe match {
      case (t: VectorType) => {
        val wx = sv_bitWidth(tpe)
        val field_str = if (wx > 0) s"[$wx]" else ""
        val str_element = tpe.tpe match {
          case tpe_elem: VectorType => stringify_vec(tpe_elem)
        }
        str_element + field_str
      }
      case (_: UIntType | _: SIntType | _: AnalogType) => {
        val wx = sv_bitWidth(tpe)
        val vec_str = if (wx > 0) s"[$wx]" else ""
        vec_str
      }
      case ClockType | AsyncResetType | AsyncResetNType => ""
      case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
    }
    elem_type
  }

最後に、stringify_vec()の結果をポート宣言に追加する。

      // dirs are already padded
      val pad_to_port    = padToMax(tpes)
      val pad_to_portvec = padToMax(tpes_vec)
      val pad_vec = pad_to_port.zip(pad_to_portvec)
      (dirs, pad_vec, m.ports).zipped.toSeq.zipWithIndex.foreach {
        case ((dir, (tpe, tpe_vec), Port(info, name, _, _)), i) =>
...
          if (i != m.ports.size - 1) {
            portdefs += Seq(dir, " ", tpe, " ", name, " ", tpe_vec, ",", info)
          } else {
            portdefs += Seq(dir, " ", tpe, " ", name, " ", tpe_vec, info)
          }
...

これにより、上記のFIRRTLは以下のようになる。

./utils/bin/firrtl -td regress -i ./regress/VecBundle.fir -X sverilog -ll trace 2>&1 | tee VecBundle.log
module VecBundle8(
  input logic  [7:0] in_a_b_c [4][8][16][32],
  output logic [7:0] out_b [4]
);
  assign out_b = in_a_b_c[0][1][2];
endmodule

ある程度形になってきた。まだ配列を正常に処理できていないと思うけど、とりあえず形になってきた。

ついでに、以下のようなFIRのテストを動かしてみた。

  module VecBundle9  :
    input in: { a : { b : { c : UInt<8>[4] } [8] } [16] }[32]
    input sel1: UInt<5>
    input sel2: UInt<4>
    input sel3: UInt<3>
    output out : { b : UInt<8>[4] }
    out.b <= in[sel1].a[sel2].b[sel3].c
module VecBundle9(
  input logic  [7:0] in_a_b_c [4][8][16][32],
  input logic  [4:0] sel1               ,
  input logic  [3:0] sel2               ,
  input logic  [2:0] sel3               ,
  output logic [7:0] out_b [4]
);
  assign out_b = in_a_b_c[sel1][sel2][sel3];
endmodule

良さそうだ。