55#include "riscv.h"
66#include "riscv_private.h"
77
8+ #if !defined(__GNUC__ ) && !defined(__clang__ )
9+ /* Portable parity implementation for non-GCC/Clang compilers */
10+ static inline unsigned int __builtin_parity (unsigned int x )
11+ {
12+ x ^= x >> 16 ;
13+ x ^= x >> 8 ;
14+ x ^= x >> 4 ;
15+ x ^= x >> 2 ;
16+ x ^= x >> 1 ;
17+ return x & 1 ;
18+ }
19+ #endif
20+
821/* Return the string representation of an error code identifier */
922static const char * vm_error_str (vm_error_t err )
1023{
@@ -169,9 +182,12 @@ static inline uint32_t read_rs2(const hart_t *vm, uint32_t insn)
169182
170183/* virtual addressing */
171184
172- static void mmu_invalidate (hart_t * vm )
185+ void mmu_invalidate (hart_t * vm )
173186{
174187 vm -> cache_fetch .n_pages = 0xFFFFFFFF ;
188+ vm -> cache_load [0 ].n_pages = 0xFFFFFFFF ;
189+ vm -> cache_load [1 ].n_pages = 0xFFFFFFFF ;
190+ vm -> cache_store .n_pages = 0xFFFFFFFF ;
175191}
176192
177193/* Pre-verify the root page table to minimize page table access during
@@ -284,6 +300,9 @@ static void mmu_fetch(hart_t *vm, uint32_t addr, uint32_t *value)
284300{
285301 uint32_t vpn = addr >> RV_PAGE_SHIFT ;
286302 if (unlikely (vpn != vm -> cache_fetch .n_pages )) {
303+ #ifdef MMU_CACHE_STATS
304+ vm -> cache_fetch .misses ++ ;
305+ #endif
287306 mmu_translate (vm , & addr , (1 << 3 ), (1 << 6 ), false, RV_EXC_FETCH_FAULT ,
288307 RV_EXC_FETCH_PFAULT );
289308 if (vm -> error )
@@ -295,6 +314,11 @@ static void mmu_fetch(hart_t *vm, uint32_t addr, uint32_t *value)
295314 vm -> cache_fetch .n_pages = vpn ;
296315 vm -> cache_fetch .page_addr = page_addr ;
297316 }
317+ #ifdef MMU_CACHE_STATS
318+ else {
319+ vm -> cache_fetch .hits ++ ;
320+ }
321+ #endif
298322 * value = vm -> cache_fetch .page_addr [(addr >> 2 ) & MASK (RV_PAGE_SHIFT - 2 )];
299323}
300324
@@ -304,17 +328,41 @@ static void mmu_load(hart_t *vm,
304328 uint32_t * value ,
305329 bool reserved )
306330{
307- mmu_translate (vm , & addr , (1 << 1 ) | (vm -> sstatus_mxr ? (1 << 3 ) : 0 ),
308- (1 << 6 ), vm -> sstatus_sum && vm -> s_mode , RV_EXC_LOAD_FAULT ,
309- RV_EXC_LOAD_PFAULT );
310- if (vm -> error )
311- return ;
312- vm -> mem_load (vm , addr , width , value );
331+ uint32_t vpn = addr >> RV_PAGE_SHIFT ;
332+ uint32_t phys_addr ;
333+ /* 2-entry direct-mapped cache: use parity hash to select entry */
334+ uint32_t index = __builtin_parity (vpn ) & 0x1 ;
335+
336+ if (unlikely (vpn != vm -> cache_load [index ].n_pages )) {
337+ /* Cache miss: do full translation */
338+ #ifdef MMU_CACHE_STATS
339+ vm -> cache_load [index ].misses ++ ;
340+ #endif
341+ phys_addr = addr ;
342+ mmu_translate (vm , & phys_addr ,
343+ (1 << 1 ) | (vm -> sstatus_mxr ? (1 << 3 ) : 0 ), (1 << 6 ),
344+ vm -> sstatus_sum && vm -> s_mode , RV_EXC_LOAD_FAULT ,
345+ RV_EXC_LOAD_PFAULT );
346+ if (vm -> error )
347+ return ;
348+ /* Cache physical page number (not a pointer) */
349+ vm -> cache_load [index ].n_pages = vpn ;
350+ vm -> cache_load [index ].phys_ppn = phys_addr >> RV_PAGE_SHIFT ;
351+ } else {
352+ /* Cache hit: reconstruct physical address from cached PPN */
353+ #ifdef MMU_CACHE_STATS
354+ vm -> cache_load [index ].hits ++ ;
355+ #endif
356+ phys_addr = (vm -> cache_load [index ].phys_ppn << RV_PAGE_SHIFT ) |
357+ (addr & MASK (RV_PAGE_SHIFT ));
358+ }
359+
360+ vm -> mem_load (vm , phys_addr , width , value );
313361 if (vm -> error )
314362 return ;
315363
316364 if (unlikely (reserved ))
317- vm -> lr_reservation = addr | 1 ;
365+ vm -> lr_reservation = phys_addr | 1 ;
318366}
319367
320368static bool mmu_store (hart_t * vm ,
@@ -323,23 +371,43 @@ static bool mmu_store(hart_t *vm,
323371 uint32_t value ,
324372 bool cond )
325373{
326- mmu_translate (vm , & addr , (1 << 2 ), (1 << 6 ) | (1 << 7 ),
327- vm -> sstatus_sum && vm -> s_mode , RV_EXC_STORE_FAULT ,
328- RV_EXC_STORE_PFAULT );
329- if (vm -> error )
330- return false;
374+ uint32_t vpn = addr >> RV_PAGE_SHIFT ;
375+ uint32_t phys_addr ;
376+
377+ if (unlikely (vpn != vm -> cache_store .n_pages )) {
378+ /* Cache miss: do full translation */
379+ #ifdef MMU_CACHE_STATS
380+ vm -> cache_store .misses ++ ;
381+ #endif
382+ phys_addr = addr ;
383+ mmu_translate (vm , & phys_addr , (1 << 2 ), (1 << 6 ) | (1 << 7 ),
384+ vm -> sstatus_sum && vm -> s_mode , RV_EXC_STORE_FAULT ,
385+ RV_EXC_STORE_PFAULT );
386+ if (vm -> error )
387+ return false;
388+ /* Cache physical page number (not a pointer) */
389+ vm -> cache_store .n_pages = vpn ;
390+ vm -> cache_store .phys_ppn = phys_addr >> RV_PAGE_SHIFT ;
391+ } else {
392+ /* Cache hit: reconstruct physical address from cached PPN */
393+ #ifdef MMU_CACHE_STATS
394+ vm -> cache_store .hits ++ ;
395+ #endif
396+ phys_addr = (vm -> cache_store .phys_ppn << RV_PAGE_SHIFT ) |
397+ (addr & MASK (RV_PAGE_SHIFT ));
398+ }
331399
332400 if (unlikely (cond )) {
333- if ((vm -> lr_reservation != (addr | 1 )))
401+ if ((vm -> lr_reservation != (phys_addr | 1 )))
334402 return false;
335403 }
336404
337405 for (uint32_t i = 0 ; i < vm -> vm -> n_hart ; i ++ ) {
338406 if (unlikely (vm -> vm -> hart [i ]-> lr_reservation & 1 ) &&
339- (vm -> vm -> hart [i ]-> lr_reservation & ~3 ) == (addr & ~3 ))
407+ (vm -> vm -> hart [i ]-> lr_reservation & ~3 ) == (phys_addr & ~3 ))
340408 vm -> vm -> hart [i ]-> lr_reservation = 0 ;
341409 }
342- vm -> mem_store (vm , addr , width , value );
410+ vm -> mem_store (vm , phys_addr , width , value );
343411 return true;
344412}
345413
@@ -513,13 +581,19 @@ static void csr_write(hart_t *vm, uint16_t addr, uint32_t value)
513581 }
514582
515583 switch (addr ) {
516- case RV_CSR_SSTATUS :
584+ case RV_CSR_SSTATUS : {
585+ bool old_sum = vm -> sstatus_sum ;
586+ bool old_mxr = vm -> sstatus_mxr ;
517587 vm -> sstatus_sie = (value & (1 << (1 ))) != 0 ;
518588 vm -> sstatus_spie = (value & (1 << (5 ))) != 0 ;
519589 vm -> sstatus_spp = (value & (1 << (8 ))) != 0 ;
520590 vm -> sstatus_sum = (value & (1 << (18 ))) != 0 ;
521591 vm -> sstatus_mxr = (value & (1 << (19 ))) != 0 ;
592+ /* Invalidate load/store TLB if SUM or MXR changed */
593+ if (vm -> sstatus_sum != old_sum || vm -> sstatus_mxr != old_mxr )
594+ mmu_invalidate (vm );
522595 break ;
596+ }
523597 case RV_CSR_SIE :
524598 value &= SIE_MASK ;
525599 vm -> sie = value ;
0 commit comments