前回は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