前回の記事ではChisel BootcampのModule2.1の大体の学習が終わったところだった。
今回はModule2.1の最初のモジュールの残りを片付ける。内容的には主にデバッグの際にどうすればいいかというものだ。
Module 2.1: 最初のChiselモジュール
生成されるVeirlog/FIRRTLの確認方法
生成されたハードウェアが何か問題を持っていて、VerilogやFIRRTLを確認したい場合のために、Chiselの実行時にVerilogを確認する方法が用意されているそうだ。
// Viewing the Verilog for debugging println(getVerilog(new Passthrough))
こちらは既に使われていたが一応実行すると以下のように生成されたVerilogコードが表示できる。
[info] [0.000] Elaborating design... [info] [0.057] Done elaborating. Total FIRRTL Compile Time: 8.7 ms module cmd8HelperPassthrough( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [3:0] io_in, // @[:@6.4] output [3:0] io_out // @[:@6.4] ); assign io_out = io_in; endmodule
- FIRRTL
// Viewing the firrtl for debugging println(getFirrtl(new Passthrough))
こちらはFIRRTL
[info] [0.000] Elaborating design... [info] [0.003] Done elaborating. ;buildInfoPackage: chisel3, version: 3.1.0, scalaVersion: 2.11.12, sbtVersion: 1.1.1, builtAtString: 2018-04-17 19:22:56.455, builtAtMillis: 1523992976455 circuit cmd8HelperPassthrough : module cmd8HelperPassthrough : input clock : Clock input reset : UInt<1> output io : {flip in : UInt<4>, out : UInt<4>} io.out <= io.in @[cmd8.sc 6:10]
FIRRTLって知ってるふうに書いてみたけど、知らんので一応調べてみた。
https://github.com/freechipsproject/firrtl
Firrtl is an intermediate representation (IR) for digital circuits designed as a platform for writing circuit-level transformations. This repository consists of a collection of transformations (written in Scala) which simplify, verify, transform, or emit their input circuit.
RTLの中間表現みたいなものの様子。仕様とかも公開されてるみたいなので後でチェックしてみよう。
補足A:"printf"デバッグ
"補足"となっているが、重要なセクションな気がするので本文を丸々訳して引用させてもらう
printfデバッグはいつでも最適な方法というわけではないが、何かが思い通りに動かない場合に最初に試すには簡単な方法だ。Chiselはハードウェアを生成する言語なので、ジェネレータや回路の状態を出力するにはいくつかの微妙な部分がある。そのためデバッグの際に”いつプリント文を実行するのか”と”何を出力するのか”についてを覚えておくことは重要なことで、一般的には何を出力するかについて3つのシナリオがあり、それぞれに以下のような重要な違いがある:
- Chiselが回路生成に出力する文
- 回路が回路シミュレーション中に出力する文
- テスターがテスト時に出力する文
pringln
はコンソールへの出力を行うビルトイン関数であり、生成されたVerilogやFIRRTLはScalaではないため回路シミュレーション中のプリント出力に使うことは出来ない。
ということで回路シミュレーション中にプリント出力を行うために別の手段が用意されているようで、その例が以下のサンプルに含まれている。
class PrintingModule extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(4.W)) }) io.out := io.in printf("Print during simulation: Input is %d\n", io.in) // chisel printf has its own string interpolator too printf(p"Print during simulation: IO is $io\n") println(s"Print during generation: Input is ${io.in}") } class PrintingModuleTester(c: PrintingModule) extends PeekPokeTester(c) { poke(c.io.in, 3) step(5) // circuit will print println(s"Print during testing: Input is ${peek(c.io.in)}") } chisel3.iotesters.Driver( () => new PrintingModule ) { c => new PrintingModuleTester(c) }
上記を実行すると以下の出力が得られる。
[info] [0.000] Elaborating design... Print during generation: Input is chisel3.core.UInt@e [info] [0.024] Done elaborating. Total FIRRTL Compile Time: 31.1 ms Total FIRRTL Compile Time: 13.0 ms End of dependency graph Circuit state created [info] [0.000] SEED 1539504126474 Print during simulation: Input is 3 Print during simulation: IO is Bundle(in -> 3, out -> 3) Print during simulation: Input is 3 Print during simulation: IO is Bundle(in -> 3, out -> 3) Print during simulation: Input is 3 Print during simulation: IO is Bundle(in -> 3, out -> 3) Print during simulation: Input is 3 Print during simulation: IO is Bundle(in -> 3, out -> 3) Print during simulation: Input is 3 Print during simulation: IO is Bundle(in -> 3, out -> 3) [info] [0.010] Print during testing: Input is 3 test cmd15HelperPrintingModule Success: 0 tests passed in 10 cycles taking 0.018664 seconds [info] [0.011] RAN 5 CYCLES PASSED
いくつか見たことの無いコードが例には含まれていたが、大事なのは以下の違いになる。
PrintingModule
中のprintln
はエラボーレションの直後に一回だけ出力されるPrintingModule
中のprintf
はテスト時の各ステップにおいて毎ステップ表示される
ということなので、
- 回路シミュレーション中にデバッグプリントを仕込む場合にはChiselの
printf
を使う
ということで良さそう。
これでModule2.1 最初のモジュールが完了なので次の学習に進もう。