Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
tsc.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Timer implementation using RDTSC
25  */
26 
27 #include "precompiled.h"
29 
31 
32 #include "lib/bits.h"
33 #include "lib/sysdep/acpi.h"
34 #include "lib/sysdep/os_cpu.h"
35 #include "lib/sysdep/os/win/win.h"
37 
38 #if ARCH_X86_X64
39 # include "lib/sysdep/arch/x86_x64/x86_x64.h" // x86_x64::rdtsc
42 #endif
43 
44 
45 //-----------------------------------------------------------------------------
46 
47 static bool IsUniprocessor()
48 {
49  if(topology::NumPackages() != 1)
50  return false;
51  if(topology::CoresPerPackage() != 1)
52  return false;
53  return true;
54 }
55 
56 
57 static bool IsInvariantTSC()
58 {
59 #if ARCH_X86_X64
60  // (we no longer need to check x86_x64::Vendor - Intel and AMD
61  // agreed on the definition of this feature check)
62  x86_x64::CpuidRegs regs = { 0 };
63  regs.eax = 0x80000007;
64  if(x86_x64::cpuid(&regs))
65  {
66  // TSC is invariant across P-state, C-state, turbo, and
67  // stop grant transitions (e.g. STPCLK)
68  if(regs.edx & BIT(8))
69  return true;
70  }
71 #endif
72 
73  return false;
74 }
75 
76 
77 static bool IsThrottlingPossible()
78 {
79 #if ARCH_X86_X64
80  x86_x64::CpuidRegs regs = { 0 };
81  switch(x86_x64::Vendor())
82  {
85  return true;
86  break;
87 
89  regs.eax = 0x80000007;
90  if(x86_x64::cpuid(&regs))
91  {
92  enum AmdPowerNowFlags
93  {
94  PN_FREQ_ID_CTRL = BIT(1),
95  PN_HW_THERMAL_CTRL = BIT(4),
96  PN_SW_THERMAL_CTRL = BIT(5)
97  };
98  if(regs.edx & (PN_FREQ_ID_CTRL|PN_HW_THERMAL_CTRL|PN_SW_THERMAL_CTRL))
99  return true;
100  }
101  break;
102 
103  default:
104  break;
105  }
106 #endif
107 
108  return false;
109 }
110 
111 
112 static bool IsSandyBridge()
113 {
115  return false;
117  return true;
119  return true;
120  return false;
121 }
122 
123 
124 //-----------------------------------------------------------------------------
125 
126 class CounterTSC : public ICounter
127 {
128 public:
129  virtual const wchar_t* Name() const
130  {
131  return L"TSC";
132  }
133 
135  {
136 #if ARCH_X86_X64
138  return ERR::NOT_SUPPORTED; // NOWARN (CPU doesn't support RDTSC)
139 #endif
140 
141  return INFO::OK;
142  }
143 
144  void Shutdown()
145  {
146  }
147 
148  bool IsSafe() const
149  {
150  // using the TSC for timing is subject to a litany of
151  // potential problems, discussed below:
152 
153  if(IsInvariantTSC())
154  return true;
155 
156  // SMP or multi-core => counters are unsynchronized. both offset and
157  // drift could be solved by maintaining separate per-core
158  // counter states, but that requires atomic reads of the TSC and
159  // the current processor number.
160  //
161  // (otherwise, we have a subtle race condition: if preempted while
162  // reading the time and rescheduled on a different core, incorrect
163  // results may be returned, which would be unacceptable.)
164  //
165  // unfortunately this isn't possible without OS support or the
166  // as yet unavailable RDTSCP instruction => unsafe.
167  //
168  // (note: if the TSC is invariant, drift is no longer a concern.
169  // we could synchronize the TSC MSRs during initialization and avoid
170  // per-core counter state and the race condition mentioned above.
171  // however, we won't bother, since such platforms aren't yet widespread
172  // and would surely support the nice and safe HPET, anyway)
173  if(!IsUniprocessor())
174  return false;
175 
176  const FADT* fadt = (const FADT*)acpi_GetTable("FACP");
177  if(fadt)
178  {
179  ENSURE(fadt->header.size >= sizeof(FADT));
180 
181  // TSC isn't incremented in deep-sleep states => unsafe.
182  if(fadt->IsC3Supported())
183  return false;
184 
185  // frequency throttling possible => unsafe.
186  if(fadt->IsDutyCycleSupported())
187  return false;
188  }
189 
190 #if ARCH_X86_X64
191  // recent CPU:
192  //if(x86_x64::Generation() >= 7)
193  {
194  // note: 8th generation CPUs support C1-clock ramping, which causes
195  // drift on multi-core systems, but those were excluded above.
196 
197  // in addition to frequency changes due to P-state transitions,
198  // we're also subject to STPCLK throttling. this happens when
199  // the chipset thinks the system is dangerously overheated; the
200  // OS isn't even notified. this may be rare, but could cause
201  // incorrect results => unsafe.
202  //return false;
203  }
204 #endif
205 
206  // we're dealing with a single older CPU; the only problem there is
207  // throttling, i.e. changes to the TSC frequency. we don't want to
208  // disable this because it may be important for cooling. the OS
209  // initiates changes but doesn't notify us; jumps are too frequent
210  // and drastic to detect and account for => unsafe.
212  return false;
213 
214  return true;
215  }
216 
217  u64 Counter() const
218  {
219  return x86_x64::rdtsc();
220  }
221 
222  size_t CounterBits() const
223  {
224  return 64;
225  }
226 
227  double NominalFrequency() const
228  {
229  // WARNING: do not call x86_x64::ClockFrequency because it uses the
230  // HRT, which we're currently in the process of initializing.
231  // instead query CPU clock frequency via OS.
232  //
233  // note: even here, initial accuracy isn't critical because the
234  // clock is subject to thermal drift and would require continual
235  // recalibration anyway.
236 #if ARCH_X86_X64
238  {
239  const i64 busFrequency = IsSandyBridge()? 100000000 : 133333333;
240  const u64 platformInfo = MSR::Read(MSR::PLATFORM_INFO);
241  const u8 maxNonTurboRatio = bits(platformInfo, 8, 15);
242  return double(maxNonTurboRatio) * busFrequency;
243  }
244  else
245 #endif
246  return os_cpu_ClockFrequency();
247  }
248 
249  double Resolution() const
250  {
251  return 1.0 / NominalFrequency();
252  }
253 };
254 
255 ICounter* CreateCounterTSC(void* address, size_t size)
256 {
257  ENSURE(sizeof(CounterTSC) <= size);
258  return new(address) CounterTSC();
259 }
#define u8
Definition: types.h:39
double NominalFrequency() const
initial measurement of the tick rate.
Definition: tsc.cpp:227
double os_cpu_ClockFrequency()
Definition: os_cpu.cpp:43
size_t NumPackages()
Definition: topology.cpp:244
bool IsSafe() const
Definition: tsc.cpp:148
void Shutdown()
Definition: tsc.cpp:144
bool IsAccessible()
Definition: msr.cpp:32
static bool IsUniprocessor()
Definition: tsc.cpp:47
const Status OK
Definition: status.h:386
const AcpiTable * acpi_GetTable(const char *signature)
Definition: acpi.cpp:362
Vendors Vendor()
Definition: x86_x64.cpp:200
static bool IsSandyBridge()
Definition: tsc.cpp:112
u32 size
Definition: acpi.h:36
const Status NOT_SUPPORTED
Definition: status.h:429
double Resolution() const
actual resolution [s].
Definition: tsc.cpp:249
bool IsC3Supported() const
Definition: acpi.h:89
virtual const wchar_t * Name() const
Definition: tsc.cpp:129
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
bool cpuid(CpuidRegs *regs)
invoke CPUID instruction.
Definition: x86_x64.cpp:98
static bool IsInvariantTSC()
Definition: tsc.cpp:57
size_t CoresPerPackage()
Definition: topology.cpp:250
#define BIT(n)
pretty much the same as Bit&lt;unsigned&gt;.
Definition: bits.h:51
Status Activate()
Definition: tsc.cpp:134
i64 Status
Error handling system.
Definition: status.h:171
size_t CounterBits() const
Definition: tsc.cpp:222
T bits(T num, size_t lo_idx, size_t hi_idx)
extract the value of bits hi_idx:lo_idx within num
Definition: bits.h:97
bool HasPlatformInfo()
Definition: msr.cpp:66
#define u64
Definition: types.h:42
bool Cap(Caps cap)
Definition: x86_x64.cpp:142
u64 Counter() const
Definition: tsc.cpp:217
#define i64
Definition: types.h:37
u64 Read(u64 reg)
Definition: msr.cpp:130
ICounter * CreateCounterTSC(void *address, size_t size)
Definition: tsc.cpp:255
registers used/returned by cpuid
Definition: x86_x64.h:46
bool IsDutyCycleSupported() const
Definition: acpi.h:79
u64 rdtsc()
Definition: x86_x64.cpp:373
Definition: acpi.h:65
static bool IsThrottlingPossible()
Definition: tsc.cpp:77
AcpiTable header
Definition: acpi.h:67
size_t Model()
Definition: x86_x64.cpp:232