FPGA開発日記

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

TileLinkのDiplomacyデザインの解析 (2. TileLinkのSourceIdの計算方法について)

Diplomacyを使ったデザインでは、クロスバーを使って自由にバスを構成することができる。この場合のバスのIDは自動的に計算されるようになっている。例えば、クロスバーに対して4つのマスターが接続される場合、そしてそのマスターのIDのビット幅が最大で3ビットである場合、

  • マスター0 : 2'b00, sourceId_0[ 2: 0]
  • マスター1 : 2'b01, sourceId_1[ 2: 0]
  • マスター2 : 2'b10, sourceId_2[ 2: 0]
  • マスター3 : 2'b11, sourceId_3[ 2: 0]

のようにバスが構成される。この計算を行っているのはXbar.scalaである。さっそく解析してみる。

f:id:msyksphinz:20200112022200p:plain
  • rocket-chip/src/main/scala/tilelink/Xbar.scala
class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parameters) extends LazyModule
{
  val node = TLNexusNode(
    clientFn  = { seq =>
      seq(0).copy(
        minLatency = seq.map(_.minLatency).min,
        clients = (TLXbar.mapInputIds(seq) zip seq) flatMap { case (range, port) =>
          port.clients map { client => {
            client.copy(sourceId = client.sourceId.shift(range.start)
            )
          }}
        }
      )
    },

このsourceIdにはそれぞれのマスターが取りうるIDの範囲が指定される。それぞれsourceIdの値を出力してみると以下のようになる。下記はTLPatternPusherによる例。TLPatternPusherはIDを使わないので、1つのIDしか使用されない。

sourceId = IdRange(3,4)
sourceId = IdRange(2,3)
sourceId = IdRange(1,2)
sourceId = IdRange(0,1)

そしてTLXbarの出力側に付加するIDの計算を行う。

class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parameters) extends LazyModule
{
...
    // Transform input bundle sources (sinks use global namespace on both sides)
    val in = Wire(Vec(io_in.size, TLBundle(wide_bundle)))
    for (i <- 0 until in.size) {
      val r = inputIdRanges(i)

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

このときのr.startin(i).a.bits.sourceの値を出力してみる。

TLXbar A channel r.start = 3
TLXbar A channel after   = UInt<2>(Wire in TLXbar)
TLXbar A channel r.start = 2
TLXbar A channel after   = UInt<2>(Wire in TLXbar)
TLXbar A channel r.start = 1
TLXbar A channel after   = UInt<2>(Wire in TLXbar)
TLXbar A channel r.start = 0
TLXbar A channel after   = UInt<2>(Wire in TLXbar)

2ビットのsourceIdが生成され、さらにIDがr.startにより割り当てられる。

さらに、多くのIDを扱うためのTLFuzzerを接続するとどうなのか?

sourceId = IdRange(96,128)
sourceId = IdRange(64,96)
sourceId = IdRange(32,64)
sourceId = IdRange(0,32)
TLXbar A channel r.start = 96
TLXbar A channel after   = UInt<7>(Wire in TLXbar)
TLXbar A channel r.start = 64
TLXbar A channel after   = UInt<7>(Wire in TLXbar)
TLXbar A channel r.start = 32
TLXbar A channel after   = UInt<7>(Wire in TLXbar)
TLXbar A channel r.start = 0
TLXbar A channel after   = UInt<7>(Wire in TLXbar)

r.startinputIdRanges(i)から生成されている。

...
   for (i <- 0 until in.size) {
      val r = inputIdRanges(i)
...