今日は昨日の続き。
昨日はBundle
をRegInit
に渡すときにBundle
内の各フィールドを任意の値で初期化する方法について考えた。
目的の処理は出来ることは分かったのだが、記述がイマイチなので今日はそれを少し整えてみようと思う。
引き続き「Bundleをインスタンスして作るRegInit
で各フィールドを任意の値で初期化する方法」
昨日のコードは以下のようなものだった。
この方法でもRegInit
部分のエラボレートは通り、RTLが生成できることが確認できた。
# レジスタになってることを確認するためライト用の入力端子も追加 & 各クラスの名前を修正
やり方をまとめると
Wire
でラップしてChiselのハードウェア要素位にしたBundleRegInit0
インスタンスを生成- そのインスタンスに初期値を設定
- 初期値を設定した
BundleRegInit0
インスタンスをRegInit
に渡す
ことで、所望の処理が出来る。
import chisel3._ /** * RegInit用のBundleサンプルその1 */ class BundleRegInit0 extends Bundle { val a = UInt(4.W) val b = UInt(4.W) } /** * Bundleで作るRegInitの各フィールドを任意の値で初期化 */ class SampleBundleRegInit0 extends Module { val io = IO(new Bundle { val en = Input(Bool()) val data = Input(UInt(8.W)) val out = Output(new BundleRegInit0) }) val bundleObj = Wire(new BundleRegInit0) // Wireでラップする // 初期値を設定 bundleObj.a := 1.U bundleObj.b := 2.U // 初期値設定済みのWireで初期化してレジスタを作成 val out = RegInit(bundleObj) when (io.en) { out.a := io.data out.b := io.data } io.out <> out }
もう少し整理してみる
ただこれだと、初期値の記述をModule
内にべた書きで”なんだかなーー”という感じなのでもう少し修正してみた。
変更点はMyBundle
側に初期化用のメソッドinit
を追加して、それを呼び出すようにしたことだ。
# あとちゃんとレジスタになっていることを確認するためにio.en
/io.data
を書き込むように変更
import chisel3._ /** * RegInit用のBundleサンプルその2 */ class BundleRegInit1 extends Bundle { val a = UInt(4.W) val b = UInt(4.W) /** * 初期化用メソッド * @return 初期化後のインスタンス */ def init(): BundleRegInit1 = { a := 2.U b := 3.U this } } /** * Bundleで作るRegInitの各フィールドを任意の値で初期化 */ class SampleBundleRegInit1 extends Module { val io = IO(new Bundle { val en = Input(Bool()) val data = Input(UInt(8.W)) val out = Output(new BundleRegInit1) }) val out = RegInit(Wire(new BundleRegInit1).init()) when (io.en) { out.a := io.data out.b := io.data } io.out <> out }
少し整理したバージョンのエラボレート結果
以下のようになった。init
で設定している値がout_a
/out_b
に入っているのがわかると思う。
module SampleBundleRegInit1( input clock, input reset, input io_en, input [7:0] io_data, output [3:0] io_out_a, output [3:0] io_out_b ); reg [3:0] out_a; // @[BundleRegInit1.scala 34:20] reg [3:0] out_b; // @[BundleRegInit1.scala 34:20] wire [7:0] _GEN_0; // @[BundleRegInit1.scala 36:16] wire [7:0] _GEN_1; // @[BundleRegInit1.scala 36:16] assign _GEN_0 = io_en ? io_data : {{4'd0}, out_a}; // @[BundleRegInit1.scala 36:16] assign _GEN_1 = io_en ? io_data : {{4'd0}, out_b}; // @[BundleRegInit1.scala 36:16] assign io_out_a = out_a; // @[BundleRegInit1.scala 41:10] assign io_out_b = out_b; // @[BundleRegInit1.scala 41:10] always @(posedge clock) begin if (reset) begin out_a <= 4'h2; end else begin out_a <= _GEN_0[3:0]; end if (reset) begin out_b <= 4'h3; end else begin out_b <= _GEN_1[3:0]; end end endmodule
Chiselの方の初期化部分がちょっと野暮ったいが最初の例と比較するとこっちのほうが良いかな、と思う。
コンパニオン・オブジェクト作ってすっきりさせる
更にスッキリさせたければコンパニオン・オブジェクト作って初期化済みのWire
を返す形にすればOK。
import chisel3._ /** * RegInit用のBundleサンプルその3 */ class BundleRegInit2 extends Bundle { val a = UInt(4.W) val b = UInt(4.W) /** * 初期値を設定する * @return 初期化後の自クラス */ def init(valA: Int = 3, valB: Int = 4): BundleRegInit2 = { a := valA.U b := valB.U this } /** * ライト * @param data 書き込む値 */ def write(data: UInt): Unit = { a := data(3, 0) b := data(7, 4) } } /** * class BundleRegInit2 のコンパニオン・オブジェクト */ object BundleRegInit2 { /** * initを呼んで初期値を設定 * @return 設定後のインスタンス */ def apply(): BundleRegInit2 = { val m = Wire(new BundleRegInit2()) m.init() } /** * 生成時に引数の値で初期化 * @param data 初期化時に設定する値 * @return 設定後のインスタンス */ def apply(data: UInt): BundleRegInit2 = { val m = Wire(new BundleRegInit2()) m.write(data) m } } /** * Bundleで作るRegInitの各フィールドを任意の値で初期化 */ class SampleBundleRegInit2 extends Module { val io = IO(new Bundle { val en = Input(Bool()) val data = Input(UInt(32.W)) val out1 = Output(new BundleRegInit0) val out2 = Output(new BundleRegInit2) }) val out1 = RegInit(BundleRegInit2()) val out2 = RegInit(BundleRegInit2("h77".U)) when (io.en) { out1.write(io.data) out2.write(io.data) } io.out1 <> out1 io.out2 <> out2 }
このバージョンでRTLを生成するとしたものがgistにあるので興味があればご確認ください。
BundleとRegInitで作る初期化済みレジスタ(コンパニオン・オブジェクト版)から生成したRTL · GitHub
全部ソースみたいよーという方は、以下にテストも含めて置いてあるのでそちらもどーぞ。
これで整理したバージョンについての紹介はお終い。
今のところ使い途で思い浮かんでいるのは、外部からアクセスするレジスタを32bit単位でまとめたりするようケース。値を保持するフィールドをBundle
側に実装して、init
/write
/read
を作っておき、Array
とかをうまく使うとレジスタへのアクセス部分(デコードやらセレクタ)をいい感じに書けるかなーとか。これについても追ってまとめてみようと思う。