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
が有効化されたときの動作だが、すべてのエントリに対して以下の動作を行うのが肝になっている。
rs1
もrs2
も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 } }