ChiselのQueueを使って設計をしていて、少し考えたことがあったので今日はそれについて。
ただ単にDecoupledIO
をどう使うか、、、という話。
Queueの実装例
幾つかのデータを纏めてChiselのQueue
に入れて受け渡したいときにはDecoupledIO
等のReadyValidIO
の派生クラスを使って以下のように記述したりする。
import chisel3._ import chisel3.util._ /** * Queueに入れたいデータを纏めた型 */ class BundleA extends Bundle { val a = Bool() val b = UInt(8.W) } /** * MyQueueモジュールのIOクラス */ class MyQueueIO extends Bundle { val enq = Flipped(DecoupledIO(new BundleA)) val deq = DecoupledIO(new BundleA) } /** * MyQueue * 単にChiselのQueueをインスタンスして繋いだだけ */ class MyQueue extends Module { val io = IO(new MyQueueIO) // object chisel3.util.Queueをインスタンス // -> object Queueを使った場合はio.enqがQueueのenqに接続される val m_q = Queue(io.enq, 1) // objectを使った場合はm_qがdeqになっている io.deq <> m_q }
上記をエラボレートすると、以下のようにChiselのQueue
から生成されたモジュールがRTLにインスタンスされる。
module MyQueue( // @[:@46.2] input clock, // @[:@47.4] input reset, // @[:@48.4] output io_enq_ready, // @[:@49.4] input io_enq_valid, // @[:@49.4] input io_enq_bits_a, // @[:@49.4] input [7:0] io_enq_bits_b, // @[:@49.4] input io_deq_ready, // @[:@49.4] output io_deq_valid, // @[:@49.4] output io_deq_bits_a, // @[:@49.4] output [7:0] io_deq_bits_b // @[:@49.4] ); wire m_q_clock; // @[Decoupled.scala 294:21:@51.4] wire m_q_reset; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_enq_bits_b; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_deq_bits_b; // @[Decoupled.scala 294:21:@51.4] Queue m_q ( // @[Decoupled.scala 294:21:@51.4] .clock(m_q_clock), .reset(m_q_reset), .io_enq_ready(m_q_io_enq_ready), .io_enq_valid(m_q_io_enq_valid), .io_enq_bits_a(m_q_io_enq_bits_a), .io_enq_bits_b(m_q_io_enq_bits_b), .io_deq_ready(m_q_io_deq_ready), .io_deq_valid(m_q_io_deq_valid), .io_deq_bits_a(m_q_io_deq_bits_a), .io_deq_bits_b(m_q_io_deq_bits_b) ); assign io_enq_ready = m_q_io_enq_ready; // @[Decoupled.scala 297:17:@57.4] assign io_deq_valid = m_q_io_deq_valid; // @[MyQueue.scala 81:10:@60.4] assign io_deq_bits_a = m_q_io_deq_bits_a; // @[MyQueue.scala 81:10:@59.4] assign io_deq_bits_b = m_q_io_deq_bits_b; // @[MyQueue.scala 81:10:@58.4] assign m_q_clock = clock; // @[:@52.4] assign m_q_reset = reset; // @[:@53.4] assign m_q_io_enq_valid = io_enq_valid; // @[Decoupled.scala 295:22:@54.4] assign m_q_io_enq_bits_a = io_enq_bits_a; // @[Decoupled.scala 296:21:@56.4] assign m_q_io_enq_bits_b = io_enq_bits_b; // @[Decoupled.scala 296:21:@55.4] assign m_q_io_deq_ready = io_deq_ready; // @[MyQueue.scala 81:10:@61.4] endmodule
IOの形を書き換えてみる
上記のように書いていて「DecoupledIO
が野暮ったいな」って思って、書き換えて以下の形で多少の違和感を覚えながらもこれを使っていた。
class BundleA extends Bundle { val a = Bool() val b = UInt(8.W) } class DecoupledBundleA extends Bundle { val a = DecoupledIO(new BundleA) } class MyQueueIO extends Bundle { val enq = Flipped(new DecoupledBundleA) val deq = new DecoupledBundleA } class MyQueue extends Module { val io = IO(new MyQueueIO) val m_q = Queue(io.enq.a, 1) io.deq.a <> m_q }
- 生成されたRTL
module MyQueue( // @[:@46.2] input clock, // @[:@47.4] input reset, // @[:@48.4] output io_enq_a_ready, // @[:@49.4] input io_enq_a_valid, // @[:@49.4] input io_enq_a_bits_a, // @[:@49.4] input [7:0] io_enq_a_bits_b, // @[:@49.4] input io_deq_a_ready, // @[:@49.4] output io_deq_a_valid, // @[:@49.4] output io_deq_a_bits_a, // @[:@49.4] output [7:0] io_deq_a_bits_b // @[:@49.4] ); wire m_q_clock; // @[Decoupled.scala 294:21:@51.4] wire m_q_reset; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_enq_bits_b; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_deq_bits_b; // @[Decoupled.scala 294:21:@51.4] Queue m_q ( // @[Decoupled.scala 294:21:@51.4] .clock(m_q_clock), .reset(m_q_reset), .io_enq_ready(m_q_io_enq_ready), .io_enq_valid(m_q_io_enq_valid), .io_enq_bits_a(m_q_io_enq_bits_a), .io_enq_bits_b(m_q_io_enq_bits_b), .io_deq_ready(m_q_io_deq_ready), .io_deq_valid(m_q_io_deq_valid), .io_deq_bits_a(m_q_io_deq_bits_a), .io_deq_bits_b(m_q_io_deq_bits_b) ); assign io_enq_a_ready = m_q_io_enq_ready; // @[Decoupled.scala 297:17:@57.4] assign io_deq_a_valid = m_q_io_deq_valid; // @[MyQueue.scala 86:12:@60.4] assign io_deq_a_bits_a = m_q_io_deq_bits_a; // @[MyQueue.scala 86:12:@59.4] assign io_deq_a_bits_b = m_q_io_deq_bits_b; // @[MyQueue.scala 86:12:@58.4] assign m_q_clock = clock; // @[:@52.4] assign m_q_reset = reset; // @[:@53.4] assign m_q_io_enq_valid = io_enq_a_valid; // @[Decoupled.scala 295:22:@54.4] assign m_q_io_enq_bits_a = io_enq_a_bits_a; // @[Decoupled.scala 296:21:@56.4] assign m_q_io_enq_bits_b = io_enq_a_bits_b; // @[Decoupled.scala 296:21:@55.4] assign m_q_io_deq_ready = io_deq_a_ready; // @[MyQueue.scala 86:12:@61.4] endmodule
感じていた違和感というのは、Chiselのソースでio.enq.a
という風にアクセスをしないと行けない部分。
これはそのままRTLをにもio_enq_a
という形で現れていた。
継承してスッキリ
ふと「継承すればいいんじゃ( ゚д゚)ハッ!」って思って試してみた。
先程のDecoupledBundleA
を以下のように継承する形に変更する。変数a
がいなくなるのでそれに従ってMyQueue
も最初の形に戻す。
class DecoupledBundleA extends DecoupledIO(new BundleA) { override def cloneType: this.type = new DecoupledBundleA().asInstanceOf[this.type] } class MyQueue extends Module { val io = IO(new MyQueueIO) val m_q = Queue(io.enq, 1) io.deq <> m_q }
- 生成されたRTL
module MyQueue( // @[:@46.2] input clock, // @[:@47.4] input reset, // @[:@48.4] output io_enq_ready, // @[:@49.4] input io_enq_valid, // @[:@49.4] input io_enq_bits_a, // @[:@49.4] input [7:0] io_enq_bits_b, // @[:@49.4] input io_deq_ready, // @[:@49.4] output io_deq_valid, // @[:@49.4] output io_deq_bits_a, // @[:@49.4] output [7:0] io_deq_bits_b // @[:@49.4] ); wire m_q_clock; // @[Decoupled.scala 294:21:@51.4] wire m_q_reset; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_enq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_enq_bits_b; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_ready; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_valid; // @[Decoupled.scala 294:21:@51.4] wire m_q_io_deq_bits_a; // @[Decoupled.scala 294:21:@51.4] wire [7:0] m_q_io_deq_bits_b; // @[Decoupled.scala 294:21:@51.4] Queue m_q ( // @[Decoupled.scala 294:21:@51.4] .clock(m_q_clock), .reset(m_q_reset), .io_enq_ready(m_q_io_enq_ready), .io_enq_valid(m_q_io_enq_valid), .io_enq_bits_a(m_q_io_enq_bits_a), .io_enq_bits_b(m_q_io_enq_bits_b), .io_deq_ready(m_q_io_deq_ready), .io_deq_valid(m_q_io_deq_valid), .io_deq_bits_a(m_q_io_deq_bits_a), .io_deq_bits_b(m_q_io_deq_bits_b) ); assign io_enq_ready = m_q_io_enq_ready; // @[Decoupled.scala 297:17:@57.4] assign io_deq_valid = m_q_io_deq_valid; // @[MyQueue.scala 88:10:@60.4] assign io_deq_bits_a = m_q_io_deq_bits_a; // @[MyQueue.scala 88:10:@59.4] assign io_deq_bits_b = m_q_io_deq_bits_b; // @[MyQueue.scala 88:10:@58.4] assign m_q_clock = clock; // @[:@52.4] assign m_q_reset = reset; // @[:@53.4] assign m_q_io_enq_valid = io_enq_valid; // @[Decoupled.scala 295:22:@54.4] assign m_q_io_enq_bits_a = io_enq_bits_a; // @[Decoupled.scala 296:21:@56.4] assign m_q_io_enq_bits_b = io_enq_bits_b; // @[Decoupled.scala 296:21:@55.4] assign m_q_io_deq_ready = io_deq_ready; // @[MyQueue.scala 88:10:@61.4] endmodule
最初の場合と同様のRTLが生成された!
コンパニオンオブジェクト版
更に「コンパニオンオブジェクト使って定義する方がいいかも」と思ったのでそちらのケースも載せておく。 RTLは全く同じなので割愛。
class BundleA extends Bundle { val a = Bool() val b = UInt(8.W) } object BundleA { def apply(): DecoupledIO[BundleA] = DecoupledIO(new BundleA()) } class MyQueueIO extends Bundle { val enq = Flipped(BundleA()) val deq = BundleA() } class MyQueue extends Module { val io = IO(new MyQueueIO) val m_q = Queue(io.enq, 1) io.deq <> m_q }
個人的にはコンパニオンオブジェクトの方がいいかなーという感じ。
今まで考えが及ばずあんまりobject
を上手く活用できていないので、意識して使っていきたいと思う。