Speed things up quite a bit
[riscv-isa-sim.git] / riscv / mmu.h
index 227d5c700ecd6edc6c2d54ac81a382b0e51cad00..551fa462d43a8258b1abe448e346dac5cb99d3e0 100644 (file)
@@ -31,11 +31,11 @@ public:
 
   // template for functions that load an aligned value from memory
   #define load_func(type) \
-    type##_t load_##type(reg_t addr) { \
+    type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \
       if(unlikely(addr % sizeof(type##_t))) \
         throw trap_load_address_misaligned(addr); \
-      reg_t paddr = translate(addr, sizeof(type##_t), false, false); \
-      return *(type##_t*)(mem + paddr); \
+      void* paddr = translate(addr, sizeof(type##_t), false, false); \
+      return *(type##_t*)paddr; \
     }
 
   // load value from memory at aligned address; zero extend to register width
@@ -55,8 +55,8 @@ public:
     void store_##type(reg_t addr, type##_t val) { \
       if(unlikely(addr % sizeof(type##_t))) \
         throw trap_store_address_misaligned(addr); \
-      reg_t paddr = translate(addr, sizeof(type##_t), true, false); \
-      *(type##_t*)(mem + paddr) = val; \
+      void* paddr = translate(addr, sizeof(type##_t), true, false); \
+      *(type##_t*)paddr = val; \
     }
 
   // store value to memory at aligned address
@@ -77,25 +77,28 @@ public:
   // load instruction from memory at aligned address.
   inline insn_fetch_t load_insn(reg_t addr)
   {
-    reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
-    if (unlikely(icache_tag[idx] != addr))
+    reg_t offset = addr & (sizeof(insn_t) * (ICACHE_ENTRIES-1));
+    offset *= sizeof(icache_entry_t) / sizeof(insn_t);
+    icache_entry_t* entry = (icache_entry_t*)((char*)icache + offset);
+    insn_fetch_t data = entry->data;
+    if (likely(entry->tag == addr))
+      return data;
+
+    void* iaddr = translate(addr, sizeof(insn_t), false, true);
+    insn_fetch_t fetch;
+    fetch.insn.pad = *(decltype(fetch.insn.insn.bits())*)iaddr;
+    fetch.func = proc->decode_insn(fetch.insn.insn);
+
+    entry->tag = addr;
+    entry->data = fetch;
+
+    reg_t paddr = (char*)iaddr - mem;
+    if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
     {
-      reg_t paddr = translate(addr, sizeof(insn_t), false, true);
-      insn_fetch_t fetch;
-      fetch.insn.insn = *(insn_t*)(mem + paddr);
-      fetch.func = proc->decode_insn(fetch.insn.insn);
-
-      reg_t idx = (paddr/sizeof(insn_t)) % ICACHE_ENTRIES;
-      icache_tag[idx] = addr;
-      icache_data[idx] = fetch;
-
-      if (tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
-      {
-        icache_tag[idx] = -1;
-        tracer.trace(paddr, sizeof(insn_t), false, true);
-      }
+      entry->tag = -1;
+      tracer.trace(paddr, sizeof(insn_t), false, true);
     }
-    return icache_data[idx];
+    return entry->data;
   }
 
   void set_processor(processor_t* p) { proc = p; flush_tlb(); }
@@ -112,32 +115,38 @@ private:
   memtracer_list_t tracer;
 
   // implement an instruction cache for simulator performance
-  static const reg_t ICACHE_ENTRIES = 256;
-  insn_fetch_t icache_data[ICACHE_ENTRIES];
+  static const reg_t ICACHE_ENTRIES = 2048;
+  struct icache_entry_t {
+    reg_t tag;
+    reg_t pad;
+    insn_fetch_t data;
+  };
+  icache_entry_t icache[ICACHE_ENTRIES];
 
   // implement a TLB for simulator performance
   static const reg_t TLB_ENTRIES = 256;
-  reg_t tlb_data[TLB_ENTRIES];
+  char* 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];
-  reg_t icache_tag[ICACHE_ENTRIES];
 
   // finish translation on a TLB miss and upate the TLB
-  reg_t refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch);
+  void* refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch);
 
   // perform a page table walk for a given virtual address
   pte_t walk(reg_t addr);
 
   // translate a virtual address to a physical address
-  reg_t translate(reg_t addr, reg_t bytes, bool store, bool fetch)
+  void* translate(reg_t addr, reg_t bytes, bool store, bool fetch)
+    __attribute__((always_inline))
   {
     reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
+    reg_t expected_tag = addr & ~(PGSIZE-1);
 
     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 ((uintptr_t)addr & (PGSIZE-1)) + tlb_data[idx];
+    void* data = tlb_data[idx] + addr;
+    if (likely(tlb_tag[idx] == expected_tag))
+      return data;
 
     return refill_tlb(addr, bytes, store, fetch);
   }