1.1 --- a/Hardware/CPU/AMD10CPU.cs Mon Sep 27 23:48:41 2010 +0000
1.2 +++ b/Hardware/CPU/AMD10CPU.cs Thu Sep 30 16:51:09 2010 +0000
1.3 @@ -36,7 +36,10 @@
1.4 */
1.5
1.6 using System;
1.7 +using System.Collections.Generic;
1.8 +using System.Diagnostics;
1.9 using System.Globalization;
1.10 +using System.Runtime.InteropServices;
1.11 using System.Text;
1.12 using System.Threading;
1.13
1.14 @@ -47,16 +50,22 @@
1.15 private readonly Sensor coreTemperature;
1.16 private readonly Sensor[] coreClocks;
1.17 private readonly Sensor busClock;
1.18 -
1.19 +
1.20 + private const uint PERF_CTL_0 = 0xC0010000;
1.21 + private const uint PERF_CTR_0 = 0xC0010004;
1.22 + private const uint P_STATE_0 = 0xC0010064;
1.23 private const uint COFVID_STATUS = 0xC0010071;
1.24 - private const uint P_STATE_0 = 0xC0010064;
1.25
1.26 private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
1.27 private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
1.28 private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
1.29 -
1.30 +
1.31 private readonly uint miscellaneousControlAddress;
1.32
1.33 + private double timeStampCounterMultiplier;
1.34 +
1.35 + private StringBuilder debug = new StringBuilder();
1.36 +
1.37 public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
1.38 : base(processorIndex, cpuid, settings)
1.39 {
1.40 @@ -76,15 +85,87 @@
1.41 for (int i = 0; i < coreClocks.Length; i++) {
1.42 coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
1.43 this, settings);
1.44 - if (hasTSC)
1.45 + if (HasTimeStampCounter)
1.46 ActivateSensor(coreClocks[i]);
1.47 }
1.48
1.49 + // set affinity to the first thread for all frequency estimations
1.50 + IntPtr thread = NativeMethods.GetCurrentThread();
1.51 + UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
1.52 + (UIntPtr)(1L << cpuid[0][0].Thread));
1.53 +
1.54 + uint ctlEax, ctlEdx;
1.55 + WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
1.56 + uint ctrEax, ctrEdx;
1.57 + WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
1.58 +
1.59 + timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
1.60 +
1.61 + // restore the performance counter registers
1.62 + WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
1.63 + WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
1.64 +
1.65 + // restore the thread affinity.
1.66 + NativeMethods.SetThreadAffinityMask(thread, mask);
1.67 +
1.68 Update();
1.69 }
1.70
1.71 + private double estimateTimeStampCounterMultiplier() {
1.72 + // preload the function
1.73 + estimateTimeStampCounterMultiplier(0);
1.74 + estimateTimeStampCounterMultiplier(0);
1.75 +
1.76 + // estimate the multiplier
1.77 + List<double> estimate = new List<double>(3);
1.78 + for (int i = 0; i < 3; i++)
1.79 + estimate.Add(estimateTimeStampCounterMultiplier(0.025));
1.80 + estimate.Sort();
1.81 + return estimate[1];
1.82 + }
1.83 +
1.84 + private double estimateTimeStampCounterMultiplier(double timeWindow) {
1.85 + uint eax, edx;
1.86 +
1.87 + // select event "076h CPU Clocks not Halted" and enable the counter
1.88 + WinRing0.Wrmsr(PERF_CTL_0,
1.89 + (1 << 22) | // enable performance counter
1.90 + (1 << 17) | // count events in user mode
1.91 + (1 << 16) | // count events in operating-system mode
1.92 + 0x76, 0x00000000);
1.93 +
1.94 + // set the counter to 0
1.95 + WinRing0.Wrmsr(PERF_CTR_0, 0, 0);
1.96 +
1.97 + long ticks = (long)(timeWindow * Stopwatch.Frequency);
1.98 + uint lsbBegin, msbBegin, lsbEnd, msbEnd;
1.99 +
1.100 + long timeBegin = Stopwatch.GetTimestamp() +
1.101 + (long)Math.Ceiling(0.001 * ticks);
1.102 + long timeEnd = timeBegin + ticks;
1.103 + while (Stopwatch.GetTimestamp() < timeBegin) { }
1.104 + WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
1.105 + while (Stopwatch.GetTimestamp() < timeEnd) { }
1.106 + WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
1.107 +
1.108 + WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx);
1.109 + uint cpuDid = (eax >> 6) & 7;
1.110 + uint cpuFid = eax & 0x1F;
1.111 + double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
1.112 +
1.113 + ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
1.114 + ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
1.115 +
1.116 + double coreFrequency = 1e-6 *
1.117 + (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
1.118 + (timeEnd - timeBegin);
1.119 +
1.120 + double busFrequency = coreFrequency / coreMultiplier;
1.121 + return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
1.122 + }
1.123 +
1.124 protected override uint[] GetMSRs() {
1.125 - return new uint[] { P_STATE_0, COFVID_STATUS };
1.126 + return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
1.127 }
1.128
1.129 public override string GetReport() {
1.130 @@ -94,12 +175,18 @@
1.131 r.Append("Miscellaneous Control Address: 0x");
1.132 r.AppendLine((miscellaneousControlAddress).ToString("X",
1.133 CultureInfo.InvariantCulture));
1.134 + r.Append("Time Stamp Counter Multiplier: ");
1.135 + r.AppendLine(timeStampCounterMultiplier.ToString(
1.136 + CultureInfo.InvariantCulture));
1.137 + r.AppendLine();
1.138 +
1.139 + r.Append(debug);
1.140 r.AppendLine();
1.141
1.142 return r.ToString();
1.143 }
1.144
1.145 - private double MultiplierFromIDs(uint divisorID, uint frequencyID) {
1.146 + private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
1.147 return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
1.148 }
1.149
1.150 @@ -118,35 +205,29 @@
1.151 }
1.152 }
1.153
1.154 - if (hasTSC) {
1.155 + if (HasTimeStampCounter) {
1.156 double newBusClock = 0;
1.157
1.158 for (int i = 0; i < coreClocks.Length; i++) {
1.159 Thread.Sleep(1);
1.160
1.161 uint curEax, curEdx;
1.162 - uint maxEax, maxEdx;
1.163 if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
1.164 - (UIntPtr)(1L << cpuid[i][0].Thread)) &&
1.165 - WinRing0.RdmsrTx(P_STATE_0, out maxEax, out maxEdx,
1.166 (UIntPtr)(1L << cpuid[i][0].Thread)))
1.167 {
1.168 // 8:6 CpuDid: current core divisor ID
1.169 // 5:0 CpuFid: current core frequency ID
1.170 - uint curCpuDid = (curEax >> 6) & 7;
1.171 - uint curCpuFid = curEax & 0x1F;
1.172 - double multiplier = MultiplierFromIDs(curCpuDid, curCpuFid);
1.173 + uint cpuDid = (curEax >> 6) & 7;
1.174 + uint cpuFid = curEax & 0x1F;
1.175 + double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
1.176
1.177 - // we assume that the max multiplier (used for the TSC)
1.178 - // can be found in the P_STATE_0 MSR
1.179 - uint maxCpuDid = (maxEax >> 6) & 7;
1.180 - uint maxCpuFid = maxEax & 0x1F;
1.181 - double maxMultiplier = MultiplierFromIDs(maxCpuDid, maxCpuFid);
1.182 -
1.183 - coreClocks[i].Value = (float)(multiplier * MaxClock / maxMultiplier);
1.184 - newBusClock = (float)(MaxClock / maxMultiplier);
1.185 + coreClocks[i].Value =
1.186 + (float)(multiplier * TimeStampCounterFrequency /
1.187 + timeStampCounterMultiplier);
1.188 + newBusClock =
1.189 + (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
1.190 } else {
1.191 - coreClocks[i].Value = (float)MaxClock;
1.192 + coreClocks[i].Value = (float)TimeStampCounterFrequency;
1.193 }
1.194 }
1.195
1.196 @@ -155,6 +236,17 @@
1.197 ActivateSensor(this.busClock);
1.198 }
1.199 }
1.200 - }
1.201 + }
1.202 +
1.203 + private static class NativeMethods {
1.204 + private const string KERNEL = "kernel32.dll";
1.205 +
1.206 + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
1.207 + public static extern UIntPtr
1.208 + SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
1.209 +
1.210 + [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
1.211 + public static extern IntPtr GetCurrentThread();
1.212 + }
1.213 }
1.214 }