Skip to content

Commit

Permalink
Cache MMU fetch access
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jserv committed Jan 17, 2024
1 parent e788620 commit ac75287
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 15 deletions.
7 changes: 4 additions & 3 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
/* Define fetch separately since it is simpler (fixed width, already checked
* alignment, only main RAM is executable).
*/
static void mem_fetch(vm_t *vm, uint32_t addr, uint32_t *value)
static void mem_fetch(vm_t *vm, uint32_t n_pages, uint32_t **page_addr)
{
emu_state_t *data = (emu_state_t *) vm->priv;
if (unlikely(addr >= RAM_SIZE)) {
if (unlikely(n_pages >= RAM_SIZE / RV_PAGE_SIZE)) {
/* TODO: check for other regions */
vm_set_exception(vm, RV_EXC_FETCH_FAULT, vm->exc_val);
return;
}
*value = data->ram[addr >> 2];
*page_addr = &data->ram[n_pages << (RV_PAGE_SHIFT - 2)];
}

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

/* Set up RAM */
emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE,
Expand Down
36 changes: 29 additions & 7 deletions riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,17 @@ static inline uint32_t read_rs2(const vm_t *vm, uint32_t insn)

/* virtual addressing */

static void mmu_invalidate(vm_t *vm)
{
vm->cache_fetch.n_pages = 0xFFFFFFFF;
}

/* Pre-verify the root page table to minimize page table access during
* translation time.
*/
static void mmu_set(vm_t *vm, uint32_t satp)
{
mmu_invalidate(vm);
if (satp >> 31) {
uint32_t *page_table = vm->mem_page_table(vm, satp & MASK(22));
if (!page_table)
Expand Down Expand Up @@ -267,18 +273,27 @@ static void mmu_translate(vm_t *vm,
*addr = ((*addr) & MASK(RV_PAGE_SHIFT)) | (ppn << RV_PAGE_SHIFT);
}

static void mmu_fence(vm_t *vm UNUSED, uint32_t insn UNUSED)
static void mmu_fence(vm_t *vm, uint32_t insn UNUSED)
{
/* no-op for now */
mmu_invalidate(vm);
}

static void mmu_fetch(vm_t *vm, uint32_t addr, uint32_t *value)
{
mmu_translate(vm, &addr, (1 << 3), (1 << 6), false, RV_EXC_FETCH_FAULT,
RV_EXC_FETCH_PFAULT);
if (vm->error)
return;
vm->mem_fetch(vm, addr, value);
uint32_t vpn = addr >> RV_PAGE_SHIFT;
if (unlikely(vpn != vm->cache_fetch.n_pages)) {
mmu_translate(vm, &addr, (1 << 3), (1 << 6), false, RV_EXC_FETCH_FAULT,
RV_EXC_FETCH_PFAULT);
if (vm->error)
return;
uint32_t *page_addr;
vm->mem_fetch(vm, addr >> RV_PAGE_SHIFT, &page_addr);
if (vm->error)
return;
vm->cache_fetch.n_pages = vpn;
vm->cache_fetch.page_addr = page_addr;
}
*value = vm->cache_fetch.page_addr[(addr >> 2) & MASK(RV_PAGE_SHIFT - 2)];
}

static void mmu_load(vm_t *vm,
Expand Down Expand Up @@ -347,6 +362,7 @@ void vm_trap(vm_t *vm)

/* Set */
vm->sstatus_sie = false;
mmu_invalidate(vm);
vm->s_mode = true;
vm->pc = vm->stvec_addr;
if (vm->stvec_vectored)
Expand All @@ -359,6 +375,7 @@ static void op_sret(vm_t *vm)
{
/* Restore from stack */
vm->pc = vm->sepc;
mmu_invalidate(vm);
vm->s_mode = vm->sstatus_spp;
vm->sstatus_sie = vm->sstatus_spie;

Expand Down Expand Up @@ -763,6 +780,11 @@ static void op_amo(vm_t *vm, uint32_t insn)
}
}

void vm_init(vm_t *vm)
{
mmu_invalidate(vm);
}

void vm_step(vm_t *vm)
{
if (unlikely(vm->error))
Expand Down
19 changes: 14 additions & 5 deletions riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ typedef enum {
ERR_USER, /**< user-specific error */
} vm_error_t;

/* To use the emulator, start by initializing a "vm_t" struct with zero values
* and set the required environment-supplied callbacks. You may also set other
* necessary fields such as argument registers and s_mode, ensuring that all
* field restrictions are met to avoid undefined behavior.
typedef struct {
uint32_t n_pages;
uint32_t *page_addr;
} mmu_cache_t;

/* To use the emulator, start by initializing a vm_t object with zero values,
* invoke vm_init(), and set the required environment-supplied callbacks. You
* may also set other necessary fields such as argument registers and s_mode,
* ensuring that all field restrictions are met to avoid undefined behavior.
*
* Once the emulator is set up, execute the emulation loop by calling
* "vm_step()" repeatedly. Each call attempts to execute a single instruction.
Expand Down Expand Up @@ -77,6 +82,8 @@ struct __vm_internal {
*/
uint32_t exc_cause, exc_val;

mmu_cache_t cache_fetch;

/* Supervisor state */
bool s_mode;
bool sstatus_spp; /**< state saved at trap */
Expand All @@ -101,7 +108,7 @@ struct __vm_internal {
/* Memory access sets the vm->error to indicate failure. On successful
* access, it reads or writes the specified "value".
*/
void (*mem_fetch)(vm_t *vm, uint32_t addr, uint32_t *value);
void (*mem_fetch)(vm_t *vm, uint32_t n_pages, uint32_t **page_addr);
void (*mem_load)(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value);
void (*mem_store)(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value);

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

void vm_init(vm_t *vm);

/* Emulate the next instruction. This is a no-op if the error is already set. */
void vm_step(vm_t *vm);

Expand Down

0 comments on commit ac75287

Please sign in to comment.