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

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

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

スポンサーリンク

Chiselのtwitterをフォローしている人はご存知と思いますが、先日ついにChiselの3.2.0のリリースが公式にアナウンスされました!

ということで今回はRC1/RC2/3.2.0のリリースノートから気になる機能をピックアップして確認しておこうと思います。

Chisel 3.2.0での変更点

先に書いたように3.2.0のリリースにあたって2つのRC版がリリースされており、そのリリースノートに各リリース版での修正内容が記載されています。

3.2.0-RC1 : https://github.com/freechipsproject/chisel3/releases/tag/v3.2.0-RC1 3.2.0-RC2 : https://github.com/freechipsproject/chisel3/releases/tag/v3.2.0-RC2 3.2.0 : https://github.com/freechipsproject/chisel3/releases/tag/v3.2.0

主だった修正内容はほぼほぼRC1に含まれており、RC2と3.2.0でそこに含みきれないけど入れたいものが修正された感じのようです。

ざっと読んだ感じで、機能面で変更された部分を上げていくと:

  1. dontTouch/RawModule/MultiIOがstableになった
  2. toBool/toBoolsasBool/asBoolsに変更された
  3. MuxDontCareが使えるようになった。
  4. ヘルパー関数としてUnsignedBitsRequired/UnsignedWidthRequiredが追加
    1. リリースバージョンでは見当たらない。。
  5. Bundle Literalsのサポート
  6. Verilog形式のメモリ読み込みのサポート
  7. MixedVecのサポート
  8. Strong enumsのサポート
  9. HasBlackBoxPathが追加された
  10. BaseModule.namelazyになった
    1. これChisel3.1 → Chisel 3.2の間に変わったのか。Chisel触り始めた時には既にこうだった気がする。
  11. BigInt/IntBoolの変換が追加された
  12. WireInitWireDefaultに変更
  13. 非同期リセットのサポート

こんな感じ。 気になる機能を実際に軽く試してみた。

MuxにDontCareが使えるように

これはそのまんまでMuxを使用した際の選択される要素にDontCareを指定できるようになった。

import chisel3._
import chisel3.util._

class MuxDontCare extends Module {
  val io = IO(new Bundle {
    val sel = Input(Bool())
    val in = Input(UInt(8.W))
    val out = Output(Bool())
  })

  io.out := Mux(io.sel, io.in, DontCare)
}
  • 生成されるRTL
module MuxDontCare(
  input        clock,
  input        reset,
  input        io_sel,
  input  [7:0] io_in,
  output       io_out
);
  wire [7:0] _T; // @[MuxDontCare.scala 12:16]
  assign _T = io_sel ? io_in : 8'h0; // @[MuxDontCare.scala 12:16]
  assign io_out = _T[0]; // @[MuxDontCare.scala 12:10]
endmodule

MuxCaseでも同様。

class MuxCaseDontCare extends Module {
  val io = IO(new Bundle {
    val sel = Input(UInt(2.W))
    val in1 = Input(UInt(8.W))
    val in2 = Input(UInt(8.W))
    val out = Output(Bool())
  })

  io.out := MuxCase(DontCare, Seq(
    io.sel(0) -> io.in1,
    io.sel(1) -> io.in2
  ))
}
module MuxCaseDontCare(
  input        clock,
  input        reset,
  input  [1:0] io_sel,
  input  [7:0] io_in1,
  input  [7:0] io_in2,
  output       io_out
);
  wire  _T; // @[MuxDontCare.scala 24:11]
  wire  _T_1; // @[MuxDontCare.scala 25:11]
  wire [7:0] _T_2; // @[Mux.scala 87:16]
  wire [7:0] _T_4; // @[Mux.scala 87:16]
  assign _T = io_sel[0]; // @[MuxDontCare.scala 24:11]
  assign _T_1 = io_sel[1]; // @[MuxDontCare.scala 25:11]
  assign _T_2 = _T_1 ? io_in2 : 8'h0; // @[Mux.scala 87:16]
  assign _T_4 = _T ? io_in1 : _T_2; // @[Mux.scala 87:16]
  assign io_out = _T_4[0]; // @[MuxDontCare.scala 23:10]
endmodule

確かに普通に処理が終わって生成されている。

Bundle Literalsのサポート

なんのこっちゃ??と思って読んでみたけど、どうやらBundleで作ったクラスのメンバをインスタンス時に初期化出来るようにしたということらしい。

使い方はこの機能のテストのコードを読むとわかるのだが、大体以下のような感じ。

import chisel3._
import chisel3.util._
import chisel3.experimental.BundleLiterals._ // これが必要
import chisel3.experimental.BundleLiteralException

class BundleLiterals extends Bundle {
  val bool = Bool()
  val uint = UInt(8.W)
  val bundle = new Bundle {
    val a = Bool()
  }
}

class UseBundleLiterals extends Module {
  val io = IO(Output(new BundleLiterals))

  // 以下のように.Lit()内で初期化が出来る
  val out = new BundleLiterals().Lit(
    _.bool -> true.B,
    _.uint -> 0xff.U,
    _.bundle.a -> true.B // Bundleの中でBundle使っててもOK
  )

  io := out
}

生成されたRTLは以下のように初期化した値が正しく設定されていた。

module UseBundleLiterals(
  input        clock,
  input        reset,
  output       io_bool,
  output [7:0] io_uint,
  output       io_bundle_a
);
  assign io_bool = 1'h1; // @[BundleLiterals.scala 25:6]
  assign io_uint = 8'hff; // @[BundleLiterals.scala 25:6]
  assign io_bundle_a = 1'h1; // @[BundleLiterals.scala 25:6]
endmodule

因みにVecの扱いがテストコードに見つからなくて、幾つか試してみたけどやり方が分からなかった。。前は無理やりこんな感じのことをやるメソッドを作ったりして対応していたので、結構便利に使えそう。

Verilog形式のメモリ読み込みのサポート

これイマイチどこまでのことを指して言っているのかがわからん。。。

とりあえず以下のようなコードを書いてみた。

import chisel3._
import chisel3.util.experimental._
import firrtl.annotations.MemoryLoadFileType

class MemVerilogStyle extends Module {
  val io = IO(new Bundle {
    val addr = Input(UInt(4.W))
    val rden = Input(Bool())
    val rddata = Output(UInt(32.W))
  })

  val m_mem = Mem(16, UInt(32.W))

  loadMemoryFromFile(m_mem, "subprj/chisel-3.2.0/src/main/resources/test.hex", hexOrBinary = MemoryLoadFileType.Hex)

  io.rddata := Mux(io.rden, m_mem.read(io.addr), 0.U)
}

上記に対してのテストコードが以下で、ただメモリにアクセスしてリードするだけ。

import chisel3.iotesters._

class MemVerilogStyleTester extends ChiselFlatSpec {

  it should "" in {
    Driver.execute(
      Array(
        //"-tbn=verilator" // コメントアウト時はtreadleが使用される
      ), () => new MemVerilogStyle) {
      c => new PeekPokeTester(c) {
        reset()
        poke(c.io.rden, true)
        for (addr <- 0 until 16) {
          poke(c.io.addr, addr)
          println(f"c.io.rddata = 0x${peek(c.io.rddata)}%08x")
          step(1)
        }
        step(1)
      }
    }
  }
}

バックエンドを切り替えて以下のメモリファイルを入れてみた。

@1
0000_0001 0000_0002
  • treadle
[info] MemVerilogStyleTester:
[info] - should  *** FAILED ***
[info]   treadle.executable.TreadleException: loading memory m_mem[0] <= @1: error: For input string: "@1"
[info]   at treadle.executable.MemoryInitializer$$anonfun$treadle$executable$MemoryInitializer$$doInitialize$1.apply(Memory.scala:589)
[info]   at treadle.executable.MemoryInitializer$$anonfun$treadle$executable$MemoryInitializer$$doInitialize$1.apply(Memory.scala:580)

読めないって言われた。。

  • verilator
STARTING test_run_dir/MemVerilogStyleTester453628597/VMemVerilogStyle
[info] [0.001] SEED 1571551596439
[info] [0.008] c.io.rddata = 0x00000000
[info] [0.011] c.io.rddata = 0x00000001
[info] [0.012] c.io.rddata = 0x00000002
[info] [0.012] c.io.rddata = 0x00000000

こっちは普通に動作して@を使ったアドレスの指定も可能になっていた。treadleのメモリ読み込みで$readmemh相当の処理が出来るようになってくれると、もう少し便利になるかな、と言った感じ。

MixedVecのサポート

MixedVecを使うと以下のようなコードが書けるようになる。

import chisel3._
import chisel3.util._


class TrialMixedVec extends Module {
  val io = IO(new Bundle {
    val x = Input(UInt(3.W))
    val y = Input(UInt(10.W))
    val vec = Output(MixedVec(UInt(3.W), UInt(10.W)))
  })
  io.vec(0) := io.x
  io.vec(1) := io.y
}

object ElaborateMixedVec extends App {
  Driver.execute(args, () => new TrialMixedVec)
}
module TrialMixedVec(
  input        clock,
  input        reset,
  input  [2:0] io_x,
  input  [9:0] io_y,
  output [9:0] io_vec_1,
  output [2:0] io_vec_0
);
  assign io_vec_1 = io_y; // @[TrialMixedVec.scala 13:13]
  assign io_vec_0 = io_x; // @[TrialMixedVec.scala 12:13]
endmodule

これ引数にSeq[T]を取ることも出来るので、前出来なくて諦めたVecの各要素毎のパラメタライズが出来る気がする。(Vec[T]にパラメタライズ出来るBundleを指定して、ポートごとに設定を変えるようなイメージ)

力尽きたので2回に分けます。