Skip to content

Commit ac75287

Browse files
committed
Cache MMU fetch access
The emulator's environment interface has been updated to facilitate page lookup instead of specific fetches. Additionally, a preliminary cache for fetches has been implemented, which encompasses both MMU and physical page lookups. This enhancement boosts performance. This approach, which includes bypassing the PTE bit set operation in the cache, should be compliant with RISC-V standards. It allows MMU lookups to be carried out speculatively, adhering to the architecture's guidelines.
1 parent e788620 commit ac75287

File tree

3 files changed

+47
-15
lines changed

3 files changed

+47
-15
lines changed

main.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
/* Define fetch separately since it is simpler (fixed width, already checked
1616
* alignment, only main RAM is executable).
1717
*/
18-
static void mem_fetch(vm_t *vm, uint32_t addr, uint32_t *value)
18+
static void mem_fetch(vm_t *vm, uint32_t n_pages, uint32_t **page_addr)
1919
{
2020
emu_state_t *data = (emu_state_t *) vm->priv;
21-
if (unlikely(addr >= RAM_SIZE)) {
21+
if (unlikely(n_pages >= RAM_SIZE / RV_PAGE_SIZE)) {
2222
/* TODO: check for other regions */
2323
vm_set_exception(vm, RV_EXC_FETCH_FAULT, vm->exc_val);
2424
return;
2525
}
26-
*value = data->ram[addr >> 2];
26+
*page_addr = &data->ram[n_pages << (RV_PAGE_SHIFT - 2)];
2727
}
2828

2929
/* Similarly, only main memory pages can be used as page tables. */
@@ -369,6 +369,7 @@ static int semu_start(int argc, char **argv)
369369
.mem_store = mem_store,
370370
.mem_page_table = mem_page_table,
371371
};
372+
vm_init(&vm);
372373

373374
/* Set up RAM */
374375
emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE,

riscv.c

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,17 @@ static inline uint32_t read_rs2(const vm_t *vm, uint32_t insn)
167167

168168
/* virtual addressing */
169169

170+
static void mmu_invalidate(vm_t *vm)
171+
{
172+
vm->cache_fetch.n_pages = 0xFFFFFFFF;
173+
}
174+
170175
/* Pre-verify the root page table to minimize page table access during
171176
* translation time.
172177
*/
173178
static void mmu_set(vm_t *vm, uint32_t satp)
174179
{
180+
mmu_invalidate(vm);
175181
if (satp >> 31) {
176182
uint32_t *page_table = vm->mem_page_table(vm, satp & MASK(22));
177183
if (!page_table)
@@ -267,18 +273,27 @@ static void mmu_translate(vm_t *vm,
267273
*addr = ((*addr) & MASK(RV_PAGE_SHIFT)) | (ppn << RV_PAGE_SHIFT);
268274
}
269275

270-
static void mmu_fence(vm_t *vm UNUSED, uint32_t insn UNUSED)
276+
static void mmu_fence(vm_t *vm, uint32_t insn UNUSED)
271277
{
272-
/* no-op for now */
278+
mmu_invalidate(vm);
273279
}
274280

275281
static void mmu_fetch(vm_t *vm, uint32_t addr, uint32_t *value)
276282
{
277-
mmu_translate(vm, &addr, (1 << 3), (1 << 6), false, RV_EXC_FETCH_FAULT,
278-
RV_EXC_FETCH_PFAULT);
279-
if (vm->error)
280-
return;
281-
vm->mem_fetch(vm, addr, value);
283+
uint32_t vpn = addr >> RV_PAGE_SHIFT;
284+
if (unlikely(vpn != vm->cache_fetch.n_pages)) {
285+
mmu_translate(vm, &addr, (1 << 3), (1 << 6), false, RV_EXC_FETCH_FAULT,
286+
RV_EXC_FETCH_PFAULT);
287+
if (vm->error)
288+
return;
289+
uint32_t *page_addr;
290+
vm->mem_fetch(vm, addr >> RV_PAGE_SHIFT, &page_addr);
291+
if (vm->error)
292+
return;
293+
vm->cache_fetch.n_pages = vpn;
294+
vm->cache_fetch.page_addr = page_addr;
295+
}
296+
*value = vm->cache_fetch.page_addr[(addr >> 2) & MASK(RV_PAGE_SHIFT - 2)];
282297
}
283298

284299
static void mmu_load(vm_t *vm,
@@ -347,6 +362,7 @@ void vm_trap(vm_t *vm)
347362

348363
/* Set */
349364
vm->sstatus_sie = false;
365+
mmu_invalidate(vm);
350366
vm->s_mode = true;
351367
vm->pc = vm->stvec_addr;
352368
if (vm->stvec_vectored)
@@ -359,6 +375,7 @@ static void op_sret(vm_t *vm)
359375
{
360376
/* Restore from stack */
361377
vm->pc = vm->sepc;
378+
mmu_invalidate(vm);
362379
vm->s_mode = vm->sstatus_spp;
363380
vm->sstatus_sie = vm->sstatus_spie;
364381

@@ -763,6 +780,11 @@ static void op_amo(vm_t *vm, uint32_t insn)
763780
}
764781
}
765782

783+
void vm_init(vm_t *vm)
784+
{
785+
mmu_invalidate(vm);
786+
}
787+
766788
void vm_step(vm_t *vm)
767789
{
768790
if (unlikely(vm->error))

riscv.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ typedef enum {
2929
ERR_USER, /**< user-specific error */
3030
} vm_error_t;
3131

32-
/* To use the emulator, start by initializing a "vm_t" struct with zero values
33-
* and set the required environment-supplied callbacks. You may also set other
34-
* necessary fields such as argument registers and s_mode, ensuring that all
35-
* field restrictions are met to avoid undefined behavior.
32+
typedef struct {
33+
uint32_t n_pages;
34+
uint32_t *page_addr;
35+
} mmu_cache_t;
36+
37+
/* To use the emulator, start by initializing a vm_t object with zero values,
38+
* invoke vm_init(), and set the required environment-supplied callbacks. You
39+
* may also set other necessary fields such as argument registers and s_mode,
40+
* ensuring that all field restrictions are met to avoid undefined behavior.
3641
*
3742
* Once the emulator is set up, execute the emulation loop by calling
3843
* "vm_step()" repeatedly. Each call attempts to execute a single instruction.
@@ -77,6 +82,8 @@ struct __vm_internal {
7782
*/
7883
uint32_t exc_cause, exc_val;
7984

85+
mmu_cache_t cache_fetch;
86+
8087
/* Supervisor state */
8188
bool s_mode;
8289
bool sstatus_spp; /**< state saved at trap */
@@ -101,7 +108,7 @@ struct __vm_internal {
101108
/* Memory access sets the vm->error to indicate failure. On successful
102109
* access, it reads or writes the specified "value".
103110
*/
104-
void (*mem_fetch)(vm_t *vm, uint32_t addr, uint32_t *value);
111+
void (*mem_fetch)(vm_t *vm, uint32_t n_pages, uint32_t **page_addr);
105112
void (*mem_load)(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value);
106113
void (*mem_store)(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value);
107114

@@ -112,6 +119,8 @@ struct __vm_internal {
112119
uint32_t *(*mem_page_table)(const vm_t *vm, uint32_t ppn);
113120
};
114121

122+
void vm_init(vm_t *vm);
123+
115124
/* Emulate the next instruction. This is a no-op if the error is already set. */
116125
void vm_step(vm_t *vm);
117126

0 commit comments

Comments
 (0)