FPGA開発日記

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

FIRRTLに入門する (18. 多次元配列に対する処理の検討)

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

前回のLowerTypeの実装では、多次元配列に関する処理が抜けていた。このため、以下のようなFIRのコードをコンパイルするとエラーが発生する。

  • VecBundle.fir
circuit VecBundle :
  module VecBundle :
    input in: UInt<32>[3][4]
    input sel: UInt<2>
    output out : UInt<32>[3]

    out <= in[sel]
./utils/bin/firrtl -td regress -i ./regress/VecBundle.fir -X sverilog -ll trace 2>&1 | tee VecBundle.log
Exception in thread "main" firrtl.FirrtlInternalException: Internal Error! trying to write unsupported type in the Verilog Emitter: VectorType(VectorType(UIntType(IntWidth(32)),3),4)

ぬーん、どうやら最終的なSystemVerilogEmitterのフェーズでVectorTypeのしかも入れ子になっている記述を想定していなかったようだ。このSystemVerilogEmitterの段階でのFIRのツリーはここまで変換されている。

circuit VecBundle :
  module VecBundle :
    input in : UInt<32>[3][4]
    input sel : UInt<2>
    output out : UInt<32>[3]

    out <= in[sel]

まあこれは上記の入力ファイルと同じなので変換されているとは言わない...

そこで、SystemVerilogEmitter独自のstringifyを作って、このツリーを最終的に文字列に落とし込める量にしたいと思う。そのためには、SystemVerilogEmitterにoverrideしたstringifyを追加する必要がありそうだ。

  • src/main/scala/firrtl/SystemVerilogEmitter.scala
  override def stringify(tpe: VectorType): String = {
    val ground_type = tpe.tpe
    val elem_type = ground_type match {
      case (t: VectorType) => {
        val wx = bitWidth(tpe) - 1
        val field_str = if (wx > 0) s"[$wx:0]" else ""
        val str_element = tpe.tpe match {
          case tpe_elem: VectorType => stringify(tpe_elem)
          case tpe_elem: GroundType => stringify(tpe_elem)
        }
        str_element + field_str
      }
      case (_: UIntType | _: SIntType | _: AnalogType) =>
        val wx = bitWidth(tpe) - 1
        if (wx > 0) s"[$wx:0]" else ""
      case ClockType | AsyncResetType | AsyncResetNType => ""
      case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
    }
    elem_type + s"[${tpe.size}]"
  }

case (t: VectorType)の中身をもう少し噛み砕いている。つまり、VectorTypeの中身をさらに解析し、さらにVectorTypeならばstringify(tpe: VectorType)を呼び出し、GroundTypeならばstringify(tp: GroundType)を呼び出すという算段にしている。これでVectorType入れ子も上手く行くはずだ。

circuit VecBundle :
  module VecBundle :
    input in : UInt<32>[3][4]
    input sel : UInt<2>
    output out : UInt<32>[3]

    out <= in[sel]

だがこれでは少し足りない。なんだかVectorのビット幅の挙動がおかしい。

EmittedVerilogCircuitAnnotation(EmittedVerilogCircuit(VecBundle,module VecBundle(
  input logic  [95:0][383:0] in,
  input logic  [1:0]         sel,
  output logic [95:0]        out
);
  assign out = in[sel];
endmodule

これは大変なことになっているので、bitwidth()の計算を修正する。

diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala
index 0b26e6a1..38495694 100644
--- a/src/main/scala/firrtl/Utils.scala
+++ b/src/main/scala/firrtl/Utils.scala
@@ -53,7 +53,8 @@ object getWidth {
 object bitWidth {
   def apply(dt: Type): BigInt = widthOf(dt)
   private def widthOf(dt: Type): BigInt = dt match {
-    case t: VectorType => t.size * bitWidth(t.tpe)
+    // case t: VectorType => t.size * bitWidth(t.tpe)
+    case t: VectorType => t.size
     case t: BundleType => t.fields.map(f => bitWidth(f.tpe)).foldLeft(BigInt(0))(_+_)
     case GroundType(IntWidth(width)) => width
     case t => Utils.error(s"Unknown type encountered in bitWidth: $dt")

あれー、今度は最後の32ビットが表示されない。なんでだ。

module VecBundle(
  input logic  [2:0][3:0] in,
  input logic  [1:0]      sel,
  output logic [2:0]      out
);
  assign out = in[sel];
endmodule

もうすこし改造を加える。ビットサイズの条件を付け加えた。

diff --git a/src/main/scala/firrtl/SystemVerilogEmitter.scala b/src/main/scala/firrtl/SystemVerilogEmitter.scala
index 77b50432..dcee604a 100644
--- a/src/main/scala/firrtl/SystemVerilogEmitter.scala
+++ b/src/main/scala/firrtl/SystemVerilogEmitter.scala
@@ -24,23 +24,28 @@ class SystemVerilogEmitter extends VerilogEmitter with Emitter {

   override def stringify(tpe: VectorType): String = {
     val ground_type = tpe.tpe
     val elem_type = ground_type match {
       case (t: VectorType) => {
-        val wx = bitWidth(tpe) - 1
-        val field_str = if (wx > 0) s"[$wx:0]" else ""
+        val wx = sv_bitWidth(tpe) - 1
+        val field_str = if (wx > 0) s"[$wx]" else ""
         val str_element = tpe.tpe match {
           case tpe_elem: VectorType => stringify(tpe_elem)
           case tpe_elem: GroundType => stringify(tpe_elem)
         }
         str_element + field_str
       }
-      case (_: UIntType | _: SIntType | _: AnalogType) =>
-        val wx = bitWidth(tpe) - 1
-        if (wx > 0) s"[$wx:0]" else ""
+      case (_: UIntType | _: SIntType | _: AnalogType) => {
+        val wx = sv_bitWidth(tpe) - 1
+        val elem_w = sv_bitWidth(ground_type) - 1
+        val elem_str = if (elem_w > 0) s"[${elem_w}:0]" else ""
+        val vec_str = if (wx > 0) s"[$wx]" else ""
+        elem_str + vec_str
+      }
       case ClockType | AsyncResetType | AsyncResetNType => ""
       case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
     }
-    elem_type + s"[${tpe.size}]"
+    elem_type
   }
object sv_bitWidth {
  def apply(dt: Type): BigInt = widthOf(dt)
  private def widthOf(dt: Type): BigInt = dt match {
    case t: VectorType => t.size
    case t: BundleType => t.fields.map(f => bitWidth(f.tpe)).foldLeft(BigInt(0))(_+_)
    case GroundType(IntWidth(width)) => width
    case t => Utils.error(s"Unknown type encountered in bitWidth: $dt")
  }
}

再度Verilogを生成してみる。不格好だが何とか形になっている。

module VecBundle(
  input logic  [31:0][2][3] in,
  input logic  [1:0]        sel,
  output logic [31:0][2]    out
);
  assign out = in[sel];
endmodule