Files
Xenia-Canary/src/xenia/base/clock_x64.cc
Joel Linn 15d422d988 [Base] Optional raw Clock source.
New cvar clock_source_raw allows to use the cpu cycle counter itself as an alternative time source, if system timing resolution is to low and causes problems.
2019-12-01 17:11:58 -06:00

106 lines
3.7 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/platform.h"
#if XE_ARCH_AMD64
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
// Wrap all these different cpu compiler intrinsics.
// So no inline assembler here and the compiler will remove the clutter.
#if XE_COMPILER_MSVC
#define xe_cpu_cpuid(level, eax, ebx, ecx, edx) \
{ \
int __xe_cpuid_registers_[4]; \
__cpuid(__xe_cpuid_registers_, (level)); \
(eax) = static_cast<uint32_t>(__xe_cpuid_registers_[0]); \
(ebx) = static_cast<uint32_t>(__xe_cpuid_registers_[1]); \
(ecx) = static_cast<uint32_t>(__xe_cpuid_registers_[2]); \
(edx) = static_cast<uint32_t>(__xe_cpuid_registers_[3]); \
}
#define xe_cpu_rdtsc() __rdtsc()
#elif XE_COMPILER_CLANG || XE_COMPILER_GNUC
#include <cpuid.h>
#define xe_cpu_cpuid(level, eax, ebx, ecx, edx) \
__cpuid((level), (eax), (ebx), (ecx), (edx));
#define xe_cpu_rdtsc() __rdtsc()
#else
#error "No cpu instruction wrappers for current compiler implemented."
#endif
#define CLOCK_FATAL(msg) \
xe::FatalError( \
"The raw clock source is not supported on your CPU. \n" \
"%s \n" \
"Set the cvar 'clock_source_raw' to 'false'.", \
(msg));
namespace xe {
// Getting the TSC frequency can be a bit tricky. This method here only works on
// Intel as it seems. There is no easy way to get the frequency outside of ring0
// on AMD, so we fail gracefully if not possible.
uint64_t Clock::host_tick_frequency_raw() {
uint32_t eax, ebx, ecx, edx;
// 00H Get max supported cpuid level.
xe_cpu_cpuid(0x0, eax, ebx, ecx, edx);
auto max_cpuid = eax;
// 80000000H Get max extended cpuid level
xe_cpu_cpuid(0x80000000, eax, ebx, ecx, edx);
auto max_cpuid_ex = eax;
// 80000007H Get extended power feature info
if (max_cpuid_ex >= 0x80000007) {
xe_cpu_cpuid(0x80000007, eax, ebx, ecx, edx);
// Invariant TSC bit at position 8
auto tsc_invariant = edx & (1 << 8);
// If the TSC is not invariant it will change its frequency with power
// states and across cores.
if (!tsc_invariant) {
CLOCK_FATAL("The CPU has no invariant TSC.");
return 0;
}
} else {
CLOCK_FATAL("Unclear if the CPU has an invariant TSC.")
return 0;
}
if (max_cpuid >= 0x15) {
// 15H Get TSC/Crystal ratio and Crystal Hz.
xe_cpu_cpuid(0x15, eax, ebx, ecx, edx);
uint64_t ratio_num = ebx;
uint64_t ratio_den = eax;
uint64_t cryst_freq = ecx;
// For some CPUs, Crystal frequency is not reported.
if (ratio_num && ratio_den && cryst_freq) {
// If it is, calculate the TSC frequency
auto tsc_freq = cryst_freq * ratio_num / ratio_den;
}
}
if (max_cpuid >= 0x16) {
// 16H Get CPU base frequency MHz in EAX.
xe_cpu_cpuid(0x16, eax, ebx, ecx, edx);
uint64_t cpu_base_freq = static_cast<uint64_t>(eax) * 1000000;
assert(cpu_base_freq);
return cpu_base_freq;
}
CLOCK_FATAL("The clock frequency could not be determined.");
return 0;
}
uint64_t Clock::host_tick_count_raw() { return xe_cpu_rdtsc(); }
} // namespace xe
#endif