Chiselのtwitterをフォローしている人はご存知と思いますが、先日ついにChiselの3.2.0のリリースが公式にアナウンスされました!
🥳 #ChiselLang 3.2.0 and #FIRRTL 1.2.0 are officially out! Update your build.sbt and get Chisel-ing!
— Chisel (@chisel_lang) October 17, 2019
A huge thanks to all the developers who made this release possible.
Release Notes are on GitHub (warning... they're long!):https://t.co/o6lPSmHUdmhttps://t.co/vJPfU1o4FB pic.twitter.com/x8hMacgXXZ
ということで今回は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でそこに含みきれないけど入れたいものが修正された感じのようです。
ざっと読んだ感じで、機能面で変更された部分を上げていくと:
dontTouch
/RawModule
/MultiIO
がstableになったtoBool
/toBools
がasBool
/asBools
に変更されたMux
にDontCare
が使えるようになった。- ヘルパー関数として
UnsignedBitsRequired
/UnsignedWidthRequired
が追加- リリースバージョンでは見当たらない。。
- Bundle Literalsのサポート
- Verilog形式のメモリ読み込みのサポート
MixedVec
のサポート- Strong enumsのサポート
- HasBlackBoxPathが追加された
BaseModule.name
がlazy
になった- これChisel3.1 → Chisel 3.2の間に変わったのか。Chisel触り始めた時には既にこうだった気がする。
BigInt
/Int
→Bool
の変換が追加されたWireInit
がWireDefault
に変更- 非同期リセットのサポート
こんな感じ。 気になる機能を実際に軽く試してみた。
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回に分けます。