Hardware/CPU/IntelCPU.cs
author moel.mich
Wed, 27 Jul 2011 18:27:16 +0000
changeset 317 1ccf99e620a9
parent 315 158ec57434e8
child 320 df3493f75225
permissions -rw-r--r--
Added support for Intel CPU power sensors (package and cores).
     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-2011
    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.Globalization;
    40 using System.Text;
    41 
    42 namespace OpenHardwareMonitor.Hardware.CPU {
    43   internal sealed class IntelCPU : GenericCPU {
    44 
    45     private enum Microarchitecture {
    46       Unknown,
    47       NetBurst,
    48       Core,
    49       Atom,
    50       Nehalem,
    51       SandyBridge
    52     }
    53 
    54     private readonly Sensor[] coreTemperatures;
    55     private readonly Sensor packageTemperature;
    56     private readonly Sensor[] coreClocks;
    57     private readonly Sensor busClock;
    58     private readonly Sensor packagePower;
    59     private readonly Sensor coresPower;
    60 
    61     private readonly Microarchitecture microarchitecture;
    62     private readonly double timeStampCounterMultiplier;
    63 
    64     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    65     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    66     private const uint IA32_PERF_STATUS = 0x0198;
    67     private const uint MSR_PLATFORM_INFO = 0xCE;
    68     private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
    69     private const uint MSR_RAPL_POWER_UNIT = 0x606;
    70     private const uint MSR_PKG_ENERY_STATUS = 0x611;
    71     private const uint MSR_PP0_ENERY_STATUS = 0x639;
    72 
    73     private float energyUnitMultiplier = 0;
    74     private DateTime lastPackageTime;
    75     private uint lastPackageEnergyConsumed;
    76     private DateTime lastCoresTime;
    77     private uint lastCoresEnergyConsumed;
    78 
    79 
    80 
    81     private float[] Floats(float f) {
    82       float[] result = new float[coreCount];
    83       for (int i = 0; i < coreCount; i++)
    84         result[i] = f;
    85       return result;
    86     }
    87 
    88     private float[] GetTjMaxFromMSR() {
    89       uint eax, edx;
    90       float[] result = new float[coreCount];
    91       for (int i = 0; i < coreCount; i++) {
    92         if (Ring0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
    93           out edx, 1UL << cpuid[i][0].Thread)) {
    94           result[i] = (eax >> 16) & 0xFF;
    95         } else {
    96           result[i] = 100;
    97         }
    98       }
    99       return result;
   100     }
   101 
   102     public IntelCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
   103       : base(processorIndex, cpuid, settings) 
   104     {
   105       // set tjMax
   106       float[] tjMax;
   107       switch (family) {
   108         case 0x06: {
   109             switch (model) {
   110               case 0x0F: // Intel Core 2 (65nm)
   111                 microarchitecture = Microarchitecture.Core;
   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 2 (45nm)
   131                 microarchitecture = Microarchitecture.Core;
   132                 tjMax = Floats(100); break;
   133               case 0x1C: // Intel Atom (45nm)
   134                 microarchitecture = Microarchitecture.Atom;
   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 0x1F: // Intel Core i5, i7 
   146               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   147               case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
   148               case 0x2E: // Intel Xeon Processor 7500 series
   149                 microarchitecture = Microarchitecture.Nehalem;
   150                 tjMax = GetTjMaxFromMSR();
   151                 break;
   152               case 0x2A: // Intel Core i5, i7 2xxx LGA1155 (32nm)
   153               case 0x2D: // Next Generation Intel Xeon Processor
   154                 microarchitecture = Microarchitecture.SandyBridge;
   155                 tjMax = GetTjMaxFromMSR();
   156                 break;
   157               default:
   158                 microarchitecture = Microarchitecture.Unknown;
   159                 tjMax = Floats(100); 
   160                 break;
   161             }
   162           } break;
   163         case 0x0F: {
   164             switch (model) {
   165               case 0x00: // Pentium 4 (180nm)
   166               case 0x01: // Pentium 4 (130nm)
   167               case 0x02: // Pentium 4 (130nm)
   168               case 0x03: // Pentium 4, Celeron D (90nm)
   169               case 0x04: // Pentium 4, Pentium D, Celeron D (90nm)
   170               case 0x06: // Pentium 4, Pentium D, Celeron D (65nm)
   171                 microarchitecture = Microarchitecture.NetBurst;
   172                 tjMax = Floats(100); 
   173                 break;
   174               default:
   175                 microarchitecture = Microarchitecture.Unknown;
   176                 tjMax = Floats(100);
   177                 break;
   178             }
   179           } break;
   180         default:
   181           microarchitecture = Microarchitecture.Unknown;
   182           tjMax = Floats(100); 
   183           break;
   184       }
   185 
   186       // set timeStampCounterMultiplier
   187       switch (microarchitecture) {
   188         case Microarchitecture.NetBurst:
   189         case Microarchitecture.Atom:
   190         case Microarchitecture.Core: {
   191             uint eax, edx;
   192             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   193               timeStampCounterMultiplier = 
   194                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   195             }
   196           } break;
   197         case Microarchitecture.Nehalem: 
   198         case Microarchitecture.SandyBridge: {
   199             uint eax, edx;
   200             if (Ring0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   201               timeStampCounterMultiplier = (eax >> 8) & 0xff;
   202             }
   203           } break;
   204         default: {
   205             timeStampCounterMultiplier = 1;
   206             uint eax, edx;
   207             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   208               timeStampCounterMultiplier =
   209                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   210             }
   211           } break;
   212       }
   213 
   214       // check if processor supports a digital thermal sensor at core level
   215       if (cpuid[0][0].Data.GetLength(0) > 6 &&
   216         (cpuid[0][0].Data[6, 0] & 1) != 0) 
   217       {
   218         coreTemperatures = new Sensor[coreCount];
   219         for (int i = 0; i < coreTemperatures.Length; i++) {
   220           coreTemperatures[i] = new Sensor(CoreString(i), i,
   221             SensorType.Temperature, this, new [] { 
   222               new ParameterDescription(
   223                 "TjMax [°C]", "TjMax temperature of the core sensor.\n" + 
   224                 "Temperature = TjMax - TSlope * Value.", tjMax[i]), 
   225               new ParameterDescription("TSlope [°C]", 
   226                 "Temperature slope of the digital thermal sensor.\n" + 
   227                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   228           ActivateSensor(coreTemperatures[i]);
   229         }
   230       } else {
   231         coreTemperatures = new Sensor[0];
   232       }
   233 
   234       // check if processor supports a digital thermal sensor at package level
   235       if (cpuid[0][0].Data.GetLength(0) > 6 &&
   236         (cpuid[0][0].Data[6, 0] & 0x40) != 0) 
   237       {
   238           packageTemperature = new Sensor("CPU Package", 
   239             coreTemperatures.Length, SensorType.Temperature, this, new[] { 
   240               new ParameterDescription(
   241                 "TjMax [°C]", "TjMax temperature of the package sensor.\n" + 
   242                 "Temperature = TjMax - TSlope * Value.", tjMax[0]), 
   243               new ParameterDescription("TSlope [°C]", 
   244                 "Temperature slope of the digital thermal sensor.\n" + 
   245                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   246         ActivateSensor(packageTemperature);
   247       } 
   248 
   249       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
   250       coreClocks = new Sensor[coreCount];
   251       for (int i = 0; i < coreClocks.Length; i++) {
   252         coreClocks[i] =
   253           new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
   254         if (HasTimeStampCounter)
   255           ActivateSensor(coreClocks[i]);
   256       }
   257 
   258       if (microarchitecture == Microarchitecture.SandyBridge) {
   259         uint eax, edx;
   260         if (Ring0.Rdmsr(MSR_RAPL_POWER_UNIT, out eax, out edx))
   261           energyUnitMultiplier = 1.0f / (1 << (int)((eax >> 8) & 0x1FF));
   262 
   263 
   264         if (energyUnitMultiplier != 0 && 
   265           Ring0.Rdmsr(MSR_PKG_ENERY_STATUS, out eax, out edx)) 
   266         {
   267           lastPackageTime = DateTime.UtcNow;
   268           lastPackageEnergyConsumed = eax;
   269           packagePower = new Sensor("CPU Package", 0, SensorType.Power, this, 
   270             settings);          
   271           ActivateSensor(packagePower);
   272         }
   273 
   274         if (energyUnitMultiplier != 0 &&
   275           Ring0.Rdmsr(MSR_PP0_ENERY_STATUS, out eax, out edx)) 
   276         {
   277           lastCoresTime = DateTime.UtcNow;
   278           lastCoresEnergyConsumed = eax;
   279           coresPower = new Sensor("CPU Cores", 1, SensorType.Power, this,
   280             settings);
   281           ActivateSensor(coresPower);
   282         }
   283       }
   284 
   285       Update();
   286     }
   287 
   288     protected override uint[] GetMSRs() {
   289       return new [] {
   290         MSR_PLATFORM_INFO,
   291         IA32_PERF_STATUS ,
   292         IA32_THERM_STATUS_MSR,
   293         IA32_TEMPERATURE_TARGET,
   294         IA32_PACKAGE_THERM_STATUS,
   295         MSR_RAPL_POWER_UNIT,
   296         MSR_PKG_ENERY_STATUS,
   297         MSR_PP0_ENERY_STATUS
   298       };
   299     }
   300 
   301     public override string GetReport() {
   302       StringBuilder r = new StringBuilder();
   303       r.Append(base.GetReport());
   304 
   305       r.Append("Microarchitecture: ");
   306       r.AppendLine(microarchitecture.ToString());
   307       r.Append("Time Stamp Counter Multiplier: ");
   308       r.AppendLine(timeStampCounterMultiplier.ToString(
   309         CultureInfo.InvariantCulture));
   310       r.AppendLine();
   311 
   312       return r.ToString();
   313     }
   314 
   315     public override void Update() {
   316       base.Update();
   317 
   318       for (int i = 0; i < coreTemperatures.Length; i++) {
   319         uint eax, edx;
   320         if (Ring0.RdmsrTx(
   321           IA32_THERM_STATUS_MSR, out eax, out edx,
   322             1UL << cpuid[i][0].Thread)) {
   323           // if reading is valid
   324           if ((eax & 0x80000000) != 0) {
   325             // get the dist from tjMax from bits 22:16
   326             float deltaT = ((eax & 0x007F0000) >> 16);
   327             float tjMax = coreTemperatures[i].Parameters[0].Value;
   328             float tSlope = coreTemperatures[i].Parameters[1].Value;
   329             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   330           } else {
   331             coreTemperatures[i].Value = null;
   332           }
   333         }
   334       }
   335 
   336       if (packageTemperature != null) {
   337         uint eax, edx;
   338         if (Ring0.RdmsrTx(
   339           IA32_PACKAGE_THERM_STATUS, out eax, out edx,
   340             1UL << cpuid[0][0].Thread)) {
   341           // get the dist from tjMax from bits 22:16
   342           float deltaT = ((eax & 0x007F0000) >> 16);
   343           float tjMax = packageTemperature.Parameters[0].Value;
   344           float tSlope = packageTemperature.Parameters[1].Value;
   345           packageTemperature.Value = tjMax - tSlope * deltaT;
   346         } else {
   347           packageTemperature.Value = null;
   348         }
   349       }
   350 
   351       if (HasTimeStampCounter) {
   352         double newBusClock = 0;
   353         uint eax, edx;
   354         for (int i = 0; i < coreClocks.Length; i++) {
   355           System.Threading.Thread.Sleep(1);
   356           if (Ring0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   357             1UL << cpuid[i][0].Thread)) 
   358           {
   359             newBusClock = 
   360               TimeStampCounterFrequency / timeStampCounterMultiplier;
   361             switch (microarchitecture) {
   362               case Microarchitecture.Nehalem: {
   363                   uint multiplier = eax & 0xff;
   364                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   365                 } break;
   366               case Microarchitecture.SandyBridge: {
   367                   uint multiplier = (eax >> 8) & 0xff;
   368                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   369                 } break;
   370               default: {
   371                   double multiplier = 
   372                     ((eax >> 8) & 0x1f) + 0.5 * ((eax >> 14) & 1);
   373                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   374                 } break;
   375             }         
   376           } else { 
   377             // if IA32_PERF_STATUS is not available, assume TSC frequency
   378             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   379           }
   380         }
   381         if (newBusClock > 0) {
   382           this.busClock.Value = (float)newBusClock;
   383           ActivateSensor(this.busClock);
   384         }
   385       }
   386 
   387 
   388       if (packagePower != null) {
   389         uint eax, edx;
   390         if (Ring0.Rdmsr(MSR_PKG_ENERY_STATUS, out eax, out edx)) {
   391           DateTime time = DateTime.UtcNow;    
   392           uint energyConsumed = eax;
   393           float deltaTime = (float)(time - lastPackageTime).TotalSeconds;
   394           if (deltaTime > 0.01) {
   395             packagePower.Value = energyUnitMultiplier * 
   396               unchecked(energyConsumed - lastPackageEnergyConsumed) / deltaTime;
   397             lastPackageTime = time;
   398             lastPackageEnergyConsumed = energyConsumed;
   399           }
   400         }         
   401       }
   402 
   403       if (coresPower != null) {
   404         uint eax, edx;
   405         if (Ring0.Rdmsr(MSR_PP0_ENERY_STATUS, out eax, out edx)) {
   406           DateTime time = DateTime.UtcNow;
   407           uint energyConsumed = eax;
   408           float deltaTime = (float)(time - lastCoresTime).TotalSeconds;
   409           if (deltaTime > 0.01) {
   410             coresPower.Value = energyUnitMultiplier *
   411               unchecked(energyConsumed - lastCoresEnergyConsumed) / deltaTime;
   412             lastCoresTime = time;
   413             lastCoresEnergyConsumed = energyConsumed;
   414           }
   415         }
   416       }
   417     }
   418   }
   419 }