FPGA開発日記

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

ChiselのParametersとConfigによるパラメタライズの方法

Chiselで記述されたRocket-Chipのデザインを見ていると、不思議な演算子が使われているのを見たことがあるかもしれない。

f:id:msyksphinz:20190517003942p:plain
ConfigInRV32, ConfigInRV64, ConfigOutによるパラメタライズ
  • src/main/scala/system/Configs.scala
class DefaultConfig extends Config(new WithNBigCores(1) ++ new BaseConfig)

Rocket-ChipはこのようなConfigParametersをうまく活用して、非常にフレキシブルな構成を可能にしている。 ただしこの部分は非常にややこしく、理解するのに時間がかかる。Chiselにおけるパラメタリゼーションについて少し調べて見た。

ベースにしているのは、以下の資料 Advanced Chisel Parameterization だ。しかしこれはChisel2をベースにしているので少し古いかもしれない。

まずConfig(new WithNBigCores(1) ++ new BaseConfig)のような記述を実現しているのは、Chiselで作られたConfigクラスの役割だ。これはChiselデフォルトの機能ではなく、freechipsの成果物であるのでソースコードを持ってきて自分のプロジェクトに配置する必要がある。

  • src/main/scala/subsystem/Configs.scala
/* Composable partial function Configs to set individual parameters */

class WithNBigCores(n: Int) extends Config((site, here, up) => {
  case RocketTilesKey => {
    val big = RocketTileParams(
      core   = RocketCoreParams(mulDiv = Some(MulDivParams(
        mulUnroll = 8,
...

このConfigクラスの実体はRocket-Chipリポジトリ./src/main/scala/config/Config.scalaに置かれている。

github.com

abstract class View {
  final def apply[T](pname: Field[T]): T = apply(pname, this)
  final def apply[T](pname: Field[T], site: View): T = {
...
}
abstract class Parameters extends View {
  final def ++ (x: Parameters): Parameters =
    new ChainParameters(this, x)
...
}
class Config(p: Parameters) extends Parameters {
  def this(f: (View, View, View) => PartialFunction[Any,Any]) = this(Parameters(f))
....
}

Configクラスの使い方は上記のAdvanced Chisel Parameterにある程度説明がある。 例えばパラメータ化したい要素を追加するときは以下のようにする(ここではSyntax Sugarを使っている)。

以下ではInWidthというパラメータを持つコンフィグレーションを2つ用意している。

case object InWidth extends Field[Int]
class ConfigInRV64 extends Config((site, here, up) => {
  case InWidth => 64
})
class ConfigInRV32 extends Config((site, here, up) => {
  case InWidth => 32
})

一方で、もう一つOutWidthパラメータを持つコンフィグレーションを1つ定義する。この状態でConfigInRV64/ConfigInRV32ConfigOutは全く別のコンフィグレーションとして定義されている。

case object OutWidth extends Field[Int]
class ConfigOut extends Config((site, here, up) => {
  case OutWidth => 32
})

しかし、これらのConfigをベースとしたクラスどうしは++演算子を用いて接続し、新たなクラスを作成することができるという特徴がある。

class DefaultConfig32 extends Config(new ConfigRV32 ++ new ConfigOut)
class DefaultConfig64 extends Config(new ConfigRV64 ++ new ConfigOut)

これらのコンフィグレーションは、パラメータクラスに変換して、モジュールのパラメータとして渡すことができる。

  val tile_parameters = (new DefaultConfig32).toInstance
  chisel3.Driver.execute(args, () => new TestModule()(tile_parameters))

TestModuleは以下のように定義されている。implicit p: Parameterstile_parametersが渡される。

class TestModule (implicit p: Parameters) extends Module {
  val io = IO(new Bundle {
    val in = Input(UInt(p(InWidth).W))
    val success = Output(UInt(p(OutWidth).W))
  })

  io.success := io.in
}

p = ParametersDefaultConifg32を設定した状態でVerilogを生成すると、inポートが32ビット、outポートが32ビットとなる。

module TestModule( // @[:@3.2]
  input         clock, // @[:@4.4]
  input         reset, // @[:@5.4]
  input  [31:0] io_in, // @[:@6.4]
  output [31:0] io_success // @[:@6.4]
);
  assign io_success = io_in; // @[param_test.scala 27:14:@11.4]
endmodule

一方で、DefaultConfig64をパラメータに渡してVerilogを生成するとinポートが64ビット、outポートが64ビットとなる。

module TestModule( // @[:@3.2]
  input         clock, // @[:@4.4]
  input         reset, // @[:@5.4]
  input  [63:0] io_in, // @[:@6.4]
  output [31:0] io_success // @[:@6.4]
);
  assign io_success = io_in[31:0]; // @[param_test.scala 27:14:@11.4]
endmodule

このように、パラメタライズしたい項目に合わせて定数値を切り替えたり、パラメータの有無でモジュールのON/OFFを切り替えるということが柔軟に行うことができるようになる。