FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Diplomacyを使ってOCPバスを作成する (8. PatternPusherの改良)

前回はDiplomacyを使ったOCPバスの作成で、Bundleを作成してValid/Readyの信号をCmd/Readyに変換した。次はこれに基づいてテスト環境を構築する。テスト環境ではまずはPatternPusherを使ってコマンドを挿入する。まずはPatternPusherを改造しなければ始まらない。

PatternPusherでは、これまでValid信号の判定に使っていた信号を、Cmdを使う方式に切り替える。

  • chisel-hw/src/main/scala/ocp/PatternPusher.scala
val (plegal, pcmds,   pbits) = pattern.map(_.bits  (edgeOut).getOrElse(edgeOut.NopCmd ())).unzip3
val (wlegal, wvalids, wbits) = pattern.map(_.wrdata(edgeOut).getOrElse(edgeOut.NopData())).unzip3

cmd.mcmd   := Mux(pcmds(step) =/= OCPMessages.NopCmd && io.run && !flight && ready && !end, pcmds(step), OCPMessages.NopCmd)
cmd.bits   := pbits(step)

pcmd(step)の作り方だが、パタンのビットフィールドから構築している。コマンドは以下の4種類を用意した。

  • WriteReqPattern : 書き込み要求。アドレスとデータサイズを指定する。データは含まれない。Cmdチャネルを使用する。
case class WriteReqPattern(address: BigInt, size: Int) extends Pattern
{
  def bits (edge: OCPEdgeOut) = Some(edge.Write(0.U, address.U))
  def wrdata (edge: OCPEdgeOut) = None
}
  • WriteDataPattern : 書き込みデータ要求。書き込みデータを指定する。Dataチャネルを使用する。
case class WriteDataPattern(data: BigInt) extends Pattern
{
  override def address = 0
  override def size = 0
  def bits (edge: OCPEdgeOut) = None
  def wrdata (edge: OCPEdgeOut) = Some(edge.WriteData(0.U, data.U))
}
  • ReadPattern : 読み込み要求。コマンドチャネルを指定する。
case class ReadPattern(address: BigInt, size: Int) extends Pattern
{
  def bits(edge: OCPEdgeOut) = Some(edge.Read(0.U, address.U))
  def wrdata (edge: OCPEdgeOut) = None
}
  • ReadExpectPattern : 読み込み要求。コマンドチャネルとレスポンスチャネルを使用する。データの比較を行う。
case class ReadExpectPattern(address: BigInt, size: Int, data: BigInt) extends Pattern
{
  def bits(edge: OCPEdgeOut) = Some(edge.Read(0.U, address.U))
  def wrdata (edge: OCPEdgeOut) = None
  override def dataIn = Some(data)
}

このedge.Write, edge.WriteData, edge.Readの作り方だが、これはEdges.scalaで記述している。

  • chisel-hw/src/main/scala/ocp/Edges.scala
class OCPEdgeOut(
  client:  OCPClientPortParameters,
  manager: OCPManagerPortParameters,
  params:  Parameters,
  sourceInfo: SourceInfo)
  extends OCPEdge(client, manager, params, sourceInfo)
{
  // Nop
  def NopCmd(): (Bool, UInt, OCPBundleCmd) = {
    val cmd = Wire(new OCPBundleCmd(bundle))
    cmd.tagId   := 0.U
    cmd.address := 0.U
    (true.B, OCPMessages.NopCmd, cmd)
  }

  // Accesses
  def Read(fromTagId: UInt, toAddress: UInt): (Bool, UInt, OCPBundleCmd) = {
    val cmd = Wire(new OCPBundleCmd(bundle))
    cmd.tagId  := fromTagId
    cmd.address := toAddress
    (true.B, OCPMessages.Read, cmd)
  }

  def Write(fromTagId: UInt, toAddress: UInt): (Bool, UInt, OCPBundleCmd) = {
    val cmd = Wire(new OCPBundleCmd(bundle))
    cmd.tagId   := fromTagId
    cmd.address := toAddress
    (true.B, OCPMessages.Write, cmd)
  }

  def NopData(): (Bool, Bool, OCPBundleData) = {
    val data = Wire(new OCPBundleData(bundle))
    data.tagId := 0.U
    data.data  := 0.U
    (true.B, false.B, data)
  }

  def WriteData(fromTagId: UInt, data: UInt): (Bool, Bool, OCPBundleData) = {
    val d = Wire(new OCPBundleData(bundle))
    d.tagId := fromTagId
    d.data  := data
    (true.B, true.B, d)
  }
}

コマンドチャネルを使用するメソッドの戻り値は、

  • Bool : 命令が有効か
  • UInt : コマンドを使用するか
  • OCPBundleCmd : コマンド発行に使用するビット群

データチャネルを使用するメソッドの戻り値は、

  • Bool : 命令が有効か
  • Bool : そのチャネルを本当に使用するか
  • OCPBundleData : データチャネルに使用するビット群

これに基づいてSRAMとDelayerの実装を変更して、テストを実行した。

  val pusher = LazyModule(new OCPPatternPusher("pat1", Seq(
    new WriteReqPattern(0x100, 0x2),
    new WriteDataPattern(0x012345678L),
    new WriteReqPattern(0x104, 0x2),
    new WriteDataPattern(0x0abcdef01L),
    new WriteReqPattern(0x108, 0x2),
    new WriteDataPattern(0x0deadbeefL),
    new WriteReqPattern(0x10c, 0x2),
    new WriteDataPattern(0x087654321L),
    new ReadExpectPattern(0x100, 0x2, 0x012345678L),
    new ReadExpectPattern(0x104, 0x2, 0x0abcdef01L),
    new ReadExpectPattern(0x108, 0x2, 0x0deadbeefL),
    new ReadExpectPattern(0x10c, 0x2, 0x087654321L)
  )))
  val ram = LazyModule(new OCPRAM(AddressSet(0x0, 0x3ff)))

  ram.node := OCPDelayer(0.01) := pusher.node

実行結果は、ただしく動作しているようだ。

./tilelink
Started UnitTest OCPUnitDelayerTest
Count = 1000
Resp Fired : same as expected. 12345678
Resp Fired : same as expected. abcdef01
Count = 2000
Resp Fired : same as expected. deadbeef
Resp Fired : same as expected. 87654321
f:id:msyksphinz:20200208141659p:plain
図. OCPでコマンドを発行し、SRAMから読みだした様子。