Improve performance for branchy code
[riscv-isa-sim.git] / riscv / mmu.h
1 // See LICENSE for license details.
2
3 #ifndef _RISCV_MMU_H
4 #define _RISCV_MMU_H
5
6 #include "decode.h"
7 #include "icache.h"
8 #include "trap.h"
9 #include "common.h"
10 #include "config.h"
11 #include "processor.h"
12 #include "memtracer.h"
13 #include <vector>
14
15 // virtual memory configuration
16 typedef reg_t pte_t;
17 const reg_t LEVELS = sizeof(pte_t) == 8 ? 3 : 2;
18 const reg_t PTIDXBITS = 10;
19 const reg_t PGSHIFT = PTIDXBITS + (sizeof(pte_t) == 8 ? 3 : 2);
20 const reg_t PGSIZE = 1 << PGSHIFT;
21 const reg_t VPN_BITS = PTIDXBITS * LEVELS;
22 const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
23 const reg_t VA_BITS = VPN_BITS + PGSHIFT;
24
25 struct insn_fetch_t
26 {
27 insn_func_t func;
28 union {
29 insn_t insn;
30 uint_fast32_t pad;
31 } insn;
32 };
33
34 struct icache_entry_t {
35 reg_t tag;
36 reg_t pad;
37 insn_fetch_t data;
38 };
39
40 // this class implements a processor's port into the virtual memory system.
41 // an MMU and instruction cache are maintained for simulator performance.
42 class mmu_t
43 {
44 public:
45 mmu_t(char* _mem, size_t _memsz);
46 ~mmu_t();
47
48 // template for functions that load an aligned value from memory
49 #define load_func(type) \
50 type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \
51 void* paddr = translate(addr, sizeof(type##_t), false, false); \
52 return *(type##_t*)paddr; \
53 }
54
55 // load value from memory at aligned address; zero extend to register width
56 load_func(uint8)
57 load_func(uint16)
58 load_func(uint32)
59 load_func(uint64)
60
61 // load value from memory at aligned address; sign extend to register width
62 load_func(int8)
63 load_func(int16)
64 load_func(int32)
65 load_func(int64)
66
67 // template for functions that store an aligned value to memory
68 #define store_func(type) \
69 void store_##type(reg_t addr, type##_t val) { \
70 void* paddr = translate(addr, sizeof(type##_t), true, false); \
71 *(type##_t*)paddr = val; \
72 }
73
74 // store value to memory at aligned address
75 store_func(uint8)
76 store_func(uint16)
77 store_func(uint32)
78 store_func(uint64)
79
80 // load instruction from memory at aligned address.
81 inline icache_entry_t access_icache(reg_t addr)
82 {
83 reg_t idx = (addr / sizeof(insn_t)) % ICACHE_SIZE;
84 icache_entry_t entry = icache[idx];
85 if (likely(entry.tag == addr))
86 return entry;
87
88 void* iaddr = translate(addr, sizeof(insn_t), false, true);
89 insn_fetch_t fetch;
90 fetch.insn.pad = *(decltype(fetch.insn.insn.bits())*)iaddr;
91 fetch.func = proc->decode_insn(fetch.insn.insn);
92
93 icache[idx].tag = addr;
94 icache[idx].data = fetch;
95
96 reg_t paddr = (char*)iaddr - mem;
97 if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
98 {
99 icache[idx].tag = -1;
100 tracer.trace(paddr, sizeof(insn_t), false, true);
101 }
102 return icache[idx];
103 }
104
105 inline insn_fetch_t load_insn(reg_t addr)
106 {
107 return access_icache(addr).data;
108 }
109
110 void set_processor(processor_t* p) { proc = p; flush_tlb(); }
111
112 void flush_tlb();
113 void flush_icache();
114
115 void register_memtracer(memtracer_t*);
116
117 private:
118 char* mem;
119 size_t memsz;
120 processor_t* proc;
121 memtracer_list_t tracer;
122
123 // implement an instruction cache for simulator performance
124 icache_entry_t icache[ICACHE_SIZE];
125
126 // implement a TLB for simulator performance
127 static const reg_t TLB_ENTRIES = 256;
128 char* tlb_data[TLB_ENTRIES];
129 reg_t tlb_insn_tag[TLB_ENTRIES];
130 reg_t tlb_load_tag[TLB_ENTRIES];
131 reg_t tlb_store_tag[TLB_ENTRIES];
132
133 // finish translation on a TLB miss and upate the TLB
134 void* refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch);
135
136 // perform a page table walk for a given virtual address
137 pte_t walk(reg_t addr);
138
139 // translate a virtual address to a physical address
140 void* translate(reg_t addr, reg_t bytes, bool store, bool fetch)
141 __attribute__((always_inline))
142 {
143 reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
144 reg_t expected_tag = addr >> PGSHIFT;
145 reg_t* tags = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag;
146 reg_t tag = tags[idx];
147 void* data = tlb_data[idx] + addr;
148
149 if (unlikely(addr & (bytes-1)))
150 store ? throw trap_store_address_misaligned(addr) : throw trap_load_address_misaligned(addr);
151
152 if (likely(tag == expected_tag))
153 return data;
154
155 return refill_tlb(addr, bytes, store, fetch);
156 }
157
158 friend class processor_t;
159 };
160
161 #endif