X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=riscv%2Fmmu.h;h=c3d8f41b78ce458595add5b378e23b6b61b9f4e6;hb=c31742961bdab9094c35ef6c1b33186892aa34d1;hp=9d648ef4de657d9d52a12e0c57fed6b2993d7477;hpb=069c07f440ac207a3bbe71f79c2834a9f0b919e5;p=riscv-isa-sim.git diff --git a/riscv/mmu.h b/riscv/mmu.h index 9d648ef..c3d8f41 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -1,34 +1,41 @@ +// See LICENSE for license details. + #ifndef _RISCV_MMU_H #define _RISCV_MMU_H #include "decode.h" +#include "icache.h" #include "trap.h" #include "common.h" +#include "config.h" #include "processor.h" - -class processor_t; +#include "memtracer.h" +#include // virtual memory configuration typedef reg_t pte_t; -const reg_t LEVELS = sizeof(pte_t) == sizeof(uint64_t) ? 3 : 2; -const reg_t PGSHIFT = 13; +const reg_t LEVELS = sizeof(pte_t) == 8 ? 3 : 2; +const reg_t PTIDXBITS = 10; +const reg_t PGSHIFT = PTIDXBITS + (sizeof(pte_t) == 8 ? 3 : 2); const reg_t PGSIZE = 1 << PGSHIFT; -const reg_t PTIDXBITS = PGSHIFT - (sizeof(pte_t) == 8 ? 3 : 2); +const reg_t VPN_BITS = PTIDXBITS * LEVELS; const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT; +const reg_t VA_BITS = VPN_BITS + PGSHIFT; + +struct insn_fetch_t +{ + insn_func_t func; + union { + insn_t insn; + uint_fast32_t pad; + } insn; +}; -// page table entry (PTE) fields -#define PTE_T 0x001 // Entry is a page Table descriptor -#define PTE_E 0x002 // Entry is a page table Entry -#define PTE_R 0x004 // Referenced -#define PTE_D 0x008 // Dirty -#define PTE_UX 0x010 // User eXecute permission -#define PTE_UW 0x020 // User Read permission -#define PTE_UR 0x040 // User Write permission -#define PTE_SX 0x080 // Supervisor eXecute permission -#define PTE_SW 0x100 // Supervisor Read permission -#define PTE_SR 0x200 // Supervisor Write permission -#define PTE_PERM (PTE_SR | PTE_SW | PTE_SX | PTE_UR | PTE_UW | PTE_UX) -#define PTE_PPN_SHIFT 13 // LSB of physical page number in the PTE +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. @@ -40,13 +47,8 @@ public: // template for functions that load an aligned value from memory #define load_func(type) \ - type##_t load_##type(reg_t addr) { \ - if(unlikely(addr % sizeof(type##_t))) \ - { \ - badvaddr = addr; \ - throw trap_load_address_misaligned; \ - } \ - void* paddr = translate(addr, false, false); \ + type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \ + void* paddr = translate(addr, sizeof(type##_t), false, false); \ return *(type##_t*)paddr; \ } @@ -65,12 +67,7 @@ public: // template for functions that store an aligned value to memory #define store_func(type) \ void store_##type(reg_t addr, type##_t val) { \ - if(unlikely(addr % sizeof(type##_t))) \ - { \ - badvaddr = addr; \ - throw trap_store_address_misaligned; \ - } \ - void* paddr = translate(addr, true, false); \ + void* paddr = translate(addr, sizeof(type##_t), true, false); \ *(type##_t*)paddr = val; \ } @@ -81,107 +78,81 @@ public: store_func(uint64) // load instruction from memory at aligned address. - // (needed because instruction alignment requirement is variable - // if RVC is supported) - // returns the instruction at the specified address, given the current - // RVC mode. func is set to a pointer to a function that knows how to - // execute the returned instruction. - insn_t __attribute__((always_inline)) load_insn(reg_t addr, bool rvc, - insn_func_t* func) + inline icache_entry_t* access_icache(reg_t addr) { - insn_t insn; + reg_t idx = (addr / sizeof(insn_t)) % ICACHE_SIZE; + icache_entry_t* entry = &icache[idx]; + if (likely(entry->tag == addr)) + return entry; - #ifdef RISCV_ENABLE_RVC - if(addr % 4 == 2 && rvc) // fetch across word boundary - { - void* addr_lo = translate(addr, false, true); - insn.bits = *(uint16_t*)addr_lo; + void* iaddr = translate(addr, sizeof(insn_t), false, true); + insn_fetch_t fetch; + fetch.insn.pad = *(decltype(fetch.insn.insn.bits())*)iaddr; + fetch.func = proc->decode_insn(fetch.insn.insn); - *func = processor_t::dispatch_table - [insn.bits % processor_t::DISPATCH_TABLE_SIZE]; + icache[idx].tag = addr; + icache[idx].data = fetch; - if(!INSN_IS_RVC(insn.bits)) - { - void* addr_hi = translate(addr+2, false, true); - insn.bits |= (uint32_t)*(uint16_t*)addr_hi << 16; - } - } - else - #endif + reg_t paddr = (char*)iaddr - mem; + if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true)) { - reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES; - insn_t data = icache_data[idx]; - *func = icache_func[idx]; - if(likely(icache_tag[idx] == addr)) - return data; - - // the processor guarantees alignment based upon rvc mode - void* paddr = translate(addr, false, true); - insn = *(insn_t*)paddr; - - icache_tag[idx] = addr; - icache_data[idx] = insn; - icache_func[idx] = *func = processor_t::dispatch_table - [insn.bits % processor_t::DISPATCH_TABLE_SIZE]; + icache[idx].tag = -1; + tracer.trace(paddr, sizeof(insn_t), false, true); } - - return insn; + return &icache[idx]; } - // get the virtual address that caused a fault - reg_t get_badvaddr() { return badvaddr; } - - // get/set the page table base register - reg_t get_ptbr() { return ptbr; } - void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); flush_tlb(); } + inline insn_fetch_t load_insn(reg_t addr) + { + return access_icache(addr)->data; + } - // keep the MMU in sync with processor mode - void set_supervisor(bool sup) { supervisor = sup; } - void set_vm_enabled(bool en) { vm_enabled = en; } + void set_processor(processor_t* p) { proc = p; flush_tlb(); } - // flush the TLB and instruction cache void flush_tlb(); void flush_icache(); + void register_memtracer(memtracer_t*); + private: char* mem; size_t memsz; - reg_t badvaddr; + processor_t* proc; + memtracer_list_t tracer; - reg_t ptbr; - bool supervisor; - bool vm_enabled; + // implement an instruction cache for simulator performance + icache_entry_t icache[ICACHE_SIZE]; // implement a TLB for simulator performance static const reg_t TLB_ENTRIES = 256; - long tlb_data[TLB_ENTRIES]; + 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]; - // implement an instruction cache for simulator performance - static const reg_t ICACHE_ENTRIES = 256; - insn_t icache_data[ICACHE_ENTRIES]; - insn_func_t icache_func[ICACHE_ENTRIES]; - reg_t icache_tag[ICACHE_ENTRIES]; - // finish translation on a TLB miss and upate the TLB - void* refill(reg_t addr, bool store, bool fetch); + void* refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch); // perform a page table walk for a given virtual address pte_t walk(reg_t addr); // translate a virtual address to a physical address - void* translate(reg_t addr, bool store, bool fetch) + void* translate(reg_t addr, reg_t bytes, bool store, bool fetch) + __attribute__((always_inline)) { reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES; + reg_t expected_tag = addr >> PGSHIFT; + reg_t* tags = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag; + reg_t tag = tags[idx]; + void* data = tlb_data[idx] + addr; + + if (unlikely(addr & (bytes-1))) + store ? throw trap_store_address_misaligned(addr) : throw trap_load_address_misaligned(addr); - reg_t* tlb_tag = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag; - reg_t expected_tag = addr & ~(PGSIZE-1); - if(likely(tlb_tag[idx] == expected_tag)) - return (void*)(((long)addr & (PGSIZE-1)) + tlb_data[idx]); + if (likely(tag == expected_tag)) + return data; - return refill(addr, store, fetch); + return refill_tlb(addr, bytes, store, fetch); } friend class processor_t;