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