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