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

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

Chisel3.2.0が出たので変更点を確認する(2)

スポンサーリンク

前回の続きでChisel3.2.0の気になる機能を使ってみる、の第2回目。

Chisel 3.2.0での変更点

今回は後半の以下の機能を確認していきます。

  1. Strong enumsのサポート
  2. HasBlackBoxPathが追加された
  3. BigInt/IntBoolの変換が追加された
  4. 非同期リセットのサポート

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と比べると:

  • 数を自分で指定しなくていい(これまではval a :: b :: c :: Nil = Enum(3)
  • object名前空間が区切られるので、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

また宣言時の右辺に指定しているValueUIntを引数に取れるので、以下のように各変数に任意の値を設定することも可能だ。

  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/IntBoolの変換が追加された

これは簡単にコードだけ掲載します。以下のように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の気になる機能を試してみました。