cen64/arch/x86_64/tlb/tlb.c
Derek "Turtle" Roe c4afd44ed7 See long description
Replaced all references to simulation with emulation
Updated copyright year
Updated .gitignore to reduce chances of random files being uploaded to
the repo
Added .gitattributes to normalize all text files, and to ignore binary
files (which includes the logo and the NEC PDF)
2015-07-03 08:18:16 -04:00

98 lines
2.9 KiB
C

//
// arch/x86_64/tlb/tlb.c: Translation lookaside buffer.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#include "common.h"
#include "arch/x86_64/tlb/tlb.h"
#include <emmintrin.h>
// Initializes the TLB with invalid entries.
void tlb_init(struct cen64_tlb *tlb) {
unsigned i;
for (i = 0; i < 32; i++)
tlb->vpn2.data[i] = ~0;
}
// Probes the TLB for matching entry. Returns the index or -1.
unsigned tlb_probe(const struct cen64_tlb *tlb,
uint64_t vaddr, uint8_t vasid, unsigned *index) {
int one_hot_idx;
uint32_t vpn2;
unsigned i;
vpn2 =
(vaddr >> 35 & 0x18000000U) |
(vaddr >> 13 & 0x7FFFFFF);
__m128i vpn = _mm_set1_epi32(vpn2);
__m128i asid = _mm_set1_epi8(vasid);
// Scan 8 entries in parallel.
for (i = 0; i < 32 / 8; i += 8) {
__m128i check_l, check_h, vpn_check;
__m128i check_a, check_g, asid_check;
__m128i check;
__m128i page_mask_l = _mm_load_si128((__m128i*) (tlb->page_mask.data + i + 0));
__m128i page_mask_h = _mm_load_si128((__m128i*) (tlb->page_mask.data + i + 4));
__m128i vpn_l = _mm_load_si128((__m128i*) (tlb->vpn2.data + i + 0));
__m128i vpn_h = _mm_load_si128((__m128i*) (tlb->vpn2.data + i + 4));
// Check for matching VPNs.
check_l = _mm_and_si128(vpn, page_mask_l);
check_l = _mm_cmpeq_epi32(check_l, vpn_l);
check_h = _mm_and_si128(vpn, page_mask_h);
check_h = _mm_cmpeq_epi32(check_h, vpn_h);
vpn_check = _mm_packs_epi32(check_l, check_h);
vpn_check = _mm_packs_epi16(vpn_check, vpn_check);
// Check for matching ASID/global, too.
check_g = _mm_loadl_epi64((__m128i*) (tlb->global + i));
check_a = _mm_loadl_epi64((__m128i*) (tlb->asid + i));
asid_check = _mm_cmpeq_epi8(check_a, asid);
asid_check = _mm_or_si128(check_g, asid_check);
// Match only on VPN match && (asid match || global)
check = _mm_and_si128(vpn_check, asid_check);
if ((one_hot_idx = _mm_movemask_epi8(check)) != 0) {
*index = i + cen64_one_hot_lut[one_hot_idx & 0xFF];
return 0;
}
}
*index = 0;
return 1;
}
// Reads data from the specified TLB index.
int tlb_read(const struct cen64_tlb *tlb, unsigned index, uint64_t *entry_hi) {
*entry_hi =
((tlb->vpn2.data[index] & 0x18000000LLU) << 35) |
((tlb->vpn2.data[index] & 0x7FFFFFFLLU) << 13) |
((tlb->global[index] & 1) << 12) |
(tlb->asid[index]);
return 0;
}
// Writes an entry to the TLB.
int tlb_write(struct cen64_tlb *tlb, unsigned index, uint64_t entry_hi,
uint64_t entry_lo_0, uint64_t entry_lo_1, uint32_t page_mask) {
tlb->page_mask.data[index] = ~(page_mask >> 13);
tlb->vpn2.data[index] =
(entry_hi >> 35 & 0x18000000U) |
(entry_hi >> 13 & 0x7FFFFFF);
tlb->global[index] = (entry_lo_0 & 0x1) && (entry_lo_1 & 0x1) ? 0xFF : 0x00;
tlb->asid[index] = entry_hi & 0xFF;
return 0;
}