Hardware/CPU/IntelCPU.cs
author moel.mich
Thu, 11 Aug 2011 22:18:53 +0000
changeset 321 1da7448c8e10
parent 320 df3493f75225
child 344 3145aadca3d2
permissions -rw-r--r--
Added additional Intel Sandy Bridge CPU power sensors.
     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[] powerSensors;
    59 
    60     private readonly Microarchitecture microarchitecture;
    61     private readonly double timeStampCounterMultiplier;
    62 
    63     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    64     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    65     private const uint IA32_PERF_STATUS = 0x0198;
    66     private const uint MSR_PLATFORM_INFO = 0xCE;
    67     private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
    68     private const uint MSR_RAPL_POWER_UNIT = 0x606;
    69     private const uint MSR_PKG_ENERY_STATUS = 0x611;
    70     private const uint MSR_DRAM_ENERGY_STATUS = 0x619;
    71     private const uint MSR_PP0_ENERY_STATUS = 0x639;
    72     private const uint MSR_PP1_ENERY_STATUS = 0x641;
    73 
    74     private readonly uint[] energyStatusMSRs = { MSR_PKG_ENERY_STATUS, 
    75       MSR_PP0_ENERY_STATUS, MSR_PP1_ENERY_STATUS, MSR_DRAM_ENERGY_STATUS };
    76     private readonly string[] powerSensorLabels = 
    77       { "CPU Package", "CPU Cores", "CPU Graphics", "CPU DRAM" };
    78     private float energyUnitMultiplier = 0;
    79     private DateTime[] lastEnergyTime;
    80     private uint[] lastEnergyConsumed;
    81 
    82 
    83     private float[] Floats(float f) {
    84       float[] result = new float[coreCount];
    85       for (int i = 0; i < coreCount; i++)
    86         result[i] = f;
    87       return result;
    88     }
    89 
    90     private float[] GetTjMaxFromMSR() {
    91       uint eax, edx;
    92       float[] result = new float[coreCount];
    93       for (int i = 0; i < coreCount; i++) {
    94         if (Ring0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
    95           out edx, 1UL << cpuid[i][0].Thread)) {
    96           result[i] = (eax >> 16) & 0xFF;
    97         } else {
    98           result[i] = 100;
    99         }
   100       }
   101       return result;
   102     }
   103 
   104     public IntelCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
   105       : base(processorIndex, cpuid, settings) {
   106       // set tjMax
   107       float[] tjMax;
   108       switch (family) {
   109         case 0x06: {
   110             switch (model) {
   111               case 0x0F: // Intel Core 2 (65nm)
   112                 microarchitecture = Microarchitecture.Core;
   113                 switch (stepping) {
   114                   case 0x06: // B2
   115                     switch (coreCount) {
   116                       case 2:
   117                         tjMax = Floats(80 + 10); break;
   118                       case 4:
   119                         tjMax = Floats(90 + 10); break;
   120                       default:
   121                         tjMax = Floats(85 + 10); break;
   122                     }
   123                     tjMax = Floats(80 + 10); break;
   124                   case 0x0B: // G0
   125                     tjMax = Floats(90 + 10); break;
   126                   case 0x0D: // M0
   127                     tjMax = Floats(85 + 10); break;
   128                   default:
   129                     tjMax = Floats(85 + 10); break;
   130                 } break;
   131               case 0x17: // Intel Core 2 (45nm)
   132                 microarchitecture = Microarchitecture.Core;
   133                 tjMax = Floats(100); break;
   134               case 0x1C: // Intel Atom (45nm)
   135                 microarchitecture = Microarchitecture.Atom;
   136                 switch (stepping) {
   137                   case 0x02: // C0
   138                     tjMax = Floats(90); break;
   139                   case 0x0A: // A0, B0
   140                     tjMax = Floats(100); break;
   141                   default:
   142                     tjMax = Floats(90); break;
   143                 } break;
   144               case 0x1A: // Intel Core i7 LGA1366 (45nm)
   145               case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
   146               case 0x1F: // Intel Core i5, i7 
   147               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   148               case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
   149               case 0x2E: // Intel Xeon Processor 7500 series
   150                 microarchitecture = Microarchitecture.Nehalem;
   151                 tjMax = GetTjMaxFromMSR();
   152                 break;
   153               case 0x2A: // Intel Core i5, i7 2xxx LGA1155 (32nm)
   154               case 0x2D: // Next Generation Intel Xeon Processor
   155                 microarchitecture = Microarchitecture.SandyBridge;
   156                 tjMax = GetTjMaxFromMSR();
   157                 break;
   158               default:
   159                 microarchitecture = Microarchitecture.Unknown;
   160                 tjMax = Floats(100);
   161                 break;
   162             }
   163           } break;
   164         case 0x0F: {
   165             switch (model) {
   166               case 0x00: // Pentium 4 (180nm)
   167               case 0x01: // Pentium 4 (130nm)
   168               case 0x02: // Pentium 4 (130nm)
   169               case 0x03: // Pentium 4, Celeron D (90nm)
   170               case 0x04: // Pentium 4, Pentium D, Celeron D (90nm)
   171               case 0x06: // Pentium 4, Pentium D, Celeron D (65nm)
   172                 microarchitecture = Microarchitecture.NetBurst;
   173                 tjMax = Floats(100);
   174                 break;
   175               default:
   176                 microarchitecture = Microarchitecture.Unknown;
   177                 tjMax = Floats(100);
   178                 break;
   179             }
   180           } break;
   181         default:
   182           microarchitecture = Microarchitecture.Unknown;
   183           tjMax = Floats(100);
   184           break;
   185       }
   186 
   187       // set timeStampCounterMultiplier
   188       switch (microarchitecture) {
   189         case Microarchitecture.NetBurst:
   190         case Microarchitecture.Atom:
   191         case Microarchitecture.Core: {
   192             uint eax, edx;
   193             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   194               timeStampCounterMultiplier =
   195                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   196             }
   197           } break;
   198         case Microarchitecture.Nehalem:
   199         case Microarchitecture.SandyBridge: {
   200             uint eax, edx;
   201             if (Ring0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   202               timeStampCounterMultiplier = (eax >> 8) & 0xff;
   203             }
   204           } break;
   205         default: {
   206             timeStampCounterMultiplier = 1;
   207             uint eax, edx;
   208             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   209               timeStampCounterMultiplier =
   210                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   211             }
   212           } break;
   213       }
   214 
   215       // check if processor supports a digital thermal sensor at core level
   216       if (cpuid[0][0].Data.GetLength(0) > 6 &&
   217         (cpuid[0][0].Data[6, 0] & 1) != 0) {
   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         packageTemperature = new Sensor("CPU Package",
   238           coreTemperatures.Length, SensorType.Temperature, this, new[] { 
   239               new ParameterDescription(
   240                 "TjMax [°C]", "TjMax temperature of the package sensor.\n" + 
   241                 "Temperature = TjMax - TSlope * Value.", tjMax[0]), 
   242               new ParameterDescription("TSlope [°C]", 
   243                 "Temperature slope of the digital thermal sensor.\n" + 
   244                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   245         ActivateSensor(packageTemperature);
   246       }
   247 
   248       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
   249       coreClocks = new Sensor[coreCount];
   250       for (int i = 0; i < coreClocks.Length; i++) {
   251         coreClocks[i] =
   252           new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
   253         if (HasTimeStampCounter)
   254           ActivateSensor(coreClocks[i]);
   255       }
   256 
   257       if (microarchitecture == Microarchitecture.SandyBridge) {
   258 
   259         powerSensors = new Sensor[energyStatusMSRs.Length];
   260         lastEnergyTime = new DateTime[energyStatusMSRs.Length];
   261         lastEnergyConsumed = new uint[energyStatusMSRs.Length];
   262 
   263         uint eax, edx;
   264         if (Ring0.Rdmsr(MSR_RAPL_POWER_UNIT, out eax, out edx))
   265           energyUnitMultiplier = 1.0f / (1 << (int)((eax >> 8) & 0x1FF));
   266 
   267         if (energyUnitMultiplier != 0) {
   268           for (int i = 0; i < energyStatusMSRs.Length; i++) {
   269             if (!Ring0.Rdmsr(energyStatusMSRs[i], out eax, out edx))
   270               continue;
   271 
   272             lastEnergyTime[i] = DateTime.UtcNow;
   273             lastEnergyConsumed[i] = eax;
   274             powerSensors[i] = new Sensor(powerSensorLabels[i], i,
   275               SensorType.Power, this, settings);
   276             ActivateSensor(powerSensors[i]);
   277           }
   278         }
   279       }
   280 
   281       Update();
   282     }
   283 
   284     protected override uint[] GetMSRs() {
   285       return new[] {
   286         MSR_PLATFORM_INFO,
   287         IA32_PERF_STATUS ,
   288         IA32_THERM_STATUS_MSR,
   289         IA32_TEMPERATURE_TARGET,
   290         IA32_PACKAGE_THERM_STATUS,
   291         MSR_RAPL_POWER_UNIT,
   292         MSR_PKG_ENERY_STATUS,
   293         MSR_DRAM_ENERGY_STATUS,
   294         MSR_PP0_ENERY_STATUS,
   295         MSR_PP1_ENERY_STATUS
   296       };
   297     }
   298 
   299     public override string GetReport() {
   300       StringBuilder r = new StringBuilder();
   301       r.Append(base.GetReport());
   302 
   303       r.Append("Microarchitecture: ");
   304       r.AppendLine(microarchitecture.ToString());
   305       r.Append("Time Stamp Counter Multiplier: ");
   306       r.AppendLine(timeStampCounterMultiplier.ToString(
   307         CultureInfo.InvariantCulture));
   308       r.AppendLine();
   309 
   310       return r.ToString();
   311     }
   312 
   313     public override void Update() {
   314       base.Update();
   315 
   316       for (int i = 0; i < coreTemperatures.Length; i++) {
   317         uint eax, edx;
   318         if (Ring0.RdmsrTx(
   319           IA32_THERM_STATUS_MSR, out eax, out edx,
   320             1UL << cpuid[i][0].Thread)) {
   321           // if reading is valid
   322           if ((eax & 0x80000000) != 0) {
   323             // get the dist from tjMax from bits 22:16
   324             float deltaT = ((eax & 0x007F0000) >> 16);
   325             float tjMax = coreTemperatures[i].Parameters[0].Value;
   326             float tSlope = coreTemperatures[i].Parameters[1].Value;
   327             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   328           } else {
   329             coreTemperatures[i].Value = null;
   330           }
   331         }
   332       }
   333 
   334       if (packageTemperature != null) {
   335         uint eax, edx;
   336         if (Ring0.RdmsrTx(
   337           IA32_PACKAGE_THERM_STATUS, out eax, out edx,
   338             1UL << cpuid[0][0].Thread)) {
   339           // get the dist from tjMax from bits 22:16
   340           float deltaT = ((eax & 0x007F0000) >> 16);
   341           float tjMax = packageTemperature.Parameters[0].Value;
   342           float tSlope = packageTemperature.Parameters[1].Value;
   343           packageTemperature.Value = tjMax - tSlope * deltaT;
   344         } else {
   345           packageTemperature.Value = null;
   346         }
   347       }
   348 
   349       if (HasTimeStampCounter) {
   350         double newBusClock = 0;
   351         uint eax, edx;
   352         for (int i = 0; i < coreClocks.Length; i++) {
   353           System.Threading.Thread.Sleep(1);
   354           if (Ring0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   355             1UL << cpuid[i][0].Thread)) {
   356             newBusClock =
   357               TimeStampCounterFrequency / timeStampCounterMultiplier;
   358             switch (microarchitecture) {
   359               case Microarchitecture.Nehalem: {
   360                   uint multiplier = eax & 0xff;
   361                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   362                 } break;
   363               case Microarchitecture.SandyBridge: {
   364                   uint multiplier = (eax >> 8) & 0xff;
   365                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   366                 } break;
   367               default: {
   368                   double multiplier =
   369                     ((eax >> 8) & 0x1f) + 0.5 * ((eax >> 14) & 1);
   370                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   371                 } break;
   372             }
   373           } else {
   374             // if IA32_PERF_STATUS is not available, assume TSC frequency
   375             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   376           }
   377         }
   378         if (newBusClock > 0) {
   379           this.busClock.Value = (float)newBusClock;
   380           ActivateSensor(this.busClock);
   381         }
   382       }
   383 
   384       if (powerSensors != null) {
   385         foreach (Sensor sensor in powerSensors) {
   386           if (sensor == null)
   387             continue;
   388 
   389           uint eax, edx;
   390           if (!Ring0.Rdmsr(energyStatusMSRs[sensor.Index], out eax, out edx))
   391             continue;
   392 
   393           DateTime time = DateTime.UtcNow;
   394           uint energyConsumed = eax;
   395           float deltaTime =
   396             (float)(time - lastEnergyTime[sensor.Index]).TotalSeconds;
   397           if (deltaTime < 0.01)
   398             continue;
   399 
   400           sensor.Value = energyUnitMultiplier * unchecked(
   401             energyConsumed - lastEnergyConsumed[sensor.Index]) / deltaTime;
   402           lastEnergyTime[sensor.Index] = time;
   403           lastEnergyConsumed[sensor.Index] = energyConsumed;
   404         }
   405       }
   406     }
   407   }
   408 }