[sim] initial support for virtual memory
authorAndrew Waterman <waterman@s141.Millennium.Berkeley.EDU>
Sat, 14 May 2011 02:19:53 +0000 (19:19 -0700)
committerAndrew Waterman <waterman@s141.Millennium.Berkeley.EDU>
Sat, 14 May 2011 02:20:24 +0000 (19:20 -0700)
riscv/decode.h
riscv/mmu.h
riscv/processor.cc

index 63e71cd98193354f735179620861c5b2c3705ded..9d8960bd387ea45a081bd5a731f930da55a1694f 100644 (file)
@@ -42,7 +42,8 @@ const int JUMP_ALIGN_BITS = 1;
 #define SR_UX    0x0000000000000040ULL
 #define SR_SX    0x0000000000000080ULL
 #define SR_IM    0x000000000000FF00ULL
-#define SR_ZERO  ~(SR_ET|SR_EF|SR_EV|SR_EC|SR_PS|SR_S|SR_UX|SR_SX|SR_IM)
+#define SR_VM    0x0000000000010000ULL
+#define SR_ZERO  ~(SR_ET|SR_EF|SR_EV|SR_EC|SR_PS|SR_S|SR_UX|SR_SX|SR_IM|SR_VM)
 #define SR_IM_SHIFT 8
 #define TIMER_IRQ 7
 
index 12778562ca6477f4e35d5ac6f51bd64293587598..2f897ff07740d51c3aad580f1826a77521a2dd19 100644 (file)
@@ -1,14 +1,37 @@
 #include "decode.h"
 #include "trap.h"
 #include "icsim.h"
+#include <assert.h>
 
 class processor_t;
 
+const reg_t LEVELS = 4;
+const reg_t PGSHIFT = 12;
+const reg_t PGSIZE = 1 << PGSHIFT;
+const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
+
+struct pte_t
+{
+  reg_t v  : 1;
+  reg_t e  : 1;
+  reg_t r  : 1;
+  reg_t d  : 1;
+  reg_t ux : 1;
+  reg_t ur : 1;
+  reg_t uw : 1;
+  reg_t sx : 1;
+  reg_t sr : 1;
+  reg_t sw : 1;
+  reg_t unused1 : 2;
+  reg_t ppn : PPN_BITS;
+};
+
 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)
   {
   }
@@ -28,27 +51,40 @@ public:
 
   #define load_func(type) \
     type##_t load_##type(reg_t addr) { \
-      check_align_and_bounds(addr, sizeof(type##_t), false, false); \
+      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); \
     }
 
   #define store_func(type) \
     void store_##type(reg_t addr, type##_t val) { \
-      check_align_and_bounds(addr, sizeof(type##_t), true, false); \
+      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; \
     }
 
   insn_t load_insn(reg_t addr, bool rvc)
   {
+    insn_t insn;
+
     #ifdef RISCV_ENABLE_RVC
-    check_align_and_bounds(addr, rvc ? 2 : 4, false, true);
-    uint16_t lo = *(uint16_t*)(mem+addr);
-    uint16_t hi = *(uint16_t*)(mem+addr+2);
+    check_align(addr, rvc ? 2 : 4, false, true);
 
-    insn_t insn; 
-    insn.bits = lo | ((uint32_t)hi << 16);
+    reg_t paddr_lo = translate(addr, false, true);
+    insn.bits = *(uint16_t*)(mem+paddr_lo);
+
+    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;
+    }
+    #else
+    check_align(addr, 4, false, true);
+    reg_t paddr = translate(addr, false, true);
+    insn = *(insn_t*)(mem+paddr);
+    #endif
 
     #ifdef RISCV_ENABLE_ICSIM
     if(icsim)
@@ -58,10 +94,6 @@ public:
     #endif
 
     return insn;
-    #else
-    check_align_and_bounds(addr, 4, false, true);
-    return *(insn_t*)(mem+addr);
-    #endif
   }
 
   load_func(uint8)
@@ -80,12 +112,23 @@ public:
   store_func(uint64)
 
   reg_t get_badvaddr() { return badvaddr; }
+  void set_supervisor(bool sup) { supervisor = sup; }
+  void set_vm_enabled(bool en) { vm_enabled = en; }
+  void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); }
 
 private:
   char* mem;
   size_t memsz;
   reg_t badvaddr;
 
+  reg_t ptbr;
+  bool supervisor;
+  bool vm_enabled;
+
+  static const reg_t TLB_ENTRIES = 32;
+  pte_t tlb_data[TLB_ENTRIES];
+  reg_t tlb_tag[TLB_ENTRIES];
+
   icsim_t* icsim;
   icsim_t* dcsim;
   icsim_t* itlbsim;
@@ -104,6 +147,78 @@ private:
     }
   }
 
+  reg_t 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;
+
+    if(!pte.v || tag != (addr >> PGSHIFT))
+    {
+      pte = walk(addr);
+      if(!pte.v)
+        throw trap;
+
+      tlb_data[idx] = pte;
+      tlb_tag[idx] = addr >> PGSHIFT;
+    }
+
+    if(store && !(supervisor ? pte.sw : pte.uw) ||
+       !store && !fetch && !(supervisor ? pte.sr : pte.ur) ||
+       !store && !fetch && !(supervisor ? pte.sr : pte.ur))
+      throw trap;
+
+    return (addr % PGSIZE) | (pte.ppn << PGSHIFT);
+  }
+
+  pte_t walk(reg_t addr)
+  {
+    pte_t pte;
+
+    if(!vm_enabled)
+    {
+      pte.v = addr < memsz;
+      pte.e = 1;
+      pte.r = pte.d = 0;
+      pte.ur = pte.uw = pte.ux = pte.sr = pte.sw = pte.sx = 1;
+      pte.ppn = addr >> PGSHIFT;
+    }
+    else
+    {
+      pte.v = 0;
+
+      int lg_ptesz = sizeof(pte_t) == 4 ? 2
+                   : sizeof(pte_t) == 8 ? 3
+                   : 0;
+      assert(lg_ptesz);
+
+      reg_t base = ptbr;
+
+      for(int i = LEVELS-1; i >= 0; i++)
+      {
+        reg_t idx = addr >> (PGSHIFT + i*(PGSHIFT - lg_ptesz));
+        idx &= (1<<(PGSHIFT - lg_ptesz)) - 1;
+
+        reg_t pte_addr = base + idx*sizeof(pte_t);
+        if(pte_addr >= memsz)
+          break;
+
+        pte = *(pte_t*)(mem+pte_addr);
+        if(!pte.v || pte.e)
+          break;
+
+        base = pte.ppn << PGSHIFT;
+      }
+      pte.v &= pte.e;
+    }
+
+    return pte;
+  }
+
   void check_bounds(reg_t addr, int size, bool store, bool fetch)
   {
     if(addr >= memsz || addr + size > memsz)
@@ -114,12 +229,6 @@ private:
       throw store ? trap_store_access_fault : trap_load_access_fault;
     }
   }
-
-  void check_align_and_bounds(reg_t addr, int size, bool store, bool fetch)
-  {
-    check_align(addr, size, store, fetch);
-    check_bounds(addr, size, store, fetch);
-  }
   
   friend class processor_t;
 };
index 4494c5953df8941a8abc42caea5747fd9af4216e..118d4d8592b033bc5d4a0724626f38ad555a9c29 100644 (file)
@@ -121,6 +121,9 @@ void processor_t::set_sr(uint32_t val)
   sr &= ~SR_EV;
 #endif
 
+  mmu.set_vm_enabled(sr & SR_VM);
+  mmu.set_supervisor(sr & SR_S);
+
   xprlen = ((sr & SR_S) ? (sr & SR_SX) : (sr & SR_UX)) ? 64 : 32;
 }