[xcc] minor performance tweaks
[riscv-isa-sim.git] / riscv / mmu.h
index b107b71055869a7bc18da4611f49cf85294c1e8f..e25e90a0b61d36d8413169357f426b697232240c 100644 (file)
@@ -1,8 +1,11 @@
+#ifndef _RISCV_MMU_H
+#define _RISCV_MMU_H
+
 #include "decode.h"
 #include "trap.h"
 #include "icsim.h"
 #include "common.h"
-#include <assert.h>
+#include "processor.h"
 
 class processor_t;
 
@@ -31,12 +34,8 @@ const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
 class mmu_t
 {
 public:
-  mmu_t(char* _mem, size_t _memsz)
-   : mem(_mem), memsz(_memsz), badvaddr(0),
-     ptbr(0), supervisor(true), vm_enabled(false),
-     icsim(NULL), dcsim(NULL), itlbsim(NULL), dtlbsim(NULL)
-  {
-  }
+  mmu_t(char* _mem, size_t _memsz);
+  ~mmu_t();
 
   #ifdef RISCV_ENABLE_ICSIM
   # define dcsim_tick(dcsim, dtlbsim, addr, size, st) \
@@ -48,50 +47,65 @@ public:
 
   #define load_func(type) \
     type##_t load_##type(reg_t addr) { \
-      check_align(addr, sizeof(type##_t), false, false); \
-      addr = translate(addr, false, false); \
-      dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \
-      return *(type##_t*)(mem+addr); \
+      if(unlikely(addr % sizeof(type##_t))) \
+      { \
+        badvaddr = addr; \
+        throw trap_load_address_misaligned; \
+      } \
+      void* paddr = translate(addr, false, false); \
+      dcsim_tick(dcsim, dtlbsim, paddr-mem, sizeof(type##_t), false); \
+      return *(type##_t*)paddr; \
     }
 
   #define store_func(type) \
     void store_##type(reg_t addr, type##_t val) { \
-      check_align(addr, sizeof(type##_t), true, false); \
-      addr = translate(addr, true, false); \
-      dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \
-      *(type##_t*)(mem+addr) = val; \
+      if(unlikely(addr % sizeof(type##_t))) \
+      { \
+        badvaddr = addr; \
+        throw trap_store_address_misaligned; \
+      } \
+      void* paddr = translate(addr, true, false); \
+      dcsim_tick(dcsim, dtlbsim, paddr-mem, sizeof(type##_t), true); \
+      *(type##_t*)paddr = val; \
     }
 
-  insn_t __attribute__((always_inline)) load_insn(reg_t addr, bool rvc)
+  insn_t __attribute__((always_inline)) load_insn(reg_t addr, bool rvc,
+                                                  insn_func_t* func)
   {
     insn_t insn;
 
-    reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
-    bool hit = addr % 4 == 0 && icache_tag[idx] == (addr | 1);
-    if(likely(hit))
-      return icache_data[idx];
-
     #ifdef RISCV_ENABLE_RVC
-    if(addr % 4 == 2 && rvc)
+    if(addr % 4 == 2 && rvc) // fetch across word boundary
     {
-      reg_t paddr_lo = translate(addr, false, true);
-      insn.bits = *(uint16_t*)(mem+paddr_lo);
+      void* addr_lo = translate(addr, false, true);
+      insn.bits = *(uint16_t*)addr_lo;
+
+      *func = processor_t::dispatch_table
+               [insn.bits % processor_t::DISPATCH_TABLE_SIZE];
 
       if(!INSN_IS_RVC(insn.bits))
       {
-        reg_t paddr_hi = translate(addr+2, false, true);
-        insn.bits |= (uint32_t)*(uint16_t*)(mem+paddr_hi) << 16;
+        void* addr_hi = translate(addr+2, false, true);
+        insn.bits |= (uint32_t)*(uint16_t*)addr_hi << 16;
       }
     }
     else
     #endif
     {
-      check_align(addr, 4, false, true);
-      reg_t paddr = translate(addr, false, true);
-      insn = *(insn_t*)(mem+paddr);
+      reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
+      insn_t data = icache_data[idx];
+      *func = icache_func[idx];
+      if(likely(icache_tag[idx] == addr))
+        return data;
+
+      // the processor guarantees alignment based upon rvc mode
+      void* paddr = translate(addr, false, true);
+      insn = *(insn_t*)paddr;
 
-      icache_tag[idx] = addr | 1;
+      icache_tag[idx] = addr;
       icache_data[idx] = insn;
+      icache_func[idx] = *func = processor_t::dispatch_table
+                                 [insn.bits % processor_t::DISPATCH_TABLE_SIZE];
     }
 
     #ifdef RISCV_ENABLE_ICSIM
@@ -144,11 +158,14 @@ private:
   bool vm_enabled;
 
   static const reg_t TLB_ENTRIES = 256;
-  pte_t tlb_data[TLB_ENTRIES];
-  reg_t tlb_tag[TLB_ENTRIES];
+  long tlb_data[TLB_ENTRIES];
+  reg_t tlb_insn_tag[TLB_ENTRIES];
+  reg_t tlb_load_tag[TLB_ENTRIES];
+  reg_t tlb_store_tag[TLB_ENTRIES];
 
   static const reg_t ICACHE_ENTRIES = 256;
   insn_t icache_data[ICACHE_ENTRIES];
+  insn_func_t icache_func[ICACHE_ENTRIES];
   reg_t icache_tag[ICACHE_ENTRIES];
 
   icsim_t* icsim;
@@ -156,90 +173,22 @@ private:
   icsim_t* itlbsim;
   icsim_t* dtlbsim;
 
-  void check_align(reg_t addr, int size, bool store, bool fetch)
-  {
-    if(unlikely(addr & (size-1)))
-    {
-      badvaddr = addr;
-      if(fetch)
-        throw trap_instruction_address_misaligned;
-      if(store)
-        throw trap_store_address_misaligned;
-      throw trap_load_address_misaligned;
-    }
-  }
+  void* refill(reg_t addr, bool store, bool fetch);
+  pte_t walk(reg_t addr);
 
-  reg_t translate(reg_t addr, bool store, bool fetch)
+  void* translate(reg_t addr, bool store, bool fetch)
   {
     reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
-    pte_t pte = tlb_data[idx];
-    reg_t tag = tlb_tag[idx];
-
-    trap_t trap = store ? trap_store_access_fault
-                : fetch ? trap_instruction_access_fault
-                :         trap_load_access_fault;
-
-    bool hit = (pte & PTE_E) && tag == (addr >> PGSHIFT);
-    if(unlikely(!hit))
-    {
-      pte = walk(addr);
-      if(!(pte & PTE_E))
-        throw trap;
-
-      tlb_data[idx] = pte;
-      tlb_tag[idx] = addr >> PGSHIFT;
-    }
-
-    reg_t access_type = store ? PTE_UW : fetch ? PTE_UX : PTE_UR;
-    if(supervisor)
-      access_type <<= 3;
-    if(unlikely(!(access_type & pte & PTE_PERM)))
-      throw trap;
 
-    return (addr & (PGSIZE-1)) | ((pte >> PTE_PPN_SHIFT) << PGSHIFT);
-  }
+    reg_t* tlb_tag = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag;
+    reg_t expected_tag = addr & ~(PGSIZE-1);
+    if(likely(tlb_tag[idx] == expected_tag))
+      return (void*)(((long)addr & (PGSIZE-1)) | tlb_data[idx]);
 
-  pte_t walk(reg_t addr)
-  {
-    pte_t pte = 0;
-  
-    if(!vm_enabled)
-    {
-      if(addr < memsz)
-        pte = PTE_E | PTE_PERM | ((addr >> PGSHIFT) << PTE_PPN_SHIFT);
-    }
-    else
-    {
-      reg_t base = ptbr;
-      reg_t ptd;
-  
-      int ptshift = (LEVELS-1)*PTIDXBITS;
-      for(reg_t i = 0; i < LEVELS; i++, ptshift -= PTIDXBITS)
-      {
-        reg_t idx = (addr >> (PGSHIFT+ptshift)) & ((1<<PTIDXBITS)-1);
-  
-        reg_t pte_addr = base + idx*sizeof(pte_t);
-        if(pte_addr >= memsz)
-          break;
-  
-        ptd = *(pte_t*)(mem+pte_addr);
-        if(ptd & PTE_E)
-        {
-          // if this PTE is from a larger PT, fake a leaf
-          // PTE so the TLB will work right
-          reg_t vpn = addr >> PGSHIFT;
-          pte |= ptd | (vpn & ((1<<(ptshift))-1)) << PTE_PPN_SHIFT;
-          break;
-        }
-        else if(!(ptd & PTE_T))
-          break;
-  
-        base = (ptd >> PTE_PPN_SHIFT) << PGSHIFT;
-      }
-    }
-  
-    return pte;
+    return refill(addr, store, fetch);
   }
   
   friend class processor_t;
 };
+
+#endif