X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=riscv%2Fmmu.h;h=a87b6af30480ec5d51ddea728bf9d83f1fc76253;hb=91a4f8114dad884b19081f09e6fe17ea2820ec52;hp=f8ee597f5e62758b349d169e5ac5a9bbb71f8b49;hpb=46f2fb1d9e33b4cf98c2cc15c2a2da14f0e3580d;p=riscv-isa-sim.git diff --git a/riscv/mmu.h b/riscv/mmu.h index f8ee597..a87b6af 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -1,125 +1,181 @@ +// See LICENSE for license details. + +#ifndef _RISCV_MMU_H +#define _RISCV_MMU_H + #include "decode.h" #include "trap.h" -#include "icsim.h" +#include "common.h" +#include "config.h" +#include "sim.h" +#include "processor.h" +#include "memtracer.h" +#include +#include + +// virtual memory configuration +#define PGSHIFT 12 +const reg_t PGSIZE = 1 << PGSHIFT; +const reg_t PGMASK = ~(PGSIZE-1); + +struct insn_fetch_t +{ + insn_func_t func; + insn_t insn; +}; -class processor_t; +struct icache_entry_t { + reg_t tag; + reg_t pad; + insn_fetch_t data; +}; +// this class implements a processor's port into the virtual memory system. +// an MMU and instruction cache are maintained for simulator performance. class mmu_t { public: - mmu_t(char* _mem, size_t _memsz) - : mem(_mem), memsz(_memsz), badvaddr(0), - icsim(NULL), dcsim(NULL), itlbsim(NULL), dtlbsim(NULL) - { - } - - void set_icsim(icsim_t* _icsim) { icsim = _icsim; } - void set_dcsim(icsim_t* _dcsim) { dcsim = _dcsim; } - void set_itlbsim(icsim_t* _itlbsim) { itlbsim = _itlbsim; } - void set_dtlbsim(icsim_t* _dtlbsim) { dtlbsim = _dtlbsim; } - - #ifdef RISCV_ENABLE_ICSIM - # define dcsim_tick(dcsim, dtlbsim, addr, size, st) \ - do { if(dcsim) (dcsim)->tick(addr, size, st); \ - if(dtlbsim) (dtlbsim)->tick(addr, sizeof(reg_t), false); } while(0) - #else - # define dcsim_tick(dcsim, addr, size) - #endif + mmu_t(sim_t* sim, processor_t* proc); + ~mmu_t(); + // template for functions that load an aligned value from memory #define load_func(type) \ - type##_t load_##type(reg_t addr) { \ - check_align_and_bounds(addr, sizeof(type##_t), false, false); \ - dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \ - return *(type##_t*)(mem+addr); \ + type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \ + if (addr & (sizeof(type##_t)-1)) \ + throw trap_load_address_misaligned(addr); \ + reg_t vpn = addr >> PGSHIFT; \ + if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) \ + return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \ + type##_t res; \ + load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res); \ + return res; \ } - #define store_func(type) \ - void store_##type(reg_t addr, type##_t val) { \ - check_align_and_bounds(addr, sizeof(type##_t), true, false); \ - dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \ - *(type##_t*)(mem+addr) = val; \ - } - - insn_t load_insn(reg_t addr, bool rvc) - { - #ifdef RISCV_ENABLE_RVC - check_align_and_bounds(addr, rvc ? 2 : 4, false, true); - uint16_t lo = *(uint16_t*)(mem+addr); - uint16_t hi = *(uint16_t*)(mem+addr+2); - - insn_t insn; - insn.bits = lo | ((uint32_t)hi << 16); - - #ifdef RISCV_ENABLE_ICSIM - if(icsim) - icsim->tick(addr, insn_length(insn), false); - if(itlbsim) - itlbsim->tick(addr, sizeof(reg_t), false); - #endif - - return insn; - #else - check_align_and_bounds(addr, 4, false, true); - return *(insn_t*)(mem+addr); - #endif - } - + // load value from memory at aligned address; zero extend to register width load_func(uint8) load_func(uint16) load_func(uint32) load_func(uint64) + // load value from memory at aligned address; sign extend to register width load_func(int8) load_func(int16) load_func(int32) load_func(int64) + // template for functions that store an aligned value to memory + #define store_func(type) \ + void store_##type(reg_t addr, type##_t val) { \ + if (addr & (sizeof(type##_t)-1)) \ + throw trap_store_address_misaligned(addr); \ + reg_t vpn = addr >> PGSHIFT; \ + if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \ + *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \ + else \ + store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&val); \ + } + + // store value to memory at aligned address store_func(uint8) store_func(uint16) store_func(uint32) store_func(uint64) - reg_t get_badvaddr() { return badvaddr; } + static const reg_t ICACHE_ENTRIES = 1024; -private: - char* mem; - size_t memsz; - reg_t badvaddr; - - icsim_t* icsim; - icsim_t* dcsim; - icsim_t* itlbsim; - icsim_t* dtlbsim; + inline size_t icache_index(reg_t addr) + { + return (addr / PC_ALIGN) % ICACHE_ENTRIES; + } - void check_align(reg_t addr, int size, bool store, bool fetch) + inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry) { - if(addr & (size-1)) - { - badvaddr = addr; - if(fetch) - throw trap_instruction_address_misaligned; - if(store) - throw trap_store_address_misaligned; - throw trap_load_address_misaligned; + const uint16_t* iaddr = translate_insn_addr(addr); + insn_bits_t insn = *iaddr; + int length = insn_length(insn); + + if (likely(length == 4)) { + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16; + } else if (length == 2) { + insn = (int16_t)insn; + } else if (length == 6) { + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 4) << 32; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16; + } else { + static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t"); + insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 6) << 48; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 4) << 32; + insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr(addr + 2) << 16; + } + + insn_fetch_t fetch = {proc->decode_insn(insn), insn}; + entry->tag = addr; + entry->data = fetch; + + reg_t paddr = sim->mem_to_addr((char*)iaddr); + if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) { + entry->tag = -1; + tracer.trace(paddr, length, FETCH); } + return entry; } - void check_bounds(reg_t addr, int size, bool store, bool fetch) + inline icache_entry_t* access_icache(reg_t addr) { - if(addr >= memsz || addr + size > memsz) - { - badvaddr = addr; - if(fetch) - throw trap_instruction_access_fault; - throw store ? trap_store_access_fault : trap_load_access_fault; - } + icache_entry_t* entry = &icache[icache_index(addr)]; + if (likely(entry->tag == addr)) + return entry; + return refill_icache(addr, entry); } - void check_align_and_bounds(reg_t addr, int size, bool store, bool fetch) + inline insn_fetch_t load_insn(reg_t addr) { - check_align(addr, size, store, fetch); - check_bounds(addr, size, store, fetch); + return access_icache(addr)->data; + } + + void flush_tlb(); + void flush_icache(); + + void register_memtracer(memtracer_t*); + +private: + sim_t* sim; + processor_t* proc; + memtracer_list_t tracer; + uint16_t fetch_temp; + + // implement an instruction cache for simulator performance + icache_entry_t icache[ICACHE_ENTRIES]; + + // implement a TLB for simulator performance + static const reg_t TLB_ENTRIES = 256; + char* tlb_data[TLB_ENTRIES]; + reg_t tlb_insn_tag[TLB_ENTRIES]; + reg_t tlb_load_tag[TLB_ENTRIES]; + reg_t tlb_store_tag[TLB_ENTRIES]; + + // finish translation on a TLB miss and update the TLB + void refill_tlb(reg_t vaddr, reg_t paddr, access_type type); + const char* fill_from_mmio(reg_t vaddr, reg_t paddr); + + // perform a page table walk for a given VA; set referenced/dirty bits + reg_t walk(reg_t addr, access_type type, bool supervisor, bool pum); + + // handle uncommon cases: TLB misses, page faults, MMIO + const uint16_t* fetch_slow_path(reg_t addr); + void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes); + void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes); + reg_t translate(reg_t addr, access_type type); + + // ITLB lookup + const uint16_t* translate_insn_addr(reg_t addr) __attribute__((always_inline)) { + reg_t vpn = addr >> PGSHIFT; + if (likely(tlb_insn_tag[vpn % TLB_ENTRIES] == vpn)) + return (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); + return fetch_slow_path(addr); } - + friend class processor_t; }; + +#endif