趣味でVerilogで書いたデザインをChiselに移植していたのだが、VerilogとChiselの文法的な違い、移植するにあたり注意すべき点が少しずつ分かってきた。
まだ文法的な部分しか見ていないが、Verilogで書いたデザインをChiselに移植するために気を付けなければならないところを、まずはSyntaxの面からみていこうと思う。
Bit ReassignmentはChiselでは不可能。
Bit Reassignmentとは、Verilogで言うところの変数のビットに対してassignを実行すること。 Chiselで書くと以下のようになる。ただしこれはエラーとなる。 outという変数の、とあるビット位置にだけ書き込むというのはできない。まあ、もともとがScalaというプログラミング言語なので、不可能なのは何となくわかる気がする。
class BitReassignError extends Module { val io = IO(new Bundle { val out = Output(UInt(2.W)) val in = Input(UInt(2.W)) }) io.out(0) := io.in(1) io.out(1) := io.in(0) }
エラーが出る。
[info] [0.003] Elaborating design... [error] (run-main-0) chisel3.internal.ChiselException: Cannot reassign to read-only chisel3.core.Bool@11 [error] chisel3.internal.ChiselException: Cannot reassign to read-only chisel3.core.Bool@11
ビット書き込みをしたい場合は、Vec型に分解すること。 これをうまく活用するためには、最初からエレガントに設計することが求められる気がする。
class BitReassignError extends Module { val io = IO(new Bundle { val out = Output(Vec(2, UInt(1.W))) // <-- ココ。1ビットの信号を2つのベクトルに分解。 val in = Input(UInt(2.W)) }) io.out(0) := io.in(1) io.out(1) := io.in(0) }
When条件が多重、抜け
これもやってしまいがち。例えば、以下の構文だとio.outはどのような条件でも代入されるように思えるが文法的にio.outに抜けが生じるのでエラー。.otherwise
をちゃんと使おう。
class MultiWhen extends Module { val io = IO(new Bundle { val out = Output(UInt(2.W)) val inA = Input(UInt(2.W)) val inB = Input(UInt(2.W)) val en = Input(Bool()) }) when (io.en) { io.out := io.inA } when (~io.en) { io.out := io.inB } }
[error] (run-main-0) firrtl.passes.CheckInitialization$RefNotInitializedException: @[:@6.4] : [module MultiWhen] Reference io is not fully initialized. [error] @[convert_verilog.scala 26:16:@8.4] : node _GEN_0 = mux(io.en, io.inA, VOID) @[convert_verilog.scala 26:16:@8.4] [error] @[convert_verilog.scala 29:17:@12.4] : node _GEN_1 = mux(_T_13, io.inB, _GEN_0) @[convert_verilog.scala 29:17:@12.4] [error] @[convert_verilog.scala 27:12:@9.6 convert_verilog.scala 30:12:@13.6] : io.out <= _GEN_1 @[convert_verilog.scala 27:12:@9.6 convert_verilog.scala 30:12:@13.6] [error] firrtl.passes.CheckInitialization$RefNotInitializedException: @[:@6.4] : [module MultiWhen] Reference io is not fully initialized. [error] @[convert_verilog.scala 26:16:@8.4] : node _GEN_0 = mux(io.en, io.inA, VOID) @[convert_verilog.scala 26:16:@8.4] [error] @[convert_verilog.scala 29:17:@12.4] : node _GEN_1 = mux(_T_13, io.inB, _GEN_0) @[convert_verilog.scala 29:17:@12.4] [error] @[convert_verilog.scala 27:12:@9.6 convert_verilog.scala 30:12:@13.6] : io.out <= _GEN_1 @[convert_verilog.scala 27:12:@9.6 convert_verilog.scala 30:12:@13.6]
.otherwiseを使うと直る。
when (io.en) { io.out := io.inA } .otherwise { io.out := io.inB }
使用していない(代入のない)変数
Verilog的には不定となって終わりだが、Chisel的には代入のない変数はNG。未初期化変数としてエラーになる。VerilogをChiselに変換するとこのエラーが大量に出てきて、結構使っていない変数が残っているんだなと絶望する。
class UnUsedVal extends Module { val io = IO(new Bundle { val outA = Output(UInt(2.W)) val outB = Output(UInt(2.W)) }) io.outA := 0.U // io.outB := 1.U }
io.outB
を初期化していないのでエラー。
[error] (run-main-0) firrtl.passes.CheckInitialization$RefNotInitializedException: @[:@6.4] : [module UnUsedVal] Reference io is not fully initialized. [error] : io.outB <= VOID [error] firrtl.passes.CheckInitialization$RefNotInitializedException: @[:@6.4] : [module UnUsedVal] Reference io is not fully initialized. [error] : io.outB <= VOID
整数iとハードウェア整数i.Uを正しく使い分けよう
これは最初にハマる。Chiselではハードウェアとして使われる整数は全て.Uを末尾に付ける。 これはScalaで使用される整数(.Uのつかないもの)と明確に区別される。
0.U 1.U 2.U(31, 0) // ビットフィールドを正しく使い分けよう。Scalaの整数2をChiselのハードウェア整数に変換する。
じゃあScalaの変数はどうするかというと、Verilogのgenerate forのような感じで使うとよろしい。
for (i <- 0 until 8) { // このiはScalaの変数 io.out(i) := function(io.inA, i.U) // i.Uはハードウェアで取り扱われる整数。 }
所々でCatは使えない。
例えばswitch内の条件でCat(ビット連結)を使うと失敗する。
io.out := io.default // switchのデフォルト値はここに書く。これがないとエラー。 switch(io.idx) { is (Cat(1.U(2, 0), 0.U(1, 0)) { // つまりビット値で言うと0b001_00 io.out := io.in } }
上記はswitch文のis要素でCatを使ったのでエラー。
[error] (run-main-0) java.lang.IllegalArgumentException: requirement failed: is conditions must be literals! [error] java.lang.IllegalArgumentException: requirement failed: is conditions must be literals!
文法的にPassさせるためには、以下のように書こう。
io.out := io.in_default switch(io.idx) { // is (Cat(1.U(2, 0), 0.U(1, 0))) { is (((1 << 2) | 0).U) { // ビットシフトで肩代わり。 io.out := io.in } }