Hardware/CPU/IntelCPU.cs
author moel.mich
Sun, 27 May 2012 14:23:31 +0000
changeset 344 3145aadca3d2
parent 321 1da7448c8e10
child 350 6de77245e32b
permissions -rw-r--r--
Changed the license to the Mozilla Public License 2.0 and update the licensing information.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2009-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Globalization;
    13 using System.Text;
    14 
    15 namespace OpenHardwareMonitor.Hardware.CPU {
    16   internal sealed class IntelCPU : GenericCPU {
    17 
    18     private enum Microarchitecture {
    19       Unknown,
    20       NetBurst,
    21       Core,
    22       Atom,
    23       Nehalem,
    24       SandyBridge
    25     }
    26 
    27     private readonly Sensor[] coreTemperatures;
    28     private readonly Sensor packageTemperature;
    29     private readonly Sensor[] coreClocks;
    30     private readonly Sensor busClock;
    31     private readonly Sensor[] powerSensors;
    32 
    33     private readonly Microarchitecture microarchitecture;
    34     private readonly double timeStampCounterMultiplier;
    35 
    36     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    37     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    38     private const uint IA32_PERF_STATUS = 0x0198;
    39     private const uint MSR_PLATFORM_INFO = 0xCE;
    40     private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
    41     private const uint MSR_RAPL_POWER_UNIT = 0x606;
    42     private const uint MSR_PKG_ENERY_STATUS = 0x611;
    43     private const uint MSR_DRAM_ENERGY_STATUS = 0x619;
    44     private const uint MSR_PP0_ENERY_STATUS = 0x639;
    45     private const uint MSR_PP1_ENERY_STATUS = 0x641;
    46 
    47     private readonly uint[] energyStatusMSRs = { MSR_PKG_ENERY_STATUS, 
    48       MSR_PP0_ENERY_STATUS, MSR_PP1_ENERY_STATUS, MSR_DRAM_ENERGY_STATUS };
    49     private readonly string[] powerSensorLabels = 
    50       { "CPU Package", "CPU Cores", "CPU Graphics", "CPU DRAM" };
    51     private float energyUnitMultiplier = 0;
    52     private DateTime[] lastEnergyTime;
    53     private uint[] lastEnergyConsumed;
    54 
    55 
    56     private float[] Floats(float f) {
    57       float[] result = new float[coreCount];
    58       for (int i = 0; i < coreCount; i++)
    59         result[i] = f;
    60       return result;
    61     }
    62 
    63     private float[] GetTjMaxFromMSR() {
    64       uint eax, edx;
    65       float[] result = new float[coreCount];
    66       for (int i = 0; i < coreCount; i++) {
    67         if (Ring0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
    68           out edx, 1UL << cpuid[i][0].Thread)) {
    69           result[i] = (eax >> 16) & 0xFF;
    70         } else {
    71           result[i] = 100;
    72         }
    73       }
    74       return result;
    75     }
    76 
    77     public IntelCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    78       : base(processorIndex, cpuid, settings) {
    79       // set tjMax
    80       float[] tjMax;
    81       switch (family) {
    82         case 0x06: {
    83             switch (model) {
    84               case 0x0F: // Intel Core 2 (65nm)
    85                 microarchitecture = Microarchitecture.Core;
    86                 switch (stepping) {
    87                   case 0x06: // B2
    88                     switch (coreCount) {
    89                       case 2:
    90                         tjMax = Floats(80 + 10); break;
    91                       case 4:
    92                         tjMax = Floats(90 + 10); break;
    93                       default:
    94                         tjMax = Floats(85 + 10); break;
    95                     }
    96                     tjMax = Floats(80 + 10); break;
    97                   case 0x0B: // G0
    98                     tjMax = Floats(90 + 10); break;
    99                   case 0x0D: // M0
   100                     tjMax = Floats(85 + 10); break;
   101                   default:
   102                     tjMax = Floats(85 + 10); break;
   103                 } break;
   104               case 0x17: // Intel Core 2 (45nm)
   105                 microarchitecture = Microarchitecture.Core;
   106                 tjMax = Floats(100); break;
   107               case 0x1C: // Intel Atom (45nm)
   108                 microarchitecture = Microarchitecture.Atom;
   109                 switch (stepping) {
   110                   case 0x02: // C0
   111                     tjMax = Floats(90); break;
   112                   case 0x0A: // A0, B0
   113                     tjMax = Floats(100); break;
   114                   default:
   115                     tjMax = Floats(90); break;
   116                 } break;
   117               case 0x1A: // Intel Core i7 LGA1366 (45nm)
   118               case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
   119               case 0x1F: // Intel Core i5, i7 
   120               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   121               case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
   122               case 0x2E: // Intel Xeon Processor 7500 series
   123                 microarchitecture = Microarchitecture.Nehalem;
   124                 tjMax = GetTjMaxFromMSR();
   125                 break;
   126               case 0x2A: // Intel Core i5, i7 2xxx LGA1155 (32nm)
   127               case 0x2D: // Next Generation Intel Xeon Processor
   128                 microarchitecture = Microarchitecture.SandyBridge;
   129                 tjMax = GetTjMaxFromMSR();
   130                 break;
   131               default:
   132                 microarchitecture = Microarchitecture.Unknown;
   133                 tjMax = Floats(100);
   134                 break;
   135             }
   136           } break;
   137         case 0x0F: {
   138             switch (model) {
   139               case 0x00: // Pentium 4 (180nm)
   140               case 0x01: // Pentium 4 (130nm)
   141               case 0x02: // Pentium 4 (130nm)
   142               case 0x03: // Pentium 4, Celeron D (90nm)
   143               case 0x04: // Pentium 4, Pentium D, Celeron D (90nm)
   144               case 0x06: // Pentium 4, Pentium D, Celeron D (65nm)
   145                 microarchitecture = Microarchitecture.NetBurst;
   146                 tjMax = Floats(100);
   147                 break;
   148               default:
   149                 microarchitecture = Microarchitecture.Unknown;
   150                 tjMax = Floats(100);
   151                 break;
   152             }
   153           } break;
   154         default:
   155           microarchitecture = Microarchitecture.Unknown;
   156           tjMax = Floats(100);
   157           break;
   158       }
   159 
   160       // set timeStampCounterMultiplier
   161       switch (microarchitecture) {
   162         case Microarchitecture.NetBurst:
   163         case Microarchitecture.Atom:
   164         case Microarchitecture.Core: {
   165             uint eax, edx;
   166             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   167               timeStampCounterMultiplier =
   168                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   169             }
   170           } break;
   171         case Microarchitecture.Nehalem:
   172         case Microarchitecture.SandyBridge: {
   173             uint eax, edx;
   174             if (Ring0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   175               timeStampCounterMultiplier = (eax >> 8) & 0xff;
   176             }
   177           } break;
   178         default: {
   179             timeStampCounterMultiplier = 1;
   180             uint eax, edx;
   181             if (Ring0.Rdmsr(IA32_PERF_STATUS, out eax, out edx)) {
   182               timeStampCounterMultiplier =
   183                 ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
   184             }
   185           } break;
   186       }
   187 
   188       // check if processor supports a digital thermal sensor at core level
   189       if (cpuid[0][0].Data.GetLength(0) > 6 &&
   190         (cpuid[0][0].Data[6, 0] & 1) != 0) {
   191         coreTemperatures = new Sensor[coreCount];
   192         for (int i = 0; i < coreTemperatures.Length; i++) {
   193           coreTemperatures[i] = new Sensor(CoreString(i), i,
   194             SensorType.Temperature, this, new[] { 
   195               new ParameterDescription(
   196                 "TjMax [°C]", "TjMax temperature of the core sensor.\n" + 
   197                 "Temperature = TjMax - TSlope * Value.", tjMax[i]), 
   198               new ParameterDescription("TSlope [°C]", 
   199                 "Temperature slope of the digital thermal sensor.\n" + 
   200                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   201           ActivateSensor(coreTemperatures[i]);
   202         }
   203       } else {
   204         coreTemperatures = new Sensor[0];
   205       }
   206 
   207       // check if processor supports a digital thermal sensor at package level
   208       if (cpuid[0][0].Data.GetLength(0) > 6 &&
   209         (cpuid[0][0].Data[6, 0] & 0x40) != 0) {
   210         packageTemperature = new Sensor("CPU Package",
   211           coreTemperatures.Length, SensorType.Temperature, this, new[] { 
   212               new ParameterDescription(
   213                 "TjMax [°C]", "TjMax temperature of the package sensor.\n" + 
   214                 "Temperature = TjMax - TSlope * Value.", tjMax[0]), 
   215               new ParameterDescription("TSlope [°C]", 
   216                 "Temperature slope of the digital thermal sensor.\n" + 
   217                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   218         ActivateSensor(packageTemperature);
   219       }
   220 
   221       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
   222       coreClocks = new Sensor[coreCount];
   223       for (int i = 0; i < coreClocks.Length; i++) {
   224         coreClocks[i] =
   225           new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
   226         if (HasTimeStampCounter)
   227           ActivateSensor(coreClocks[i]);
   228       }
   229 
   230       if (microarchitecture == Microarchitecture.SandyBridge) {
   231 
   232         powerSensors = new Sensor[energyStatusMSRs.Length];
   233         lastEnergyTime = new DateTime[energyStatusMSRs.Length];
   234         lastEnergyConsumed = new uint[energyStatusMSRs.Length];
   235 
   236         uint eax, edx;
   237         if (Ring0.Rdmsr(MSR_RAPL_POWER_UNIT, out eax, out edx))
   238           energyUnitMultiplier = 1.0f / (1 << (int)((eax >> 8) & 0x1FF));
   239 
   240         if (energyUnitMultiplier != 0) {
   241           for (int i = 0; i < energyStatusMSRs.Length; i++) {
   242             if (!Ring0.Rdmsr(energyStatusMSRs[i], out eax, out edx))
   243               continue;
   244 
   245             lastEnergyTime[i] = DateTime.UtcNow;
   246             lastEnergyConsumed[i] = eax;
   247             powerSensors[i] = new Sensor(powerSensorLabels[i], i,
   248               SensorType.Power, this, settings);
   249             ActivateSensor(powerSensors[i]);
   250           }
   251         }
   252       }
   253 
   254       Update();
   255     }
   256 
   257     protected override uint[] GetMSRs() {
   258       return new[] {
   259         MSR_PLATFORM_INFO,
   260         IA32_PERF_STATUS ,
   261         IA32_THERM_STATUS_MSR,
   262         IA32_TEMPERATURE_TARGET,
   263         IA32_PACKAGE_THERM_STATUS,
   264         MSR_RAPL_POWER_UNIT,
   265         MSR_PKG_ENERY_STATUS,
   266         MSR_DRAM_ENERGY_STATUS,
   267         MSR_PP0_ENERY_STATUS,
   268         MSR_PP1_ENERY_STATUS
   269       };
   270     }
   271 
   272     public override string GetReport() {
   273       StringBuilder r = new StringBuilder();
   274       r.Append(base.GetReport());
   275 
   276       r.Append("Microarchitecture: ");
   277       r.AppendLine(microarchitecture.ToString());
   278       r.Append("Time Stamp Counter Multiplier: ");
   279       r.AppendLine(timeStampCounterMultiplier.ToString(
   280         CultureInfo.InvariantCulture));
   281       r.AppendLine();
   282 
   283       return r.ToString();
   284     }
   285 
   286     public override void Update() {
   287       base.Update();
   288 
   289       for (int i = 0; i < coreTemperatures.Length; i++) {
   290         uint eax, edx;
   291         if (Ring0.RdmsrTx(
   292           IA32_THERM_STATUS_MSR, out eax, out edx,
   293             1UL << cpuid[i][0].Thread)) {
   294           // if reading is valid
   295           if ((eax & 0x80000000) != 0) {
   296             // get the dist from tjMax from bits 22:16
   297             float deltaT = ((eax & 0x007F0000) >> 16);
   298             float tjMax = coreTemperatures[i].Parameters[0].Value;
   299             float tSlope = coreTemperatures[i].Parameters[1].Value;
   300             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   301           } else {
   302             coreTemperatures[i].Value = null;
   303           }
   304         }
   305       }
   306 
   307       if (packageTemperature != null) {
   308         uint eax, edx;
   309         if (Ring0.RdmsrTx(
   310           IA32_PACKAGE_THERM_STATUS, out eax, out edx,
   311             1UL << cpuid[0][0].Thread)) {
   312           // get the dist from tjMax from bits 22:16
   313           float deltaT = ((eax & 0x007F0000) >> 16);
   314           float tjMax = packageTemperature.Parameters[0].Value;
   315           float tSlope = packageTemperature.Parameters[1].Value;
   316           packageTemperature.Value = tjMax - tSlope * deltaT;
   317         } else {
   318           packageTemperature.Value = null;
   319         }
   320       }
   321 
   322       if (HasTimeStampCounter) {
   323         double newBusClock = 0;
   324         uint eax, edx;
   325         for (int i = 0; i < coreClocks.Length; i++) {
   326           System.Threading.Thread.Sleep(1);
   327           if (Ring0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   328             1UL << cpuid[i][0].Thread)) {
   329             newBusClock =
   330               TimeStampCounterFrequency / timeStampCounterMultiplier;
   331             switch (microarchitecture) {
   332               case Microarchitecture.Nehalem: {
   333                   uint multiplier = eax & 0xff;
   334                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   335                 } break;
   336               case Microarchitecture.SandyBridge: {
   337                   uint multiplier = (eax >> 8) & 0xff;
   338                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   339                 } break;
   340               default: {
   341                   double multiplier =
   342                     ((eax >> 8) & 0x1f) + 0.5 * ((eax >> 14) & 1);
   343                   coreClocks[i].Value = (float)(multiplier * newBusClock);
   344                 } break;
   345             }
   346           } else {
   347             // if IA32_PERF_STATUS is not available, assume TSC frequency
   348             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   349           }
   350         }
   351         if (newBusClock > 0) {
   352           this.busClock.Value = (float)newBusClock;
   353           ActivateSensor(this.busClock);
   354         }
   355       }
   356 
   357       if (powerSensors != null) {
   358         foreach (Sensor sensor in powerSensors) {
   359           if (sensor == null)
   360             continue;
   361 
   362           uint eax, edx;
   363           if (!Ring0.Rdmsr(energyStatusMSRs[sensor.Index], out eax, out edx))
   364             continue;
   365 
   366           DateTime time = DateTime.UtcNow;
   367           uint energyConsumed = eax;
   368           float deltaTime =
   369             (float)(time - lastEnergyTime[sensor.Index]).TotalSeconds;
   370           if (deltaTime < 0.01)
   371             continue;
   372 
   373           sensor.Value = energyUnitMultiplier * unchecked(
   374             energyConsumed - lastEnergyConsumed[sensor.Index]) / deltaTime;
   375           lastEnergyTime[sensor.Index] = time;
   376           lastEnergyConsumed[sensor.Index] = energyConsumed;
   377         }
   378       }
   379     }
   380   }
   381 }