FPGA開発日記

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

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

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

FIRRTLで配列を扱えるように改造を行っている。単純に、配列をキープしながらFIRRTLの処理を進めていくことは可能なのだが、例えば以下のような状態が発生ると問題だ。下記の状況では、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

この場合、Bundleを分解しつつ、配列をキープしなければならない。このための手法について、まずはポートや変数宣言の分解方法を調査した。

FIRRTLにおいて配列は、LowerTypeというPassによって分解される。したがって、このLowerTypeを改造することになる。まずはLowerTypeの処理全体を見てみると、

  • src/main/scala/firrtl/passes/LowerTypes.scala
  def lowerTypes(renames: RenameMap)(m: DefModule): DefModule = {
    val memDataTypeMap = new MemDataTypeMap
    renames.setModule(m.name)
    // Lower Ports
    val portsx = m.ports flatMap { p =>
      val exps = create_exps(WRef(p.name, p.tpe, PortKind, to_flow(p.direction)))
      val names = exps map loweredName
      renameExps(renames, p.name, p.tpe)
      (exps zip names) map { case (e, n) =>
        Port(p.info, n, to_dir(flow(e)), e.tpe)
      }
    }
      ...

create_expsによりまずはすべての構造を分解する。そしてこれをloweredNameにより文字列に分解する。したがって、create_exps()に改造すればよい。このcreate_exps()src/main/scala/firrtl/Utils.scalaに定義があるのだが、これをそのまま変更すると影響が大きすぎるのでLowerTypes()にコピーしてから編集する。

  • src/main/scala/firrtl/passes/LowerTypes.scala
  def create_exps(n: String, t: Type): Seq[Expression] =
    create_exps(WRef(n, t, ExpKind, UnknownFlow))
  def create_exps(e: Expression): Seq[Expression] = e match {
    case ex: Mux =>
      val e1s = create_exps(ex.tval)
...
    case ex: WRef => {
      ex.tpe match {
        case (_: GroundType) => Seq(ex)
        case t: BundleType => {
          val ret = t.fields.flatMap(f => create_exps(WSubField(ex, f.name, f.tpe, times(flow(ex), f.flip))))
          ret
        }
        case t: VectorType => {
          val elem_exp = create_exps(WRef(ex.name, t.tpe, ExpKind, flow(ex)))
          elem_exp.map(e => WRef(loweredName(e) , VectorType(e.tpe, t.size), ExpKind, flow(e)))
        }
      }
    }

WRefの形式をしているときの実装が問題だ。BundleTypeの時は変えなくてよい。問題はVectorTypeの時に、通常はベクトルのサイズ分だけ展開していたのだがそうはいかない。VectorTypeをキープしつつその中のTypeに処理を加えていく。このため、まずはcreate_exps(WRef(ex.name, t.tpe, ExpKind, flow(ex)))でVectorTypeの内部の信号(t.tpe)に対してcreate_expsを適用し、内部信号を分解する。

その後、その結果をもとに内部のすべての信号に対してWRefで再度VectorTypeのラッパーを追加することで、VectorTypeの型を維持しつつ内部のBundleを分解する。試行錯誤の結果、このような実装になった。

上記のテストコードで試してみると、以下のようになった。

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[sel]_a
    out_b <= in[sel]_b

展開自体はできたようだ。ただし、まだin[sel]_aが処理できていない。これは更なる調査が必要となる。