FPGA開発日記

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

Diplomacyを使ってOCPバスを作成する (9. Xbarの作成)

Diplomacyを使ってOCPバスを作成するプロジェクト、次はクロスバーを作成してみよう。クロスバーは2つ以上のスレーブノードを接続してバスをスプリットする。

  val ram0 = LazyModule(new OCPRAM(AddressSet(0x000, 0x3ff)))
  val ram1 = LazyModule(new OCPRAM(AddressSet(0x400, 0x3ff)))
  val xbar = LazyModule(new OCPXbar())

  xbar.node := OCPDelayer(0.01) := pusher.node
  ram0.node := xbar.node
  ram1.node := xbar.node

上記のように、クロスバーの実装を代入演算子の左側に置くことでソースノードを指定する。一方でクロスバーの実装を代入演算子の右側に置くことでスレーブノードを指定している。このスレーブノードはいくつでも指定することができる。

この実装はどのように実現されているのだろうか?中身を見てみる。

  • chisel-hw/src/main/scala/ocp/Xbar.scala
class OCPXbar(policy: OCPArbiter.Policy = OCPArbiter.roundRobin)(implicit p: Parameters) extends LazyModule
{
  val node = OCPNexusNode(
    clientFn  = { seq =>
...
    }
    managerFn = { seq =>
...
    }

ノードの種類としてはネクサスノードを使用している。ネクサスノードの解説はこうだ。

https://chipyard.readthedocs.io/en/latest/TileLink-Diplomacy-Reference/NodeTypes.html#nexus-node

The nexus node is similar to the adapter node in that it has a different output interface than input interface. But it can also have a different number of inputs than it does outputs. This node type is mainly used by the TLXbar widget, which provides a TileLink crossbar generator. You will also likely not need to define this node type manually, but its invocation is as follows.

  • chisel-hw/src/main/scala/ocp/Nodes.scala
case class OCPNexusNode(
  clientFn:        Seq[OCPClientPortParameters]  => OCPClientPortParameters,
  managerFn:       Seq[OCPManagerPortParameters] => OCPManagerPortParameters)(
  implicit valName: ValName)
  extends NexusNode(OCPImp)(clientFn, managerFn) with OCPFormatNode

ノードの接続を行うのは以下の実装だ。複数の入力を1つのベクトルに代入する。

  • chisel-hw/src/main/scala/ocp/Xbar.scala
for (i <- 0 until in.size) {
  val r = inputIdRanges(i)

  if (connectAIO(i).exists(x=>x)) {
    in(i).cmd <> io_in(i).cmd
    in(i).cmd.bits.tagId := io_in(i).cmd.bits.tagId | UInt(r.start)
  } else {
    in(i).cmd.mcmd := UInt(0)
    io_in(i).cmd.ready := Bool(true)
  }

  if (connectCIO(i).exists(x=>x)) {
    in(i).data <> io_in(i).data
    in(i).data.bits.tagId := io_in(i).data.bits.tagId | UInt(r.start)
  } else {
    in(i).data.valid := Bool(false)
    io_in(i).data.ready := Bool(true)
  }

  if (connectDIO(i).exists(x=>x)) {
    in(i).resp <> io_in(i).resp
  } else {
    in(i).resp.valid := Bool(false)
    io_in(i).resp.ready := Bool(true)
  }
}

ノードの出力先は以下のようにしている。

  • chisel-hw/src/main/scala/ocp/Xbar.scala
val out = Wire(Vec(io_out.size, OCPBundle(wide_bundle)))
for (o <- 0 until out.size) {
  val r = outputIdRanges(o)

  if (connectAOI(o).exists(x=>x)) {
    io_out(o).cmd <> out(o).cmd
  } else {
    out(o).cmd.ready := Bool(true)
    io_out(o).cmd.mcmd := UInt(0)
  }

  if (connectCOI(o).exists(x=>x)) {
    io_out(o).data <> out(o).data
  } else {
    out(o).data.ready := Bool(true)
    io_out(o).data.valid := Bool(false)
  }

  if (connectDOI(o).exists(x=>x)) {
    out(o).resp <> io_out(o).resp
    out(o).resp.bits.tagId := io_out(o).resp.bits.tagId | UInt(r.start)
  } else {
    out(o).resp.valid := Bool(false)
    io_out(o).resp.ready := Bool(true)
  }
}

OCPアービタの接続を行う。

  • chisel-hw/src/main/scala/ocp/Xbar.scala
// Arbitrate amongst the sources
for (o <- 0 until out.size) {
  OCPArbiter(policy, out(o).cmd,  filter(beatsCmdI  zip portsCmdOI(o),  connectAOI(o)):_*)
  OCPArbiter(policy, out(o).data, filter(beatsDataI zip portsDataOI(o), connectCOI(o)):_*)
}

for (i <- 0 until in.size) {
  OCPArbiter(policy, in(i).resp, filter(beatsRespO zip portsRespIO(i), connectDIO(i)):_*)
}