11 const reg_t LEVELS
= 4;
12 const reg_t PGSHIFT
= 12;
13 const reg_t PGSIZE
= 1 << PGSHIFT
;
14 const reg_t PTIDXBITS
= PGSHIFT
- (sizeof(pte_t
) == 8 ? 3 : 2);
15 const reg_t PPN_BITS
= 8*sizeof(reg_t
) - PGSHIFT
;
17 #define PTE_T 0x001 // Entry is a page Table descriptor
18 #define PTE_E 0x002 // Entry is a page table Entry
19 #define PTE_R 0x004 // Referenced
20 #define PTE_D 0x008 // Dirty
21 #define PTE_UX 0x010 // User eXecute permission
22 #define PTE_UW 0x020 // User Read permission
23 #define PTE_UR 0x040 // User Write permission
24 #define PTE_SX 0x080 // Supervisor eXecute permission
25 #define PTE_SW 0x100 // Supervisor Read permission
26 #define PTE_SR 0x200 // Supervisor Write permission
27 #define PTE_PERM (PTE_SR | PTE_SW | PTE_SX | PTE_UR | PTE_UW | PTE_UX)
28 #define PTE_PERM_SHIFT 4
29 #define PTE_PPN_SHIFT 12
34 mmu_t(char* _mem
, size_t _memsz
)
35 : mem(_mem
), memsz(_memsz
), badvaddr(0),
36 ptbr(0), supervisor(true), vm_enabled(false),
37 icsim(NULL
), dcsim(NULL
), itlbsim(NULL
), dtlbsim(NULL
)
41 #ifdef RISCV_ENABLE_ICSIM
42 # define dcsim_tick(dcsim, dtlbsim, addr, size, st) \
43 do { if(dcsim) (dcsim)->tick(addr, size, st); \
44 if(dtlbsim) (dtlbsim)->tick(addr, sizeof(reg_t), false); } while(0)
46 # define dcsim_tick(dcsim, dtlbsim, addr, size, st)
49 #define load_func(type) \
50 type##_t load_##type(reg_t addr) { \
51 check_align(addr, sizeof(type##_t), false, false); \
52 addr = translate(addr, false, false); \
53 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \
54 return *(type##_t*)(mem+addr); \
57 #define store_func(type) \
58 void store_##type(reg_t addr, type##_t val) { \
59 check_align(addr, sizeof(type##_t), true, false); \
60 addr = translate(addr, true, false); \
61 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \
62 *(type##_t*)(mem+addr) = val; \
65 insn_t
__attribute__((always_inline
)) load_insn(reg_t addr
, bool rvc
)
69 reg_t idx
= (addr
/sizeof(insn_t
)) % ICACHE_ENTRIES
;
70 bool hit
= addr
% 4 == 0 && icache_tag
[idx
] == (addr
| 1);
72 return icache_data
[idx
];
74 #ifdef RISCV_ENABLE_RVC
75 if(addr
% 4 == 2 && rvc
)
77 reg_t paddr_lo
= translate(addr
, false, true);
78 insn
.bits
= *(uint16_t*)(mem
+paddr_lo
);
80 if(!INSN_IS_RVC(insn
.bits
))
82 reg_t paddr_hi
= translate(addr
+2, false, true);
83 insn
.bits
|= (uint32_t)*(uint16_t*)(mem
+paddr_hi
) << 16;
89 check_align(addr
, 4, false, true);
90 reg_t paddr
= translate(addr
, false, true);
91 insn
= *(insn_t
*)(mem
+paddr
);
93 icache_tag
[idx
] = addr
| 1;
94 icache_data
[idx
] = insn
;
97 #ifdef RISCV_ENABLE_ICSIM
99 icsim
->tick(addr
, insn_length(insn
.bits
), false);
101 itlbsim
->tick(addr
, sizeof(reg_t
), false);
122 reg_t
get_badvaddr() { return badvaddr
; }
123 reg_t
get_ptbr() { return ptbr
; }
125 void set_supervisor(bool sup
) { supervisor
= sup
; }
126 void set_vm_enabled(bool en
) { vm_enabled
= en
; }
127 void set_ptbr(reg_t addr
) { ptbr
= addr
& ~(PGSIZE
-1); flush_tlb(); }
129 void set_icsim(icsim_t
* _icsim
) { icsim
= _icsim
; }
130 void set_dcsim(icsim_t
* _dcsim
) { dcsim
= _dcsim
; }
131 void set_itlbsim(icsim_t
* _itlbsim
) { itlbsim
= _itlbsim
; }
132 void set_dtlbsim(icsim_t
* _dtlbsim
) { dtlbsim
= _dtlbsim
; }
146 static const reg_t TLB_ENTRIES
= 256;
147 pte_t tlb_data
[TLB_ENTRIES
];
148 reg_t tlb_tag
[TLB_ENTRIES
];
150 static const reg_t ICACHE_ENTRIES
= 256;
151 insn_t icache_data
[ICACHE_ENTRIES
];
152 reg_t icache_tag
[ICACHE_ENTRIES
];
159 void check_align(reg_t addr
, int size
, bool store
, bool fetch
)
161 if(unlikely(addr
& (size
-1)))
165 throw trap_instruction_address_misaligned
;
167 throw trap_store_address_misaligned
;
168 throw trap_load_address_misaligned
;
172 reg_t
translate(reg_t addr
, bool store
, bool fetch
)
174 reg_t idx
= (addr
>> PGSHIFT
) % TLB_ENTRIES
;
175 pte_t pte
= tlb_data
[idx
];
176 reg_t tag
= tlb_tag
[idx
];
178 trap_t trap
= store
? trap_store_access_fault
179 : fetch
? trap_instruction_access_fault
180 : trap_load_access_fault
;
182 bool hit
= (pte
& PTE_E
) && tag
== (addr
>> PGSHIFT
);
190 tlb_tag
[idx
] = addr
>> PGSHIFT
;
193 reg_t access_type
= store
? PTE_UW
: fetch
? PTE_UX
: PTE_UR
;
196 if(unlikely(!(access_type
& pte
& PTE_PERM
)))
199 return (addr
& (PGSIZE
-1)) | ((pte
>> PTE_PPN_SHIFT
) << PGSHIFT
);
202 pte_t
walk(reg_t addr
)
209 pte
= PTE_E
| PTE_PERM
| ((addr
>> PGSHIFT
) << PTE_PPN_SHIFT
);
216 int ptshift
= (LEVELS
-1)*PTIDXBITS
;
217 for(reg_t i
= 0; i
< LEVELS
; i
++, ptshift
-= PTIDXBITS
)
219 reg_t idx
= (addr
>> (PGSHIFT
+ptshift
)) & ((1<<PTIDXBITS
)-1);
221 reg_t pte_addr
= base
+ idx
*sizeof(pte_t
);
222 if(pte_addr
>= memsz
)
225 ptd
= *(pte_t
*)(mem
+pte_addr
);
228 // if this PTE is from a larger PT, fake a leaf
229 // PTE so the TLB will work right
230 reg_t vpn
= addr
>> PGSHIFT
;
231 pte
|= ptd
| (vpn
& ((1<<(ptshift
))-1)) << PTE_PPN_SHIFT
;
234 else if(!(ptd
& PTE_T
))
237 base
= (ptd
>> PTE_PPN_SHIFT
) << PGSHIFT
;
244 friend class processor_t
;