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()
{
memset(tlb_insn_tag, -1, sizeof(tlb_insn_tag));
memset(tlb_load_tag, -1, sizeof(tlb_load_tag));
memset(tlb_store_tag, -1, sizeof(tlb_store_tag));
memset(tlb_insn, -1, sizeof(tlb_insn));
memset(tlb_load, -1, sizeof(tlb_load));
memset(tlb_store, -1, sizeof(tlb_store));
flush_icache();
}
@ -76,7 +76,7 @@ tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
tlb_entry_t result;
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]));
if (auto host_addr = sim->addr_to_mem(paddr)) {
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)};
}
} 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))));
@ -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 transformed_addr = access_info.transformed_vaddr;
reg_t vpn = transformed_addr >> PGSHIFT;
if (!access_info.flags.is_special_access() && vpn == (tlb_load_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) {
auto host_addr = (const void*)(tlb_data[vpn % TLB_ENTRIES].host_addr + (transformed_addr % PGSIZE));
if (!access_info.flags.is_special_access() && vpn == (tlb_load[vpn % TLB_ENTRIES].tag & ~TLB_CHECK_TRIGGERS)) {
auto host_addr = (const void*)(tlb_load[vpn % TLB_ENTRIES].data.host_addr + (transformed_addr % PGSIZE));
memcpy(bytes, host_addr, len);
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 transformed_addr = access_info.transformed_vaddr;
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) {
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);
}
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)};
if (in_mprv())
if (in_mprv() || !pmp_homogeneous(paddr & ~reg_t(PGSIZE - 1), PGSIZE))
return entry;
if ((tlb_load_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
tlb_load_tag[idx] = -1;
if ((tlb_store_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
tlb_store_tag[idx] = -1;
if ((tlb_insn_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
tlb_insn_tag[idx] = -1;
if ((check_triggers_fetch && type == FETCH) ||
(check_triggers_load && type == LOAD) ||
(check_triggers_store && type == STORE))
expected_tag |= TLB_CHECK_TRIGGERS;
if (pmp_homogeneous(paddr & ~reg_t(PGSIZE - 1), PGSIZE)) {
if (type == FETCH) tlb_insn_tag[idx] = expected_tag;
else if (type == STORE) tlb_store_tag[idx] = expected_tag;
else tlb_load_tag[idx] = expected_tag;
switch (type) {
case FETCH:
tlb_insn[idx].data = entry;
tlb_insn[idx].tag = expected_tag | (check_triggers_fetch ? TLB_CHECK_TRIGGERS : 0);
break;
case LOAD:
tlb_load[idx].data = entry;
tlb_load[idx].tag = expected_tag | (check_triggers_load ? TLB_CHECK_TRIGGERS : 0);
break;
case STORE:
tlb_store[idx].data = entry;
tlb_store[idx].tag = expected_tag | (check_triggers_store ? TLB_CHECK_TRIGGERS : 0);
break;
default:
abort();
}
tlb_data[idx] = entry;
return entry;
}

24
riscv/mmu.h

@ -37,6 +37,11 @@ struct tlb_entry_t {
reg_t target_addr;
};
struct dtlb_entry_t {
tlb_entry_t data;
reg_t tag;
};
struct xlate_flags_t {
const bool forced_virt : 1 {false};
const bool hlvx : 1 {false};
@ -77,10 +82,10 @@ public:
target_endian<T> res;
reg_t vpn = addr >> PGSHIFT;
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)) {
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 {
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 = {}) {
reg_t vpn = addr >> PGSHIFT;
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)) {
*(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 {
target_endian<T> target_val = to_target(val);
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
// trigger match before completing an access.
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];
dtlb_entry_t tlb_load[TLB_ENTRIES];
dtlb_entry_t tlb_store[TLB_ENTRIES];
dtlb_entry_t tlb_insn[TLB_ENTRIES];
// temporary location to store instructions fetched from an MMIO region
uint16_t fetch_temp[PGSIZE / sizeof(uint16_t)];
@ -477,8 +481,8 @@ private:
// ITLB lookup
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 tlb_data[vpn % TLB_ENTRIES];
if (likely(tlb_insn[vpn % TLB_ENTRIES].tag == vpn))
return tlb_insn[vpn % TLB_ENTRIES].data;
return fetch_slow_path(addr);
}

Loading…
Cancel
Save