前回のChiselの記事では標準ライブラリに含まれているマルチプレクサに関するブロック(PriorityMux/Mux1H)を紹介した。
今日は幕間で紹介されているChiselのライブラリとしては最後になるCounter
について見ていく。
Module 3.2幕間: Chiselの標準ライブラリ
カウンタ
そのまんま”カウンタ”である。Chiselの標準ライブラリのカウンタの機能は
- 特定の値になるまで1サイクル毎に1ずつカウントアップ
である。
なお、Chiselの標準ライブラリで提供されるCounter
はモジュールでは無い点は覚えておく必要がある(詳細は後述)
では早速サンプルコードを確認していこう。もう最後で今更だけど、今回はテスト対象のモジュールを直接Module
内に実装するのではなく、class
として独立させた。
class MyCounter extends Module { val io = IO(new Bundle { val count = Input(Bool()) val out = Output(UInt(2.W)) }) val counter = Counter(3) // 3-カウントのカウンタ (出力の範囲は[0...2]になる) when(io.count) { counter.inc() } io.out := counter.value } Driver(() => new MyCounter) { c => new PeekPokeTester(c) { poke(c.io.count, 1) println(s"start: counter value=${peek(c.io.out)}") step(1) println(s"step 1: counter value=${peek(c.io.out)}") step(1) println(s"step 2: counter value=${peek(c.io.out)}") poke(c.io.count, 0) step(1) println(s"step without increment: counter value=${peek(c.io.out)}") poke(c.io.count, 1) step(1) println(s"step again: counter value=${peek(c.io.out)}") } }
上記を動作させると以下の出力が得られる。
[info] [0.001] Elaborating design... [info] [0.695] Done elaborating. Total FIRRTL Compile Time: 207.9 ms Total FIRRTL Compile Time: 15.5 ms End of dependency graph Circuit state created [info] [0.001] SEED 1545749900235 [info] [0.003] start: counter value=0 [info] [0.003] step 1: counter value=1 [info] [0.004] step 2: counter value=2 [info] [0.005] step without increment: counter value=2 [info] [0.006] step again: counter value=0 test cmd2HelperMyCounter Success: 0 tests passed in 9 cycles taking 0.015189 seconds [info] [0.007] RAN 4 CYCLES PASSED
テストコードの中ではpoke(c.io.count, x)
で設定を行い、step
を使って1サイクルずつサイクルを進めて、その時のカウント値を表示している。
"step 2"の実行後にio.count
を0に設定してサイクルを進めると、カウントの値が止まっていることからio.count
がカウントアップのイネーブルとして動作する仕組みになっていることがわかると思う。
生成されるRTL
先ほどのMyCounter
をRTLに変換すると以下のコードが得られる。ご覧になってわかる通りChiselのCounter
に相当する部分はカウンターモジュールがインスタンスされるわけではなくreg [1:0] value
として実装されている。
これが冒頭に記載した"Chiselの標準ライブラリで提供されるCounter
はモジュールでは無い"という意味だ。
module cmd4HelperMyCounter( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input io_count, // @[:@6.4] output [1:0] io_out // @[:@6.4] ); reg [1:0] value; // @[Counter.scala 26:33:@8.4] reg [31:0] _RAND_0; wire _T_12; // @[Counter.scala 34:24:@10.6] wire [2:0] _T_14; // @[Counter.scala 35:22:@11.6] wire [1:0] _T_15; // @[Counter.scala 35:22:@12.6] wire [1:0] _GEN_0; // @[Counter.scala 37:21:@14.6] wire [1:0] _GEN_1; // @[cmd4.sc 9:18:@9.4] assign _T_12 = value == 2'h2; // @[Counter.scala 34:24:@10.6] assign _T_14 = value + 2'h1; // @[Counter.scala 35:22:@11.6] assign _T_15 = _T_14[1:0]; // @[Counter.scala 35:22:@12.6] assign _GEN_0 = _T_12 ? 2'h0 : _T_15; // @[Counter.scala 37:21:@14.6] assign _GEN_1 = io_count ? _GEN_0 : value; // @[cmd4.sc 9:18:@9.4] assign io_out = value; always @(posedge clock) begin if (reset) begin value <= 2'h0; end else begin if (io_count) begin if (_T_12) begin value <= 2'h0; end else begin value <= _T_15; end end end end endmodule
Counter
のソースコード
ソースコードがどうなっているかも確認しておこう。
実際に呼び出しているのは、これまでにもいくつか見てきたCounter
のobject
になっている。このためただ単にConuter
と記載して呼び出すとapply
が暗黙的に呼びだされて、その結果が返却されることになる。
Counter
のapply
ではclass Counter
をインスタンスしている。
object Counter { /** Instantiate a [[Counter! counter]] with the specified number of counts. */ def apply(n: Int): Counter = new Counter(n) /** Instantiate a [[Counter! counter]] with the specified number of counts and a gate. * * @param cond condition that controls whether the counter increments this cycle * @param n number of counts before the counter resets * @return tuple of the counter value and whether the counter will wrap (the value is at * maximum and the condition is true). */ @chiselName def apply(cond: Bool, n: Int): (UInt, Bool) = { val c = new Counter(n) var wrap: Bool = null when (cond) { wrap = c.inc() } (c.value, cond && wrap) } }
では、実際にインスタンスされるCounter
クラスはどうなってるのかを見てみよう。
@chiselName class Counter(val n: Int) { require(n >= 0) val value = if (n > 1) RegInit(0.U(log2Ceil(n).W)) else 0.U /** Increment the counter, returning whether the counter currently is at the * maximum and will wrap. The incremented value is registered and will be * visible on the next cycle. */ def inc(): Bool = { if (n > 1) { val wrap = value === (n-1).asUInt value := value + 1.U if (!isPow2(n)) { when (wrap) { value := 0.U } } wrap } else { true.B } } }
上記の様にシンプルな構成となっており、以下の2つを行っている。
class
の宣言部分を見ると一目瞭然だが、extends Module
を行っていないためChiselのモジュールでは無いことがわかる。
とりあえずChisel-Bootcampの"3.2 Interlude"としてはChiselの標準ライブラリに関する紹介はここまでになる。この幕間に入って最初↓に書いた記事では、まだいくつかライブラリを記載しているのだが、それはまた時間を見つけて確認してくことにする。
ということで、今日はここまで。
次回からはModule3.3に入り高階関数についてを勉強していく。