Browse Source

Separate ITLB/LTLB/STLB into separate structures

This is a performance enhancement, because it prevents some pathological
conflict cases (e.g. aligned memcpy), but it also cleans up some aspects
of the code (e.g. ITLB refills don't interact with the DTLB).
pull/1966/head
Andrew Waterman 1 year ago
parent
commit
52517f7719
  1. 52
      riscv/mmu.cc
  2. 24
      riscv/mmu.h

52
riscv/mmu.cc

@ -36,9 +36,9 @@ void mmu_t::flush_icache()
void mmu_t::flush_tlb() void mmu_t::flush_tlb()
{ {
memset(tlb_insn_tag, -1, sizeof(tlb_insn_tag)); memset(tlb_insn, -1, sizeof(tlb_insn));
memset(tlb_load_tag, -1, sizeof(tlb_load_tag)); memset(tlb_load, -1, sizeof(tlb_load));
memset(tlb_store_tag, -1, sizeof(tlb_store_tag)); memset(tlb_store, -1, sizeof(tlb_store));
flush_icache(); flush_icache();
} }
@ -76,7 +76,7 @@ tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
tlb_entry_t result; tlb_entry_t result;
reg_t vpn = vaddr >> PGSHIFT; reg_t vpn = vaddr >> PGSHIFT;
if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] != (vpn | TLB_CHECK_TRIGGERS))) { if (unlikely(tlb_insn[vpn % TLB_ENTRIES].tag != (vpn | TLB_CHECK_TRIGGERS))) {
reg_t paddr = translate(access_info, sizeof(fetch_temp[0])); reg_t paddr = translate(access_info, sizeof(fetch_temp[0]));
if (auto host_addr = sim->addr_to_mem(paddr)) { if (auto host_addr = sim->addr_to_mem(paddr)) {
result = refill_tlb(vaddr, paddr, host_addr, FETCH); result = refill_tlb(vaddr, paddr, host_addr, FETCH);
@ -86,7 +86,7 @@ tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
result = {uintptr_t(&fetch_temp), paddr - (vaddr % PGSIZE)}; result = {uintptr_t(&fetch_temp), paddr - (vaddr % PGSIZE)};
} }
} else { } else {
result = tlb_data[vpn % TLB_ENTRIES]; result = tlb_insn[vpn % TLB_ENTRIES].data;
} }
check_triggers(triggers::OPERATION_EXECUTE, vaddr, access_info.effective_virt, from_le(*(const uint16_t*)(result.host_addr + (vaddr % PGSIZE)))); check_triggers(triggers::OPERATION_EXECUTE, vaddr, access_info.effective_virt, from_le(*(const uint16_t*)(result.host_addr + (vaddr % PGSIZE))));
@ -196,8 +196,8 @@ void mmu_t::load_slow_path_intrapage(reg_t len, uint8_t* bytes, mem_access_info_
reg_t addr = access_info.vaddr; reg_t addr = access_info.vaddr;
reg_t transformed_addr = access_info.transformed_vaddr; reg_t transformed_addr = access_info.transformed_vaddr;
reg_t vpn = transformed_addr >> PGSHIFT; reg_t vpn = transformed_addr >> PGSHIFT;
if (!access_info.flags.is_special_access() && vpn == (tlb_load_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) { if (!access_info.flags.is_special_access() && vpn == (tlb_load[vpn % TLB_ENTRIES].tag & ~TLB_CHECK_TRIGGERS)) {
auto host_addr = (const void*)(tlb_data[vpn % TLB_ENTRIES].host_addr + (transformed_addr % PGSIZE)); auto host_addr = (const void*)(tlb_load[vpn % TLB_ENTRIES].data.host_addr + (transformed_addr % PGSIZE));
memcpy(bytes, host_addr, len); memcpy(bytes, host_addr, len);
return; return;
} }
@ -263,9 +263,9 @@ void mmu_t::store_slow_path_intrapage(reg_t len, const uint8_t* bytes, mem_acces
reg_t addr = access_info.vaddr; reg_t addr = access_info.vaddr;
reg_t transformed_addr = access_info.transformed_vaddr; reg_t transformed_addr = access_info.transformed_vaddr;
reg_t vpn = transformed_addr >> PGSHIFT; reg_t vpn = transformed_addr >> PGSHIFT;
if (!access_info.flags.is_special_access() && vpn == (tlb_store_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) { if (!access_info.flags.is_special_access() && vpn == (tlb_store[vpn % TLB_ENTRIES].tag & ~TLB_CHECK_TRIGGERS)) {
if (actually_store) { if (actually_store) {
auto host_addr = (void*)(tlb_data[vpn % TLB_ENTRIES].host_addr + (transformed_addr % PGSIZE)); auto host_addr = (void*)(tlb_store[vpn % TLB_ENTRIES].data.host_addr + (transformed_addr % PGSIZE));
memcpy(host_addr, bytes, len); memcpy(host_addr, bytes, len);
} }
return; return;
@ -327,28 +327,26 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_
tlb_entry_t entry = {uintptr_t(host_addr) - (vaddr % PGSIZE), paddr - (vaddr % PGSIZE)}; tlb_entry_t entry = {uintptr_t(host_addr) - (vaddr % PGSIZE), paddr - (vaddr % PGSIZE)};
if (in_mprv()) if (in_mprv() || !pmp_homogeneous(paddr & ~reg_t(PGSIZE - 1), PGSIZE))
return entry; return entry;
if ((tlb_load_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag) switch (type) {
tlb_load_tag[idx] = -1; case FETCH:
if ((tlb_store_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag) tlb_insn[idx].data = entry;
tlb_store_tag[idx] = -1; tlb_insn[idx].tag = expected_tag | (check_triggers_fetch ? TLB_CHECK_TRIGGERS : 0);
if ((tlb_insn_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag) break;
tlb_insn_tag[idx] = -1; case LOAD:
tlb_load[idx].data = entry;
if ((check_triggers_fetch && type == FETCH) || tlb_load[idx].tag = expected_tag | (check_triggers_load ? TLB_CHECK_TRIGGERS : 0);
(check_triggers_load && type == LOAD) || break;
(check_triggers_store && type == STORE)) case STORE:
expected_tag |= TLB_CHECK_TRIGGERS; tlb_store[idx].data = entry;
tlb_store[idx].tag = expected_tag | (check_triggers_store ? TLB_CHECK_TRIGGERS : 0);
if (pmp_homogeneous(paddr & ~reg_t(PGSIZE - 1), PGSIZE)) { break;
if (type == FETCH) tlb_insn_tag[idx] = expected_tag; default:
else if (type == STORE) tlb_store_tag[idx] = expected_tag; abort();
else tlb_load_tag[idx] = expected_tag;
} }
tlb_data[idx] = entry;
return entry; return entry;
} }

24
riscv/mmu.h

@ -37,6 +37,11 @@ struct tlb_entry_t {
reg_t target_addr; reg_t target_addr;
}; };
struct dtlb_entry_t {
tlb_entry_t data;
reg_t tag;
};
struct xlate_flags_t { struct xlate_flags_t {
const bool forced_virt : 1 {false}; const bool forced_virt : 1 {false};
const bool hlvx : 1 {false}; const bool hlvx : 1 {false};
@ -77,10 +82,10 @@ public:
target_endian<T> res; target_endian<T> res;
reg_t vpn = addr >> PGSHIFT; reg_t vpn = addr >> PGSHIFT;
bool aligned = (addr & (sizeof(T) - 1)) == 0; bool aligned = (addr & (sizeof(T) - 1)) == 0;
bool tlb_hit = tlb_load_tag[vpn % TLB_ENTRIES] == vpn; bool tlb_hit = tlb_load[vpn % TLB_ENTRIES].tag == vpn;
if (likely(!xlate_flags.is_special_access() && aligned && tlb_hit)) { if (likely(!xlate_flags.is_special_access() && aligned && tlb_hit)) {
res = *(target_endian<T>*)(tlb_data[vpn % TLB_ENTRIES].host_addr + (addr % PGSIZE)); res = *(target_endian<T>*)(tlb_load[vpn % TLB_ENTRIES].data.host_addr + (addr % PGSIZE));
} else { } else {
load_slow_path(addr, sizeof(T), (uint8_t*)&res, xlate_flags); load_slow_path(addr, sizeof(T), (uint8_t*)&res, xlate_flags);
} }
@ -118,10 +123,10 @@ public:
void ALWAYS_INLINE store(reg_t addr, T val, xlate_flags_t xlate_flags = {}) { void ALWAYS_INLINE store(reg_t addr, T val, xlate_flags_t xlate_flags = {}) {
reg_t vpn = addr >> PGSHIFT; reg_t vpn = addr >> PGSHIFT;
bool aligned = (addr & (sizeof(T) - 1)) == 0; bool aligned = (addr & (sizeof(T) - 1)) == 0;
bool tlb_hit = tlb_store_tag[vpn % TLB_ENTRIES] == vpn; bool tlb_hit = tlb_store[vpn % TLB_ENTRIES].tag == vpn;
if (!xlate_flags.is_special_access() && likely(aligned && tlb_hit)) { if (!xlate_flags.is_special_access() && likely(aligned && tlb_hit)) {
*(target_endian<T>*)(tlb_data[vpn % TLB_ENTRIES].host_addr + (addr % PGSIZE)) = to_target(val); *(target_endian<T>*)(tlb_store[vpn % TLB_ENTRIES].data.host_addr + (addr % PGSIZE)) = to_target(val);
} else { } else {
target_endian<T> target_val = to_target(val); target_endian<T> target_val = to_target(val);
store_slow_path(addr, sizeof(T), (const uint8_t*)&target_val, xlate_flags, true, false); store_slow_path(addr, sizeof(T), (const uint8_t*)&target_val, xlate_flags, true, false);
@ -392,10 +397,9 @@ private:
// If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a // If a TLB tag has TLB_CHECK_TRIGGERS set, then the MMU must check for a
// trigger match before completing an access. // trigger match before completing an access.
static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63; static const reg_t TLB_CHECK_TRIGGERS = reg_t(1) << 63;
tlb_entry_t tlb_data[TLB_ENTRIES]; dtlb_entry_t tlb_load[TLB_ENTRIES];
reg_t tlb_insn_tag[TLB_ENTRIES]; dtlb_entry_t tlb_store[TLB_ENTRIES];
reg_t tlb_load_tag[TLB_ENTRIES]; dtlb_entry_t tlb_insn[TLB_ENTRIES];
reg_t tlb_store_tag[TLB_ENTRIES];
// temporary location to store instructions fetched from an MMIO region // temporary location to store instructions fetched from an MMIO region
uint16_t fetch_temp[PGSIZE / sizeof(uint16_t)]; uint16_t fetch_temp[PGSIZE / sizeof(uint16_t)];
@ -477,8 +481,8 @@ private:
// ITLB lookup // ITLB lookup
inline tlb_entry_t translate_insn_addr(reg_t addr) { inline tlb_entry_t translate_insn_addr(reg_t addr) {
reg_t vpn = addr >> PGSHIFT; reg_t vpn = addr >> PGSHIFT;
if (likely(tlb_insn_tag[vpn % TLB_ENTRIES] == vpn)) if (likely(tlb_insn[vpn % TLB_ENTRIES].tag == vpn))
return tlb_data[vpn % TLB_ENTRIES]; return tlb_insn[vpn % TLB_ENTRIES].data;
return fetch_slow_path(addr); return fetch_slow_path(addr);
} }

Loading…
Cancel
Save