diff --git a/sim/questa/coverage-exclusions-rv64gc.do b/sim/questa/coverage-exclusions-rv64gc.do index 6660a8bdf7..b7de4a92a0 100644 --- a/sim/questa/coverage-exclusions-rv64gc.do +++ b/sim/questa/coverage-exclusions-rv64gc.do @@ -329,7 +329,6 @@ coverage exclude -scope /dut/core/lsu/dmmu/dmmu/pmp/pmpchecker -linerange $line- set line [GetLineNum ${SRC}/mmu/pmpchecker.sv "EnforcePMP & ExecuteAccessF"] coverage exclude -scope /dut/core/ifu/immu/immu/pmp/pmpchecker -linerange $line-$line -item e 1 -fecexprrow 3 - ## The IFU has ReadAccess = WriteAccess = 0 and ExecuteAccess = 1 hardwired, so exclude alternatives set line [GetLineNum ${SRC}/mmu/pmachecker.sv "ReadAccessM \\| WriteAccessM"] coverage exclude -scope /dut/core/ifu/immu/immu/pmachecker -linerange $line-$line -item e 1 -fecexprrow 2 4 @@ -412,10 +411,6 @@ coverage exclude -scope /dut/core/lsu/hptw/hptw -linerange $line-$line -item e 1 set line [GetLineNum ${SRC}/mmu/hptw.sv "assign HPTWUpdateDA"] coverage exclude -scope /dut/core/lsu/hptw/hptw -linerange $line-$line -item e 1 -fecexprrow 3 -# NOTE: the hptw instance is at /dut/core/lsu/hptw/hptw (single lsu). The four exclusions above use a -# stale /dut/core/lsu/hptw/hptw path that no longer resolves (silent no-ops) -- left untouched here; -# the two exclusions below use the correct path. - # UPDATE_PTE never self-loops on a cache-bus stall: the PTE being A/D-updated was just read during the # same uninterrupted walk (LSU stalled, line cannot be evicted), so it is resident/writable in the D$ and # the UPDATE_PTE store hits. DCacheBusStallM therefore cannot assert in UPDATE_PTE, so the diff --git a/src/mmu/hptw.sv b/src/mmu/hptw.sv index fdd9695efe..e4fd6291f2 100644 --- a/src/mmu/hptw.sv +++ b/src/mmu/hptw.sv @@ -88,6 +88,8 @@ module hptw import cvw::*; #(parameter cvw_t P) ( logic ValidPTE, LeafPTE, ValidLeafPTE, ValidNonLeafPTE; logic StartWalk; logic TLBMissOrUpdateDA; + logic ITLBMissReady; + logic MStageMemPending; logic PRegEn; logic [2:0] NextPageType; logic [P.SVMODE_BITS-1:0] SvMode; @@ -143,7 +145,25 @@ module hptw import cvw::*; #(parameter cvw_t P) ( // Extract bits from CSRs and inputs assign SvMode = SATP_REGW[P.XLEN-1:P.XLEN-P.SVMODE_BITS]; assign BasePageTablePPN = SATP_REGW[P.PPN_BITS-1:0]; - assign TLBMissOrUpdateDA = DTLBMissOrUpdateDAM | ITLBMissOrUpdateAF; + // Defer servicing an instruction-side (ITLB) miss while a committed M-stage data access + // (load/store/AMO/CMO) is still present. At IDLE, SelHPTW=0 so DCacheBusStallM reflects the LSU + // data op (not an HPTW access). Two hazards motivate the deferral: + // (#1538) Starting the walk while the data op's cache/bus access is mid-flight would seize the + // data cache and drop a committed store. Covered by ~DCacheBusStallM. + // (#1766) Even after the data op has *hit* in the cache (DCacheBusStallM low), the load's read + // data is only valid combinationally while the access still owns the cache. If the + // pipeline is stalled for another reason (here HPTWStall itself, from the pending ITLB + // miss) the load is held in M; starting the walk now re-points the cache SRAMs at the + // PTE sets and the load captures stale data when it finally retires. Covered by + // ~MStageMemPending: hold the walk until the data op leaves the M stage (MemRWM/CMOpM + // deassert), then start the walk against an idle cache. + // This cannot deadlock: the data op's retirement never depends on the (younger) instruction-fetch + // walk, so it drains through the normal LSU stall and the walk starts once it leaves M. Because + // TLBMissOrUpdateDA is low while deferred, HPTWStall is also low for the ITLB miss, so the HPTW + // does not freeze the pipeline against the data op. DTLB misses are never deferred. + assign MStageMemPending = (|MemRWM) | (|CMOpM); + assign ITLBMissReady = ITLBMissOrUpdateAF & ~DCacheBusStallM & ~MStageMemPending; + assign TLBMissOrUpdateDA = DTLBMissOrUpdateDAM | ITLBMissReady; // Determine which address to translate mux2 #(P.XLEN) vadrmux(PCSpillF, IEUAdrExtM[P.XLEN-1:0], DTLBWalk, TranslationVAdr);