FPGA開発日記

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

BOOMのTLBに関する調査 (5. Rocket-Chip / BOOMのArbiterの仕組み調査)

Rocket-Chip / BOOMで使用されているArbiterについて調査する。以下のソースコードを参照した。

@chiselName
class Arbiter[T <: Data](val gen: T, val n: Int) extends Module {
  val io = IO(new ArbiterIO(gen, n))

  io.chosen := (n-1).asUInt
  io.out.bits := io.in(n-1).bits
  for (i <- n-2 to 0 by -1) {
    when (io.in(i).valid) {
      io.chosen := i.asUInt
      io.out.bits := io.in(i).bits
    }
  }

  val grant = ArbiterCtrl(io.in.map(_.valid))
  for ((in, g) <- io.in zip grant)
    in.ready := g && io.out.ready
  io.out.valid := !grant.last || io.in.last.valid
}

このコードを見ても一瞬では訳が分からないのだが、実に変な記述がしてあって、以下のfor文は展開すると以下のようになって、(n=3である場合)

  io.chosen := (n-1).asUInt
  io.out.bits := io.in(n-1).bits
  for (i <- n-2 to 0 by -1) {
    when (io.in(i).valid) {
      io.chosen := i.asUInt
      io.out.bits := io.in(i).bits
    }
  }
    io.chosen := 2.asUInt
    io.out.bits := io.in(2).bits
    when (io.in(1).valid) {
      io.chosen := 1.asUInt
      io.out.bits := io.in(1).bits
    }
    when (io.in(0).valid) {
      io.chosen := 0.asUInt
      io.out.bits := io.in(0).bits
    }

と展開されるが、Chiselは行数が大きくなるほど優先度が強くなるため、実際にはVerilogで書き下すと以下と一緒のコードになる。

always_comb begin
    if (io.in(0).valid) begin
        io.chosen = 0;
        io.out.bits = io.in(0).bits;
    end else if (io.in(1).valid) begin
        io.chosen = 1;
        io.out.bits = io.in(1).bits;
    end else begin
        io.chosen = 2;
        io.out.bits = io.in(2).bits;
    end
end

これは良しとして、次はGrantの記述なのだが、以下のようになっている。ArbiterCtrl()は以下のような実装になっている。

  val grant = ArbiterCtrl(io.in.map(_.valid))
private object ArbiterCtrl {
  def apply(request: Seq[Bool]): Seq[Bool] = request.length match {
    case 0 => Seq()
    case 1 => Seq(true.B)
    case _ => true.B +: request.tail.init.scanLeft(request.head)(_ || _).map(!_)
  }
}

つまり、Validビット列の内LSBのビットを抽出しているだけだ。なんと単純なことだろう。Ready信号はValid信号によって作られているのでデザイン的にちょっとどうかと思うが、とりあえずこれでいいのだろう。生成されるVerilogコードは以下のようになっている。

module Arbiter_20( // @[chipyard.TestHarness.MediumBoomConfig.fir 343881:2]
  output        io_in_0_ready, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input         io_in_0_valid, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input         io_in_0_bits_valid, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input  [26:0] io_in_0_bits_bits_addr, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  output        io_in_1_ready, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input         io_in_1_valid, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input  [26:0] io_in_1_bits_bits_addr, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  input         io_out_ready, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  output        io_out_valid, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  output        io_out_bits_valid, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  output [26:0] io_out_bits_bits_addr, // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
  output [1:0]  io_chosen // @[chipyard.TestHarness.MediumBoomConfig.fir 343884:4]
);
  wire [1:0] _GEN_0 = io_in_1_valid ? 2'h1 : 2'h2; // @[Arbiter.scala 126:27 chipyard.TestHarness.MediumBoomConfig.fir 343889:4 Arbiter.scala 127:17 chipyard.TestHarness.MediumBoomConfig.fir 343890:6 Arbiter.scala 123:13 chipyard.TestHarness.MediumBoomConfig.fir 343886:4]
  wire [26:0] _GEN_1 = io_in_1_valid ? io_in_1_bits_bits_addr : 27'h0; // @[Arbiter.scala 126:27 chipyard.TestHarness.MediumBoomConfig.fir 343889:4 Arbiter.scala 128:19 chipyard.TestHarness.MediumBoomConfig.fir 343891:6 Arbiter.scala 124:15 chipyard.TestHarness.MediumBoomConfig.fir 343887:4]
  wire  _grant_T = io_in_0_valid | io_in_1_valid; // @[Arbiter.scala 31:68 chipyard.TestHarness.MediumBoomConfig.fir 343899:4]
  wire  grant_1 = ~io_in_0_valid; // @[Arbiter.scala 31:78 chipyard.TestHarness.MediumBoomConfig.fir 343900:4]
  wire  grant_2 = ~_grant_T; // @[Arbiter.scala 31:78 chipyard.TestHarness.MediumBoomConfig.fir 343901:4]
  assign io_in_0_ready = io_out_ready; // @[Arbiter.scala 134:19 chipyard.TestHarness.MediumBoomConfig.fir 343902:4]
  assign io_in_1_ready = grant_1 & io_out_ready; // @[Arbiter.scala 134:19 chipyard.TestHarness.MediumBoomConfig.fir 343904:4]
  assign io_out_valid = ~grant_2; // @[Arbiter.scala 135:19 chipyard.TestHarness.MediumBoomConfig.fir 343908:4]
  assign io_out_bits_valid = io_in_0_valid ? io_in_0_bits_valid : io_in_1_valid; // @[Arbiter.scala 126:27 chipyard.TestHarness.MediumBoomConfig.fir 343894:4 Arbiter.scala 128:19 chipyard.TestHarness.MediumBoomConfig.fir 343897:6]
  assign io_out_bits_bits_addr = io_in_0_valid ? io_in_0_bits_bits_addr : _GEN_1; // @[Arbiter.scala 126:27 chipyard.TestHarness.MediumBoomConfig.fir 343894:4 Arbiter.scala 128:19 chipyard.TestHarness.MediumBoomConfig.fir 343896:6]
  assign io_chosen = io_in_0_valid ? 2'h0 : _GEN_0; // @[Arbiter.scala 126:27 chipyard.TestHarness.MediumBoomConfig.fir 343894:4 Arbiter.scala 127:17 chipyard.TestHarness.MediumBoomConfig.fir 343895:6]
endmodule
f:id:msyksphinz:20210508233534p:plain