Diplomacyのスレーブノードに続いて、今度はマスターノードを作成してみる。マスターノードには以下の機能を持たせようと思う。
- アドレスをインクリメントさせながら特定のデータを16回書き込むリクエストを発行する。
- アドレスを元に戻す。
- アドレスをインクリメントさせながら読み込みリクエストを16回発行し、戻ってきたデータを確認して書き込みデータと同じであることを確認する。
このノードのことをTLMasterPusherと呼ぶことにする。TLMasterPusherは最初にPutコマンドを発行し、次にGetコマンドを発行するという訳である。

PatternPusherについてはLazyModuleを使って実装するのだが、スレーブノードの時と作り方は一緒である。まずはLazyModule内でノードを定義して、LazyModuleの内部に定義したModuleにノードを引き込む。
src/main/scala/TLUnitTest/TLMasterPusher.scala
class TLMasterPusher(name: String)(implicit p: Parameters) extends LazyModule { val node = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters(name = name))))) lazy val module = new LazyModuleImp(this) { val io = IO(new Bundle { val run = Input(Bool()) val done = Output(Bool()) val error = Output(Bool()) }) val (out, edge) = node.out(0) ...
という訳で、前半はPutコマンドを挿入し、後半はGetコマンドを挿入するように論理を組み上げていく。
when (a.fire()) {
req_counter := req_counter + 1.U
when (req_counter(3, 0).andR) {
a_addr := 0.U
} .otherwise {
a_addr := a_addr + beatBytes.U
}
}
...
val (_, get_pbits) = edge.Get(0.U , a_addr, 2.U)
val (_, put_pbits) = edge.Put(0.U , a_addr, 2.U, 0xdead0000L.U | a_addr)
a.valid := io.run && !finish
a.bits := Mux(req_counter(4), get_pbits, put_pbits)
Aチャネルにコマンドが発行されるたびにアドレスをインクリメントさせていく。GetコマンドとPutコマンドをインスタンス化し、カウンタの最上位ビットに応じてコマンドを切り替える。
edge.Get(ソースID , アドレス, リクエストサイズ)edge.Put(ソースID,アドレス, リクエストサイズ、データ)
そしてGetコマンドに対するレスポンスを、想定するデータと比較して想定したデータと異なる場合はエラー信号を出力するように組み上げる。
val r_error = Reg(Bool()) r_error := false.B io.error := r_error val answer = 0xdead0000L.U | Cat(ack_counter(3, 0), 0.U(5.W)) dontTouch(answer) when (d.fire()) { ack_counter := ack_counter + 1.U when(ack_counter(4) && (d.bits.data =/= answer)) { r_error := true.B } }
これで回路を生成してシミュレーションしてみた。

うまく動作しているようだ。エラーも発生していない。