Files
ReLibreHardwareMonitor/LibreHardwareMonitorLib/Hardware/Cpu/IntelCpu.cs
T
2022-02-26 10:14:09 +01:00

665 lines
30 KiB
C#

// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
// All Rights Reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
namespace LibreHardwareMonitor.Hardware.CPU
{
internal sealed class IntelCpu : GenericCpu
{
private readonly Sensor _busClock;
private readonly Sensor _coreAvg;
private readonly Sensor[] _coreClocks;
private readonly Sensor _coreMax;
private readonly Sensor[] _coreTemperatures;
private readonly Sensor[] _distToTjMaxTemperatures;
private readonly uint[] _energyStatusMsrs = { MSR_PKG_ENERY_STATUS, MSR_PP0_ENERY_STATUS, MSR_PP1_ENERY_STATUS, MSR_DRAM_ENERGY_STATUS };
private readonly float _energyUnitMultiplier;
private readonly uint[] _lastEnergyConsumed;
private readonly DateTime[] _lastEnergyTime;
private readonly MicroArchitecture _microArchitecture;
private readonly Sensor _packageTemperature;
private readonly Sensor[] _powerSensors;
private readonly double _timeStampCounterMultiplier;
public float EnergyUnitsMultiplier => _energyUnitMultiplier;
public IntelCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
{
// set tjMax
float[] tjMax;
switch (_family)
{
case 0x06:
{
switch (_model)
{
case 0x0F: // Intel Core 2 (65nm)
_microArchitecture = MicroArchitecture.Core;
switch (_stepping)
{
case 0x06: // B2
switch (_coreCount)
{
case 2:
tjMax = Floats(80 + 10);
break;
case 4:
tjMax = Floats(90 + 10);
break;
default:
tjMax = Floats(85 + 10);
break;
}
break;
case 0x0B: // G0
tjMax = Floats(90 + 10);
break;
case 0x0D: // M0
tjMax = Floats(85 + 10);
break;
default:
tjMax = Floats(85 + 10);
break;
}
break;
case 0x17: // Intel Core 2 (45nm)
_microArchitecture = MicroArchitecture.Core;
tjMax = Floats(100);
break;
case 0x1C: // Intel Atom (45nm)
_microArchitecture = MicroArchitecture.Atom;
switch (_stepping)
{
case 0x02: // C0
tjMax = Floats(90);
break;
case 0x0A: // A0, B0
tjMax = Floats(100);
break;
default:
tjMax = Floats(90);
break;
}
break;
case 0x1A: // Intel Core i7 LGA1366 (45nm)
case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
case 0x1F: // Intel Core i5, i7
case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
case 0x2E: // Intel Xeon Processor 7500 series (45nm)
case 0x2F: // Intel Xeon Processor (32nm)
_microArchitecture = MicroArchitecture.Nehalem;
tjMax = GetTjMaxFromMsr();
break;
case 0x2A: // Intel Core i5, i7 2xxx LGA1155 (32nm)
case 0x2D: // Next Generation Intel Xeon, i7 3xxx LGA2011 (32nm)
_microArchitecture = MicroArchitecture.SandyBridge;
tjMax = GetTjMaxFromMsr();
break;
case 0x3A: // Intel Core i5, i7 3xxx LGA1155 (22nm)
case 0x3E: // Intel Core i7 4xxx LGA2011 (22nm)
_microArchitecture = MicroArchitecture.IvyBridge;
tjMax = GetTjMaxFromMsr();
break;
case 0x3C: // Intel Core i5, i7 4xxx LGA1150 (22nm)
case 0x3F: // Intel Xeon E5-2600/1600 v3, Core i7-59xx
// LGA2011-v3, Haswell-E (22nm)
case 0x45: // Intel Core i5, i7 4xxxU (22nm)
case 0x46:
_microArchitecture = MicroArchitecture.Haswell;
tjMax = GetTjMaxFromMsr();
break;
case 0x3D: // Intel Core M-5xxx (14nm)
case 0x47: // Intel i5, i7 5xxx, Xeon E3-1200 v4 (14nm)
case 0x4F: // Intel Xeon E5-26xx v4
case 0x56: // Intel Xeon D-15xx
_microArchitecture = MicroArchitecture.Broadwell;
tjMax = GetTjMaxFromMsr();
break;
case 0x36: // Intel Atom S1xxx, D2xxx, N2xxx (32nm)
_microArchitecture = MicroArchitecture.Atom;
tjMax = GetTjMaxFromMsr();
break;
case 0x37: // Intel Atom E3xxx, Z3xxx (22nm)
case 0x4A:
case 0x4D: // Intel Atom C2xxx (22nm)
case 0x5A:
case 0x5D:
_microArchitecture = MicroArchitecture.Silvermont;
tjMax = GetTjMaxFromMsr();
break;
case 0x4E:
case 0x5E: // Intel Core i5, i7 6xxxx LGA1151 (14nm)
case 0x55: // Intel Core X i7, i9 7xxx LGA2066 (14nm)
_microArchitecture = MicroArchitecture.Skylake;
tjMax = GetTjMaxFromMsr();
break;
case 0x4C: // Intel Airmont (Cherry Trail, Braswell)
_microArchitecture = MicroArchitecture.Airmont;
tjMax = GetTjMaxFromMsr();
break;
case 0x8E: // Intel Core i5, i7 7xxxx (14nm) (Kaby Lake) and 8xxxx (14nm++) (Coffee Lake)
case 0x9E:
_microArchitecture = MicroArchitecture.KabyLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x5C: // Goldmont (Apollo Lake)
case 0x5F: // (Denverton)
_microArchitecture = MicroArchitecture.Goldmont;
tjMax = GetTjMaxFromMsr();
break;
case 0x7A: // Goldmont plus (Gemini Lake)
_microArchitecture = MicroArchitecture.GoldmontPlus;
tjMax = GetTjMaxFromMsr();
break;
case 0x66: // Intel Core i3 8xxx (10nm) (Cannon Lake)
_microArchitecture = MicroArchitecture.CannonLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x7D: // Intel Core i3, i5, i7 10xxx (10nm) (Ice Lake)
case 0x7E:
case 0x6A: // Ice Lake server
case 0x6C:
_microArchitecture = MicroArchitecture.IceLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xA5:
case 0xA6: // Intel Core i3, i5, i7 10xxxU (14nm)
_microArchitecture = MicroArchitecture.CometLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x86: // Tremont (10nm) (Elkhart Lake, Skyhawk Lake)
_microArchitecture = MicroArchitecture.Tremont;
tjMax = GetTjMaxFromMsr();
break;
case 0x8C: // Tiger Lake (10nm)
case 0x8D:
_microArchitecture = MicroArchitecture.TigerLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x97: // Alder Lake (7nm)
_microArchitecture = MicroArchitecture.AlderLake;
tjMax = GetTjMaxFromMsr();
break;
case 0x9C: // Jasper Lake (10nm)
_microArchitecture = MicroArchitecture.JasperLake;
tjMax = GetTjMaxFromMsr();
break;
case 0xA7: // Intel Core i5, i6, i7 11xxx (14nm) (Rocket Lake)
_microArchitecture = MicroArchitecture.RocketLake;
tjMax = GetTjMaxFromMsr();
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
}
break;
case 0x0F:
{
switch (_model)
{
case 0x00: // Pentium 4 (180nm)
case 0x01: // Pentium 4 (130nm)
case 0x02: // Pentium 4 (130nm)
case 0x03: // Pentium 4, Celeron D (90nm)
case 0x04: // Pentium 4, Pentium D, Celeron D (90nm)
case 0x06: // Pentium 4, Pentium D, Celeron D (65nm)
_microArchitecture = MicroArchitecture.NetBurst;
tjMax = Floats(100);
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
}
break;
default:
_microArchitecture = MicroArchitecture.Unknown;
tjMax = Floats(100);
break;
}
// set timeStampCounterMultiplier
switch (_microArchitecture)
{
case MicroArchitecture.Atom:
case MicroArchitecture.Core:
case MicroArchitecture.NetBurst:
{
if (Ring0.ReadMsr(IA32_PERF_STATUS, out uint _, out uint edx))
{
_timeStampCounterMultiplier = ((edx >> 8) & 0x1f) + 0.5 * ((edx >> 14) & 1);
}
break;
}
case MicroArchitecture.Airmont:
case MicroArchitecture.AlderLake:
case MicroArchitecture.Broadwell:
case MicroArchitecture.CannonLake:
case MicroArchitecture.CometLake:
case MicroArchitecture.Goldmont:
case MicroArchitecture.GoldmontPlus:
case MicroArchitecture.Haswell:
case MicroArchitecture.IceLake:
case MicroArchitecture.IvyBridge:
case MicroArchitecture.JasperLake:
case MicroArchitecture.KabyLake:
case MicroArchitecture.Nehalem:
case MicroArchitecture.RocketLake:
case MicroArchitecture.SandyBridge:
case MicroArchitecture.Silvermont:
case MicroArchitecture.Skylake:
case MicroArchitecture.TigerLake:
case MicroArchitecture.Tremont:
{
if (Ring0.ReadMsr(MSR_PLATFORM_INFO, out uint eax, out uint _))
{
_timeStampCounterMultiplier = (eax >> 8) & 0xff;
}
}
break;
default:
_timeStampCounterMultiplier = 0;
break;
}
int coreSensorId = 0;
// check if processor supports a digital thermal sensor at core level
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 1) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_coreTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _coreTemperatures.Length; i++)
{
_coreTemperatures[i] = new Sensor(CoreString(i),
coreSensorId,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("TjMax [°C]", "TjMax temperature of the core sensor.\n" + "Temperature = TjMax - TSlope * Value.", tjMax[i]),
new ParameterDescription("TSlope [°C]", "Temperature slope of the digital thermal sensor.\n" + "Temperature = TjMax - TSlope * Value.", 1)
},
settings);
ActivateSensor(_coreTemperatures[i]);
coreSensorId++;
}
}
else
_coreTemperatures = new Sensor[0];
// check if processor supports a digital thermal sensor at package level
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 0x40) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_packageTemperature = new Sensor("CPU Package",
coreSensorId,
SensorType.Temperature,
this,
new[]
{
new ParameterDescription("TjMax [°C]", "TjMax temperature of the package sensor.\n" + "Temperature = TjMax - TSlope * Value.", tjMax[0]),
new ParameterDescription("TSlope [°C]", "Temperature slope of the digital thermal sensor.\n" + "Temperature = TjMax - TSlope * Value.", 1)
},
settings);
ActivateSensor(_packageTemperature);
coreSensorId++;
}
// dist to tjmax sensor
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 1) != 0 && _microArchitecture != MicroArchitecture.Unknown)
{
_distToTjMaxTemperatures = new Sensor[_coreCount];
for (int i = 0; i < _distToTjMaxTemperatures.Length; i++)
{
_distToTjMaxTemperatures[i] = new Sensor(CoreString(i) + " Distance to TjMax", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_distToTjMaxTemperatures[i]);
coreSensorId++;
}
}
else
_distToTjMaxTemperatures = new Sensor[0];
//core temp avg and max value
//is only available when the cpu has more than 1 core
if (cpuId[0][0].Data.GetLength(0) > 6 && (cpuId[0][0].Data[6, 0] & 0x40) != 0 && _microArchitecture != MicroArchitecture.Unknown && _coreCount > 1)
{
_coreMax = new Sensor("Core Max", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_coreMax);
coreSensorId++;
_coreAvg = new Sensor("Core Average", coreSensorId, SensorType.Temperature, this, settings);
ActivateSensor(_coreAvg);
}
else
{
_coreMax = null;
_coreAvg = null;
}
_busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
_coreClocks = new Sensor[_coreCount];
for (int i = 0; i < _coreClocks.Length; i++)
{
_coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
if (HasTimeStampCounter && _microArchitecture != MicroArchitecture.Unknown)
ActivateSensor(_coreClocks[i]);
}
if (_microArchitecture == MicroArchitecture.Airmont ||
_microArchitecture == MicroArchitecture.AlderLake ||
_microArchitecture == MicroArchitecture.Broadwell ||
_microArchitecture == MicroArchitecture.CannonLake ||
_microArchitecture == MicroArchitecture.CometLake ||
_microArchitecture == MicroArchitecture.Goldmont ||
_microArchitecture == MicroArchitecture.GoldmontPlus ||
_microArchitecture == MicroArchitecture.Haswell ||
_microArchitecture == MicroArchitecture.IceLake ||
_microArchitecture == MicroArchitecture.IvyBridge ||
_microArchitecture == MicroArchitecture.JasperLake ||
_microArchitecture == MicroArchitecture.KabyLake ||
_microArchitecture == MicroArchitecture.RocketLake ||
_microArchitecture == MicroArchitecture.SandyBridge ||
_microArchitecture == MicroArchitecture.Silvermont ||
_microArchitecture == MicroArchitecture.Skylake ||
_microArchitecture == MicroArchitecture.TigerLake ||
_microArchitecture == MicroArchitecture.Tremont)
{
_powerSensors = new Sensor[_energyStatusMsrs.Length];
_lastEnergyTime = new DateTime[_energyStatusMsrs.Length];
_lastEnergyConsumed = new uint[_energyStatusMsrs.Length];
if (Ring0.ReadMsr(MSR_RAPL_POWER_UNIT, out uint eax, out uint _))
switch (_microArchitecture)
{
case MicroArchitecture.Silvermont:
case MicroArchitecture.Airmont:
_energyUnitMultiplier = 1.0e-6f * (1 << (int)((eax >> 8) & 0x1F));
break;
default:
_energyUnitMultiplier = 1.0f / (1 << (int)((eax >> 8) & 0x1F));
break;
}
if (_energyUnitMultiplier != 0)
{
string[] powerSensorLabels = { "CPU Package", "CPU Cores", "CPU Graphics", "CPU Memory" };
for (int i = 0; i < _energyStatusMsrs.Length; i++)
{
if (!Ring0.ReadMsr(_energyStatusMsrs[i], out eax, out uint _))
continue;
// Don't show the "GPU Graphics" sensor on windows, it will show up under the GPU instead.
if (i == 2 && !Software.OperatingSystem.IsUnix)
continue;
_lastEnergyTime[i] = DateTime.UtcNow;
_lastEnergyConsumed[i] = eax;
_powerSensors[i] = new Sensor(powerSensorLabels[i],
i,
SensorType.Power,
this,
settings);
ActivateSensor(_powerSensors[i]);
}
}
}
Update();
}
private float[] Floats(float f)
{
float[] result = new float[_coreCount];
for (int i = 0; i < _coreCount; i++)
result[i] = f;
return result;
}
private float[] GetTjMaxFromMsr()
{
float[] result = new float[_coreCount];
for (int i = 0; i < _coreCount; i++)
{
if (Ring0.ReadMsr(IA32_TEMPERATURE_TARGET, out uint eax, out uint _, _cpuId[i][0].Affinity))
result[i] = (eax >> 16) & 0xFF;
else
result[i] = 100;
}
return result;
}
protected override uint[] GetMsrs()
{
return new[]
{
MSR_PLATFORM_INFO,
IA32_PERF_STATUS,
IA32_THERM_STATUS_MSR,
IA32_TEMPERATURE_TARGET,
IA32_PACKAGE_THERM_STATUS,
MSR_RAPL_POWER_UNIT,
MSR_PKG_ENERY_STATUS,
MSR_DRAM_ENERGY_STATUS,
MSR_PP0_ENERY_STATUS,
MSR_PP1_ENERY_STATUS
};
}
public override string GetReport()
{
StringBuilder r = new StringBuilder();
r.Append(base.GetReport());
r.Append("MicroArchitecture: ");
r.AppendLine(_microArchitecture.ToString());
r.Append("Time Stamp Counter Multiplier: ");
r.AppendLine(_timeStampCounterMultiplier.ToString(CultureInfo.InvariantCulture));
r.AppendLine();
return r.ToString();
}
public override void Update()
{
base.Update();
float coreMax = float.MinValue;
float coreAvg = 0;
for (int i = 0; i < _coreTemperatures.Length; i++)
{
// if reading is valid
if (Ring0.ReadMsr(IA32_THERM_STATUS_MSR, out uint eax, out uint _, _cpuId[i][0].Affinity) && (eax & 0x80000000) != 0)
{
// get the dist from tjMax from bits 22:16
float deltaT = (eax & 0x007F0000) >> 16;
float tjMax = _coreTemperatures[i].Parameters[0].Value;
float tSlope = _coreTemperatures[i].Parameters[1].Value;
_coreTemperatures[i].Value = tjMax - tSlope * deltaT;
coreAvg += (float)_coreTemperatures[i].Value;
if (coreMax < _coreTemperatures[i].Value)
coreMax = (float)_coreTemperatures[i].Value;
_distToTjMaxTemperatures[i].Value = deltaT;
}
else
{
_coreTemperatures[i].Value = null;
_distToTjMaxTemperatures[i].Value = null;
}
}
//calculate average cpu temperature over all cores
if (_coreMax != null && coreMax != float.MinValue)
{
_coreMax.Value = coreMax;
coreAvg /= _coreTemperatures.Length;
_coreAvg.Value = coreAvg;
}
if (_packageTemperature != null)
{
// if reading is valid
if (Ring0.ReadMsr(IA32_PACKAGE_THERM_STATUS, out uint eax, out uint _, _cpuId[0][0].Affinity) && (eax & 0x80000000) != 0)
{
// get the dist from tjMax from bits 22:16
float deltaT = (eax & 0x007F0000) >> 16;
float tjMax = _packageTemperature.Parameters[0].Value;
float tSlope = _packageTemperature.Parameters[1].Value;
_packageTemperature.Value = tjMax - tSlope * deltaT;
}
else
{
_packageTemperature.Value = null;
}
}
if (HasTimeStampCounter && _timeStampCounterMultiplier > 0)
{
double newBusClock = 0;
for (int i = 0; i < _coreClocks.Length; i++)
{
System.Threading.Thread.Sleep(1);
if (Ring0.ReadMsr(IA32_PERF_STATUS, out uint eax, out uint _, _cpuId[i][0].Affinity))
{
newBusClock = TimeStampCounterFrequency / _timeStampCounterMultiplier;
switch (_microArchitecture)
{
case MicroArchitecture.Nehalem:
{
uint multiplier = eax & 0xff;
_coreClocks[i].Value = (float)(multiplier * newBusClock);
break;
}
case MicroArchitecture.Airmont:
case MicroArchitecture.AlderLake:
case MicroArchitecture.Broadwell:
case MicroArchitecture.CannonLake:
case MicroArchitecture.CometLake:
case MicroArchitecture.Goldmont:
case MicroArchitecture.GoldmontPlus:
case MicroArchitecture.Haswell:
case MicroArchitecture.IceLake:
case MicroArchitecture.IvyBridge:
case MicroArchitecture.JasperLake:
case MicroArchitecture.KabyLake:
case MicroArchitecture.RocketLake:
case MicroArchitecture.SandyBridge:
case MicroArchitecture.Silvermont:
case MicroArchitecture.Skylake:
case MicroArchitecture.TigerLake:
case MicroArchitecture.Tremont:
{
uint multiplier = (eax >> 8) & 0xff;
_coreClocks[i].Value = (float)(multiplier * newBusClock);
break;
}
default:
{
double multiplier = ((eax >> 8) & 0x1f) + 0.5 * ((eax >> 14) & 1);
_coreClocks[i].Value = (float)(multiplier * newBusClock);
break;
}
}
}
else
{
// if IA32_PERF_STATUS is not available, assume TSC frequency
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
}
}
if (newBusClock > 0)
{
_busClock.Value = (float)newBusClock;
ActivateSensor(_busClock);
}
}
if (_powerSensors != null)
{
foreach (Sensor sensor in _powerSensors)
{
if (sensor == null)
continue;
if (!Ring0.ReadMsr(_energyStatusMsrs[sensor.Index], out uint eax, out uint _))
continue;
DateTime time = DateTime.UtcNow;
uint energyConsumed = eax;
float deltaTime = (float)(time - _lastEnergyTime[sensor.Index]).TotalSeconds;
if (deltaTime < 0.01)
continue;
sensor.Value = _energyUnitMultiplier * unchecked(energyConsumed - _lastEnergyConsumed[sensor.Index]) / deltaTime;
_lastEnergyTime[sensor.Index] = time;
_lastEnergyConsumed[sensor.Index] = energyConsumed;
}
}
}
[SuppressMessage("ReSharper", "IdentifierTypo")]
private enum MicroArchitecture
{
Airmont,
AlderLake,
Atom,
Broadwell,
CannonLake,
CometLake,
Core,
Goldmont,
GoldmontPlus,
Haswell,
IceLake,
IvyBridge,
JasperLake,
KabyLake,
Nehalem,
NetBurst,
RocketLake,
SandyBridge,
Silvermont,
Skylake,
TigerLake,
Tremont,
Unknown
}
// ReSharper disable InconsistentNaming
private const uint IA32_PACKAGE_THERM_STATUS = 0x1B1;
private const uint IA32_PERF_STATUS = 0x0198;
private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
private const uint IA32_THERM_STATUS_MSR = 0x019C;
private const uint MSR_DRAM_ENERGY_STATUS = 0x619;
private const uint MSR_PKG_ENERY_STATUS = 0x611;
private const uint MSR_PLATFORM_INFO = 0xCE;
private const uint MSR_PP0_ENERY_STATUS = 0x639;
private const uint MSR_PP1_ENERY_STATUS = 0x641;
private const uint MSR_RAPL_POWER_UNIT = 0x606;
// ReSharper restore InconsistentNaming
}
}