FPGA開発日記

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

TileLinkのCache Coherencyプロトコル (4. SiFive Inclusive Cache の動作を観察する)

SiFive Inclusive Cacheは複数コアからのリクエストを処理しコヒーレントを取ることのできるL2キャッシュであるが、その内部構成を見ていこうと思う。

GTKWaveで見たところ、ざっくりと以下のような階層構成になっていた。

f:id:msyksphinz:20210528002129p:plain

InclusiveCacheのインタフェースとしては、TileLinkのマスタとスレーブが一対ずつ定義されていた。

 module InclusiveCache( // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127022:2]
   input         clock, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127023:4]
   input         reset, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127024:4]
   output        auto_ctl_in_a_ready, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input         auto_ctl_in_a_valid, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [2:0]  auto_ctl_in_a_bits_opcode, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [2:0]  auto_ctl_in_a_bits_param, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [1:0]  auto_ctl_in_a_bits_size, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [8:0]  auto_ctl_in_a_bits_source, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [25:0] auto_ctl_in_a_bits_address, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
   input  [7:0]  auto_ctl_in_a_bits_mask, // @[chipyard.TestHarness.DualMediumBoomConfig.fir 127025:4]
...

実装としてはmodsの数だけInclusiveCacheがインスタンス化されるようで、オプションを指定すればmodsの数だけL2をバンク分けすることができそうだ。

     // Create the L2 Banks
     val mods = (node.in zip node.out) map { case ((in, edgeIn), (out, edgeOut)) =>
       edgeOut.manager.managers.foreach { m =>
         require (m.supportsAcquireB.contains(xfer),
           s"All managers behind the L2 must support acquireB($xfer) " +
           s"but ${m.name} only supports (${m.supportsAcquireB})!")

       val params = InclusiveCacheParameters(cache, micro, control.isDefined, edgeIn, edgeOut)
       val scheduler = Module(new Scheduler(params))

このなかのSchedulerというのがキモのモジュールらしい。

まずはインタフェースから、TileLinkの5つのチャネルがそれぞれUp側とDown側で用意されている。

 class Scheduler(params: InclusiveCacheParameters) extends Module
 {
   val io = new Bundle {
     val in = TLBundle(params.inner.bundle).flip
     val out = TLBundle(params.outer.bundle)
     // Way permissions
     val ways = Vec(params.allClients, UInt(width = params.cache.ways)).flip
     val divs = Vec(params.allClients, UInt(width = InclusiveCacheParameters.lfsrBits + 1)).flip
     // Control port
     val req = Decoupled(new SinkXRequest(params)).flip
     val resp = Decoupled(new SourceXRequest(params))
   }

   val sourceA = Module(new SourceA(params))
   val sourceB = Module(new SourceB(params))
   val sourceC = Module(new SourceC(params))
   val sourceD = Module(new SourceD(params))
   val sourceE = Module(new SourceE(params))
   val sourceX = Module(new SourceX(params))

   io.out.a <> sourceA.io.a
   io.out.c <> sourceC.io.c
   io.out.e <> sourceE.io.e
   io.in.b <> sourceB.io.b
   io.in.d <> sourceD.io.d
   io.resp <> sourceX.io.x

   val sinkA = Module(new SinkA(params))
   val sinkC = Module(new SinkC(params))
   val sinkD = Module(new SinkD(params))
   val sinkE = Module(new SinkE(params))
   val sinkX = Module(new SinkX(params))

   sinkA.io.a <> io.in.a
   sinkC.io.c <> io.in.c
   sinkE.io.e <> io.in.e
   sinkD.io.d <> io.out.d
   sinkX.io.x <> io.req

主要なモジュールとしては以下のような感じかな。ディレクトリ、DRAM、リクエストコントローラ、MSHRランダム発生器となっているようだ。

   val directory = Module(new Directory(params))
   val bankedStore = Module(new BankedStore(params))
   val requests = Module(new ListBuffer(ListBufferParameters(new QueuedRequest(params), 3*params.mshrs, params.secondary, false)))
   val mshrs = Seq.fill(params.mshrs) { Module(new MSHR(params)) }

requestsは実体はリクエストキューになっており、単なるFIFOとなっているようだ。

 class QueuedRequest(params: InclusiveCacheParameters) extends InclusiveCacheBundle(params)
 {
   val prio   = Vec(3, Bool()) // A=001, B=010, C=100
   val control= Bool() // control command
   val opcode = UInt(width = 3)
   val param  = UInt(width = 3)
   val size   = UInt(width = params.inner.bundle.sizeBits)
   val source = UInt(width = params.inner.bundle.sourceBits)
   val tag    = UInt(width = params.tagBits)
   val offset = UInt(width = params.offsetBits)
   val put    = UInt(width = params.putBits)
 }