Conversation
There was a problem hiding this comment.
🟡 Changes recommended
There are correctness issues in --numa-high-bit handling for start_index_bit, and the L3Exclusive eviction path can drop dirty data (plus a confirmed argparse default type hazard now affecting new TCC bank usage).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds an optional, configurable memory-side GPU L3 (Infinity Cache–like) behavior to the MOESI_AMD_Base GPU directory path used by GPU_VIPER, and extends GPU_VIPER configurability for directory/TCC/L3 banking and address-bit placement.
Changes:
- Add GPU L3 retention vs. consume behavior (inclusive vs. experimental exclusive) in
MOESI_AMD_Base-dir.sm, plus L3 fill behavior on relevant completion paths. - Add GPU_VIPER CLI options to enable GPU L3 and exclusive mode, and make TCC/L3 per-slice bank counts independently configurable.
- Adjust L3 indexing bit placement intent by explicitly setting
start_index_bitin GPU_VIPER directory construction.
File summaries
| File | Description |
|---|---|
| src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm | Implements GPU L3 retention/fill policies and adds L3Exclusive handling hooks in the directory SLICC machine. |
| configs/ruby/GPU_VIPER.py | Adds GPU L3 config flags, separates bank-count configurability, and sets L3 start_index_bit during directory construction. |
Review details
Suppressed comments (3)
configs/ruby/GPU_VIPER.py:605
options.numa_high_bitchanges which bits are used for directory interleaving (intlvHighBit=numa_bit). SettingL3CacheMemory.start_index_bittoblock_size_bits + dir_bitscan overlap with/underlap the actual directory-selection bits when--numa-high-bitis provided. To ensure the L3 set-index bits are always above the directory-selection bits, derive this fromnuma_bit(the current interleave high bit) instead.
dir_cntrl = DirCntrl(noTCCdir=True, TCC_select_num_bits=TCC_bits)
dir_cntrl.create(options, dir_ranges, ruby_system, system)
dir_cntrl.L3CacheMemory.start_index_bit = block_size_bits + dir_bits
dir_cntrl.number_of_TBEs = options.num_tbes
configs/ruby/GPU_VIPER.py:675
- Same issue as the CPU-side directories:
start_index_bitshould be derived from the interleave high bit (numa_bit) so the L3 set-index bits remain above the directory-selection bits regardless of how the address is interleaved.
dir_cntrl.create(options, [addr_range], ruby_system, system,
num_dirs=options.dgpu_num_dirs)
dir_cntrl.L3CacheMemory.start_index_bit = block_size_bits + dir_bits
dir_cntrl.number_of_TBEs = options.num_tbes
src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm:1297
- Same eviction-path issue as above: the silent-drop path should be limited to the non-exclusive GPU L3 policy. In
L3Exclusivemode, evicted victim-cache entries may be dirty and need a writeback.
if (L3CacheMemory.cacheAvail(address) == false) {
Addr victim := L3CacheMemory.cacheProbe(address);
if (GPUonly) {
// GPU L3 (Infinity Cache) is read-only — evicted entries are
// always clean, so silent drop is correct.
- Files reviewed: 2/2 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (L3CacheMemory.cacheAvail(address) == false) { | ||
| Addr victim := L3CacheMemory.cacheProbe(address); | ||
| CacheEntry victim_entry := static_cast(CacheEntry, "pointer", | ||
| L3CacheMemory.lookup(victim)); | ||
| enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) { | ||
| out_msg.addr := victim; | ||
| out_msg.Type := MemoryRequestType:MEMORY_WB; | ||
| out_msg.Sender := machineID; | ||
| out_msg.MessageSize := MessageSizeType:Writeback_Data; | ||
| out_msg.DataBlk := victim_entry.DataBlk; | ||
| if (GPUonly) { | ||
| // GPU L3 (Infinity Cache) is read-only — evicted entries are | ||
| // always clean, so silent drop is correct. |
There was a problem hiding this comment.
VicDirty runs d_writeDataToMemory before al_allocateL3Block. The dirty data reaches memory before entering L3, so the resulting L3 copy is clean. Silent GPU-L3 eviction is safe in both policies.
There was a problem hiding this comment.
how do you guarantee it reaches memory before entering the L3? Is there an ACK you wait on?
There was a problem hiding this comment.
There is no ACK, but I don't think its needed (?) because the writeback is issued before any later memory access to that address. L3 holds only a clean replica, it never becomes the sole owner of dirty data.
1eaa94d to
16742eb
Compare
16742eb to
387a0a7
Compare
Add a configurable GPU L3 cache to the GPU-only MOESI_AMD_Base directory and support both inclusive and exclusive allocation policies. Keep the feature disabled by default so existing GPU_VIPER configurations retain their current behavior. Handle L3 hits, fills, writebacks, atomics, DMA reads, and probe responses without leaving stale copies behind. Preserve inclusive L3 entries on ordinary GPU and DMA reads, invalidate or update an L3 entry when a dirty TCC probe supplies newer data, and retain the exclusive mode as an explicit option. Also compute directory and block index widths before processing --numa-high-bit so configurations using an explicit NUMA bit can construct the L3 cache safely.
387a0a7 to
114ae95
Compare
|
@abmerop this is the patch we discussed earlier |
mattsinc
left a comment
There was a problem hiding this comment.
@v-ramadas : I believe we'd found that the L2 should be WB, but I don't know if we found the same for the L3 (yet) or not? It is a little weird to me that the L2 would be WB but the L3 WT ... but @Basemism is indicating the documentation says to do it that way. Thoughts?
| self.size = MemorySize(size) | ||
| self.assoc = assoc | ||
| self.replacement_policy = TreePLRURP() | ||
| self.replacement_policy = BRRIPRP() |
There was a problem hiding this comment.
should we update this to use the variant Jarvis integrated into the mainline where the config tells us what RP to use? Then we can just pick BRRIP from there, and it's much more extensible ...
| 256 / options.num_tccs | ||
| ) # number of data banks | ||
| self.tagArrayBanks = 256 / options.num_tccs # number of tag banks | ||
| self.dataArrayBanks = options.tcc_num_banks # number of data banks |
There was a problem hiding this comment.
@v-ramadas were you relying on the L2 banking policy not changing in any of your changes?
@Basemism since this doesn't seem to have anything to do with adding an L3, this should really be a separate PR.
There was a problem hiding this comment.
Agreed. The TCC banking changes are not required for the L3. Ill prepare a pr for this
There was a problem hiding this comment.
My changes don't really rely on the number of banks. I think this doesn't affect me.
| tagArrayBanks = 16 | ||
|
|
||
| def create(self, options, ruby_system, system): | ||
| def create(self, options, ruby_system, system, num_dirs=None): |
There was a problem hiding this comment.
most of the changes here also seem unrelated to adding an L3?
There was a problem hiding this comment.
Agreed. The TCC banking changes are not required for the L3. Ill prepare a pr for this
| self.tagAccessLatency = options.l3_tag_latency | ||
| self.resourceStalls = False | ||
| self.replacement_policy = TreePLRURP() | ||
| self.replacement_policy = BRRIPRP() |
There was a problem hiding this comment.
same thing here -- we should use Jarvis' configurable RP code (like @Basemism you did for the ScalarCache) to make this easier to play around with.
| dir_cntrl.number_of_TBEs = options.num_tbes | ||
| dir_cntrl.useL3OnWT = False | ||
| dir_cntrl.GPUonly = True | ||
| dir_cntrl.useL3OnWT = options.use_gpu_l3 |
There was a problem hiding this comment.
@Basemism do you support a WB L3 in this patch? If not, there probably needs to be a warning here.
There was a problem hiding this comment.
Not currently, I'll prepare a warning shortly
| tbe.MemData := true; | ||
| L3CacheMemory.deallocate(address); | ||
| if (GPUonly && !L3Exclusive) { | ||
| // CDNA 3 Infinity Cache is a non-destructive memory-side cache, |
There was a problem hiding this comment.
this comment doesn't seem to have anything to do with setting MRU?
There was a problem hiding this comment.
The comment describes the retain-on-hit policy, not setMRU() itself
| } | ||
| } | ||
| } | ||
| out_msg.addr := address; |
There was a problem hiding this comment.
it's hard for me to tell, but it seems like a space was accidentally removed here?
There was a problem hiding this comment.
Yes, its whitespace... I’ll restore it.
| out_msg.MessageSize := MessageSizeType:Writeback_Data; | ||
| out_msg.DataBlk := victim_entry.DataBlk; | ||
| if (GPUonly) { | ||
| // GPU L3 (Infinity Cache) is read-only — evicted entries are |
There was a problem hiding this comment.
where did you find that it's read only?
There was a problem hiding this comment.
"The AMD Infinity Cache is an entirely new and massive structure for the AMD CDNA 3 architecture that
boosts generational performance and efficiency by increasing cache bandwidth and reducing the number
of off-chip memory accesses. Typically, GPU caches are more closely aligned with and physically co-located
with memory controllers and that is especially true for the AMD CDNA 3 architecture. The AMD Infinity Cache
was carefully designed as a shared memory-side cache, meaning that it caches the contents of memory and
cannot hold dirty data evicted from a lower level cache. This has two significant benefits. First, the AMD
Infinity Cache doesn’t participate in coherency and does not have to absorb or handle any snoop traffic,
which significantly improves efficiency and reduces the latency of snooping from lower level caches. Second,
it can actually hold nominally uncacheable memory such as buffers for I/O."
|
@Basemism also we'll have to figure out how to integrate the notion of scopes into your L3 code. @abmerop I know you have a patch on these we need to integrate. About read-only L3, I'm a bit confused though, because Table 49 (https://www.amd.com/content/dam/amd/en/documents/instinct-tech-docs/instruction-set-architectures/amd-instinct-mi300-cdna3-instruction-set-architecture.pdf) seems to show that SC1/SC0 writes should hit and be kept in the L3, which clashes with the whitepaper @Basemism points to... |
56d812f to
0405770
Compare
0405770 to
90e2138
Compare
The whitepaper says "The AMD Infinity Cache |
| // Write memory for data that must be persisted: | ||
| // WT/atomic updates, or dirty data returned by probes. | ||
| // Clean read data does not need writeback — memory already has it. | ||
| if (tbe.wtData || tbe.atomicData || tbe.Dirty) { |
There was a problem hiding this comment.
Is this an unrelated bug? For example, if !GPUonly it should still do this for dirty TBEs?
There was a problem hiding this comment.
The old condition wrote clean data back to memory, but skipped dirty data. I think this is a general bug and is shared, and not GPU-only.
| } | ||
|
|
||
| transition(BDW_M, MemData, U) { | ||
| transition(BDR_M, L3Hit, U) {L3TagArrayWrite, L3DataArrayWrite} { |
There was a problem hiding this comment.
Is this a missing transition, or is it require for this new support? Same with (BDW_M, L3Hit, U) and (BL2_M, L3Hit, U)
There was a problem hiding this comment.
They were missing + required. I ran into these missing transitions while testing.
Add an optional memory-side GPU L3 to the GPU directories in
MOESI_AMD_Base. The feature is disabled by default through --use-gpu-l3The default inclusive policy:
This policy is inferred from the Infinity Cache organization described on page 10 of the AMD CDNA 3 Architecture white paper (https://www.amd.com/content/dam/amd/en/documents/instinct-tech-docs/white-papers/amd-cdna-3-white-paper.pdf)
--l3-exclusive retains an experimental victim-cache policy for configurations that need an exclusive hierarchy.
Also make TCC bank counts and per-slice L3 bank counts independently configurable through
--tcc-num-banksand--l3-num-banks. Place the L3 set-index bits above the directory-selection bits and fix construction when--numa-high-bitis supplied.