FPGA開発日記

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

FIRRTLに入門する (17. LowerType時に配列を分解せずにBundleを分解する手法の調査(3))

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

LowerTypeの調査を続けている、前回の記事からずいぶんと時間が経ってしまったが、Bundleを分解する方法について調査している。つまり、FIRRTLで以下のように記述されているコードを、Vectorを分解せずにBundleのみ分解するということである。

circuit VecBundle :
  module VecBundle :
    input in: { a : UInt<32>, b : UInt<32> }[4]
    input sel: UInt<2>
    output out : { a : UInt<32>, b : UInt<32> }

    out.a <= in[sel].a
    out.b <= in[sel].b

上記のFIRのコードを分解する。下記のようになれば良い。

circuit VecBundle :
  module VecBundle :
    input in_a : UInt<32>[4]
    input in_b : UInt<32>[4]
    input sel : UInt<2>
    output out_a : UInt<32>
    output out_b : UInt<32>

    skip
    out_a <= in_a[sel]
    out_b <= in_b[sel]

このために、LowerTypesを見直している。つまり、現時点で何が問題なのかというとLowerTypesの段階でin[sel].Ain_A[sel]に変換で来ているのは良いのだが、この時に間違えて実はin_A[sel]という1つの変数として変換してしまっており、結果的に次のResolveKindsのパスでin_A[sel]という変数は無いよと怒られてしまっている。したがって、in_Aselは別々になるように管理しておかなければならないのだ。

で、なぜin_A[sel]が1つの変数として登録されてしまったかというと、LowerTypesの中で改造をさぼって、

      case (_:SubField, _: WSubIndex) => kind(e) match {
        case InstanceKind =>
          val (root, tail) = splitRef(e)
          val name = loweredTypeName(tail)
          WSubField(root, name, e.tpe, flow(e))
        case MemKind =>
          val exps = lowerTypesMemExp(memDataTypeMap, info, mname)(e)
          exps.size match {
            case 1 => exps.head
            case _ => error("Error! lowerTypesExp called on MemKind " +
                "SubField that needs to be expanded!")(info, mname)
          }
        case _ => WRef(loweredTypeName(e), e.tpe, kind(e), flow(e))
      }

となっており、インスタンスでもメモリでもない場合は単純なWRefによってそれ以降の変数階層をすべて潰してしまい、1つの変数にしてしまっているからである。したがって、in[sel].Aのように、

  • SubField(構造体のメンバへのアクセス)が発生すると、とりあえず構造体のメンバ変数の解析と、構造体の名前の解析に進む。
  • 構造体の名前を解析した結果それが配列であれば(in[sel]のような形)、その配列名を書き直してin_Aという名前に変換してしまう。

という処理が必要になる。このために、上記の処理を下記のように書き直した。

      case e: WSubField => {
        kind(e) match {
          case InstanceKind =>
            val (root, tail) = splitRef(e)
            val name = loweredTypeName(tail)
            WSubField(root, name, e.tpe, flow(e))
          case MemKind =>
            val exps = lowerTypesMemExp(memDataTypeMap, info, mname)(e)
            exps.size match {
              case 1 => exps.head
              case _ => error("Error! lowerTypesExp called on MemKind " +
                  "SubField that needs to be expanded!")(info, mname)
            }
          case _ => {
            val exps = lowerTypesExp(memDataTypeMap, info, mname)(e.expr)
            exps match {
              case ex: WSubAccess => WSubAccess(WRef(loweredName(ex.expr) + delim + e.name, e.tpe, kind(e), flow(e)), ex.index, ex.tpe, flow(ex))
              case _ => WRef(loweredTypeName(e), e.tpe, kind(e), flow(e))
            }
          }
        }
      }

これはWSubFieldWSubIndexを分解して、WSubFieldの型にのみ適用している。InstanceKindでもMemKindでもない場合、

  • lowerTypesExpで構造体な名前の部分(つまりin[sel]の部分)を解析する。
  • 構造体の名前を解析した結果それが配列であれば(つまりWSubAccessであれば)、WSubAccess型を再度作り直す。このときに名前をloweredName(ex.expr) + delim + e.nameとする。つまり構造体の名前とメンバ変数をdelimでくっつけてそれを新しい配列の名前としてしまう。

このようにすることで、上記のテストケースは最終的に以下のように変換されるようになった。

circuit VecBundle :
  module VecBundle :
    input in_a : UInt<32>[4]
    input in_b : UInt<32>[4]
    input sel : UInt<2>
    output out_a : UInt<32>
    output out_b : UInt<32>

    out_a <= in_a[sel]
    out_b <= in_b[sel]

いいね、想定通りだ。