Hardware/CPU/IntelCPU.cs
author moel.mich
Thu, 05 Aug 2010 19:28:50 +0000
changeset 164 cc1e116d0f2c
parent 134 8b3b9b2e28e5
child 165 813d8bc3192f
permissions -rw-r--r--
Added a fan control sensor for ATI GPUs.
     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-2010
    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.Drawing;
    41 using System.Diagnostics;
    42 using System.Globalization;
    43 using System.Reflection;
    44 using System.Runtime.InteropServices;
    45 using System.Threading;
    46 using System.Text;
    47 
    48 namespace OpenHardwareMonitor.Hardware.CPU {
    49   public class IntelCPU : Hardware, IHardware {
    50 
    51     private int processorIndex;
    52     private CPUID[][] cpuid;
    53     private int coreCount;
    54     
    55     private string name;
    56     private Image icon;
    57 
    58     private uint family;
    59     private uint model;
    60     private uint stepping;
    61 
    62     private Sensor[] coreTemperatures;
    63 
    64     private Sensor totalLoad;
    65     private Sensor[] coreLoads;
    66     private Sensor[] coreClocks;
    67     private Sensor busClock;    
    68     private bool hasTSC;
    69     private bool invariantTSC;    
    70     private double estimatedMaxClock;
    71 
    72     private CPULoad cpuLoad;
    73 
    74     private ulong lastTimeStampCount;    
    75     private long lastTime;
    76     private uint maxNehalemMultiplier = 0;    
    77     
    78     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    79     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    80     private const uint IA32_PERF_STATUS = 0x0198;
    81     private const uint MSR_PLATFORM_INFO = 0xCE;
    82 
    83     private string CoreString(int i) {
    84       if (coreCount == 1)
    85         return "CPU Core";
    86       else
    87         return "CPU Core #" + (i + 1);
    88     }
    89 
    90     private float[] Floats(float f) {
    91       float[] result = new float[coreCount];
    92       for (int i = 0; i < coreCount; i++)
    93         result[i] = f;
    94       return result;
    95     }
    96 
    97     public IntelCPU(int processorIndex, CPUID[][] cpuid) {
    98 
    99       this.processorIndex = processorIndex;
   100       this.cpuid = cpuid;
   101       this.coreCount = cpuid.Length;
   102       this.name = cpuid[0][0].Name;
   103       this.icon = Utilities.EmbeddedResources.GetImage("cpu.png");
   104 
   105       this.family = cpuid[0][0].Family;
   106       this.model = cpuid[0][0].Model;
   107       this.stepping = cpuid[0][0].Stepping;
   108 
   109       float[] tjMax;
   110       switch (family) {
   111         case 0x06: {
   112             switch (model) {
   113               case 0x0F: // Intel Core (65nm)
   114                 switch (stepping) {
   115                   case 0x06: // B2
   116                     switch (coreCount) {
   117                       case 2:
   118                         tjMax = Floats(80 + 10); break;
   119                       case 4:
   120                         tjMax = Floats(90 + 10); break;
   121                       default:
   122                         tjMax = Floats(85 + 10); break;
   123                     }
   124                     tjMax = Floats(80 + 10); break;
   125                   case 0x0B: // G0
   126                     tjMax = Floats(90 + 10); break;
   127                   case 0x0D: // M0
   128                     tjMax = Floats(85 + 10); break;
   129                   default:
   130                     tjMax = Floats(85 + 10); break;
   131                 } break;
   132               case 0x17: // Intel Core (45nm)
   133                 tjMax = Floats(100); break;
   134               case 0x1C: // Intel Atom (45nm)
   135                 switch (stepping) {
   136                   case 0x02: // C0
   137                     tjMax = Floats(90); break;
   138                   case 0x0A: // A0, B0
   139                     tjMax = Floats(100); break;
   140                   default:
   141                     tjMax = Floats(90); break;
   142                 } break;                
   143               case 0x1A: // Intel Core i7 LGA1366 (45nm)
   144               case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
   145               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   146               case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
   147                 uint eax, edx;
   148                 tjMax = new float[coreCount];
   149                 for (int i = 0; i < coreCount; i++) {
   150                   if (WinRing0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
   151                     out edx, (UIntPtr)(1L << cpuid[i][0].Thread)))
   152                   {
   153                     tjMax[i] = (eax >> 16) & 0xFF;
   154                   } else {
   155                     tjMax[i] = 100;
   156                   }
   157                 }
   158                 if (WinRing0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   159                   maxNehalemMultiplier = (eax >> 8) & 0xff;
   160                 }
   161                 break;
   162               default:
   163                 tjMax = Floats(100); break;
   164             }
   165           } break;
   166         default: tjMax = Floats(100); break;
   167       }
   168 
   169       // check if processor supports a digital thermal sensor
   170       if (cpuid[0][0].Data.GetLength(0) > 6 && 
   171         (cpuid[0][0].Data[6, 0] & 1) != 0) 
   172       {
   173         coreTemperatures = new Sensor[coreCount];
   174         for (int i = 0; i < coreTemperatures.Length; i++) {
   175           coreTemperatures[i] = new Sensor(CoreString(i), i,
   176             SensorType.Temperature, this, new ParameterDescription[] { 
   177               new ParameterDescription(
   178                 "TjMax [°C]", "TjMax temperature of the core.\n" + 
   179                 "Temperature = TjMax - TSlope * Value.", tjMax[i]), 
   180               new ParameterDescription("TSlope [°C]", 
   181                 "Temperature slope of the digital thermal sensor.\n" + 
   182                 "Temperature = TjMax - TSlope * Value.", 1)});
   183           ActivateSensor(coreTemperatures[i]);
   184         }
   185       } else {
   186         coreTemperatures = new Sensor[0];
   187       }
   188 
   189       if (coreCount > 1)
   190         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
   191       else
   192         totalLoad = null;
   193       coreLoads = new Sensor[coreCount];
   194       for (int i = 0; i < coreLoads.Length; i++)
   195         coreLoads[i] = new Sensor(CoreString(i), i + 1,
   196           SensorType.Load, this);     
   197       cpuLoad = new CPULoad(cpuid);
   198       if (cpuLoad.IsAvailable) {
   199         foreach (Sensor sensor in coreLoads)
   200           ActivateSensor(sensor);
   201         if (totalLoad != null)
   202           ActivateSensor(totalLoad);
   203       }
   204 
   205       // check if processor has TSC
   206       if (cpuid[0][0].Data.GetLength(0) > 1 
   207         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
   208         hasTSC = true;
   209       else
   210         hasTSC = false; 
   211 
   212       // check if processor supports invariant TSC 
   213       if (cpuid[0][0].ExtData.GetLength(0) > 7 
   214         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   215         invariantTSC = true;
   216       else
   217         invariantTSC = false;
   218 
   219       // preload the function
   220       EstimateMaxClock(0); 
   221       EstimateMaxClock(0); 
   222 
   223       // estimate the max clock in MHz      
   224       List<double> estimatedMaxClocks = new List<double>(3);
   225       for (int i = 0; i < 3; i++)
   226         estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025));
   227       estimatedMaxClocks.Sort();
   228       estimatedMaxClock = estimatedMaxClocks[1];
   229 
   230       lastTimeStampCount = 0;
   231       lastTime = 0;
   232       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this);      
   233       coreClocks = new Sensor[coreCount];
   234       for (int i = 0; i < coreClocks.Length; i++) {
   235         coreClocks[i] =
   236           new Sensor(CoreString(i), i + 1, SensorType.Clock, this);
   237         if (hasTSC)
   238           ActivateSensor(coreClocks[i]);
   239       }
   240       
   241       Update();                   
   242     }
   243 
   244     public override string Name {
   245       get { return name; }
   246     }
   247 
   248     public override Identifier Identifier {
   249       get { return new Identifier("intelcpu", processorIndex.ToString()); }
   250     }
   251 
   252     public override Image Icon {
   253       get { return icon; }
   254     }
   255 
   256     private void AppendMSRData(StringBuilder r, uint msr, int thread) {
   257       uint eax, edx;
   258       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   259         r.Append(" ");
   260         r.Append((msr).ToString("X8"));
   261         r.Append("  ");
   262         r.Append((edx).ToString("X8"));
   263         r.Append("  ");
   264         r.Append((eax).ToString("X8"));
   265         r.AppendLine();
   266       }
   267     }
   268 
   269     public override string GetReport() {
   270       StringBuilder r = new StringBuilder();
   271 
   272       r.AppendLine("Intel CPU");
   273       r.AppendLine();
   274       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   275       r.AppendFormat("Number of Cores: {0}{1}", coreCount, 
   276         Environment.NewLine);
   277       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   278         Environment.NewLine);     
   279       r.AppendLine("TSC: " + 
   280         (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
   281       r.AppendLine(string.Format(CultureInfo.InvariantCulture, 
   282         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   283       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   284         "Max Clock: {0} MHz", Math.Round(estimatedMaxClock * 100) * 0.01));
   285       r.AppendLine();
   286 
   287       for (int i = 0; i < cpuid.Length; i++) {
   288         r.AppendLine("MSR Core #" + (i + 1));
   289         r.AppendLine();
   290         r.AppendLine(" MSR       EDX       EAX");
   291         AppendMSRData(r, MSR_PLATFORM_INFO, cpuid[i][0].Thread);
   292         AppendMSRData(r, IA32_PERF_STATUS, cpuid[i][0].Thread);
   293         AppendMSRData(r, IA32_THERM_STATUS_MSR, cpuid[i][0].Thread);
   294         AppendMSRData(r, IA32_TEMPERATURE_TARGET, cpuid[i][0].Thread);
   295         r.AppendLine();
   296       }
   297 
   298       return r.ToString();
   299     }
   300 
   301     private double EstimateMaxClock(double timeWindow) {
   302       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   303       uint lsbBegin, msbBegin, lsbEnd, msbEnd; 
   304       
   305       Thread.BeginThreadAffinity();
   306       long timeBegin = Stopwatch.GetTimestamp() + 
   307         (long)Math.Ceiling(0.001 * ticks);
   308       long timeEnd = timeBegin + ticks;      
   309       while (Stopwatch.GetTimestamp() < timeBegin) { }
   310       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   311       while (Stopwatch.GetTimestamp() < timeEnd) { }
   312       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   313       Thread.EndThreadAffinity();
   314 
   315       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   316       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   317 
   318       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / 
   319         (timeEnd - timeBegin);
   320     }
   321 
   322     public override void Update() {      
   323       for (int i = 0; i < coreTemperatures.Length; i++) {
   324         uint eax, edx;
   325         if (WinRing0.RdmsrTx(
   326           IA32_THERM_STATUS_MSR, out eax, out edx, 
   327             (UIntPtr)(1L << cpuid[i][0].Thread))) {
   328           // if reading is valid
   329           if ((eax & 0x80000000) != 0) {
   330             // get the dist from tjMax from bits 22:16
   331             float deltaT = ((eax & 0x007F0000) >> 16);
   332             float tjMax = coreTemperatures[i].Parameters[0].Value;
   333             float tSlope = coreTemperatures[i].Parameters[1].Value;
   334             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   335           } else {
   336             coreTemperatures[i].Value = null;
   337           }
   338         }
   339       }
   340 
   341       if (cpuLoad.IsAvailable) {
   342         cpuLoad.Update();
   343         for (int i = 0; i < coreLoads.Length; i++)
   344           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   345         if (totalLoad != null)
   346           totalLoad.Value = cpuLoad.GetTotalLoad();
   347       }
   348 
   349       if (hasTSC) {
   350         uint lsb, msb;
   351         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   352         long time = Stopwatch.GetTimestamp();
   353         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   354         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   355         if (delta > 0.5) {
   356           double maxClock;
   357           if (invariantTSC)
   358             maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   359           else
   360             maxClock = estimatedMaxClock;
   361 
   362           double busClock = 0;
   363           uint eax, edx;
   364           for (int i = 0; i < coreClocks.Length; i++) {
   365             System.Threading.Thread.Sleep(1);
   366             if (WinRing0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   367               (UIntPtr)(1L << cpuid[i][0].Thread))) {
   368               if (maxNehalemMultiplier > 0) { // Core i3, i5, i7
   369                 uint nehalemMultiplier = eax & 0xff;
   370                 coreClocks[i].Value =
   371                   (float)(nehalemMultiplier * maxClock / maxNehalemMultiplier);
   372                 busClock = (float)(maxClock / maxNehalemMultiplier);
   373               } else { // Core 2
   374                 uint multiplier = (eax >> 8) & 0x1f;
   375                 uint maxMultiplier = (edx >> 8) & 0x1f;
   376                 // factor = multiplier * 2 to handle non integer multipliers 
   377                 uint factor = (multiplier << 1) | ((eax >> 14) & 1);
   378                 uint maxFactor = (maxMultiplier << 1) | ((edx >> 14) & 1);
   379                 if (maxFactor > 0) {
   380                   coreClocks[i].Value = (float)(factor * maxClock / maxFactor);
   381                   busClock = (float)(2 * maxClock / maxFactor);
   382                 }
   383               }
   384             } else { // Intel Pentium 4
   385               // if IA32_PERF_STATUS is not available, assume maxClock
   386               coreClocks[i].Value = (float)maxClock;
   387             }
   388           }
   389           if (busClock > 0) {
   390             this.busClock.Value = (float)busClock;
   391             ActivateSensor(this.busClock);
   392           }
   393         }
   394         lastTimeStampCount = timeStampCount;
   395         lastTime = time;
   396       }
   397     }
   398   }  
   399 }