Fixed the bus and core clock reading on AMD family 10h model Ah CPUs. The new "Core Performance Boost" feature of these CPUs resulted in very low accuracy of the bus speed (and as a consequence also an inaccurate TSC multiplier). This fixed Issue 205.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2009-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Collections.Generic;
40 using System.Diagnostics;
41 using System.Globalization;
43 using System.Runtime.InteropServices;
45 using System.Threading;
47 namespace OpenHardwareMonitor.Hardware.CPU {
49 internal sealed class AMD10CPU : AMDCPU {
51 private readonly Sensor coreTemperature;
52 private readonly Sensor[] coreClocks;
53 private readonly Sensor busClock;
55 private const uint PERF_CTL_0 = 0xC0010000;
56 private const uint PERF_CTR_0 = 0xC0010004;
57 private const uint HWCR = 0xC0010015;
58 private const uint P_STATE_0 = 0xC0010064;
59 private const uint COFVID_STATUS = 0xC0010071;
61 private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
62 private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
63 private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
64 private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
65 private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
66 private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
68 private readonly uint miscellaneousControlAddress;
69 private readonly ushort miscellaneousControlDeviceId;
71 private readonly FileStream temperatureStream;
73 private readonly double timeStampCounterMultiplier;
74 private readonly bool corePerformanceBoostSupport;
76 public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
77 : base(processorIndex, cpuid, settings)
79 // AMD family 10h/11h processors support only one temperature sensor
80 coreTemperature = new Sensor(
81 "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
82 SensorType.Temperature, this, new [] {
83 new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
87 case 0x10: miscellaneousControlDeviceId =
88 FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
89 case 0x11: miscellaneousControlDeviceId =
90 FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
91 case 0x14: miscellaneousControlDeviceId =
92 FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
93 default: miscellaneousControlDeviceId = 0; break;
96 // get the pci address for the Miscellaneous Control registers
97 miscellaneousControlAddress = GetPciAddress(
98 MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);
100 busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
101 coreClocks = new Sensor[coreCount];
102 for (int i = 0; i < coreClocks.Length; i++) {
103 coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
105 if (HasTimeStampCounter)
106 ActivateSensor(coreClocks[i]);
109 corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
111 // set affinity to the first thread for all frequency estimations
112 ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
114 // disable core performance boost
115 uint hwcrEax, hwcrEdx;
116 Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
117 if (corePerformanceBoostSupport)
118 Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
121 Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
123 Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
125 timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
127 // restore the performance counter registers
128 Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
129 Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
131 // restore core performance boost
132 if (corePerformanceBoostSupport)
133 Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
135 // restore the thread affinity.
136 ThreadAffinity.Set(mask);
138 // the file reader for lm-sensors support on Linux
139 temperatureStream = null;
140 int p = (int)Environment.OSVersion.Platform;
141 if ((p == 4) || (p == 128)) {
142 string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
143 foreach (string path in devicePaths) {
146 using (StreamReader reader = new StreamReader(path + "/device/name"))
147 name = reader.ReadLine();
148 } catch (IOException) { }
151 temperatureStream = new FileStream(path + "/device/temp1_input",
152 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
161 private double estimateTimeStampCounterMultiplier() {
162 // preload the function
163 estimateTimeStampCounterMultiplier(0);
164 estimateTimeStampCounterMultiplier(0);
166 // estimate the multiplier
167 List<double> estimate = new List<double>(3);
168 for (int i = 0; i < 3; i++)
169 estimate.Add(estimateTimeStampCounterMultiplier(0.025));
174 private double estimateTimeStampCounterMultiplier(double timeWindow) {
177 // select event "076h CPU Clocks not Halted" and enable the counter
178 Ring0.Wrmsr(PERF_CTL_0,
179 (1 << 22) | // enable performance counter
180 (1 << 17) | // count events in user mode
181 (1 << 16) | // count events in operating-system mode
184 // set the counter to 0
185 Ring0.Wrmsr(PERF_CTR_0, 0, 0);
187 long ticks = (long)(timeWindow * Stopwatch.Frequency);
188 uint lsbBegin, msbBegin, lsbEnd, msbEnd;
190 long timeBegin = Stopwatch.GetTimestamp() +
191 (long)Math.Ceiling(0.001 * ticks);
192 long timeEnd = timeBegin + ticks;
193 while (Stopwatch.GetTimestamp() < timeBegin) { }
194 Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
196 while (Stopwatch.GetTimestamp() < timeEnd) { }
197 Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
198 Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
200 double coreMultiplier;
201 if (family == 0x14) {
202 uint divisorIdMSD = (eax >> 4) & 0x1F;
203 uint divisorIdLSD = eax & 0xF;
205 Ring0.ReadPciConfig(miscellaneousControlAddress,
206 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
207 uint frequencyId = value & 0x1F;
210 MultiplierFromIDs(divisorIdMSD, divisorIdLSD, frequencyId);
212 uint cpuDid = (eax >> 6) & 7;
213 uint cpuFid = eax & 0x1F;
214 coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
216 ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
217 ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
219 double coreFrequency = 1e-6 *
220 (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
221 (timeEnd - timeBegin);
223 double busFrequency = coreFrequency / coreMultiplier;
225 return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
228 protected override uint[] GetMSRs() {
229 return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0,
233 public override string GetReport() {
234 StringBuilder r = new StringBuilder();
235 r.Append(base.GetReport());
237 r.Append("Miscellaneous Control Address: 0x");
238 r.AppendLine((miscellaneousControlAddress).ToString("X",
239 CultureInfo.InvariantCulture));
240 r.Append("Time Stamp Counter Multiplier: ");
241 r.AppendLine(timeStampCounterMultiplier.ToString(
242 CultureInfo.InvariantCulture));
243 if (family == 0x14) {
245 Ring0.ReadPciConfig(miscellaneousControlAddress,
246 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
247 r.Append("PCI Register D18F3xD4: ");
248 r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
255 // calculate the multiplier for family 10h based on Did and Fid
256 private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
257 return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
260 // calculate the multiplier for family 14h based on DidMSD, DidLSD and Fid
261 private static double MultiplierFromIDs(uint divisorIdMSD,
262 uint divisorIdLSD, uint frequencyId)
264 return (frequencyId + 0x10) / (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
267 private string ReadFirstLine(Stream stream) {
268 StringBuilder sb = new StringBuilder();
270 stream.Seek(0, SeekOrigin.Begin);
271 int b = stream.ReadByte();
272 while (b != -1 && b != 10) {
274 b = stream.ReadByte();
277 return sb.ToString();
280 public override void Update() {
283 if (temperatureStream == null) {
284 if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
286 if (Ring0.ReadPciConfig(miscellaneousControlAddress,
287 REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
288 coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
289 coreTemperature.Parameters[0].Value;
290 ActivateSensor(coreTemperature);
292 DeactivateSensor(coreTemperature);
296 string s = ReadFirstLine(temperatureStream);
298 coreTemperature.Value = 0.001f *
299 long.Parse(s, CultureInfo.InvariantCulture);
300 ActivateSensor(coreTemperature);
302 DeactivateSensor(coreTemperature);
306 if (HasTimeStampCounter) {
307 double newBusClock = 0;
309 for (int i = 0; i < coreClocks.Length; i++) {
313 if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
314 1UL << cpuid[i][0].Thread))
317 if (family == 0x14) {
318 uint divisorIdMSD = (curEax >> 4) & 0x1F;
319 uint divisorIdLSD = curEax & 0xF;
321 Ring0.ReadPciConfig(miscellaneousControlAddress,
322 CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
323 uint frequencyId = value & 0x1F;
325 MultiplierFromIDs(divisorIdMSD, divisorIdLSD, frequencyId);
327 // 8:6 CpuDid: current core divisor ID
328 // 5:0 CpuFid: current core frequency ID
329 uint cpuDid = (curEax >> 6) & 7;
330 uint cpuFid = curEax & 0x1F;
331 multiplier = MultiplierFromIDs(cpuDid, cpuFid);
334 coreClocks[i].Value =
335 (float)(multiplier * TimeStampCounterFrequency /
336 timeStampCounterMultiplier);
338 (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
340 coreClocks[i].Value = (float)TimeStampCounterFrequency;
344 if (newBusClock > 0) {
345 this.busClock.Value = (float)newBusClock;
346 ActivateSensor(this.busClock);
351 public override void Close() {
352 if (temperatureStream != null) {
353 temperatureStream.Close();