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