以前にBunldeで構造化したデータを使ってレジスタをインスタンスする方法を紹介したのだがその記事を書いたときにもう一つコレ出来たらいいなっと思いながら出来なかったことがあった。
それは”Bundle
を使ってレジスタ郡を作る際に、各フィールド毎に任意の値で初期化出来ないか?”というものだった。
んでやっとそのやり方が分かったので、今日はそのやり方についてまとめようと思う。
Bundleをインスタンスして作るRegInit
で各フィールドを任意の値で初期化する方法
やりたい事をまとめると以下のようになる。
これが出来ると例えば外からアクセスする1wordのレジスタの各ビットフィールドを全てBundle
の要素として定義して、各フィールドに任意の初期値を入れたり出来る。
何となくChiselで書くと以下のような感じになるのだが???
の部分をどうすれば上記の要件を満たす形でレジスタを生成できるのかについて考えていく。
class MyBundle extends Bundle { val a = UInt(2.W) val b = UInt(3.W) } class MyModule extends Module { val io = IO(new Bundle { val in = Input(UInt(5.W)) val out = Output(new MyBundle) }) // RegInitの???を工夫して // 初期化後の値を以下に設定したい // a = 3.U // b = 5.U val out = RegInit(???) out.a := op.in(1, 0) out.b := op.in(4, 2) io.out <> out }
おさらい:Bundle
+ RegInit
で作るレジスタを"0"で初期化する方法
と、その前におさらいから。これは以前に以下の記事でまとめた話。
MyBundle
で作ったレジスタを0.U
で初期化するソースコード
次のようにすればBundle
の各フィールドを"0"で初期化出来た。
初期化時の値はあえて0.U
にした。
class MyBundle extends Bundle { val a = UInt(2.W) val b = UInt(3.W) } class MyModule extends Module { val io = IO(new Bundle { val in = Input(UInt(5.W)) val a = Output(UInt(2.W)) val b = Output(UInt(3.W)) }) val out = RegInit(2.U.asTypeOf(new MyBundle)) out.a := op.in(1, 0) out.b := op.in(4, 2) io.a := out.a io.b := out.b }
生成されたRTL
以下のようにout_a
/out_b
が共に0.U
で初期化されているのがわかるかと思う。
#見にくいのでifdef
は消してます。
module cmd18WrapperHelperMyModule( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [4:0] io_in, // @[:@6.4] output [1:0] io_out_a, // @[:@6.4] output [2:0] io_out_b // @[:@6.4] ); reg [1:0] out_a; // @[cmd18.sc 12:20:@15.4] reg [31:0] _RAND_0; reg [2:0] out_b; // @[cmd18.sc 12:20:@15.4] reg [31:0] _RAND_1; wire [1:0] _T_18; // @[cmd18.sc 14:17:@16.4] wire [2:0] _T_19; // @[cmd18.sc 15:17:@18.4] assign _T_18 = io_in[1:0]; // @[cmd18.sc 14:17:@16.4] assign _T_19 = io_in[4:2]; // @[cmd18.sc 15:17:@18.4] assign io_out_a = out_a; assign io_out_b = out_b; always @(posedge clock) begin if (reset) begin out_a <= 2'h0; end else begin out_a <= _T_18; end if (reset) begin out_b <= 3'h0; end else begin out_b <= _T_19; end end endmodule
実は今回この初期化の方法を試しているときに気づいたのだが、この方法だと"0"で初期化する以外は意図通りには動かなかった。
上の見出しを「"0"で初期化する方法」と書いたのはそのため。。
例えば2.U
というような値で初期化してみると、out_a
の初期値が2.U
にはならなかった。
各々をフィールドを任意の値で初期化する方法
以上の前置きを踏まえて、ここからが本題。 各種試してみて駄目だったことも含めて書くので、結論が見たい方は以下のページ内リンクで移動ください。
RegInit
の実装
まずはRegInit
の実装から確認してみた。RegInit
はobject
となっており、以下の2つのapply
メソッドを持つ。
// Chisel3.core.Reg.scala の該当部分を抜粋 object RegInit { /** Returns a register pre-initialized (on reset) to the specified value. * Register type is inferred from the initializer. */ def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { val model = (init.litArg match { // For e.g. Reg(init=UInt(0, k)), fix the Reg's width to k case Some(lit) if lit.forcedWidth => init.cloneTypeFull case _ => init match { case init: Bits => init.cloneTypeWidth(Width()) case init => init.cloneTypeFull } }).asInstanceOf[T] RegInit(model, init) } /** Creates a register given an explicit type and an initialization (reset) value. */ def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { if (compileOptions.declaredTypeMustBeUnbound) { requireIsChiselType(t, "reg type") } val reg = t.cloneTypeFull val clock = Node(Builder.forcedClock) val reset = Node(Builder.forcedReset) reg.bind(RegBinding(Builder.forcedUserModule)) requireIsHardware(init, "reg initializer") pushCommand(DefRegInit(sourceInfo, reg, clock, reset, init.ref)) reg } }
通常している形RegInit(0.U(32.W))
の形式が最初の方で、2つ目の方は実際にレジスタとしての情報を構築して設定しているような形となっている。
ここから先はあまり追えていないが、2つ目の各処理の名前から推測するとRegInit
が宣言されたモジュールにT
のデータを束縛しているような感じだろうか?
とりあえずRegInit
に初期値を渡しても、戻ってくるのはT
というのが分かったのでこれを元に実験してみよう。
MyBundle
のインスタンスをRegInit
に設定
いずれにしても”最終的にはT
が戻り値になっている”ということはが分かったので、とりあえず自分で定義したBundle
をインスタンスしたものをRegInit
に入れてみればいいのでは??という考えの元に試してみたのが以下のコードだ。
class MyBundle extends Bundle { val a = UInt(32.W) val b = UInt(32.W) } class MyModule extends Module { val io = IO(Output(new MyBundle)) val bundleObj = new MyBundle bundleObj.a := 1.U bundleObj.b := 2.U val out = RegInit(bundleObj) io <> out }
早速試してみよう。
chisel3.core.Binding$ExpectedHardwareException: data to be connected 'chisel3.core.UInt@12' must be hardware, not a bare Chisel type chisel3.core.requireIsHardware$.apply(Binding.scala:31) chisel3.core.Data.connect(Data.scala:293) chisel3.core.Data.$colon$eq(Data.scala:365) $sess.cmd20Wrapper$Helper$MyModule.<init>(cmd20.sc:14) $sess.cmd20Wrapper$Helper$$anonfun$1.apply(cmd20.sc:23) $sess.cmd20Wrapper$Helper$$anonfun$1.apply(cmd20.sc:23) chisel3.core.Module$.do_apply(Module.scala:49) chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93) chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93) chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:297) chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:295) scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) chisel3.internal.Builder$.build(Builder.scala:295) chisel3.Driver$.elaborate(Driver.scala:93) chisel3.Driver$.execute(Driver.scala:140) chisel3.Driver$.execute(Driver.scala:202)
渡すデータはハードウェアでないといかん!!とエラーになった。これがどこかで見かけた気もする”Chiselのデータ型とハードウェアの違いを意識しよう”って部分ですな。
先程の2つ目のapply
メソッドからも何となく推測できるのだが、"Chiselのハードウェア要素 == Chsielの型にハードウェアとしての付加情報を足したもの"と捉えておくのが良さそう。
MyBundle
のインスタンスをハードウェア要素にして渡そう
先の結果を受けて次に試すのは「じゃあハードウェアにして渡せばいいよね」という考えだ。コードは先ほどをそう変わりはなく、ただ単にMyBundle
のインスタンスをWire
でラップするだけ。
class MyBundle extends Bundle { val a = UInt(32.W) val b = UInt(32.W) } class MyModule extends Module { val io = IO(Output(new MyBundle)) val bundleObj = Wire(new MyBundle) // Wireでラップする bundleObj.a := 1.U bundleObj.b := 2.U val out = RegInit(bundleObj) io <> out }
- エラボレート結果
今度は正常にエラボレートが終了して、RTLが生成された。
なお、エラボレート後に最適化が行われているようで「値が変化しないからレジスタやめて固定値クリップ」されたRTLが生成された。
assign
文の固定値を見てもらえれば分かる通り、bundleObj
に設定した値がそのまま出力されており、意図した動作が得られた。
[info] [0.000] Elaborating design... [info] [0.010] Done elaborating. Total FIRRTL Compile Time: 25.4 ms module cmd28WrapperHelperMyModule( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] output [31:0] io_a, // @[:@6.4] output [31:0] io_b // @[:@6.4] ); assign io_a = 32'h1; assign io_b = 32'h2; endmodule
今日は力尽きたのでここまで。。
やりたいことは出来たのは出来たけどなんか野暮ったいので、次回は自分なりにこのコードをキレイにしてみようと思う。