OCP Pattern Pusherのデバッグをしながら、Diplomacyの構築に必要なクラスの構成について見ていこう。
OCP Pattern PusherではDiplomacyのノードが定義され、出力ノードに対してコマンドが発行されている。
上記の図ではTileLinkの仕様で書いてあるが、実際にはOCP向けのEdgeで定義してある。
ノードの定義は下記のようになっている。OCPClientNode
を定義している。OCPClientPortParameters
とOCPClientParmeters
は以下のように定義されている。
chisel-hw/src/main/scala/ocp/PatternPusher.scala
val node = OCPClientNode(Seq(OCPClientPortParameters(Seq(OCPClientParameters(name = name)))))
chisel-hw/src/main/scala/ocp/Nodes.scala
case class OCPClientNode (portParams: Seq[OCPClientPortParameters ])(implicit valName: ValName) extends SourceNode(OCPImp)(portParams) with OCPFormatNode case class OCPManagerNode(portParams: Seq[OCPManagerPortParameters])(implicit valName: ValName) extends SinkNode (OCPImp)(portParams) with OCPFormatNode
ocp_out
は各コマンドのチャネルが挿入されている。
Cmd
チャネルWriteData
チャネルResp
チャネル
の3つが定義されている。このチャネルに対してコマンドを発行するなどの制御を行う。
実際に制御を行っているのは以下の記述だ。
val (plegal, pvalid, pbits) = pattern.map(_.bits (edgeOut).getOrElse(edgeOut.NopCmd ())).unzip3 val (wlegal, wvalid, wbits) = pattern.map(_.wrdata(edgeOut).getOrElse(edgeOut.NopData())).unzip3 assert (end || plegal(step), s"Pattern pusher ${name} tried to push an illegal request") cmd.valid := pvalid(step) && io.run && ready && !end cmd.bits := pbits(step) data.valid := wvalid(step) && io.run && ready && !end data.bits := wbits(step)
この読み方だが、pattern.bits(edgeOut)
(mapを除く)の結果は(Bool, Bool, OCPBundleCmd)
が返される。一方でpattern.wrdata(edgeOut)
(mapを除く)の結果は(Bool, Bool, OCPBundleData)
が返される。それぞれ定義は以下のようになっている。
trait Pattern { def address: BigInt def size: Int def bits(edge: OCPEdgeOut): Option[(Bool, Bool, OCPBundleCmd)] def wrdata(edge: OCPEdgeOut): Option[(Bool, Bool, OCPBundleData)] def dataIn: Option[BigInt] = None require ((address & ((BigInt(1) << size) - 1)) == 0) }
bits
とwrdata
についてはOption
型で定義されているので、もしNone
であった場合はgetOrElse
でデフォルト値を挿入することになっている。デフォルト値としてはedgeOut.NopCmd
とedgeOut.NopData
を使用している。
// Nop def NopCmd(): (Bool, Bool, OCPBundleCmd) = { val cmd = Wire(new OCPBundleCmd(bundle)) cmd.mcmd := OCPMessages.NopCmd cmd.tagId := 0.U cmd.address := 0.U // Illegal?, Valid?, Command Data (true.B, false.B, cmd) }
def NopData(): (Bool, Bool, OCPBundleData) = { val data = Wire(new OCPBundleData(bundle)) data.tagId := 0.U data.data := 0.U // Illegal?, Valid?, Write Data (true.B, false.B, data) }
そしてコマンドとデータをそれぞれの条件で発行している。
cmd.valid := pvalid(step) && io.run && ready && !end cmd.bits := pbits(step) data.valid := wvalid(step) && io.run && ready && !end data.bits := wbits(step)
step
はパタンの進捗を示すインデックスなので、コマンドやデータの発行が完了するたびにステップを進めるような仕組みにしている。
when (cmd.fire() || data.fire()) {
step := step + UInt(1)
}
これで一応書き込みパタンを送信できるようになった。次はメモリの受け取り側だ。