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)))
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)) }