Diplomacyを使って独自OCPバスの作成を行っているのだが、どうも上手く行かない。いきなりXbarを作るのはさすがに難易度が高かったか。とりあえずまずはBufferだけを作って動作を確認する。
Bufferは、単純に信号を右から左に渡すだけの機能だ。ただ渡すだけでなく、スライサを挿入したりすることもできる。
Buffer
の実装だが、内部は単純に右から左に信号を渡すだけとなっている。
rocketchip/src/main/scala/tilelink/Buffer.scala
class TLBuffer( a: BufferParams, b: BufferParams, c: BufferParams, d: BufferParams, e: BufferParams)(implicit p: Parameters) extends LazyModule { def this(ace: BufferParams, bd: BufferParams)(implicit p: Parameters) = this(ace, bd, ace, bd, ace) def this(abcde: BufferParams)(implicit p: Parameters) = this(abcde, abcde) def this()(implicit p: Parameters) = this(BufferParams.default) val node = new TLBufferNode(a, b, c, d, e) lazy val module = new LazyModuleImp(this) { (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => out.a <> a(in .a) in .d <> d(out.d) if (edgeOut.manager.anySupportAcquireB && edgeOut.client.anySupportProbe) { in .b <> b(out.b) out.c <> c(in .c) out.e <> e(in .e) } else { in.b.valid := Bool(false) in.c.ready := Bool(true) in.e.ready := Bool(true) out.b.ready := Bool(true) out.c.valid := Bool(false) out.e.valid := Bool(false) } } } }
TL-ULの場合は単純にout.a <> a(in .a)
とin .d <> d(out.d)
が有効で、このa
とd
はTLBufferのインスタンス化時に渡されているパラメータだ。BufferParams
というのは、
rocketchip/src/main/scala/diplomacy/Parameters.scala
case class BufferParams(depth: Int, flow: Boolean, pipe: Boolean) { require (depth >= 0, "Buffer depth must be >= 0") def isDefined = depth > 0 def latency = if (isDefined && !flow) 1 else 0 def apply[T <: Data](x: DecoupledIO[T]) = if (isDefined) Queue(x, depth, flow=flow, pipe=pipe) else x def sq[T <: Data](x: DecoupledIO[T]) = if (!isDefined) x else { val sq = Module(new ShiftQueue(x.bits, depth, flow=flow, pipe=pipe)) sq.io.enq <> x sq.io.deq } override def toString() = "BufferParams:%d%s%s".format(depth, if (flow) "F" else "", if (pipe) "P" else "") }
このように、単純にa(io:DecoupledIO)
という形式で呼ばれた場合、depth
が1以上の場合はQueueが挿入され、そうでない場合はそのまま渡される。
同様に、OCPの実装にもBufferを作ってみよう。
class OCPBuffer( a: BufferCmdParams, c: BufferParams, d: BufferParams)(implicit p: Parameters) extends LazyModule { def this(cmd: BufferCmdParams, ace: BufferParams)(implicit p: Parameters) = this(cmd, ace, ace) def this()(implicit p: Parameters) = this(BufferCmdParams.default, BufferParams.default) val node = new OCPBufferNode(a, c, d) lazy val module = new LazyModuleImp(this) { (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) => out.cmd <> a(in .cmd ) out.data <> c(in .data) in .resp <> d(out.resp) } } }
同様にすべてのチャネルを接続したが、どうだろうか?BufferCmdParams
は、DecoupledIO
ではなくCmdReadyIO
用に作った特別なパラメータで、
case class BufferCmdParams(depth: Int, flow: Boolean, pipe: Boolean) { require (depth >= 0, "Buffer depth must be >= 0") def isDefined = depth > 0 def latency = if (isDefined && !flow) 1 else 0 def apply[T <: Data](x: CmdReadyIO[T]) = if (isDefined) CmdQueue(x, depth, flow=flow, pipe=pipe) else x override def toString() = "BufferCmdParams:%d%s%s".format(depth, if (flow) "F" else "", if (pipe) "P" else "") }
としてこちらも単純に信号を受け渡す仕組みにした。
これでFIRを生成してみたのだが、どうもCmdReadyIOが上手く接続してくれない。Ready信号が消えてしまっている?
val ram0 = LazyModule(new OCPRAM(AddressSet(0x000, 0x3ff))) val buffer = LazyModule(new OCPBuffer()) ram0.node := buffer.node := pusher.node
Buffer
を使用しないとシミュレーションには成功するので、何かがおかしい。
ram0.node := pusher.node
いろいろ確認すると、CmdQueueの実装が誤っていたことが分かったので修正。再度シミュレーションを実行すると上手く動作した。