FPGA開発日記

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

BOOMのTLBに関する調査 (2. TLBのエントリ構成)

TLBのインスタンスは以下で定義されている。

val tlb = Module(new TLB(true, log2Ceil(fetchBytes), TLBConfig(nTLBSets, nTLBWays)))

このときの設定は、nTLBSets = 1, nTLBWays = 32, cfg.nSectors = 4となっているようだ。

   val sectored_entries = Reg(Vec(cfg.nSets, Vec(cfg.nWays / cfg.nSectors, new TLBEntry(cfg.nSectors, false, false))))
   val superpage_entries = Reg(Vec(cfg.nSuperpageEntries, new TLBEntry(1, true, true)))
   val special_entry = (!pageGranularityPMPs).option(Reg(new TLBEntry(1, true, false)))
f:id:msyksphinz:20210502001202p:plain

TLB Entriesの構成(つまり、上記の図のdata[3]-data[0]の構成)は以下のようになっていた。

  • boom/src/main/scala/lsu/tlb.scala
   class EntryData extends Bundle {
     val ppn = UInt(ppnBits.W)
     val u = Bool()
     val g = Bool()
     val ae = Bool()
     val sw = Bool()
     val sx = Bool()
     val sr = Bool()
     val pw = Bool()
     val px = Bool()
     val pr = Bool()
     val pal = Bool() // AMO logical
     val paa = Bool() // AMO arithmetic
     val eff = Bool() // get/put effects
     val c = Bool()
     val fragmented_superpage = Bool()
   }

つまりこれはPTEそのものではなく、PTEの情報を仕込んだものであると思われる。

まず、PTWが完了すると、エントリのどこかにこの情報が格納される。

     // permission bit arrays
   when (do_refill) {
     val pte = io.ptw.resp.bits.pte
     val newEntry = Wire(new EntryData)
     newEntry.ppn := pte.ppn
     newEntry.c := cacheable(0)
     newEntry.u := pte.u
     newEntry.g := pte.g
     newEntry.ae := io.ptw.resp.bits.ae
     newEntry.sr := pte.sr()
     newEntry.sw := pte.sw()
     newEntry.sx := pte.sx()
     newEntry.pr := prot_r(0)
     newEntry.pw := prot_w(0)
     newEntry.px := prot_x(0)
     newEntry.pal := prot_al(0)
     newEntry.paa := prot_aa(0)
     newEntry.eff := prot_eff(0)
     newEntry.fragmented_superpage := io.ptw.resp.bits.fragmented_superpage

     // Special Entryの場合
     when (special_entry.nonEmpty.B && !io.ptw.resp.bits.homogeneous) {
       special_entry.foreach(_.insert(r_refill_tag, io.ptw.resp.bits.level, newEntry))
     }.elsewhen (io.ptw.resp.bits.level < (pgLevels-1).U) {
       // SuperPage Entriesの場合(つまり中間のエントリの場合)
       for ((e, i) <- superpage_entries.zipWithIndex) when (r_superpage_repl_addr === i.U) {
         e.insert(r_refill_tag, io.ptw.resp.bits.level, newEntry)
       }
     }.otherwise {
       // Leafのエントリの場合
       val waddr = Mux(r_sectored_hit, r_sectored_hit_addr, r_sectored_repl_addr)
       for ((e, i) <- sectored_entries.zipWithIndex) when (waddr === i.U) {
         when (!r_sectored_hit) { e.invalidate() }
         e.insert(r_refill_tag, 0.U, newEntry)
       }
     }
   }

これらの信号書き込みのポリシについては、TLBリクエスト時のヒット情報に基づいているようだった。

   if (usingVM) {
     val sfence = io.sfence.valid
     for (w <- 0 until memWidth) {
       when (io.req(w).fire() && tlb_miss(w) && state === s_ready) {
         state := s_request
         r_refill_tag := vpn(w)

         r_superpage_repl_addr := replacementEntry(superpage_entries, superpage_plru.way)
         r_sectored_repl_addr  := replacementEntry(sectored_entries, sectored_plru.way)
         r_sectored_hit_addr   := OHToUInt(sector_hits(w))
         r_sectored_hit        := sector_hits(w).orR
       }
     }

replacementEntry()によってどのエントリを載せ替えるのかを決めているようだった。これは単純な順番チェックか?

   def replacementEntry(set: Seq[Entry], alt: UInt) = {
     val valids = set.map(_.valid.orR).asUInt
     Mux(valids.andR, alt, PriorityEncoder(~valids))
   }