今回はデバッグ用にポートを作る場合の方法について、今の時点の知識で考えなおしてみた。
案1:traitにしといてMix-in(結果→失敗でした)
以下の様にMultiIOModule
を使うことでMix-in出来るかと思ったけど、無理だった。
// See LICENSE for license details. import chisel3._ import chisel3.util._ import chisel3.experimental.MultiIOModule import chisel3.iotesters.PeekPokeTester class SomeData extends Bundle { val a = UInt(8.W) val b = UInt(16.W) val c = UInt(24.W) } class SomeDataIO extends DecoupledIO(new SomeData) { override def cloneType: this.type = new SomeDataIO().asInstanceOf[this.type] } trait DebugIO extends Bundle { val bits: Int val dbg = IO(new Bundle { val count = Output(UInt(bits.W)) }) } // 以下のようにMix-inでポートを足したかった。 class DebugWithMultiIOModule(debug: Boolean) extends MultiIOModule with DebugIO { val bits = log2Ceil(4) val io = IO(new Bundle { val in = Flipped(new SomeDataIO) val out = new SomeDataIO }) val q = Queue(io.in, 4) io.out <> q } object Test extends App { val module = "DebugWithMultiIOModule" iotesters.Driver.execute( Array( s"-tn=$module", s"-td=test_run_dir/$module" ), () => new DebugWithMultiIOModule(true)) { c => new PeekPokeTester(c) { step(100) } } }
コメントに書いたとおりでBundle
を継承したクラス内で、デバッグ用のポートを宣言してその中でIO
に包んだポートを作成してしまおうとした。
これが出来ればモジュール生成時にデバッグ用のパラメータを見てデバッグ論理をインスタンスできそうなんだけど。。。
既に記載した通りでこれはChiselの仕組み上、うまく行かなくて以下の様に2つのエラーが発生する。
[IJ]sbt:dirv> runMain Test [info] Compiling 27 Scala sources to /home/diningyo/prj/risc-v/dirv/target/scala-2.11/classes ... [error] /home/diningyo/prj/risc-v/dirv/src/main/scala/Test.scala:18:13: not found: value IO [error] val dbg = IO(new Bundle { [error] ^ [error] /home/diningyo/prj/risc-v/dirv/src/main/scala/Test.scala:23:73: illegal inheritance; superclass ImplicitModule [error] is not a subclass of the superclass Bundle [error] of the mixin trait DebugIO [error] class DebugWithMultiIOModule(debug: Boolean) extends MultiIOModule with DebugIO { [error] ^ [error] two errors found [error] (Compile / compileIncremental) Compilation failed [error] Total time: 2 s, completed 2019/09/15 21:06:17
ひとつは「Bundle
内ではIO
を使うことが出来ないということ」で、もうひとつは「Module
の派生クラスにBundle
を継承したクラスはミックスイン出来ない」ということだ。
もう少し粘ってみる
ふと思ったけどBundle
を継承する必要無い気がした。ということで素のtrait
にして再トライ。
trait DebugIO { val bits: Int val dbg = new Bundle { val count = Output(UInt(bits.W)) } }
以下がエラボレートの際のログ。
[IJ]sbt:debugWithMultiIOModule> runMain Test [info] Compiling 1 Scala source to /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/classes ... [info] Done compiling. [info] Packaging /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/debugwithmultiiomodule_2.11-2.0.jar ... [info] Done packaging. [info] Running Test [info] [0.000] Elaborating design... [info] [1.421] Done elaborating. Total FIRRTL Compile Time: 624.3 ms Total FIRRTL Compile Time: 372.7 ms file loaded in 0.550296164 seconds, 99 symbols, 70 statements [info] [0.000] SEED 1568165285822 test DebugWithMultiIOModule Success: 0 tests passed in 105 cycles in 0.026754 seconds 3924.58 Hz [info] [0.000] RAN 100 CYCLES PASSED [success] Total time: 7 s, completed 2019/09/11 10:28:10
おっ、通った!!でもこれならMix-inする必要ないわ。
ということで、debug
で囲ってデバッグ用のポートはベタ書きにする。
class DebugWithMultiIOModule(debug: Boolean) extends MultiIOModule { val bits = log2Ceil(4) val io = IO(new Bundle { val in = Flipped(new SomeDataIO) val out = new SomeDataIO }) val q = Module(new Queue(chiselTypeOf(io.in.bits), 4)) q.io.enq <> io.in io.out <> q.io.deq if (debug) { val dbg = IO(new Bundle { val count = Output(UInt(bits.W)) }) dbg.count := q.io.count } }
ところが、上記にするとポートの名前が拾えなくてエラーになる。
[IJ]sbt:debugWithMultiIOModule> runMain Test [info] Compiling 1 Scala source to /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/classes ... [info] Done compiling. [info] Packaging /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/debugwithmultiiomodule_2.11-2.0.jar ... [info] Done packaging. [info] Running Test [info] [0.000] Elaborating design... [info] [1.343] Done elaborating. Total FIRRTL Compile Time: 591.5 ms Total FIRRTL Compile Time: 237.9 ms file loaded in 0.431778518 seconds, 107 symbols, 78 statements [info] [0.000] SEED 1568166255516 test DebugWithMultiIOModule Success: 0 tests passed in 105 cycles in 0.039715 seconds 2643.82 Hz [info] [0.015] RAN 100 CYCLES PASSED [success] Total time: 6 s, completed 2019/09/11 10:44:19 [IJ]sbt:debugWithMultiIOModule> runMain Test [info] Compiling 1 Scala source to /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/classes ... [info] Done compiling. [info] Packaging /home/diningyo/prj/100_Chisel/000_learning-chisel3/subprj/debug-with-MultiIOModule/target/scala-2.11/debugwithmultiiomodule_2.11-2.0.jar ... [info] Done packaging. [info] Running Test [info] [0.000] Elaborating design... [error] (run-main-9) java.lang.IllegalArgumentException: requirement failed: Unable to name port DebugWithMultiIOModule$$anon$2@101 in DebugWithMultiIOModule@0 [error] java.lang.IllegalArgumentException: requirement failed: Unable to name port DebugWithMultiIOModule$$anon$2@101 in DebugWithMultiIOModule@0 [error] at scala.Predef$.require(Predef.scala:224) [error] at chisel3.core.UserModule$$anonfun$generateComponent$2.apply(UserModule.scala:49) [error] at chisel3.core.UserModule$$anonfun$generateComponent$2.apply(UserModule.scala:48) [error] at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59) [error] at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48) [error] at chisel3.core.UserModule.generateComponent(UserModule.scala:48) [error] at chisel3.core.Module$.do_apply(Module.scala:63) [error] at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93) [error] at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93) [error] at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:297) [error] at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:295) [error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) [error] at chisel3.internal.Builder$.build(Builder.scala:295) [error] at chisel3.Driver$.elaborate(Driver.scala:93) [error] at chisel3.Driver$.execute(Driver.scala:140) [error] at chisel3.iotesters.setupTreadleBackend$.apply(TreadleBackend.scala:139) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply$mcZ$sp(Driver.scala:54) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply(Driver.scala:39) [error] at logger.Logger$$anonfun$makeScope$1.apply(Logger.scala:138) [error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) [error] at logger.Logger$.makeScope(Logger.scala:136) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply$mcZ$sp(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply(Driver.scala:39) [error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) [error] at chisel3.iotesters.Driver$.execute(Driver.scala:38) [error] at chisel3.iotesters.Driver$.execute(Driver.scala:100) [error] at Test$.delayedEndpoint$Test$1(DebugWithMultiIOModule.scala:50) [error] at Test$delayedInit$body.apply(DebugWithMultiIOModule.scala:41) [error] at scala.Function0$class.apply$mcV$sp(Function0.scala:34) [error] at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) [error] at scala.App$$anonfun$main$1.apply(App.scala:76) [error] at scala.App$$anonfun$main$1.apply(App.scala:76) [error] at scala.collection.immutable.List.foreach(List.scala:392) [error] at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35) [error] at scala.App$class.main(App.scala:76) [error] at Test$.main(DebugWithMultiIOModule.scala:41) [error] at Test.main(DebugWithMultiIOModule.scala) [error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [error] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [error] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [error] at java.lang.reflect.Method.invoke(Method.java:498) [error] Nonzero exit code: 1 [error] (Compile / runMain) Nonzero exit code: 1 [error] Total time: 4 s, completed 2019/09/11 10:46:26
どうもif
文でくくったため、名前が維持できずにエラーになっている様子。
@chiselName
入れても結果は一緒。
[error] (run-main-a) java.lang.IllegalArgumentException: requirement failed: Unable to name port DebugWithMultiIOModule$$anon$2@101 in DebugWithMultiIOModule@0
案2:ポートのビット幅0にして最適化で消してもらう(こちらは成功)
デバッグポートは以下のようにビット幅0にしとくと接続の記述だけにすることが出来る。
class DebugIO(bits: Int) extends Bundle { val count = Output(UInt(bits.W)) } class DebugWithMultiIOModule(debug: Boolean) extends MultiIOModule { val bits = log2Ceil(4) val io = IO(new Bundle { val in = Flipped(new SomeDataIO) val out = new SomeDataIO }) val q = Module(new Queue(chiselTypeOf(io.in.bits), 4)) q.io.enq <> io.in io.out <> q.io.deq // debug val dbgBits = if (debug) 32 else 0 val dbg = IO(new DebugIO(dbgBits)) dbg.count := q.io.count }
上記のようにするとFIRRTLの最適化のタイミングで、ポートのビット幅が0のポートは削除されるのでif
式を使った記述にしなくても良くなる。
以下は実際にdebug
の信号を切り替えて試してみたFIRRTLの結果。なおiotesters.Driver
でtreadle
を使ってシミュレーションを行うと"<モジュール名>.fir"と"<モジュール名>.lo.fir"の2つのファイルが作成されていて、"*.lo.fir"が最適化&展開済みのFIRRTL記述となっている。この"*.lo.fir"はほぼVerilog-HDLのRTLと一対一対応するレベルで、こちらからはデバッグ用のポートが消えているのが確認できた。
- debug == true の場合のFIRRTL(最適化前)
module DebugWithMultiIOModule : input clock : Clock input reset : UInt<1> output io : {flip in : {flip ready : UInt<1>, valid : UInt<1>, bits : {a : UInt<8>, b : UInt<16>, c : UInt<24>}}, out : {flip ready : UInt<1>, valid : UInt<1>, bits : {a : UInt<8>, b : UInt<16>, c : UInt<24>}}} output dbg : {count : UInt<0>}
- debug == false の場合のFIRRTL(最適化前)
module DebugWithMultiIOModule : input clock : Clock input reset : UInt<1> output io : {flip in : {flip ready : UInt<1>, valid : UInt<1>, bits : {a : UInt<8>, b : UInt<16>, c : UInt<24>}}, out : {flip ready : UInt<1>, valid : UInt<1>, bits : {a : UInt<8>, b : UInt<16>, c : UInt<24>}}} output dbg : {count : UInt<32>}
- debug == true の場合のFIRRTL(最適化後)
module DebugWithMultiIOModule : @[:@58.2] input clock : Clock @[:@59.4] input reset : UInt<1> @[:@60.4] output io_in_ready : UInt<1> @[:@61.4] input io_in_valid : UInt<1> @[:@61.4] input io_in_bits_a : UInt<8> @[:@61.4] input io_in_bits_b : UInt<16> @[:@61.4] input io_in_bits_c : UInt<24> @[:@61.4] input io_out_ready : UInt<1> @[:@61.4] output io_out_valid : UInt<1> @[:@61.4] output io_out_bits_a : UInt<8> @[:@61.4] output io_out_bits_b : UInt<16> @[:@61.4] output io_out_bits_c : UInt<24> @[:@61.4] output dbg_count : UInt<32> @[:@62.4] inst q of Queue @[DebugWithMultiIOModule.scala 32:17:@64.4]
- debug == false の場合のFIRRTL(最適化後)
module DebugWithMultiIOModule : @[:@58.2] input clock : Clock @[:@59.4] input reset : UInt<1> @[:@60.4] output io_in_ready : UInt<1> @[:@61.4] input io_in_valid : UInt<1> @[:@61.4] input io_in_bits_a : UInt<8> @[:@61.4] input io_in_bits_b : UInt<16> @[:@61.4] input io_in_bits_c : UInt<24> @[:@61.4] input io_out_ready : UInt<1> @[:@61.4] output io_out_valid : UInt<1> @[:@61.4] output io_out_bits_a : UInt<8> @[:@61.4] output io_out_bits_b : UInt<16> @[:@61.4] output io_out_bits_c : UInt<24> @[:@61.4] inst q of Queue @[DebugWithMultiIOModule.scala 32:17:@64.4]
ということで、すこし半端だけどデバッグ用のポートについてはOption
型(ここではSome
のこと)を使うよりはビット幅0にしたほうが、余計なif
式のブロックやget
を使ったアクセス等が消えるし、RTL的にも接頭辞がdbg_
になるしでコード的にもすっきりしていいと思ったという話でした。