FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

OCP Pattern PusherのデバッグとOCP コマンド発行のためのクラスの確認

OCP Pattern Pusherのデバッグをしながら、Diplomacyの構築に必要なクラスの構成について見ていこう。

OCP Pattern PusherではDiplomacyのノードが定義され、出力ノードに対してコマンドが発行されている。

f:id:msyksphinz:20200130235943p:plain
TileLinkのDiplomacyノード構成

上記の図ではTileLinkの仕様で書いてあるが、実際にはOCP向けのEdgeで定義してある。

f:id:msyksphinz:20200131000008p:plain
OCPのDiplomacy構成図

ノードの定義は下記のようになっている。OCPClientNodeを定義している。OCPClientPortParametersOCPClientParmetersは以下のように定義されている。

  • 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
f:id:msyksphinz:20200131000039p:plain
OCP Diplomacyノードのパラメータ

ocp_outは各コマンドのチャネルが挿入されている。

  • Cmdチャネル
  • WriteDataチャネル
  • Respチャネル

の3つが定義されている。このチャネルに対してコマンドを発行するなどの制御を行う。

f:id:msyksphinz:20200131000102p:plain
OCP Diplomacyのエッジチャネル構成

実際に制御を行っているのは以下の記述だ。

    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)
}

bitswrdataについてはOption型で定義されているので、もしNoneであった場合はgetOrElseでデフォルト値を挿入することになっている。デフォルト値としてはedgeOut.NopCmdedgeOut.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)
    }

これで一応書き込みパタンを送信できるようになった。次はメモリの受け取り側だ。

f:id:msyksphinz:20200131000232p:plain
OCP DiplomacyのRTLシミュレーション結果