Hardware/CPU/AMD10CPU.cs
author moel.mich
Sat, 16 Apr 2011 14:49:47 +0000
changeset 272 037a2d66082f
parent 268 844ba72c11de
child 273 2054d5dcb680
permissions -rw-r--r--
A small correction for AMD K8 (family 0Fh) core temperature reading, and fan reading on older revision of IT8712F chips. This should fix Issue 42 and Issue 194.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     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
     8  
     9   http://www.mozilla.org/MPL/
    10 
    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.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    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.
    21 
    22   Contributor(s):
    23 
    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.
    35  
    36 */
    37 
    38 using System;
    39 using System.Collections.Generic;
    40 using System.Diagnostics;
    41 using System.Globalization;
    42 using System.IO;
    43 using System.Runtime.InteropServices;
    44 using System.Text;
    45 using System.Threading;
    46 
    47 namespace OpenHardwareMonitor.Hardware.CPU {
    48 
    49   internal sealed class AMD10CPU : AMDCPU {
    50 
    51     private readonly Sensor coreTemperature;
    52     private readonly Sensor[] coreClocks;
    53     private readonly Sensor busClock;
    54       
    55     private const uint PERF_CTL_0 = 0xC0010000;
    56     private const uint PERF_CTR_0 = 0xC0010004;
    57     private const uint P_STATE_0 = 0xC0010064;
    58     private const uint COFVID_STATUS = 0xC0010071;
    59 
    60     private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    61     private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    62     private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
    63     private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703; 
    64     private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    65 
    66     private readonly uint miscellaneousControlAddress;
    67     private readonly ushort miscellaneousControlDeviceId;
    68 
    69     private readonly FileStream temperatureStream;
    70 
    71     private double timeStampCounterMultiplier;
    72 
    73     public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    74       : base(processorIndex, cpuid, settings) 
    75     {            
    76       // AMD family 10h/11h processors support only one temperature sensor
    77       coreTemperature = new Sensor(
    78         "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
    79         SensorType.Temperature, this, new [] {
    80             new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
    81           }, settings);
    82 
    83       switch (family) {
    84         case 0x10: miscellaneousControlDeviceId =
    85           FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    86         case 0x11: miscellaneousControlDeviceId =
    87           FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    88         case 0x14: miscellaneousControlDeviceId = 
    89           FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    90         default: miscellaneousControlDeviceId = 0; break;
    91       }
    92 
    93       // get the pci address for the Miscellaneous Control registers 
    94       miscellaneousControlAddress = GetPciAddress(
    95         MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);        
    96 
    97       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
    98       coreClocks = new Sensor[coreCount];
    99       for (int i = 0; i < coreClocks.Length; i++) {
   100         coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
   101           this, settings);
   102         if (HasTimeStampCounter)
   103           ActivateSensor(coreClocks[i]);
   104       }
   105 
   106       // set affinity to the first thread for all frequency estimations     
   107       ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
   108 
   109       uint ctlEax, ctlEdx;
   110       Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
   111       uint ctrEax, ctrEdx;
   112       Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
   113 
   114       timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
   115 
   116       // restore the performance counter registers
   117       Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
   118       Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
   119 
   120       // restore the thread affinity.
   121       ThreadAffinity.Set(mask);
   122 
   123       // the file reader for lm-sensors support on Linux
   124       temperatureStream = null;
   125       int p = (int)Environment.OSVersion.Platform;
   126       if ((p == 4) || (p == 128)) {
   127         string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
   128         foreach (string path in devicePaths) {
   129           string name = null;
   130           try {
   131             using (StreamReader reader = new StreamReader(path + "/device/name"))
   132               name = reader.ReadLine();
   133           } catch (IOException) { }
   134           switch (name) {
   135             case "k10temp":
   136               temperatureStream = new FileStream(path + "/device/temp1_input", 
   137                 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
   138               break;
   139           }
   140         }
   141       }
   142 
   143       Update();                   
   144     }
   145 
   146     private double estimateTimeStampCounterMultiplier() {
   147       // preload the function
   148       estimateTimeStampCounterMultiplier(0);
   149       estimateTimeStampCounterMultiplier(0);
   150 
   151       // estimate the multiplier
   152       List<double> estimate = new List<double>(3);
   153       for (int i = 0; i < 3; i++)
   154         estimate.Add(estimateTimeStampCounterMultiplier(0.025));
   155       estimate.Sort();
   156       return estimate[1];
   157     }
   158 
   159     private double estimateTimeStampCounterMultiplier(double timeWindow) {
   160       uint eax, edx;
   161      
   162       // select event "076h CPU Clocks not Halted" and enable the counter
   163       Ring0.Wrmsr(PERF_CTL_0,
   164         (1 << 22) | // enable performance counter
   165         (1 << 17) | // count events in user mode
   166         (1 << 16) | // count events in operating-system mode
   167         0x76, 0x00000000);
   168 
   169       // set the counter to 0
   170       Ring0.Wrmsr(PERF_CTR_0, 0, 0);
   171 
   172       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   173       uint lsbBegin, msbBegin, lsbEnd, msbEnd;
   174 
   175       long timeBegin = Stopwatch.GetTimestamp() +
   176         (long)Math.Ceiling(0.001 * ticks);
   177       long timeEnd = timeBegin + ticks;
   178       while (Stopwatch.GetTimestamp() < timeBegin) { }
   179       Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   180       while (Stopwatch.GetTimestamp() < timeEnd) { }
   181       Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   182 
   183       Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
   184       uint cpuDid = (eax >> 6) & 7;
   185       uint cpuFid = eax & 0x1F;
   186       double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
   187 
   188       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   189       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   190 
   191       double coreFrequency = 1e-6 * 
   192         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   193         (timeEnd - timeBegin);
   194 
   195       double busFrequency = coreFrequency / coreMultiplier;
   196       return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
   197     }
   198 
   199     protected override uint[] GetMSRs() {
   200       return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
   201     }
   202 
   203     public override string GetReport() {
   204       StringBuilder r = new StringBuilder();
   205       r.Append(base.GetReport());
   206 
   207       r.Append("Miscellaneous Control Address: 0x");
   208       r.AppendLine((miscellaneousControlAddress).ToString("X",
   209         CultureInfo.InvariantCulture));
   210       r.Append("Time Stamp Counter Multiplier: ");
   211       r.AppendLine(timeStampCounterMultiplier.ToString(
   212         CultureInfo.InvariantCulture));
   213       r.AppendLine();
   214 
   215       return r.ToString();
   216     }
   217 
   218     private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
   219       return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
   220     }
   221 
   222     private string ReadFirstLine(Stream stream) {
   223       StringBuilder sb = new StringBuilder();
   224       try {
   225         stream.Seek(0, SeekOrigin.Begin);
   226         int b = stream.ReadByte();
   227         while (b != -1 && b != 10) {
   228           sb.Append((char)b);
   229           b = stream.ReadByte();
   230         }
   231       } catch { }
   232       return sb.ToString();
   233     }
   234 
   235     public override void Update() {
   236       base.Update();
   237 
   238       if (temperatureStream == null) {
   239         if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
   240           uint value;
   241           if (Ring0.ReadPciConfig(miscellaneousControlAddress,
   242             REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
   243             coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
   244               coreTemperature.Parameters[0].Value;
   245             ActivateSensor(coreTemperature);
   246           } else {
   247             DeactivateSensor(coreTemperature);
   248           }
   249         }
   250       } else {
   251         string s = ReadFirstLine(temperatureStream);
   252         try {
   253           coreTemperature.Value = 0.001f *
   254             long.Parse(s, CultureInfo.InvariantCulture);
   255           ActivateSensor(coreTemperature);
   256         } catch {
   257           DeactivateSensor(coreTemperature);
   258         }        
   259       }
   260 
   261       if (HasTimeStampCounter) {
   262         double newBusClock = 0;
   263 
   264         for (int i = 0; i < coreClocks.Length; i++) {
   265           Thread.Sleep(1);
   266 
   267           uint curEax, curEdx;
   268           if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   269             1UL << cpuid[i][0].Thread)) 
   270           {
   271             // 8:6 CpuDid: current core divisor ID
   272             // 5:0 CpuFid: current core frequency ID
   273             uint cpuDid = (curEax >> 6) & 7;
   274             uint cpuFid = curEax & 0x1F;
   275             double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
   276 
   277             coreClocks[i].Value = 
   278               (float)(multiplier * TimeStampCounterFrequency / 
   279               timeStampCounterMultiplier);
   280             newBusClock = 
   281               (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   282           } else {
   283             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   284           }
   285         }
   286 
   287         if (newBusClock > 0) {
   288           this.busClock.Value = (float)newBusClock;
   289           ActivateSensor(this.busClock);
   290         }
   291       }
   292     }
   293 
   294     public override void Close() {
   295       if (temperatureStream != null) {
   296         temperatureStream.Close();
   297       }
   298     }
   299   }
   300 }