[fesvr,xcc,sim] fixed multicore sim for akaros
[riscv-isa-sim.git] / riscv / mmu.h
1 #include "decode.h"
2 #include "trap.h"
3 #include "icsim.h"
4 #include <assert.h>
5
6 class processor_t;
7
8 const reg_t LEVELS = 4;
9 const reg_t PGSHIFT = 12;
10 const reg_t PGSIZE = 1 << PGSHIFT;
11 const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
12
13 struct pte_t
14 {
15 reg_t t : 1;
16 reg_t e : 1;
17 reg_t r : 1;
18 reg_t d : 1;
19 reg_t ux : 1;
20 reg_t uw : 1;
21 reg_t ur : 1;
22 reg_t sx : 1;
23 reg_t sw : 1;
24 reg_t sr : 1;
25 reg_t unused1 : 2;
26 reg_t ppn : PPN_BITS;
27 };
28
29 class mmu_t
30 {
31 public:
32 mmu_t(char* _mem, size_t _memsz)
33 : mem(_mem), memsz(_memsz), badvaddr(0),
34 ptbr(0), supervisor(true), vm_enabled(false),
35 icsim(NULL), dcsim(NULL), itlbsim(NULL), dtlbsim(NULL)
36 {
37 }
38
39 #ifdef RISCV_ENABLE_ICSIM
40 # define dcsim_tick(dcsim, dtlbsim, addr, size, st) \
41 do { if(dcsim) (dcsim)->tick(addr, size, st); \
42 if(dtlbsim) (dtlbsim)->tick(addr, sizeof(reg_t), false); } while(0)
43 #else
44 # define dcsim_tick(dcsim, dtlbsim, addr, size, st)
45 #endif
46
47 #define load_func(type) \
48 type##_t load_##type(reg_t addr) { \
49 check_align(addr, sizeof(type##_t), false, false); \
50 addr = translate(addr, false, false); \
51 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \
52 return *(type##_t*)(mem+addr); \
53 }
54
55 #define store_func(type) \
56 void store_##type(reg_t addr, type##_t val) { \
57 check_align(addr, sizeof(type##_t), true, false); \
58 addr = translate(addr, true, false); \
59 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \
60 *(type##_t*)(mem+addr) = val; \
61 }
62
63 insn_t load_insn(reg_t addr, bool rvc)
64 {
65 insn_t insn;
66
67 reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
68 if(addr % 4 == 0 && icache_tag[idx] == (addr | 1))
69 return icache_data[idx];
70
71 #ifdef RISCV_ENABLE_RVC
72 if(addr % 4 == 2 && rvc)
73 {
74 reg_t paddr_lo = translate(addr, false, true);
75 insn.bits = *(uint16_t*)(mem+paddr_lo);
76
77 if(!INSN_IS_RVC(insn.bits))
78 {
79 reg_t paddr_hi = translate(addr+2, false, true);
80 insn.bits |= (uint32_t)*(uint16_t*)(mem+paddr_hi) << 16;
81 }
82 }
83 else
84 #endif
85 {
86 check_align(addr, 4, false, true);
87 reg_t paddr = translate(addr, false, true);
88 insn = *(insn_t*)(mem+paddr);
89
90 icache_tag[idx] = addr | 1;
91 icache_data[idx] = insn;
92 }
93
94 #ifdef RISCV_ENABLE_ICSIM
95 if(icsim)
96 icsim->tick(addr, insn_length(insn), false);
97 if(itlbsim)
98 itlbsim->tick(addr, sizeof(reg_t), false);
99 #endif
100
101 return insn;
102 }
103
104 load_func(uint8)
105 load_func(uint16)
106 load_func(uint32)
107 load_func(uint64)
108
109 load_func(int8)
110 load_func(int16)
111 load_func(int32)
112 load_func(int64)
113
114 store_func(uint8)
115 store_func(uint16)
116 store_func(uint32)
117 store_func(uint64)
118
119 reg_t get_badvaddr() { return badvaddr; }
120 reg_t get_ptbr() { return ptbr; }
121
122 void set_supervisor(bool sup) { supervisor = sup; }
123 void set_vm_enabled(bool en) { vm_enabled = en; }
124 void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); flush_tlb(); }
125
126 void set_icsim(icsim_t* _icsim) { icsim = _icsim; }
127 void set_dcsim(icsim_t* _dcsim) { dcsim = _dcsim; }
128 void set_itlbsim(icsim_t* _itlbsim) { itlbsim = _itlbsim; }
129 void set_dtlbsim(icsim_t* _dtlbsim) { dtlbsim = _dtlbsim; }
130
131 void flush_tlb();
132 void flush_icache();
133
134 private:
135 char* mem;
136 size_t memsz;
137 reg_t badvaddr;
138
139 reg_t ptbr;
140 bool supervisor;
141 bool vm_enabled;
142
143 static const reg_t TLB_ENTRIES = 32;
144 pte_t tlb_data[TLB_ENTRIES];
145 reg_t tlb_tag[TLB_ENTRIES];
146
147 static const reg_t ICACHE_ENTRIES = 32;
148 insn_t icache_data[ICACHE_ENTRIES];
149 reg_t icache_tag[ICACHE_ENTRIES];
150
151 icsim_t* icsim;
152 icsim_t* dcsim;
153 icsim_t* itlbsim;
154 icsim_t* dtlbsim;
155
156 void check_align(reg_t addr, int size, bool store, bool fetch)
157 {
158 if(addr & (size-1))
159 {
160 badvaddr = addr;
161 if(fetch)
162 throw trap_instruction_address_misaligned;
163 if(store)
164 throw trap_store_address_misaligned;
165 throw trap_load_address_misaligned;
166 }
167 }
168
169 reg_t translate(reg_t addr, bool store, bool fetch)
170 {
171 reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
172 pte_t pte = tlb_data[idx];
173 reg_t tag = tlb_tag[idx];
174
175 trap_t trap = store ? trap_store_access_fault
176 : fetch ? trap_instruction_access_fault
177 : trap_load_access_fault;
178
179 if(!pte.e || tag != (addr >> PGSHIFT))
180 {
181 pte = walk(addr);
182 if(!pte.e)
183 throw trap;
184
185 tlb_data[idx] = pte;
186 tlb_tag[idx] = addr >> PGSHIFT;
187 }
188
189 if(store && !(supervisor ? pte.sw : pte.uw) ||
190 !store && !fetch && !(supervisor ? pte.sr : pte.ur) ||
191 !store && !fetch && !(supervisor ? pte.sr : pte.ur))
192 throw trap;
193
194 return (addr & (PGSIZE-1)) | (pte.ppn << PGSHIFT);
195 }
196
197 pte_t walk(reg_t addr)
198 {
199 pte_t pte;
200
201 if(!vm_enabled)
202 {
203 pte.t = 0;
204 pte.e = addr < memsz;
205 pte.r = pte.d = 0;
206 pte.ur = pte.uw = pte.ux = pte.sr = pte.sw = pte.sx = 1;
207 pte.ppn = addr >> PGSHIFT;
208 }
209 else
210 {
211 pte.t = pte.e = 0;
212
213 int lg_ptesz = sizeof(pte_t) == 4 ? 2
214 : sizeof(pte_t) == 8 ? 3
215 : 0;
216 assert(lg_ptesz);
217
218 reg_t base = ptbr;
219
220 for(int i = LEVELS-1; i >= 0; i--)
221 {
222 int idxbits = PGSHIFT - lg_ptesz;
223 int shift = PGSHIFT + i*idxbits;
224 reg_t idx = addr >> shift;
225 idx &= (1 << idxbits) - 1;
226
227 reg_t pte_addr = base + idx*sizeof(pte_t);
228 if(pte_addr >= memsz)
229 break;
230
231 pte = *(pte_t*)(mem+pte_addr);
232 if(pte.e)
233 {
234 // if this PTE is from a larger PT, fake a leaf
235 // PTE so the TLB will work right
236 reg_t vpn = addr >> PGSHIFT;
237 pte.ppn += vpn & ((1<<(i*idxbits))-1);
238 break;
239 }
240 if(!pte.t)
241 break;
242
243 base = pte.ppn << PGSHIFT;
244 }
245 }
246
247 return pte;
248 }
249
250 friend class processor_t;
251 };