FPGA開発日記

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

Parameter / Conifgで作るコンフィグレーション可能なChiselデザイン2

複数のConfigを組み合わせる

前回のパラメータは、いくつかのコンフィグレーションを組み合わせて新しいコンフィグレーションを作り上げることができる。例えば以下の2つのConfigを組み合わせて新しいConfigを作り上げることができる。

class Bus32BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 32 / 8
})

class IfuConnectConfig extends Config((site, here, up) => {
  case ConnectIfu => true
})
class Default1Config extends Config(
  new Bus32BitConfig ++ new IfuConnectConfig
)

新たに作成したConnectIfuを使って、モジュールをインスタンスするかどうかを指定することができる。パラメータを取得するためにはp()を使用する。

  val xbar   = LazyModule(new TLXbar)
  val memory = LazyModule(new TLRAM(AddressSet(0x02000, p(AddrSize)-1), beatBytes = p(BusWidthBytes)))

  xbar.node := pusher1.node
  if (p(ConnectIfu)) {
    val ifu    = LazyModule(new ifu("ifu"))
    xbar.node := ifu.node
  }
  memory.node := xbar.node

この新しく作ったDefault1Configを使用してパラメータを設定できる。

object Generator {
    final def main(args: Array[String]) {
        val p = (new Default1Config).toInstance
        val verilog = Driver.emitVerilog(
            new TestHarness()(p)
        )
    }
}

here(), site(), up()などの使い方

Configで使用しているパラメータを計算するにあたり、さらに別のパラメータの値を使用したい場合どうするか。Verilogでいう以下のようなことを実現したい。

localparam param1 = 100;
localparam param2 = param1 * 100;

これをChiselで実現しようしようとすると以下のようになるだろう。BusWidthBytesパラメータを使用してアドレスサイズの計算をしているが、実はこれはコンパイルできない。

class Bus128BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 128 / 8
  case AddrSize => 0x100 * BusWidthBytes
})

class Bus64BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 64 / 8
  case AddrSize => 0x200 * BusWidthBytes
})
[error] /home/msyksphinz/work/riscv/chisel-development/minimal-diplomacy/src/main/scala/core_complex/Configs.scala:11:26: overloaded method value * with alternatives:
[error]   (x: Double)Double <and>
[error]   (x: Float)Float <and>
[error]   (x: Long)Long <and>
[error]   (x: Int)Int <and>
[error]   (x: Char)Int <and>
[error]   (x: Short)Int <and>
[error]   (x: Byte)Int
[error]  cannot be applied to (core_complex.BusWidthBytes.type)
[error]   case AddrSize => 0x100 * BusWidthBytes
[error]                          ^

ようするにBusWidthBytes自体がcase classなので、値として直接使用することができないのだ。そこで、同じConfig内の値を参照するためのhere()を利用する。

class Bus128BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 128 / 8
  case AddrSize => 0x100 * here(BusWidthBytes)
})

class Bus64BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 64 / 8
  case AddrSize => 0x200 * here(BusWidthBytes)
})
// Bus128BitConfigで構成したTLRAM, 12ビットのバス幅で構成される
module TLRAM(
  input         clock,
  input         reset,
  input         auto_in_a_valid,
  input  [2:0]  auto_in_a_bits_opcode,
  input  [11:0] auto_in_a_bits_address,
  input  [31:0] auto_in_a_bits_data,
  output        auto_in_d_valid,
  output [2:0]  auto_in_d_bits_opcode,
  output [31:0] auto_in_d_bits_data
);

一方で、site()を使用するとより上位のConfigを使用するようになる。例えばDefault2Configでは以下のように構成している。Bus128BitConfigsite()を使用するとBaseConfigを参照する。つまりAddrSize0x100 * (256/8)になるはずだ。

class Default2Config extends Config(
  new BaseConfig ++
  new Bus128BitConfig ++ new IfuNotConnectConfig
)
class BaseConfig extends Config((site, here, up) => {
  case BusWidthBytes => 256 / 8
})

class Bus128BitConfig extends Config((site, here, up) => {
  case BusWidthBytes => 128 / 8
  case AddrSize => 0x100 * here(BusWidthBytes)
})
// BaseConfigで構成したTLRAM, 13ビットのバス幅で構成される
module TLRAM(
  input          clock,
  input          reset,
  input          auto_in_a_valid,
  input  [2:0]   auto_in_a_bits_opcode,
  input  [12:0]  auto_in_a_bits_address,
  input  [255:0] auto_in_a_bits_data,
  output         auto_in_d_valid,
  output [2:0]   auto_in_d_bits_opcode,
  output [255:0] auto_in_d_bits_data
);