FPGA開発日記

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

FIRRTLに入門する (23. 多次元配列の出力時のループ展開の調整)

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

前回の実装で、配列に関する実装は以下のようになっていた。以下のFIRコードをコンパイルすると、Verilogファイルが生成されるのだが、

  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

これは実はよくない。なぜなら、[7:0] out_b [4]というベクタに対してそのまま代入してしまっている。これは展開して接続した方が確実だ。

for (int i = 0; i < 4; i=i+1) begin
    assign out_b[i] = in_a_b_c[sel1][sel2][sel3][i];
end

しかし、今のところFIRではfor文はサポートしていないので、とりあえず手っ取り早い方法としては全部展開してしまうという方法がある。これでも、すべての多次元配列を展開されるよりはよっぽどマシだ。

assign out_b[0] = in_a_b_c[sel1][sel2][sel3][0];
assign out_b[1] = in_a_b_c[sel1][sel2][sel3][1];
assign out_b[2] = in_a_b_c[sel1][sel2][sel3][2];
assign out_b[3] = in_a_b_c[sel1][sel2][sel3][3];

という訳で上記のように最も先頭に当たる配列を展開するためにはどのようにすればよいのか検討する。

まず、通常のFIRRTLの変換プロセスにおいて、これらの配列の展開を行っているのはExpandConnectsというPassだ。

  • ExpandConnects実行前
circuit VecExtract :
  module VecExtract :
    input in : UInt<32>[4][4][4]
    input sel1 : UInt<2>
    input sel2 : UInt<2>
    output out : { a : UInt<32>[4]}
  
    out.a <= in[sel1][sel2]
  • ExpandConnects実行後
circuit VecExtract :
  module VecExtract :
    input in : UInt<32>[4][4][4]
    input sel1 : UInt<2>
    input sel2 : UInt<2>
    output out : { a : UInt<32>[4]}
  
    out.a[0] <= in[sel1][sel2][0]
    out.a[1] <= in[sel1][sel2][1]
    out.a[2] <= in[sel1][sel2][2]
    out.a[3] <= in[sel1][sel2][3]

現在の私の実装ではこれが実現されていない。なぜかというと、ExpandConnectsの中で実装されている

  • src/main/scala/firrtl/passes/Passes.scala
object ExpandConnects extends Pass {
  def run(c: Circuit): Circuit = {
    def expand_connects(m: Module): Module = {
...
          case sx: Connect =>
            val locs = create_exps(sx.loc)
            val exps = create_exps(sx.expr)
            Block(locs.zip(exps).map { case (locx, expx) =>
               to_flip(flow(locx)) match {
                  case Default => Connect(sx.info, locx, expx)
                  case Flip => Connect(sx.info, expx, locx)
               }
            })

create_exposはさんざん改造したので分かるのだが、create_expsは配列を展開しない。したがってこのままになっているのだ。

  • src/main/scala/firrtl/Utils.scala
  def create_exps(e: Expression): Seq[Expression] = e match {
    case ex: Mux =>
      val e1s = create_exps(ex.tval)
...
    case ex => ex.tpe match {
...
      case t: VectorType => Seq(ex)
    }
  }

したがって、これを配列の数だけ展開するためのcreate_exps_connectを作成する。というか、元に戻しただけである。

  def create_exps_connect(e: Expression): Seq[Expression] = e match {
    case ex: Mux =>
      val e1s = create_exps_connect(ex.tval)
...
    case ex => ex.tpe match {
      case (_: GroundType) => Seq(ex)
...
      case t: VectorType => (0 until t.size).flatMap(i => create_exps_connect(WSubIndex(ex, i, t.tpe,flow(ex))))
    }
  }

これに従って、上記のexpand_connects()を書き換える。create_expsの所をcreate_exps_connectに置き換えるだけである。

...
          case sx: Connect =>
            val locs = create_exps_connect(sx.loc)
            val exps = create_exps_connect(sx.expr)
            Block(locs.zip(exps).map { case (locx, expx) =>
...

これでとりあえずできた。

circuit VecExtract :
  module VecExtract :
    input in : UInt<32>[4][4][4]
    input sel1 : UInt<2>
    input sel2 : UInt<2>
    output out : { a : UInt<32>[4]}
  
    out.a[0] <= in[sel1][sel2][0]
    out.a[1] <= in[sel1][sel2][1]
    out.a[2] <= in[sel1][sel2][2]
    out.a[3] <= in[sel1][sel2][3]

ただし、問題はこの後だ。ExpandWhesによりデフォルト値が挿入されるのだが、これはなぜかというとポートであるoutと記述とout.a[0], out.a[1], out.a[2], out.a[3]がずれているため、デフォルト値が挿入されているようだ。

    skip
    out.a <= VOID
    out.a[0] <= in[sel1][sel2][0]
    out.a[1] <= in[sel1][sel2][1]
    out.a[2] <= in[sel1][sel2][2]
    out.a[3] <= in[sel1][sel2][3]

これにより、VOIDの記述が残ってしまいエラーが発生してしまう。

======== Starting Transform InferTypes$ ========
Exception in thread "main" firrtl.FirrtlInternalException: Internal Error! Please file an issue at https://github.com/ucb-bar/firrtl/issues
...
    at firrtl.stage.FirrtlMain.main(FirrtlStage.scala)
Caused by: scala.MatchError: WVoid (of class firrtl.WVoid$)
    at firrtl.passes.InferTypes$.infer_types_e$1(InferTypes.scala:26)
...

色々試行錯誤しているが上手い解決方法が思いつかない。もう少し実装を観察する。