前回の記事ではChisel BootcampのModule2.4に入り、レジスタについてを学んだ。
今回はレジスタを使ったフロー制御を見ていく。
Module 2.4: 順序回路
フロー制御
レジスタを使ったフロー制御、とは言ってもやり方自体はWire
を使った時は変わらない。
すなわち
when
elsewhen
otherwise
を使って条件分岐を作り所望の動きを行うレジスタにしていけばOKだ。
例題:レジスタによるフロー制御
ここで扱う例題は「レジスタを使った最大値の保持」だ。
class FindMax extends Module { val io = IO(new Bundle { val in = Input(UInt(10.W)) val max = Output(UInt(10.W)) }) val max = RegInit(0.U(10.W)) when (io.in > max) { max := io.in } io.max := max } assert(chisel3.iotesters.Driver(() => new FindMax) { c => new PeekPokeTester(c) { expect(c.io.max, 0) poke(c.io.in, 1) step(1) expect(c.io.max, 1) poke(c.io.in, 3) step(1) expect(c.io.max, 3) poke(c.io.in, 2) step(1) expect(c.io.max, 3) poke(c.io.in, 24) step(1) expect(c.io.max, 24) } }) println("SUCCESS!!")
特に複雑なことは一切しておらず、max
レジスタの更新条件がio.in > max
となっているので、入力値がmax
レジスタの現在の値より大きければレジスタにio.in
が設定されることになる。
これを前回学んだstep
も使用したテストで確認した結果は以下のようになる。
[info] [0.001] Elaborating design... [info] [0.671] Done elaborating. Total FIRRTL Compile Time: 233.2 ms Total FIRRTL Compile Time: 16.3 ms End of dependency graph Circuit state created [info] [0.001] SEED 1540613103246 test cmd2HelperFindMax Success: 5 tests passed in 9 cycles taking 0.015826 seconds [info] [0.006] RAN 4 CYCLES PASSED SUCCESS!!
テストもやっていることは単純で
- 値をセット
step
で1サイクル実行expect
で期待値比較
が続いているだけだ。
その他のレジスタを使った例
レジスタを使った演算処理も可能で
val reg: UIint = Reg(UInt(4.W))
のように書くことも出来て、reg
ではUInt
にで処理可能な+
、 -
と言った処理が可能となる。
レジスタは必ずしもUInt
である必要はなく、Chisel3.Data
で定義されるサブクラスが使用可能だ。
コムフィルタ
以下はコムフィルタの実装例である。
class Comb extends Module { val io = IO(new Bundle { val in = Input(SInt(12.W)) val out = Output(SInt(12.W)) }) val delay: SInt = Reg(SInt(12.W)) delay := io.in io.out := io.in - delay } println(getVerilog(new Comb))
上記で記載したように、Reg
に入れた値を使用した減算処理が実行されている。
これを実行してVerilogを生成すると、次のような物になる。
[info] [0.000] Elaborating design... [info] [0.103] Done elaborating. Total FIRRTL Compile Time: 109.1 ms module cmd3HelperComb( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [11:0] io_in, // @[:@6.4] output [11:0] io_out // @[:@6.4] ); reg [11:0] delay; // @[cmd3.sc 7:24:@8.4] reg [31:0] _RAND_0; wire [12:0] _T_10; // @[cmd3.sc 9:19:@10.4] wire [11:0] _T_11; // @[cmd3.sc 9:19:@11.4] wire [11:0] _T_12; // @[cmd3.sc 9:19:@12.4] assign _T_10 = $signed(io_in) - $signed(delay); // @[cmd3.sc 9:19:@10.4] assign _T_11 = _T_10[11:0]; // @[cmd3.sc 9:19:@11.4] assign _T_12 = $signed(_T_11); // @[cmd3.sc 9:19:@12.4] assign io_out = _T_12; `ifdef RANDOMIZE_GARBAGE_ASSIGN `define RANDOMIZE `endif `ifdef RANDOMIZE_INVALID_ASSIGN `define RANDOMIZE `endif `ifdef RANDOMIZE_REG_INIT `define RANDOMIZE `endif `ifdef RANDOMIZE_MEM_INIT `define RANDOMIZE `endif `ifdef RANDOMIZE integer initvar; initial begin `ifndef verilator #0.002 begin end `endif `ifdef RANDOMIZE_REG_INIT _RAND_0 = {1{$random}}; delay = _RAND_0[11:0]; `endif // RANDOMIZE_REG_INIT end `endif // RANDOMIZE always @(posedge clock) begin delay <= io_in; end endmodule
実際の処理部分のみを抽出してみる。
reg [11:0] delay; // @[cmd3.sc 7:24:@8.4] assign _T_10 = $signed(io_in) - $signed(delay); // @[cmd3.sc 9:19:@10.4] assign _T_11 = _T_10[11:0]; // @[cmd3.sc 9:19:@11.4] assign _T_12 = $signed(_T_11); // @[cmd3.sc 9:19:@12.4] assign io_out = _T_12; always @(posedge clock) begin delay <= io_in; end
これだけに絞って書くと、
io.in
をdelay
にセットio.in
からdelay
引くio_out
に引いた結果がセットされる
となる。
ということで、まあ書いてきたとおりレジスタもVerilogで書くときと同様に各種演算が出来る、ということだ。
これで、基本的な回路を書くだけなら出来そうなところまでは来たかな。まあこれだけだと、レジスタ作るときにReg()
使えて記述量が若干減るくらいのメリットしかないけど。。
次回はModule2.4の残りである練習問題に取り組んでいく。