Chiselを書いていると、Chiselには基本的にクロックとリセットという概念が存在しない。
クロック信号とリセット信号は隠されており、普段は見える事は無いのだが、一応扱うことは可能だ。 例えば、特定のモジュールに対してクロックゲーティングを挿入するためにはどうすればよいのか、調べてみた。
方法その1. クロックゲーティングのためのVerilog Wrapperを使う
これはおそらく一般的に行われている手法。VerilogをBlackBoxとして挿入することができるので、その機能を使う。
方法その2. クロックゲーティングの論理を生成する
Chiselには、隠された信号としてclockとresetというものが存在している。これは、Module
クラスに定義されている者であり、これを継承するモジュールではすべて扱うことができる。
class clock_gating extends Module { val io = IO(new Bundle { val en_clock = Input(Bool()) val reg_in = Input(UInt(32.W)) val reg_out = Output(UInt(32.W)) }) val gated_clock = (io.en_clock & clock.asUInt()(0)).asClock val inst = Module(new gated_clock_module()) inst.clock := gated_clock inst.io.reg_in := io.reg_in io.reg_out := inst.io.reg_out }
上記がその例だ。gated_clock
はio.en_clock
により制御されるGated Clockで、Implicitに定義されたclock
信号に対して&
演算を挿入することで実現している。
val gated_clock = (io.en_clock & clock.asUInt()(0)).asClock ~~~~~~~~~~~~~~ <-- クロック信号はデフォルトでClock型であり、これをUInt型に変換 ~~~~~~~~~~~~~~~~~ <-- UInt信号は32ビットなので、最下位の1ビットを抽出する ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- クロック信号とio.en_clockのAND演算 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <-- AND演算したUInt型の信号を、再びClock型に戻す。
このカラクリは上記のように分解すると分かりやすいと思う。このように生成したクロック信号は、あとは別のモジュールに接続すれば自由に使用できる。
val inst = Module(new gated_clock_module()) inst.clock := gated_clock inst.io.reg_in := io.reg_in io.reg_out := inst.io.reg_out
これで、gated_clock_module
モジュール内で使用されるFFはすべてio.en_clock
に基づいてクロックゲーティングされている。