Hardware/CPU/IntelCPU.cs
author moel.mich
Thu, 20 May 2010 21:23:54 +0000
changeset 122 3ef997c53b50
parent 114 69dfa910e628
child 134 8b3b9b2e28e5
permissions -rw-r--r--
Fixed Issue 10.
     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, tjMax[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         }
   184       } else {
   185         coreTemperatures = new Sensor[0];
   186       }
   187 
   188       if (coreCount > 1)
   189         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
   190       else
   191         totalLoad = null;
   192       coreLoads = new Sensor[coreCount];
   193       for (int i = 0; i < coreLoads.Length; i++)
   194         coreLoads[i] = new Sensor(CoreString(i), i + 1,
   195           SensorType.Load, this);     
   196       cpuLoad = new CPULoad(cpuid);
   197       if (cpuLoad.IsAvailable) {
   198         foreach (Sensor sensor in coreLoads)
   199           ActivateSensor(sensor);
   200         if (totalLoad != null)
   201           ActivateSensor(totalLoad);
   202       }
   203 
   204       // check if processor has TSC
   205       if (cpuid[0][0].Data.GetLength(0) > 1 
   206         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
   207         hasTSC = true;
   208       else
   209         hasTSC = false; 
   210 
   211       // check if processor supports invariant TSC 
   212       if (cpuid[0][0].ExtData.GetLength(0) > 7 
   213         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   214         invariantTSC = true;
   215       else
   216         invariantTSC = false;
   217 
   218       // preload the function
   219       EstimateMaxClock(0); 
   220       EstimateMaxClock(0); 
   221 
   222       // estimate the max clock in MHz      
   223       List<double> estimatedMaxClocks = new List<double>(3);
   224       for (int i = 0; i < 3; i++)
   225         estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025));
   226       estimatedMaxClocks.Sort();
   227       estimatedMaxClock = estimatedMaxClocks[1];
   228 
   229       lastTimeStampCount = 0;
   230       lastTime = 0;
   231       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this);      
   232       coreClocks = new Sensor[coreCount];
   233       for (int i = 0; i < coreClocks.Length; i++) {
   234         coreClocks[i] =
   235           new Sensor(CoreString(i), i + 1, SensorType.Clock, this);
   236         if (hasTSC)
   237           ActivateSensor(coreClocks[i]);
   238       }
   239       
   240       Update();                   
   241     }
   242 
   243     public override string Name {
   244       get { return name; }
   245     }
   246 
   247     public override Identifier Identifier {
   248       get { return new Identifier("intelcpu", processorIndex.ToString()); }
   249     }
   250 
   251     public override Image Icon {
   252       get { return icon; }
   253     }
   254 
   255     private void AppendMSRData(StringBuilder r, uint msr, int thread) {
   256       uint eax, edx;
   257       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   258         r.Append(" ");
   259         r.Append((msr).ToString("X8"));
   260         r.Append("  ");
   261         r.Append((edx).ToString("X8"));
   262         r.Append("  ");
   263         r.Append((eax).ToString("X8"));
   264         r.AppendLine();
   265       }
   266     }
   267 
   268     public override string GetReport() {
   269       StringBuilder r = new StringBuilder();
   270 
   271       r.AppendLine("Intel CPU");
   272       r.AppendLine();
   273       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   274       r.AppendFormat("Number of Cores: {0}{1}", coreCount, 
   275         Environment.NewLine);
   276       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   277         Environment.NewLine);     
   278       r.AppendLine("TSC: " + 
   279         (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
   280       r.AppendLine(string.Format(CultureInfo.InvariantCulture, 
   281         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   282       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   283         "Max Clock: {0} MHz", Math.Round(estimatedMaxClock * 100) * 0.01));
   284       r.AppendLine();
   285 
   286       for (int i = 0; i < cpuid.Length; i++) {
   287         r.AppendLine("MSR Core #" + (i + 1));
   288         r.AppendLine();
   289         r.AppendLine(" MSR       EDX       EAX");
   290         AppendMSRData(r, MSR_PLATFORM_INFO, cpuid[i][0].Thread);
   291         AppendMSRData(r, IA32_PERF_STATUS, cpuid[i][0].Thread);
   292         AppendMSRData(r, IA32_THERM_STATUS_MSR, cpuid[i][0].Thread);
   293         AppendMSRData(r, IA32_TEMPERATURE_TARGET, cpuid[i][0].Thread);
   294         r.AppendLine();
   295       }
   296 
   297       return r.ToString();
   298     }
   299 
   300     private double EstimateMaxClock(double timeWindow) {
   301       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   302       uint lsbBegin, msbBegin, lsbEnd, msbEnd; 
   303       
   304       Thread.BeginThreadAffinity();
   305       long timeBegin = Stopwatch.GetTimestamp() + 
   306         (long)Math.Ceiling(0.001 * ticks);
   307       long timeEnd = timeBegin + ticks;      
   308       while (Stopwatch.GetTimestamp() < timeBegin) { }
   309       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   310       while (Stopwatch.GetTimestamp() < timeEnd) { }
   311       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   312       Thread.EndThreadAffinity();
   313 
   314       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   315       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   316 
   317       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / 
   318         (timeEnd - timeBegin);
   319     }
   320 
   321     public override void Update() {      
   322       for (int i = 0; i < coreTemperatures.Length; i++) {
   323         uint eax, edx;
   324         if (WinRing0.RdmsrTx(
   325           IA32_THERM_STATUS_MSR, out eax, out edx, 
   326             (UIntPtr)(1L << cpuid[i][0].Thread))) {
   327           // if reading is valid
   328           if ((eax & 0x80000000) != 0) {
   329             // get the dist from tjMax from bits 22:16
   330             float deltaT = ((eax & 0x007F0000) >> 16);
   331             float tjMax = coreTemperatures[i].Parameters[0].Value;
   332             float tSlope = coreTemperatures[i].Parameters[1].Value;
   333             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   334             ActivateSensor(coreTemperatures[i]);
   335           } else {
   336             DeactivateSensor(coreTemperatures[i]);
   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 }