[xcc] cleaned up mmu code
[riscv-isa-sim.git] / riscv / mmu.h
1 #ifndef _RISCV_MMU_H
2 #define _RISCV_MMU_H
3
4 #include "decode.h"
5 #include "trap.h"
6 #include "icsim.h"
7 #include "common.h"
8
9 class processor_t;
10
11 typedef reg_t pte_t;
12
13 const reg_t LEVELS = 4;
14 const reg_t PGSHIFT = 12;
15 const reg_t PGSIZE = 1 << PGSHIFT;
16 const reg_t PTIDXBITS = PGSHIFT - (sizeof(pte_t) == 8 ? 3 : 2);
17 const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
18
19 #define PTE_T 0x001 // Entry is a page Table descriptor
20 #define PTE_E 0x002 // Entry is a page table Entry
21 #define PTE_R 0x004 // Referenced
22 #define PTE_D 0x008 // Dirty
23 #define PTE_UX 0x010 // User eXecute permission
24 #define PTE_UW 0x020 // User Read permission
25 #define PTE_UR 0x040 // User Write permission
26 #define PTE_SX 0x080 // Supervisor eXecute permission
27 #define PTE_SW 0x100 // Supervisor Read permission
28 #define PTE_SR 0x200 // Supervisor Write permission
29 #define PTE_PERM (PTE_SR | PTE_SW | PTE_SX | PTE_UR | PTE_UW | PTE_UX)
30 #define PTE_PERM_SHIFT 4
31 #define PTE_PPN_SHIFT 12
32
33 class mmu_t
34 {
35 public:
36 mmu_t(char* _mem, size_t _memsz);
37 ~mmu_t();
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 if(unlikely(addr % sizeof(type##_t))) \
50 { \
51 badvaddr = addr; \
52 throw trap_load_address_misaligned; \
53 } \
54 addr = translate(addr, false, false); \
55 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \
56 return *(type##_t*)(mem+addr); \
57 }
58
59 #define store_func(type) \
60 void store_##type(reg_t addr, type##_t val) { \
61 if(unlikely(addr % sizeof(type##_t))) \
62 { \
63 badvaddr = addr; \
64 throw trap_store_address_misaligned; \
65 } \
66 addr = translate(addr, true, false); \
67 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \
68 *(type##_t*)(mem+addr) = val; \
69 }
70
71 insn_t __attribute__((always_inline)) load_insn(reg_t addr, bool rvc)
72 {
73 insn_t insn;
74
75 #ifdef RISCV_ENABLE_RVC
76 if(addr % 4 == 2 && rvc) // fetch across word boundary
77 {
78 reg_t paddr_lo = translate(addr, false, true);
79 insn.bits = *(uint16_t*)(mem+paddr_lo);
80
81 if(!INSN_IS_RVC(insn.bits))
82 {
83 reg_t paddr_hi = translate(addr+2, false, true);
84 insn.bits |= (uint32_t)*(uint16_t*)(mem+paddr_hi) << 16;
85 }
86 }
87 else
88 #endif
89 {
90 reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
91 bool hit = icache_tag[idx] == addr;
92 if(likely(hit))
93 return icache_data[idx];
94
95 // the processor guarantees alignment based upon rvc mode
96 reg_t paddr = translate(addr, false, true);
97 insn = *(insn_t*)(mem+paddr);
98
99 icache_tag[idx] = addr;
100 icache_data[idx] = insn;
101 }
102
103 #ifdef RISCV_ENABLE_ICSIM
104 if(icsim)
105 icsim->tick(addr, insn_length(insn.bits), false);
106 if(itlbsim)
107 itlbsim->tick(addr, sizeof(reg_t), false);
108 #endif
109
110 return insn;
111 }
112
113 load_func(uint8)
114 load_func(uint16)
115 load_func(uint32)
116 load_func(uint64)
117
118 load_func(int8)
119 load_func(int16)
120 load_func(int32)
121 load_func(int64)
122
123 store_func(uint8)
124 store_func(uint16)
125 store_func(uint32)
126 store_func(uint64)
127
128 reg_t get_badvaddr() { return badvaddr; }
129 reg_t get_ptbr() { return ptbr; }
130
131 void set_supervisor(bool sup) { supervisor = sup; }
132 void set_vm_enabled(bool en) { vm_enabled = en; }
133 void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); flush_tlb(); }
134
135 void set_icsim(icsim_t* _icsim) { icsim = _icsim; }
136 void set_dcsim(icsim_t* _dcsim) { dcsim = _dcsim; }
137 void set_itlbsim(icsim_t* _itlbsim) { itlbsim = _itlbsim; }
138 void set_dtlbsim(icsim_t* _dtlbsim) { dtlbsim = _dtlbsim; }
139
140 void flush_tlb();
141 void flush_icache();
142
143 private:
144 char* mem;
145 size_t memsz;
146 reg_t badvaddr;
147
148 reg_t ptbr;
149 bool supervisor;
150 bool vm_enabled;
151
152 static const reg_t TLB_ENTRIES = 256;
153 pte_t tlb_data[TLB_ENTRIES];
154 reg_t tlb_insn_tag[TLB_ENTRIES];
155 reg_t tlb_load_tag[TLB_ENTRIES];
156 reg_t tlb_store_tag[TLB_ENTRIES];
157
158 static const reg_t ICACHE_ENTRIES = 256;
159 insn_t icache_data[ICACHE_ENTRIES];
160 reg_t icache_tag[ICACHE_ENTRIES];
161
162 icsim_t* icsim;
163 icsim_t* dcsim;
164 icsim_t* itlbsim;
165 icsim_t* dtlbsim;
166
167 reg_t refill(reg_t addr, bool store, bool fetch);
168 pte_t walk(reg_t addr);
169
170 reg_t translate(reg_t addr, bool store, bool fetch)
171 {
172 reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
173
174 reg_t* tlb_tag = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag;
175 reg_t expected_tag = addr & ~(PGSIZE-1);
176 if(likely(tlb_tag[idx] == expected_tag))
177 return (addr & (PGSIZE-1)) | tlb_data[idx];
178
179 return refill(addr, store, fetch);
180 }
181
182 friend class processor_t;
183 };
184
185 #endif