前回の続きでChisel3.2.0の気になる機能を使ってみる、の第2回目。
Chisel 3.2.0での変更点
今回は後半の以下の機能を確認していきます。
- Strong enumsのサポート
- HasBlackBoxPathが追加された
BigInt
/Int
→Bool
の変換が追加された- 非同期リセットのサポート
Strong Enumsのサポート
説明は以下の一文が記載されています。
Added support for strongly-typed, self-annotating enums
実際に使ってみると以下のような感じになります。
import chisel3._ import chisel3.experimental.ChiselEnum import chisel3.util._ class StrongEnums extends Module { val io = IO(new Bundle { val wren = Input(Bool()) val rden = Input(Bool()) val rddata = Output(UInt(8.W)) }) /** * これがStrong Enums */ object State extends ChiselEnum { val sIdle, sWrite, sRead = Value } val r_state = RegInit(State.sIdle) switch (r_state) { is (State.sIdle) { when (io.wren) { r_state := State.sWrite } .elsewhen(io.rden) { r_state := State.sRead } } is (State.sWrite) { r_state := State.sIdle } is (State.sRead) { r_state := State.sIdle } } io.rddata := Mux(r_state === State.sRead, 100.U, 0.U) }
これまでのEnum
と比べると:
というあたりが異なる。
- 生成されるRTL
module StrongEnums( input clock, input reset, input io_wren, input io_rden, output [7:0] io_rddata ); reg [1:0] r_state; // @[StrongEnum.scala 22:24] reg [31:0] _RAND_0; wire [1:0] _T_1; // @[Conditional.scala 37:39] wire _T_2; // @[Conditional.scala 37:30] wire _T_5; // @[Conditional.scala 37:30] wire _T_8; // @[Conditional.scala 37:30] wire _T_9; // @[StrongEnum.scala 42:28] wire [6:0] _T_10; // @[StrongEnum.scala 42:19] assign _T_1 = $unsigned(r_state); // @[Conditional.scala 37:39] assign _T_2 = 2'h0 == _T_1; // @[Conditional.scala 37:30] assign _T_5 = 2'h1 == _T_1; // @[Conditional.scala 37:30] assign _T_8 = 2'h2 == _T_1; // @[Conditional.scala 37:30] assign _T_9 = r_state == 2'h2; // @[StrongEnum.scala 42:28] assign _T_10 = _T_9 ? 7'h64 : 7'h0; // @[StrongEnum.scala 42:19] assign io_rddata = {{1'd0}, _T_10}; // @[StrongEnum.scala 42:13] always @(posedge clock) begin if (reset) begin r_state <= 2'h0; end else begin if (_T_2) begin if (io_wren) begin r_state <= 2'h1; end else begin if (io_rden) begin r_state <= 2'h2; end end end else begin if (_T_5) begin r_state <= 2'h0; end else begin if (_T_8) begin r_state <= 2'h0; end end end end end endmodule
また宣言時の右辺に指定しているValue
はUInt
を引数に取れるので、以下のように各変数に任意の値を設定することも可能だ。
object State extends ChiselEnum { val sIdle, sWrite= Value(0.U) val sRead = Value(3.U) }
HasBlackBoxPathが追加された
これは”追加された”とだけ書いてあって、確かにみると増えてるんだけど最初イマイチ使い方が不明でした。。
HasBlackBoxPath
をMix-inするとaddPath
というメソッドが使用可能になるのは分かったんだけれども、”パス”の意味がピンと来ませんでした。。
(最初"Path"の意味を”インクルードパス的”な意味で捉えてて。。)
最終的にテストコードでエラボレートが通ったのは以下のコード。
import chisel3._ import chisel3.util._ class VerilogA extends BlackBox with HasBlackBoxPath { val io = IO(new Bundle { val a = Input(Bool()) val aa = Output(Bool()) }) addPath("subprj/chisel-3.2.0/src/main/resources/vsrc/VerilogA.sv") } class VerilogB extends BlackBox with HasBlackBoxPath { val io = IO(new Bundle { val b = Input(Bool()) val bb = Output(Bool()) }) addPath("subprj/chisel-3.2.0/src/main/resources/vsrc/VerilogB.sv") } class TrialHasBlackBoxPath extends Module { val io = IO(new Bundle { val a = Input(Bool()) val aa = Output(Bool()) val b = Input(Bool()) val bb = Output(Bool()) }) val m_aaa = Module(new VerilogA) val m_bbb = Module(new VerilogB) m_aaa.io.a := io.a io.aa := m_aaa.io.aa m_bbb.io.b := io.b io.bb := m_bbb.io.bb }
どうもaddPath
のパスの意味はsbt
のディレクトリ構成に縛られずにBlackBox
を構成するRTLの場所を指定できる、という意味のようです。
BigInt
/Int
→Bool
の変換が追加された
これは簡単にコードだけ掲載します。以下のようにBigInt
/Int
の値からBool
の値を生成できるようになりました。
val a: Bool = 0.B // false.Bと一緒 val b: Bool = 1.B // true.Bと一緒
なおBool
型に変換を行うため"0"/"1"以外の値を変換した場合にはエラーになります。
val c: Bool = 2.B // [error] TrialHasBlackBoxPath.scala:44: Cannot convert 2 to Bool, must be 0 or 1 in class TrialHasBlackBoxPath
非同期リセットのサポート
個人的にはこれが3.2.0の目玉機能。ついに非同期リセットが使えるようになりました。
以下のようwithReset
の信号指定時にReset
型に追加されたasAsyncReset
を読んでおくとRTL変換時に非同期リセットにマッピングされるようになります。
import chisel3._ import chisel3.internal.naming.chiselName import chisel3.util._ @chiselName class TrialAsyncReset extends Module { val io = IO(new Bundle { val a = Input(Bool()) val b = Input(UInt(2.W)) val out_a = Output(Bool()) val out_b = Output(Bool()) val out_aa = Output(Bool()) }) val r_a = withReset(reset.asAsyncReset()) { RegInit(0.U(8.W)) } r_a := io.a withReset(reset.asAsyncReset()) { val r_b = RegInit(2.U) val r_c = RegNext(io.a, 0.B) r_b := io.b io.out_b := r_b io.out_aa := r_c } io.out_a := r_a } object ElaborateTrialAsyncReset extends App { Driver.execute(args, () => new TrialAsyncReset) }
- 生成されたRTL
- 確かに非同期リセットになっているのが確認できました。
- どうも
posedge
限定っぽい。。
module TrialAsyncReset( input clock, input reset, input io_a, input [1:0] io_b, output io_out_a, output io_out_b, output io_out_aa ); wire _T; // @[TrialAsyncReset.scala 16:41] reg [7:0] r_a; // @[TrialAsyncReset.scala 17:12] reg [31:0] _RAND_0; reg [1:0] r_b; // @[TrialAsyncReset.scala 23:22] reg [31:0] _RAND_1; reg r_c; // @[TrialAsyncReset.scala 24:22] reg [31:0] _RAND_2; assign _T = reset; // @[TrialAsyncReset.scala 16:41] assign io_out_a = r_a[0]; // @[TrialAsyncReset.scala 31:12] assign io_out_b = r_b[0]; // @[TrialAsyncReset.scala 27:14] assign io_out_aa = r_c; // @[TrialAsyncReset.scala 28:15] always @(posedge clock or posedge _T) begin if (_T) begin r_a <= 8'h0; end else begin r_a <= {{7'd0}, io_a}; end end always @(posedge clock or posedge _T) begin if (_T) begin r_b <= 2'h2; end else begin r_b <= io_b; end end always @(posedge clock or posedge _T) begin if (_T) begin r_c <= 1'h0; end else begin r_c <= io_a; end end endmodule
ということでざっとChisel3.2.0の気になる機能を試してみました。