Repository navigation
cpu: keep valid R10000-family cache lines in each CPU's own view of memory - #448
Draft
jamesbraid wants to merge 5 commits into
Draft
jamesbraid wants to merge 5 commits into
jamesbraid wants to merge 5 commits into
Conversation
jamesbraid
force-pushed
the
r10k-cache-line-staging
branch
2 times, most recently
from
November 8, 2026 02:53
4eba0c8 to
2d6c223
Compare
Owner
|
o2 prom does it for stack. so we invented crazy bus device to fake two views of the same physical address so ram sizing doesnt destroy stuff in "cache". that allowed us to remove branch from cached reads and get rid of the shadow cache transition. |
jamesbraid
force-pushed
the
r10k-cache-line-staging
branch
from
November 8, 2026 06:31
2d6c223 to
1945e4f
Compare
Owner
|
is this necessary? we do the prom stack hack using bus devices? it works on o2 and octane? |
Collaborator
Author
|
originally tezro and fuel needed it, but that was before I understood the iris approach more. I'm trying with the bus devices for tezro/fuel to see if this can be dropped |
A CPU's cache, its decode cache and its compiled code reach RAM through ppmem's window, and every CPU and Physical's direct paths share that one window. The R10000-family PROMs use the caches as working memory before memory is set up: lines made valid with Index Store Tag and Index Store Data hold a stack or code while uncached accesses, which bypass the caches (NEC VR10000 user's manual p. 75), see memory. The Tezro's processors hold lines for the same addresses with different contents at once. One shared window has no room for any of that. PpMemSpace::add_cached_view gives a CPU a window of its own. Every mapping laid in the shared window is laid in each view as well, and a view publishes its bitmap to that CPU's inline fields, so the CPU reads its view through the same base pointer and bitmap test it used before. hold_line_page turns a page of a view into private memory, starting as a copy of memory, and release_line_page lays memory back when the last line goes, dropping what the page held. A remap saves and restores the held pages around it. A page with no memory behind it is refused. One in a region the window maps only in part takes the whole region in that view, reading zeros where nothing is mapped, because the region bitmap is all a CPU tests: the O2's POST holds lines for page 0 before CRIME maps the rest of its region. Nothing holds a page yet. The views are not coherent with each other or with DMA. The generic machine, the Octane, the O2 and the IP27 machine give each CPU a view now. Physical and the devices that read the window directly keep the shared one. Nothing on a load, store or fetch changes. The CPU no longer takes over the shared window's bitmap, so set_bitmap_sink and add_bitmap_sink lose their last callers and go.
The Fuel's and the Tezro's PROMs run code from primary instruction lines they make valid with Index Store Tag and Index Store Data, and on iris those words differ from memory once memory is set up there (measured on the model, not on hardware). Compiles read memory and compiled code is shared by every CPU, so code compiled from memory would run in place of the lines. Upstream iris has no machine that runs code from a held page yet: the O2 keeps only a stack in lines and runs POST uncached, and the Octane's lines name no memory. This is for the Fuel and the Tezro. The first CPU to hold a page now puts GEN_HELD on the page's generation counter, which retires what was compiled there, and the last release takes it off with an ordinary bump. A compile that finds the mark publishes no code for that generation and denies every offset, so arrivals interpret the page from the CPU's own view without asking again. The first compile after the release allows every offset once more. The release lowers the counter, so publish treats any change of generation as new bytes, not only a rise. ppmem owns the mark, so it is one mark however many CPUs hold the page, and a remap moves it to the counter that backs the page afterwards. All of it is at compile time. Dispatch and compiled code are unchanged, and every other page stays compiled. Without the denial, four Tezro processors asking for the same held PROM page took 65-77 s to the BASEIO monitor against 38-42 s with it (an earlier version of this change, jitv2 loop-profile builds).
…view The R10000-family PROMs keep stacks and data in cache lines they make valid with Index Store Tag and Index Store Data. The O2's POST tests page 0 uncached while its stack lives in secondary and primary data lines for the same page, the Fuel's and Tezro's stacks sit in primary data lines for 0x100000-0x107fff, and on the Tezro every processor holds those lines at once with its own contents. The shadow cache kept the tags and data arrays for diagnostics only, so a cached access read memory. A tag change that makes a primary data or secondary line valid now holds its page in the CPU's cached view and writes the line's data array into it, since Index Store Tag writes the tag array alone (NEC VR5000/VR10000 instruction manual pp. 24-25). Loads, stores, fetches, the decode cache and compiled code read the view as before and change not at all. Uncached accesses bypass the caches (NEC VR10000 user's manual p. 75) and keep seeing memory, as do other masters, since the views are not coherent. A tag that names another line, or none, ends the line, and the page goes with its last line. Index Store Data into a held line writes through. The tags stay the only line state, plus a held bit. The O2's R5000 POST keeps its stack the same way, in primary data lines for page 0 made valid by Index Store Tag (D) with TagLo 0xc0, and tests page 0 uncached meanwhile. An R5000 primary data tag has the R10000's PTag and PState fields (NEC VR5000 user's manual U11761EJ6V0UM p. 133), and a store hit on a write-back line makes no bus request (p. 243), so a valid one holds its page too. The R5000 has no Index Store Data, so the page starts as memory. Its instruction lines hold nothing: with no Index Store Data to fill one, it could hold only what memory holds. Every line the shipped PROMs stage ends by Index Store Tag, none is written back, and none is read back with Index Load Data while it holds a page, so the writeback and invalidate operations stay as they were. The R12000 zeroing a primary data tag at Index Writeback Invalidate ends a line like any other tag change. A secondary doubleword puts TagLo at the lower address. The manuals fix only that TagHi is the doubleword's high word and TagLo its low word (MIPS R10000 user's manual p. 201, NEC VR10000 user's manual p. 289); which address each pairs with is inferred, not established, from shipped PROMs. PROM 6.210 on the Fuel and the Tezro stages code with the word at A in TagLo and the word at A + 4 in TagHi, 990 pairs in a Tezro boot, and its staged code equals its image only this way. The IP35 boot-flash PROM's staging pairs them the same way: simulated with the other order, 378 of its 562 staged fetches break, every trampoline fetch among them. With the other order the Tezro's PROM falls back to running its cache test uncached, which took twice as long to reach the BASEIO monitor. Limits: a page, not a line, is private, so the rest of a held page shows memory as it was when the page was taken, and nothing is coherent with other CPUs or DMA. A snapshot taken while lines hold pages is refused at restore. Lines that name no memory, like the Octane's tag test patterns, hold nothing. `l2 probe 0` lists the pages held.
Upstream iris has no machine that needs this yet. The Fuel's and the Tezro's PROM 6.210 copies itself to memory from code it stages in primary instruction lines with Index Store Tag and Index Store Data (I) and runs through cached xkphys, and once memory is configured, memory at those addresses holds something else. Without the lines the PROM hangs on iris at "Copying PROM code to memory ..." (measured on the model). A valid primary instruction line now holds its page in the CPU's view like a data line, with each word written there as the instruction it predecodes. A primary instruction word is 36 bits of predecoded instruction (NEC VR10000 user's manual p. 288, established). Neither manual gives the layout, so it is inferred from the words PROM 6.210 stores, and only its eleven sampled classes decode. A word of any other class leaves memory's word in the page, where hardware would run it. Index Store Data (I) into a held line writes through. The PROM gets the predecoded form of other code from the hardware: it runs a secondary line it staged, which refills a primary line, and reads the words back with Index Load Data (I) to stage them itself (measured on the model). The model does not refill on a fetch, so Index Load Data (I) in a set a held secondary line maps to now answers with that line's words predecoded, as if it had been refilled, until Index Store Data (I) writes the set. A guest's own pattern in the set reads back as written (MIPS R10000 user's manual pp. 201-202). An instruction outside the eleven classes comes back in a form only this model reads, bits 35:32 set over the instruction, which takes predecode classes 0x78-0x7f. Nothing on a fetch, load or store, and nothing in the decode cache, changes. A page held only for code also answers cached loads with the code's bytes, where hardware would read memory.
The O2 kept POST's stack in bank 0 and, while a primary data tag for page 0 stood, pointed the decode table at a page of its own for the uncached path, switched by a hook on every Index Store Tag (D). The CPU's cached view holds those lines now, with the bank, the decode table and the rendering engine on memory meanwhile, so the side page, its hook in the shadow cache, the slot-1 mapping it needed at reset and MipsCache::set_pd_tag_store_hook go. A snapshot taken during POST by an older iris carries the side page but no cache lines to rebuild the stack from, and is refused.
jamesbraid
force-pushed
the
r10k-cache-line-staging
branch
from
November 8, 2026 23:27
1945e4f to
1d20edb
Compare
jamesbraid
force-pushed
the
r10k-cache-line-staging
branch
3 times, most recently
from
November 9, 2026 13:48
c3a9033 to
517a9af
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The R10000-family PROMs use the caches as working memory before DRAM is set up: they make lines valid with Index Store Tag and Index Store Data, then load, store and fetch through them while the uncached path sees memory. The O2 keeps POST's stack in page 0's lines while it tests page 0 uncached; the Fuel and Tezro keep their stack in primary data lines and run code from primary instruction lines, and the Tezro's processors hold the same addresses at once with different contents. Without this the Fuel and Tezro PROMs don't boot, and the O2 relies on CRIME's side page.
Each CPU reads memory through a window of its own, a copy of ppmem's window. A valid line makes its page private in that CPU's view; the uncached path, DMA and the other CPUs keep seeing memory. Nothing changes on a load, store or fetch, in the decode cache or in compiled code: those bodies are byte for byte main's. Only the CACHE instruction path changes.
Changes
Approximations
Each is stated in code and in
clanker/cpu/cache-lines-live-in-the-cpus-view.md.Testing
halt -pclean, no lines held at login: O2 with the default R5000 (jitv2 83 s, interpreter 167 s), R10000 (90 s) and R12000 (87 s); Octane, 1 CPU (94 s); Indy, R4400 (134 s) and R5000 (141 s); IP28. On the Fuel and Tezro branches with this series swapped in: Fuel to login with ALL DIAGS PASSED, network and rsh; Tezro to the BASEIO monitor with 1 CPU and to login with 2.cargo test --features jitv2 --librelease and debug (3030 passed),cargo test --workspace --no-run --features jitv2at every commit.Performance
Against main,
iris-bench run --quick --deterministic, n=5 per side, interleaved: host instructions within ±0.14% on every build (default, jitv2, jitv2 dispatch off, lightning; R4400 and R10000) with matching guest instruction counts and 40/40 checksums. Cycles and wall time moved both ways by several percent on a host at load 150-220.iris-bench ab, 20 rounds, concurrent, R10000, on a quieter host: lightning suite −1.0% [−2.1%, +0.2%], host instructions −0.1%. Release with dispatch off flagged +4.9% [+3.8%, +5.9%] against main at equal host instructions (+0.1%), and +3.6% [−1.7%, +4.3%] against the previous version of this PR. The same three release binaries pinned to one core,perf stat, 3 rounds each: cycles −2.3% against main and −1.2% against the previous version, with indirect mispredicts +7.7%. I read that as code layout and core placement rather than added work, but it is not settled.Size: the series is +776/−276 model and +441/−127 test lines. The jitv2 and primary instruction commits are +134 and +234, down from +223 and +291.