Commit preliminary (untested) TLB support.

This commit is contained in:
Tyler Stachecki 2014-08-23 14:23:50 -04:00
parent 925e1e2dcd
commit 5f5d4da9a6
13 changed files with 288 additions and 17 deletions

View file

@ -151,6 +151,7 @@ file(GLOB VI_SOURCES ${PROJECT_SOURCE_DIR}/vi/*.c)
file(GLOB VR4300_SOURCES ${PROJECT_SOURCE_DIR}/vr4300/*.c)
file(GLOB ARCH_FPU_SOURCES arch/${CEN64_ARCH_DIR}/fpu/*.c)
file(GLOB ARCH_RSP_SOURCES arch/${CEN64_ARCH_DIR}/rsp/*.c)
file(GLOB ARCH_TLB_SOURCES arch/${CEN64_ARCH_DIR}/tlb/*.c)
#
# Glob all the files together.
@ -178,7 +179,7 @@ endif (DEFINED WIN32)
add_executable(cen64 ${EXTRA_OS_EXE} ${ASM_SOURCES} ${OS_SOURCES}
${COMMON_SOURCES} ${CEN64_SOURCES} ${AI_SOURCES} ${BUS_SOURCES} ${PI_SOURCES}
${RDP_SOURCES} ${RSP_SOURCES} ${RI_SOURCES} ${SI_SOURCES} ${VI_SOURCES}
${VR4300_SOURCES} ${ARCH_FPU_SOURCES} ${ARCH_RSP_SOURCES})
${VR4300_SOURCES} ${ARCH_FPU_SOURCES} ${ARCH_RSP_SOURCES} ${ARCH_TLB_SOURCES})
target_link_libraries(cen64 ${EXTRA_OS_LIBS} ${OPENGL_gl_LIBRARY})

82
arch/x86_64/tlb/tlb.c Normal file
View file

@ -0,0 +1,82 @@
//
// arch/x86_64/tlb/tlb.c: Translation lookaside buffer.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, 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[i] = ~0;
}
// Probes the TLB for matching entry. Returns the index or -1.
int tlb_probe(struct cen64_tlb *tlb, uint64_t vaddr, uint8_t vasid) {
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 + i + 0));
__m128i page_mask_h = _mm_load_si128((__m128i*) (tlb->page_mask + i + 4));
__m128i vpn_l = _mm_load_si128((__m128i*) (tlb->vpn2 + i + 0));
__m128i vpn_h = _mm_load_si128((__m128i*) (tlb->vpn2 + i + 4));
// Check for matching VPNs.
check_l = _mm_and_si128(vpn_l, page_mask_l);
check_l = _mm_cmpeq_epi32(check_l, vpn);
check_h = _mm_and_si128(vpn_h, page_mask_h);
check_h = _mm_cmpeq_epi32(check_h, vpn);
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)
return i + cen64_one_hot_lut[one_hot_idx & 0xFF];
}
return -1;
}
// 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[index] = page_mask;
tlb->vpn2[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;
}

28
arch/x86_64/tlb/tlb.h Normal file
View file

@ -0,0 +1,28 @@
//
// arch/x86_64/tlb/tlb.h: Translation lookaside buffer.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __arch_tlb_h__
#define __arch_tlb_h__
struct cen64_tlb {
uint32_t page_mask[32];
uint32_t vpn2[32];
uint8_t global[32];
uint8_t asid[32];
};
void tlb_init(struct cen64_tlb *tlb);
int tlb_probe(struct cen64_tlb *tlb, uint64_t vpn2, uint8_t vasid);
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);
#endif

View file

@ -149,5 +149,10 @@ void cen64_return(struct bus_controller *bus)
#cmakedefine VR4300_BUSY_WAIT_DETECTION
#include "common/debug.h"
// Common/shared LUT with one-hot semantics.
// Returns index (0-based) of hot bit, or -1.
extern const int8_t cen64_one_hot_lut[256];
#endif

51
common/one_hot.c Normal file
View file

@ -0,0 +1,51 @@
//
// vr4300/one_hot.c: 8-bit one-hot LUT.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, 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"
cen64_align(const int8_t cen64_one_hot_lut[256], CACHE_LINE_SIZE) = {
-1,
// 1
0,
// 2
1, -1,
// 4
2, -1, -1, -1,
// 8
3, -1, -1, -1, -1, -1, -1, -1,
// 16
4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 32
5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 64
6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 128
7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};

17
os/unix/x86_64/tlb/tlb.h Normal file
View file

@ -0,0 +1,17 @@
//
// os/unix/x86_64/tlb/tlb.h
//
// Extern declarations for host TLB functions.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __os_tlb_h__
#define __os_tlb_h__
#include "common.h"
#include "arch/x86_64/tlb/tlb.h"
#endif

View file

@ -0,0 +1,17 @@
//
// os/windows/x86_64/tlb/tlb.h
//
// Extern declarations for host TLB functions.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __os_tlb_h__
#define __os_tlb_h__
#include "common.h"
#include "arch/x86_64/tlb/tlb.h"
#endif

View file

@ -9,9 +9,49 @@
//
#include "common.h"
#include "tlb/tlb.h"
#include "vr4300/cp0.h"
#include "vr4300/cpu.h"
static const uint64_t vr4300_cp0_reg_masks[32] = {
0x000000008000003FULL, // 0: VR4300_CP0_REGISTER_INDEX
0x000000000000003FULL, // 1: VR4300_CP0_REGISTER_RANDOM
0x000000007FFFFFFFULL, // 2: VR4300_CP0_REGISTER_ENTRYLO0
0x000000007FFFFFFFULL, // 3: VR4300_CP0_REGISTER_ENTRYLO1
0xFFFFFFFFFFFFFFF0ULL, // 4: VR4300_CP0_REGISTER_CONTEXT
0x0000000001FFE000ULL, // 5: VR4300_CP0_REGISTER_PAGEMASK
0xFFFFFFFFFFFFFFFFULL, // 6: VR4300_CP0_REGISTER_WIRED
0x000000000000003FULL, // 7:
0xFFFFFFFFFFFFFFFFULL, // 8: VR4300_CP0_REGISTER_BADVADDR
0x00000000FFFFFFFFULL, // 9: VR4300_CP0_REGISTER_COUNT
0xC00000FFFFFFE0FFULL, // 10: VR4300_CP0_REGISTER_ENTRYHI
0x00000000FFFFFFFFULL, // 11: VR4300_CP0_REGISTER_COMPARE
0x00000000FFFFFFFFULL, // 12: VR4300_CP0_REGISTER_STATUS
0x00000000B000FFFFULL, // 13: VR4300_CP0_REGISTER_CAUSE
0xFFFFFFFFFFFFFFFFULL, // 14: VR4300_CP0_REGISTER_EPC
0x000000000000FFFFULL, // 15; VR4300_CP0_REGISTER_PRID
0x000000007FFFFFFFULL, // 16: VR4300_CP0_REGISTER_CONFIG
0x00000000FFFFFFFFULL, // 17: VR4300_CP0_REGISTER_LLADDR
0x00000000FFFFFFFBULL, // 18: VR4300_CP0_REGISTER_WATCHLO
0x000000000000000FULL, // 19: VR4300_CP0_REGISTER_WATCHHI
0xFFFFFFFFFFFFFFFFULL, // 20: VR4300_CP0_REGISTER_XCONTEXT
0xFFFFFFFFFFFFFFFFULL, // 21:
0xFFFFFFFFFFFFFFFFULL, // 22:
0xFFFFFFFFFFFFFFFFULL, // 23:
0xFFFFFFFFFFFFFFFFULL, // 24:
0xFFFFFFFFFFFFFFFFULL, // 25:
0x0000000000000000ULL, // 26: VR4300_CP0_REGISTER_PARITYERROR
0x0000000000000000ULL, // 27: VR4300_CP0_REGISTER_CACHEERR
0x000000000FFFFFC0ULL, // 28: VR4300_CP0_REGISTER_TAGLO
0x0000000000000000ULL, // 29: VR4300_CP0_REGISTER_TAGHI
0xFFFFFFFFFFFFFFFFULL, // 30: VR4300_CP0_REGISTER_ERROREPC
0xFFFFFFFFFFFFFFFFULL, // 31
};
static inline uint64_t mask_reg(unsigned reg, uint64_t data) {
return vr4300_cp0_reg_masks[reg] & data;
};
//
// ERET
//
@ -49,15 +89,14 @@ int VR4300_ERET(struct vr4300 *vr4300,
//
// MFC0
// TODO/FIXME: Combine with MFC{1,2}?
//
int VR4300_MFC0(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
unsigned src = GET_RD(iw) + 32;
unsigned dest = GET_RT(iw);
unsigned src = GET_RD(iw);
exdc_latch->result = (int32_t) vr4300->regs[src];
exdc_latch->result = mask_reg(src, vr4300->regs[32 + src]);
exdc_latch->dest = dest;
return 0;
}
@ -67,20 +106,45 @@ int VR4300_MFC0(struct vr4300 *vr4300,
//
int VR4300_MTC0(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
unsigned dest = GET_RD(iw) + 32;
unsigned dest = 32 + GET_RD(iw);
if (dest == VR4300_CP0_REGISTER_COMPARE)
vr4300->regs[VR4300_CP0_REGISTER_CAUSE] &= ~0x8000;
// TODO/FIXME: Sign extend, or...?
// Would make sense for EPC, etc.
//exdc_latch->result = (int32_t) rt;
//exdc_latch->dest = dest;
vr4300->regs[dest] = (int32_t) rt;
vr4300->regs[dest] = rt;
return 0;
}
//
// TLBP
//
int VR4300_TLBP(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
uint64_t entry_hi = mask_reg(10, vr4300->regs[VR4300_CP0_REGISTER_ENTRYHI]);
if (tlb_probe(&vr4300->tlb, entry_hi, entry_hi & 0xFF) != -1)
vr4300->regs[VR4300_CP0_REGISTER_INDEX] |= 0x80000000;
return 0;
}
//
// TLBWI
//
int VR4300_TLBWI(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
uint64_t entry_hi = mask_reg(10, vr4300->regs[VR4300_CP0_REGISTER_ENTRYHI]);
uint64_t entry_lo_0 = mask_reg(2, vr4300->regs[VR4300_CP0_REGISTER_ENTRYLO0]);
uint64_t entry_lo_1 = mask_reg(3, vr4300->regs[VR4300_CP0_REGISTER_ENTRYLO1]);
uint32_t page_mask = mask_reg(5, vr4300->regs[VR4300_CP0_REGISTER_PAGEMASK]);
unsigned index = vr4300->regs[VR4300_CP0_REGISTER_INDEX] & 0x3F;
tlb_write(&vr4300->tlb, index, entry_hi, entry_lo_0, entry_lo_1, page_mask);
return 0;
}
// Initializes the coprocessor.
void vr4300_cp0_init(struct vr4300 *vr4300) {
tlb_init(&vr4300->tlb);
}

View file

@ -47,6 +47,8 @@ enum vr4300_cp0_register {
int VR4300_ERET(struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t rt);
int VR4300_MFC0(struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t rt);
int VR4300_MTC0(struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t rt);
int VR4300_TLBP(struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t rt);
int VR4300_TLBWI(struct vr4300 *vr4300, uint32_t iw, uint64_t rs, uint64_t rt);
void vr4300_cp0_init(struct vr4300 *vr4300);

View file

@ -35,10 +35,11 @@ int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus) {
vr4300_cp0_init(vr4300);
vr4300_cp1_init(vr4300);
vr4300_dcache_init(&vr4300->dcache);
vr4300_icache_init(&vr4300->icache);
vr4300_pipeline_init(&vr4300->pipeline);
vr4300_pipeline_init(&vr4300->pipeline);
vr4300->signals = VR4300_SIGNAL_COLDRESET;
// MESS uses this version, so we will too?

View file

@ -11,6 +11,7 @@
#ifndef __vr4300_cpu_h__
#define __vr4300_cpu_h__
#include "common.h"
#include "tlb/tlb.h"
#include "vr4300/cp0.h"
#include "vr4300/cp1.h"
#include "vr4300/dcache.h"
@ -90,6 +91,11 @@ extern const char *mi_register_mnemonics[NUM_MI_REGISTERS];
struct vr4300 {
struct vr4300_pipeline pipeline;
// Align the TLB to a 16-byte boundary for vectorization.
uint8_t padding_for_tlb[(16 - (sizeof(struct vr4300_pipeline) % 16)) % 16];
struct cen64_tlb tlb;
struct vr4300_cp1 cp1;
struct bus_controller *bus;

View file

@ -771,10 +771,7 @@ int VR4300_INV(struct vr4300 *vr4300,
rfex_latch->common.pc);
// TODO/FIXME: Implement this instruction later.
if (opcode == VR4300_OPCODE_TLBP)
vr4300->regs[VR4300_CP0_REGISTER_INDEX] = (int32_t) 0x80000000;
else if (opcode != VR4300_OPCODE_TLBWI && opcode != VR4300_OPCODE_TLBR)
if (opcode != VR4300_OPCODE_TLBR)
assert(0 && "Unimplemented instruction encountered.");
return 0;

View file

@ -172,9 +172,9 @@ extern const char *vr4300_opcode_mnemonics[NUM_VR4300_OPCODES];
#define CACHE VR4300_BUILD_OP(CACHE, CACHE, INFO1(NEEDRS))
#define ERET VR4300_BUILD_OP(ERET, ERET, INFO1(NONE))
#define TLBP VR4300_BUILD_OP(TLBP, INV, INFO1(NONE))
#define TLBP VR4300_BUILD_OP(TLBP, TLBP, INFO1(NONE))
#define TLBR VR4300_BUILD_OP(TLBR, INV, INFO1(NONE))
#define TLBWI VR4300_BUILD_OP(TLBWI, INV, INFO1(NONE))
#define TLBWI VR4300_BUILD_OP(TLBWI, TLBWI, INFO1(NONE))
#define TLBWR VR4300_BUILD_OP(TLBWR, INV, INFO1(NONE))
#define BC1 VR4300_BUILD_OP(BC1, BC1, INFO1(BRANCH))