Chiselで記述されたRocket-Chipのデザインを見ていると、不思議な演算子が使われているのを見たことがあるかもしれない。
src/main/scala/system/Configs.scala
class DefaultConfig extends Config(new WithNBigCores(1) ++ new BaseConfig)
Rocket-ChipはこのようなConfig
とParameters
をうまく活用して、非常にフレキシブルな構成を可能にしている。
ただしこの部分は非常にややこしく、理解するのに時間がかかる。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
に置かれている。
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/ConfigInRV32
とConfigOut
は全く別のコンフィグレーションとして定義されている。
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: Parameters
にtile_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 = Parameters
にDefaultConifg32
を設定した状態で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を切り替えるということが柔軟に行うことができるようになる。