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