0ad/source/lib/sysdep/win/whrt/tsc.cpp
janwas f331b7313d fix very very insidious bug with mapping physical memory (TLB corruption). fix weirdness reported by michael (underlying cause: using cpu_ClockFrequency instead of wcpu_*; that made the timer run at freq=0)
mahaf: now disabled on Win2k because we can't prevent the TLB bug there.
removed the CopyPhysical API.
wutil: allow querying windows version via number

View: fix warning

This was SVN commit r5169.
2007-06-12 23:29:27 +00:00

156 lines
4.4 KiB
C++

/**
* =========================================================================
* File : tsc.cpp
* Project : 0 A.D.
* Description : Timer implementation using RDTSC
* =========================================================================
*/
// license: GPL; see lib/license.txt
#include "precompiled.h"
#include "tsc.h"
#include "lib/sysdep/win/win.h"
#include "lib/sysdep/win/wcpu.h"
#include "lib/sysdep/ia32/ia32.h" // ia32_rdtsc
#include "lib/bits.h"
//-----------------------------------------------------------------------------
// detect throttling
enum AmdPowerNowFlags
{
PN_FREQ_ID_CTRL = BIT(1),
PN_SW_THERMAL_CTRL = BIT(5),
PN_INVARIANT_TSC = BIT(8)
};
static bool IsThrottlingPossible()
{
u32 regs[4];
switch(ia32_Vendor())
{
case IA32_VENDOR_INTEL:
if(ia32_cap(IA32_CAP_TM_SCC) || ia32_cap(IA32_CAP_EST))
return true;
break;
case IA32_VENDOR_AMD:
if(ia32_asm_cpuid(0x80000007, regs))
{
if(regs[EDX] & (PN_FREQ_ID_CTRL|PN_SW_THERMAL_CTRL))
return true;
}
break;
}
return false;
}
//-----------------------------------------------------------------------------
LibError CounterTSC::Activate()
{
ia32_Init();
if(!ia32_cap(IA32_CAP_TSC))
return ERR::NO_SYS; // NOWARN (CPU doesn't support RDTSC)
return INFO::OK;
}
void CounterTSC::Shutdown()
{
ia32_Shutdown();
}
bool CounterTSC::IsSafe() const
{
// use of the TSC for timing is subject to a litany of potential problems:
// - separate, unsynchronized counters with offset and drift;
// - frequency changes (P-state transitions and STPCLK throttling);
// - failure to increment in C3 and C4 deep-sleep states.
// we will discuss the specifics below.
// SMP or multi-core => counters are unsynchronized. this could be
// solved by maintaining separate per-core counter states, but that
// requires atomic reads of the TSC and the current processor number.
//
// (otherwise, we have a subtle race condition: if preempted while
// reading the time and rescheduled on a different core, incorrect
// results may be returned, which would be unacceptable.)
//
// unfortunately this isn't possible without OS support or the
// as yet unavailable RDTSCP instruction => unsafe.
//
// (note: if the TSC is invariant, drift is no longer a concern.
// we could synchronize the TSC MSRs during initialization and avoid
// per-core counter state and the abovementioned race condition.
// however, we won't bother, since such platforms aren't yet widespread
// and would surely support the nice and safe HPET, anyway)
if(ia32_NumPackages() != 1 || ia32_CoresPerPackage() != 1)
return false;
// recent CPU:
if(ia32_Generation() >= 7)
{
// note: 8th generation CPUs support C1-clock ramping, which causes
// drift on multi-core systems, but those were excluded above.
u32 regs[4];
if(ia32_asm_cpuid(0x80000007, regs))
{
// TSC is invariant WRT P-state, C-state and STPCLK => safe.
if(regs[EDX] & PN_INVARIANT_TSC)
return true;
}
// in addition to P-state transitions, we're also subject to
// STPCLK throttling. this happens when the chipset thinks the
// system is dangerously overheated; the OS isn't even notified.
// it may be rare, but could cause incorrect results => unsafe.
return false;
// newer systems also support the C3 Deep Sleep state, in which
// the TSC isn't incremented. that's not nice, but irrelevant
// since STPCLK dooms the TSC on those systems anyway.
}
// we're dealing with a single older CPU; the only problem there is
// throttling, i.e. changes to the TSC frequency. we don't want to
// disable this because it may be important for cooling. the OS
// initiates changes but doesn't notify us; jumps are too frequent
// and drastic to detect and account for => unsafe.
if(IsThrottlingPossible())
return false;
return true;
}
u64 CounterTSC::Counter() const
{
return ia32_rdtsc();
}
/**
* WHRT uses this to ensure the counter (running at nominal frequency)
* doesn't overflow more than once during CALIBRATION_INTERVAL_MS.
**/
uint CounterTSC::CounterBits() const
{
return 64;
}
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterTSC::NominalFrequency() const
{
return wcpu_ClockFrequency();
}