前回の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