ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chisel Bootcamp - Module2.4 (2) - レジスタを使ったフロー制御

スポンサーリンク

前回の記事ではChisel BootcampのModule2.4に入り、レジスタについてを学んだ。

www.tech-diningyo.info

今回はレジスタを使ったフロー制御を見ていく。

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!!

テストもやっていることは単純で

  1. 値をセット
  2. stepで1サイクル実行
  3. 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

これだけに絞って書くと、

  1. io.indelayにセット
  2. io.inからdelay引く
  3. io_outに引いた結果がセットされる

となる。

ということで、まあ書いてきたとおりレジスタVerilogで書くときと同様に各種演算が出来る、ということだ。

これで、基本的な回路を書くだけなら出来そうなところまでは来たかな。まあこれだけだと、レジスタ作るときにReg()使えて記述量が若干減るくらいのメリットしかないけど。。

次回はModule2.4の残りである練習問題に取り組んでいく。