insn_fetch_t data;
};
+struct tlb_entry_t {
+ char* host_offset;
+ reg_t target_offset;
+};
+
class trigger_matched_t
{
public:
mmu_t(sim_t* sim, processor_t* proc);
~mmu_t();
+ inline reg_t misaligned_load(reg_t addr, size_t size)
+ {
+#ifdef RISCV_ENABLE_MISALIGNED
+ reg_t res = 0;
+ for (size_t i = 0; i < size; i++)
+ res += (reg_t)load_uint8(addr + i) << (i * 8);
+ return res;
+#else
+ throw trap_load_address_misaligned(addr);
+#endif
+ }
+
+ inline void misaligned_store(reg_t addr, reg_t data, size_t size)
+ {
+#ifdef RISCV_ENABLE_MISALIGNED
+ for (size_t i = 0; i < size; i++)
+ store_uint8(addr + i, data >> (i * 8));
+#else
+ throw trap_store_address_misaligned(addr);
+#endif
+ }
+
// template for functions that load an aligned value from memory
#define load_func(type) \
inline type##_t load_##type(reg_t addr) { \
- if (addr & (sizeof(type##_t)-1)) \
- throw trap_load_address_misaligned(addr); \
+ if (unlikely(addr & (sizeof(type##_t)-1))) \
+ return misaligned_load(addr, sizeof(type##_t)); \
reg_t vpn = addr >> PGSHIFT; \
if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) \
- return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \
+ return *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \
if (unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
- type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr); \
+ type##_t data = *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr); \
if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \
if (matched_trigger) \
// 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); \
+ if (unlikely(addr & (sizeof(type##_t)-1))) \
+ return misaligned_store(addr, val, sizeof(type##_t)); \
reg_t vpn = addr >> PGSHIFT; \
if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \
- *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \
+ *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \
else if (unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_STORE, addr, val); \
if (matched_trigger) \
throw *matched_trigger; \
} \
- *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \
+ *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = val; \
} \
else \
store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&val); \
}
+ // template for functions that perform an atomic memory operation
+ #define amo_func(type) \
+ template<typename op> \
+ type##_t amo_##type(reg_t addr, op f) { \
+ if (addr & (sizeof(type##_t)-1)) \
+ throw trap_store_address_misaligned(addr); \
+ try { \
+ auto lhs = load_##type(addr); \
+ store_##type(addr, f(lhs)); \
+ return lhs; \
+ } catch (trap_load_page_fault& t) { \
+ /* AMO faults should be reported as store faults */ \
+ throw trap_store_page_fault(t.get_badaddr()); \
+ } catch (trap_load_access_fault& t) { \
+ /* AMO faults should be reported as store faults */ \
+ throw trap_store_access_fault(t.get_badaddr()); \
+ } \
+ }
+
// store value to memory at aligned address
store_func(uint8)
store_func(uint16)
store_func(uint32)
store_func(uint64)
+ // perform an atomic memory operation at an aligned address
+ amo_func(uint32)
+ amo_func(uint64)
+
static const reg_t ICACHE_ENTRIES = 1024;
inline size_t icache_index(reg_t addr)
inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry)
{
- const uint16_t* iaddr = translate_insn_addr(addr);
- insn_bits_t insn = *iaddr;
+ auto tlb_entry = translate_insn_addr(addr);
+ insn_bits_t insn = *(uint16_t*)(tlb_entry.host_offset + addr);
int length = insn_length(insn);
if (likely(length == 4)) {
- insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr(addr + 2) << 16;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(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;
+ insn |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 4) << 32;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(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 |= (insn_bits_t)*(const int16_t*)translate_insn_addr_to_host(addr + 6) << 48;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(addr + 4) << 32;
+ insn |= (insn_bits_t)*(const uint16_t*)translate_insn_addr_to_host(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);
+ reg_t paddr = tlb_entry.target_offset + addr;;
if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) {
entry->tag = -1;
tracer.trace(paddr, length, FETCH);
static const reg_t TLB_ENTRIES = 256;
// If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a
// trigger match before completing an access.
- static const reg_t TLB_CHECK_TRIGGERS = 1L<<63;
- char* tlb_data[TLB_ENTRIES];
+ static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63;
+ tlb_entry_t 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);
+ tlb_entry_t refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, 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, reg_t prv);
// handle uncommon cases: TLB misses, page faults, MMIO
- const uint16_t* fetch_slow_path(reg_t addr);
+ tlb_entry_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
- inline const uint16_t* translate_insn_addr(reg_t addr) {
+ inline tlb_entry_t translate_insn_addr(reg_t addr) {
reg_t vpn = addr >> PGSHIFT;
if (likely(tlb_insn_tag[vpn % TLB_ENTRIES] == vpn))
- return (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr);
+ return tlb_data[vpn % TLB_ENTRIES];
if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) {
- uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES] + addr);
+ uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr);
int match = proc->trigger_match(OPERATION_EXECUTE, addr, *ptr);
if (match >= 0)
throw trigger_matched_t(match, OPERATION_EXECUTE, addr, *ptr);
- return ptr;
+ return tlb_data[vpn % TLB_ENTRIES];
}
return fetch_slow_path(addr);
}
+ inline const uint16_t* translate_insn_addr_to_host(reg_t addr) {
+ return (uint16_t*)(translate_insn_addr(addr).host_offset + addr);
+ }
+
inline trigger_matched_t *trigger_exception(trigger_operation_t operation,
reg_t address, reg_t data)
{
friend class processor_t;
};
+struct vm_info {
+ int levels;
+ int idxbits;
+ int ptesize;
+ reg_t ptbase;
+};
+
+inline vm_info decode_vm_info(int xlen, reg_t prv, reg_t sptbr)
+{
+ if (prv == PRV_M) {
+ return {0, 0, 0, 0};
+ } else if (prv <= PRV_S && xlen == 32) {
+ switch (get_field(sptbr, SPTBR32_MODE)) {
+ case SPTBR_MODE_OFF: return {0, 0, 0, 0};
+ case SPTBR_MODE_SV32: return {2, 10, 4, (sptbr & SPTBR32_PPN) << PGSHIFT};
+ default: abort();
+ }
+ } else if (prv <= PRV_S && xlen == 64) {
+ switch (get_field(sptbr, SPTBR64_MODE)) {
+ case SPTBR_MODE_OFF: return {0, 0, 0, 0};
+ case SPTBR_MODE_SV39: return {3, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT};
+ case SPTBR_MODE_SV48: return {4, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT};
+ case SPTBR_MODE_SV57: return {5, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT};
+ case SPTBR_MODE_SV64: return {6, 9, 8, (sptbr & SPTBR64_PPN) << PGSHIFT};
+ default: abort();
+ }
+ } else {
+ abort();
+ }
+}
+
#endif