Skip to content

Shared PTW and other VM improvements - #392

Open
ATWeatherly wants to merge 14 commits into
vortexgpgpu:masterfrom
ATWeatherly:shared-ptw-pr
Open

Shared PTW and other VM improvements#392
ATWeatherly wants to merge 14 commits into
vortexgpgpu:masterfrom
ATWeatherly:shared-ptw-pr

Conversation

@ATWeatherly

Copy link
Copy Markdown
  • Replaces the per-MMU blocking Sv32 walker with one device-level generic Sv32/Sv39 walker
    (hw/rtl/mem/VX_mmu_ptw.sv, VX_CFG_PTW_NUM_WALKERS concurrent walks, two direct-mapped page-walk caches,
    PTE fetches on a dedicated L3 requestor port) reached through a new VX_ptw_bus_if/VX_ptw_arb hierarchy.
  • Banked per-core TLBs (VX_CFG_TLB_NUM_BANKS), superpage leaves installed at their real level, walk faults
    reported on the fill. SimX timing twin (sim/simx/mem/ptw.{h,cpp}, banked Mmu) with the TLB/PTW/PWC MPM
    counters under the MEM class.
  • RTL command-processor VM for the FPGA path: CP_SATP_LO/HI, DEV_CAPS.VM_ENABLED, and a per-chunk
    VX_cp_mmu walker in the DMA engine (F_MEM_PHYSICAL bypasses).
  • tests/regression/vm_stress; ci/testcases/vm.yaml now runs simx and rtlsim at both XLENs, an xrt
    case through the CP + DMA walker, single-bank and multi-cluster L2/L3 variants, and model_parity cases
    (sgemm, diverge) that hold the SimX MMU model to rtlsim.
  • Fixes found while validating (details in CHANGELOG [Unreleased]/Fixed): SimX lost stores around a walk;
    SimX VM timing parity (was +36%/+16% rtlsim-vs-simx cycles on sgemm/diverge, now +0.2%/+3.8%); CP walker Sv32
    root-PPN slice, translation-cache invalidation (CACHE_FLUSH/SATP), BARE bypass, superpage alignment, shared
    VX_gpu_pkg geometry; stale installs from walks straddling a flush (RTL + SimX); PTW client sizing by
    NUM_SOCKETS*SOCKET_SIZE; pinned-slab identity maps no longer re-reserve VA; perf counters reset per launch;
    VX_mmu bypass-path width on XLEN=64; CP block testbenches carry the new ports.

NO REAL FPGA TEST PERFORMED. Everything else is solid though. Waiting for Rogues Gallery admin memory configuration of U50 FPGAs due to 3.0 xrt runtime changes (something like sudo bash -c 'source /opt/xilinx/xrt/setup.sh && xrt-smi configure --host-mem -d 0000:04:00.1 --size 1G enable')

ATWeatherly and others added 14 commits August 27, 2026 19:05
TFifo::empty() reports only entries that have emerged from the pipe's
latency window (SimChannel::queue_empty ignores pending_count_), so
processFlush()'s drain guard could not see a replayed store pushed into
pipe_req_ earlier in the same tick. The walk then swept the store's set
while its line was still clean and finished before the write applied,
losing the store on an end-of-kernel flush. Observed with vm_stress on
simx/XLEN=32 once the VM MMU model added translation latency, but the
window is generic to any replay that lands as the flush starts; size()
counts in-flight entries and closes it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TxArbiter and TxRxArbiter sized their per-output request group with
log2ceil(num_inputs / num_outputs); when the input count is not a multiple
of the output count that rounds down and the trailing inputs are never
polled (VX_stream_arb uses CDIV). Callers had to pad input counts up to a
power-of-two multiple to work around it. Also adds the CHANGELOG entry for
the cache flush-walk fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…BARE cases

A stray `.` in VX_mmu's VX_mmu_tlb instantiation broke every VM RTL build
with profiling enabled, which is why rtlsim VM never made it into CI.

The MMU and PTW took a 32-bit `satp`, truncating the CSR on XLEN=64 and
testing bit 31 where Sv39 keeps its mode field in bits 63:60; the port is
now XLEN wide and the translate gate follows both encodings.

VX_mmu_tlb hardcoded 5 index bits, so VX_CFG_TLB_SIZE silently mis-indexed
the CAM for any value other than 32; the width is derived from the config.

ci/testcases/vm.yaml isa-6..10 and tests/regression/basic/start.S still
used the retired VX_CFG_VM_ADDR_MODE / VX_CFG_PAGE_TABLE_BASE_ADDR names.
VX_VM_ADDR_MODE is a resolved VX_types contract fixed at configure time and
gen_config drops unknown -D keys, so those BARE cases only re-ran the
default mode; they are removed rather than left claiming coverage, and
start.S uses the live VX_VM_* / VX_MEM_* names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3o1M5sa5KmNwsbU9wptw4
…Bs; Sv39

The per-MMU blocking Sv32 walker could not terminate on the superpage
leaves the runtime installs for identity-mapped regions, so rtlsim VM was
broken and absent from CI. It is replaced by one device-level walker
(hw/rtl/mem/VX_mmu_ptw.sv): generic over the Sv32/Sv39 page-table
geometry, VX_CFG_PTW_NUM_WALKERS concurrent walk slots, PTE fetches on a
dedicated L3 requestor port (L3_PTW_IDX), and two direct-mapped page-walk
caches (VX_mmu_pwc.sv, VX_CFG_PTW_WALK_CACHE_SIZE entries) so a warm walk
starts one (Sv32) or two (Sv39) levels below the root. Leaf, validity,
and superpage-alignment checks per the privileged spec report a fault on
the fill; the TLB then replays the access untranslated, mirroring the CP
walker's defensive pass-through.

TLB misses ride a new VX_ptw_bus_if through one VX_ptw_arb per hierarchy
level (banks -> core -> socket -> cluster -> device), each folding its
source index into the tag, so every bank owns one outstanding walk and no
per-requestor serialization is needed. SATP stays a per-core CSR; the
root PPN travels with each walk request, which also keys the walk caches.

Per-core TLBs are banked (VX_CFG_TLB_NUM_BANKS, low VPN bits select the
bank; the iTLB stays single-banked) so a miss parked in one bank no
longer blocks hits in the others, and fills record their page level so
superpage entries match and translate at their real size. The TLBs are
invalidated by a one-cycle pulse at the start of the DCR cache flush —
the flush request itself is translated, so holding the invalidate at the
level of the pending flush would livelock its own walk — and the device
walker drops its walk caches on the same event.

Walker and walk-cache counters move to sysmem_perf (device totals, same
value on every core, like the L3 counters), with new PWC1/PWC2 MPM ids;
TLB counters stay per core. DBG_TRACE_MMU traces the TLB banks and the
walker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3o1M5sa5KmNwsbU9wptw4
Mirrors the RTL topology so the two stay comparable: the per-core Mmu
keeps a banked TLB with one outstanding walk per bank and
superpage-aware, level-carrying entries, and hands misses over
PtwReq/PtwRsp channels to one device-level Ptw SimObject with concurrent
walk slots and PWC1/PWC2, fetching PTEs on the dedicated L3 requestor
port. TLB counters surface per core and the walker counters as device
totals under the MEM MPM class — previously the SimX MMU counters were
never surfaced at all. The TLBs and walk caches are invalidated on the
cache-flush sweep, and a satp write flushes only when the value changes.

The L3 bypass arbiter serves its inputs in power-of-two groups per memory
port, so the walker's odd extra port would never be polled; the L3 input
count is padded up with idle ports instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3o1M5sa5KmNwsbU9wptw4
… VA overlap

The vm catalog now runs the compute regressions on rtlsim as well as
simx at both XLENs, adds an xrt (RTL command processor) case, a
vm_stress TLB-pressure test — strided page touches per task plus a
VX_MEM_PHYS buffer — and full-tier single-bank and multi-cluster
variants. vm_stress exposed that the VA allocator hands out addresses
from the same base the pinned identity-mapped slab occupies, so a
VX_MEM_PHYS buffer created after any ordinary allocation collided in the
page table; the slab's range is now reserved out of the VA space at VM
bring-up. The VM design doc and vm.md are updated to the shared-walker
topology.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3o1M5sa5KmNwsbU9wptw4
VM previously worked on simx/rtlsim/gem5 and silently no-oped on
xrt/opae: the runtime discovers VM support from DEV_CAPS bit 24, which
the RTL regfile never set, so no page tables were built while a
VM-enabled kernel still wrote satp and walked an empty table. The RTL CP
now matches the software CP model: DEV_CAPS publishes VM_ENABLED,
CP_SATP_LO/HI (0x028/0x02C) hold the page-table root, and the DMA engine
translates its device-side operand once per chunk (chunks never cross a
page) through a new VX_cp_mmu walker that reads PTEs over the DMA's own
device AXI channel while the transfer FSM is parked. F_MEM_PHYSICAL
skips translation; a faulted walk passes the address through
untranslated, mirroring cp_translate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3o1M5sa5KmNwsbU9wptw4
needs_translation() and the two full_addr wires in VX_mmu were hardcoded
to 32 bits. On XLEN=64 the 48-bit byte address is truncated, and Verilator
flags WIDTHTRUNC; the AFU sims (xrtsim/opaesim) run without -Wno-fatal,
so every -DVX_CFG_VM_ENABLE build64 of libxrtsim.so failed. The value is
unused today (only the SATP mode gates translation), so this is lint-only,
but it blocked the vm:xrt-cp case at XLEN=64.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The banked MMU model popped a missing packet off ReqIn and parked it in a
BankMiss slot. That slot is not a SimChannel, so a store waiting on a page
walk was invisible to SimChannelBase::inflight_count(), and the processor
could declare the kernel done and flush the dcache while the store was
still walking: vm_stress on simx/XLEN=32 lost exactly one store this way
(the flush-wb swept the line one cycle before the MSHR replay landed).
Popping early also let later packets on the same lane overtake the parked
one, which the RTL's per-lane elastic buffer never does. Leave the packet
at the head of its port and pop it only when the replay is sent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ches

VX_cp_axil_regfile gained a satp output and VX_cp_dma a satp input for the
CP DMA walker, but the block-level harnesses under hw/unittest/ still
instantiated the old port lists; Verilator's PINMISSING is fatal there, so
'make -C hw/unittest' (the unittest:hw CI case) failed to build. Expose
satp on both wrapper tops so a future VM-aware DMA test can drive it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With translation on, SimX ran 16-36% fewer cycles than rtlsim on hit-heavy
kernels (sgemm, diverge) while the VM-off baseline agreed within 2%: the
model translated a TLB hit in the same tick with unlimited throughput,
whereas every RTL access crosses the per-lane elastic buffer, the
lane->bank crossbar, the single-ported bank CAM, and the gather crossbar.
Charge that path: one lookup per bank per cycle with round-robin port
arbitration, a TRANSLATE_LATENCY (5, calibrated) on translated and replayed
requests, and skip the lookup when the output port is stalled so a retry
does not double-count the TLB perf counters.

Calibrated RV32: sgemm 1,298,133 vs rtlsim 1,295,022 (+0.2%), diverge
1,149,964 vs 1,107,733 (+3.8%); RV64: sgemm +0.1%, diverge +4.1%,
vm_stress +3.0%. Gate it with model_parity-sgemm / model_parity-diverge in
the vm catalog (RV32-pinned like every check gate, full tier).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cache, share VM geometry

VX_cp_mmu took satp[43:0] as the root PPN in both modes, so under Sv32 the
MODE bit and ASID field folded into the table address (masked on xrt only
because the AFU truncates the CP address to 32 bits). Its 2-entry
translation cache was consulted even in BARE mode and cleared only by
hardware reset, so a translation cached under one page table survived a
CP_SATP rewrite, the runtime's CACHE_FLUSH, or a later process reusing the
VA; the SW CP model walks every command, so simx could not see it. And it
re-derived the Sv32/Sv39 geometry and PTE decode by hand, without the
superpage alignment check VX_mmu_ptw enforces.

The walker now imports VX_gpu_pkg (VM_* params, vm_pte_valid/is_leaf),
slices the root PPN like VX_mmu, bypasses the cache in BARE mode, drops it
on a CACHE_FLUSH DCR read or a SATP change (a walk straddling the
invalidation is answered but not cached), and faults misaligned
superpages. VX_cp_core derives the flush pulse from the DCR proxy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A TLB flush only suppressed an install in the one-cycle flush pulse, and
the walker had no flush term at all, so a walk already in flight when the
CACHE_FLUSH DCR arrived installed a translation resolved against the old
page table into the freshly flushed TLB and page-walk caches. The TLB bank
now latches flush_pending for a walk that was outstanding at the flush,
discards that fill and re-walks; the PTW marks active slots stale and keeps
their PTEs out of the PWCs. The SimX Mmu/Ptw mirror both (stale bank ->
re-walk, stale slot -> no PWC fill). Ptw::on_reset now also zeroes its perf
counters like the caches do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The shared PTW's client count and the mmu_flush loop used
  VX_CFG_NUM_CORES, but a cluster instantiates NUM_SOCKETS * SOCKET_SIZE
  cores; a non-divisible shape left trailing cores' walker ports unbound
  (first TLB miss hung the run).
- Drop the L3 request-count padding that worked around the arbiter
  grouping bug (fixed separately).
- Identity maps installed inside the pinned slab re-reserved VA space the
  slab reservation already held, printing an allocator overlap error per
  VX_MEM_PHYS buffer; VMManager now owns the slab range and skips those.
- Tlb perf counters reset per launch (Mmu::on_reset), and the RTL's
  power-of-two TLB_NUM_BANKS constraint is static_asserted in SimX.
- CHANGELOG entries for the VM fixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ATWeatherly

Copy link
Copy Markdown
Author

sorry for the monster PR. Let me know if I should make better attempt to break individual components down

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant