FPGA開発日記

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

BOOMのTLBに関する調査 (8. TLBのSFENCE.VMA命令の取り扱い)

SonicBOOM(というかRocket-Chip全体)のTLBにおけるSFENCE.VMA命令の取り扱いについて調査した。SFENCE.VMA命令はTLBなどのアドレストランスレーションに対してフェンスを作るための命令で、命令仕様的に、SFENCE.VMA命令は指定されたレジスタオペランドに応じたアドレスに対してTLBフラッシュを行う。したがってRocket-ChipのTLB実装には、SFENCE.VMAに関するインタフェースが用意されている。

 class NBDTLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: TLEdgeOut, p: Parameters) extends BoomModule()(p) {
   require(!instruction)
   val io = IO(new Bundle {
     val req = Flipped(Vec(memWidth, Decoupled(new TLBReq(lgMaxSize))))
     val miss_rdy = Output(Bool())
     val resp = Output(Vec(memWidth, new TLBResp))
     // SFENCE.VMA命令に対するインタフェース
     val sfence = Input(Valid(new SFenceReq))
     val ptw = new TLBPTWIO
     val kill = Input(Bool())
   })

SFENCE.VMA命令が実行された場合、このインタフェースがトリガされ、ステートマシンが操作される。

まず、SFENCE.VMAリクエスト実行時にステートマシンが外部メモリアクセスをリクエスト中であれば、s_wait_invalidateステートに移動しメモリアクセスの完了を待つ。また、そうでない場合は即時TLBフラッシュのモードに移動する。

   if (usingVM) {
     val sfence = io.sfence.valid
     for (w <- 0 until memWidth) {
       when (io.req(w).fire() && tlb_miss(w) && state === s_ready) {
/* ... 中略 ... */
     when (state === s_request) {
       when (sfence) { state := s_ready }
       when (io.ptw.req.ready) { state := Mux(sfence, s_wait_invalidate, s_wait) }
       when (io.kill) { state := s_ready }
     }
     when (state === s_wait && sfence) {
       state := s_wait_invalidate
     }
     when (io.ptw.resp.valid) {
       state := s_ready
     }
     when (sfence) {
       for (w <- 0 until memWidth) {
         assert(!io.sfence.bits.rs1 || (io.sfence.bits.addr >> pgIdxBits) === vpn(w))
         for (e <- all_entries) {
           when (io.sfence.bits.rs1) { e.invalidateVPN(vpn(w)) }
           .elsewhen (io.sfence.bits.rs2) { e.invalidateNonGlobal() }
           .otherwise { e.invalidate() }
         }
       }
     }

sfenceが有効化されたときの動作だが、すべてのエントリに対して以下の動作を行うのが肝になっている。

  • rs1rs2もx0である場合、すべてのテーブルをinvalidateする (e.invalidate())
  • rs1がx0でない場合、Leafのエントリに対してアドレスの一致するエントリをフラッシュする
  • rs2がx0でない場合、Leafでないエントリに対してアドレスの一致するエントリをフラッシュする
     def invalidate() { valid.foreach(_ := false.B) }
     def invalidateVPN(vpn: UInt) {
       if (superpage) {
          when (hit(vpn)) { invalidate() }
       } else {
         when (sectorTagMatch(vpn)) { valid(sectorIdx(vpn)) := false.B }

         // For fragmented superpage mappings, we assume the worst (largest)
         // case, and zap entries whose most-significant VPNs match
          when (((tag ^ vpn) >> (pgLevelBits * (pgLevels - 1))) === 0.U) {
            for ((v, e) <- valid zip entry_data)
              when (e.fragmented_superpage) { v := false.B }
          }
       }
     }
     def invalidateNonGlobal() {
        for ((v, e) <- valid zip entry_data)
          when (!e.g) { v := false.B }
     }