FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

VerilogからChiselへ移行するためのTips(文法編)

f:id:msyksphinz:20190119013850p:plain

趣味で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
    }
  }