first commit
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal sealed class Amd0FCpu : AmdCpu
|
||||
{
|
||||
private readonly Sensor _busClock;
|
||||
private readonly Sensor[] _coreClocks;
|
||||
private readonly Sensor[] _coreTemperatures;
|
||||
private readonly uint _miscellaneousControlAddress;
|
||||
|
||||
private readonly byte _thermSenseCoreSelCPU0;
|
||||
private readonly byte _thermSenseCoreSelCPU1;
|
||||
|
||||
public Amd0FCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
|
||||
{
|
||||
float offset = -49.0f;
|
||||
|
||||
// AM2+ 65nm +21 offset
|
||||
uint model = cpuId[0][0].Model;
|
||||
if (model is >= 0x69 and not 0xc1 and not 0x6c and not 0x7c)
|
||||
offset += 21;
|
||||
|
||||
if (model < 40)
|
||||
{
|
||||
// AMD Athlon 64 Processors
|
||||
_thermSenseCoreSelCPU0 = 0x0;
|
||||
_thermSenseCoreSelCPU1 = 0x4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// AMD NPT Family 0Fh Revision F, G have the core selection swapped
|
||||
_thermSenseCoreSelCPU0 = 0x4;
|
||||
_thermSenseCoreSelCPU1 = 0x0;
|
||||
}
|
||||
|
||||
// check if processor supports a digital thermal sensor
|
||||
if (cpuId[0][0].ExtData.GetLength(0) > 7 && (cpuId[0][0].ExtData[7, 3] & 1) != 0)
|
||||
{
|
||||
_coreTemperatures = new Sensor[_coreCount];
|
||||
for (int i = 0; i < _coreCount; i++)
|
||||
{
|
||||
_coreTemperatures[i] = new Sensor("Core #" + (i + 1),
|
||||
i,
|
||||
SensorType.Temperature,
|
||||
this,
|
||||
new[] { new ParameterDescription("Offset [°C]", "Temperature offset of the thermal sensor.\nTemperature = Value + Offset.", offset) },
|
||||
settings);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreTemperatures = Array.Empty<Sensor>();
|
||||
}
|
||||
|
||||
_miscellaneousControlAddress = GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, MISCELLANEOUS_CONTROL_DEVICE_ID);
|
||||
_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)
|
||||
ActivateSensor(_coreClocks[i]);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
protected override uint[] GetMsrs()
|
||||
{
|
||||
return new[] { FIDVID_STATUS };
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.Append(base.GetReport());
|
||||
r.Append("Miscellaneous Control Address: 0x");
|
||||
r.AppendLine(_miscellaneousControlAddress.ToString("X", CultureInfo.InvariantCulture));
|
||||
r.AppendLine();
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Mutexes.WaitPciBus(10))
|
||||
{
|
||||
if (_miscellaneousControlAddress != Interop.Ring0.INVALID_PCI_ADDRESS)
|
||||
{
|
||||
for (uint i = 0; i < _coreTemperatures.Length; i++)
|
||||
{
|
||||
if (Ring0.WritePciConfig(_miscellaneousControlAddress,
|
||||
THERMTRIP_STATUS_REGISTER,
|
||||
i > 0 ? _thermSenseCoreSelCPU1 : _thermSenseCoreSelCPU0))
|
||||
{
|
||||
if (Ring0.ReadPciConfig(_miscellaneousControlAddress, THERMTRIP_STATUS_REGISTER, out uint value))
|
||||
{
|
||||
_coreTemperatures[i].Value = ((value >> 16) & 0xFF) + _coreTemperatures[i].Parameters[0].Value;
|
||||
ActivateSensor(_coreTemperatures[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_coreTemperatures[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mutexes.ReleasePciBus();
|
||||
}
|
||||
|
||||
if (HasTimeStampCounter)
|
||||
{
|
||||
double newBusClock = 0;
|
||||
|
||||
for (int i = 0; i < _coreClocks.Length; i++)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
|
||||
if (Ring0.ReadMsr(FIDVID_STATUS, out uint eax, out uint _, _cpuId[i][0].Affinity))
|
||||
{
|
||||
// CurrFID can be found in eax bits 0-5, MaxFID in 16-21
|
||||
// 8-13 hold StartFID, we don't use that here.
|
||||
double curMp = 0.5 * ((eax & 0x3F) + 8);
|
||||
double maxMp = 0.5 * ((eax >> 16 & 0x3F) + 8);
|
||||
_coreClocks[i].Value = (float)(curMp * TimeStampCounterFrequency / maxMp);
|
||||
newBusClock = (float)(TimeStampCounterFrequency / maxMp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail-safe value - if the code above fails, we'll use this instead
|
||||
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBusClock > 0)
|
||||
{
|
||||
_busClock.Value = (float)newBusClock;
|
||||
ActivateSensor(_busClock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private const uint FIDVID_STATUS = 0xC0010042;
|
||||
private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1103;
|
||||
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
|
||||
private const uint THERMTRIP_STATUS_REGISTER = 0xE4;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal sealed class Amd10Cpu : AmdCpu
|
||||
{
|
||||
private readonly Sensor _busClock;
|
||||
private readonly Sensor[] _coreClocks;
|
||||
private readonly Sensor _coreTemperature;
|
||||
private readonly Sensor _coreVoltage;
|
||||
private readonly byte _cStatesIoOffset;
|
||||
private readonly Sensor[] _cStatesResidency;
|
||||
private readonly bool _hasSmuTemperatureRegister;
|
||||
private readonly bool _isSvi2;
|
||||
private readonly uint _miscellaneousControlAddress;
|
||||
private readonly Sensor _northbridgeVoltage;
|
||||
private readonly FileStream _temperatureStream;
|
||||
private readonly double _timeStampCounterMultiplier;
|
||||
|
||||
public Amd10Cpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
|
||||
{
|
||||
// AMD family 1Xh processors support only one temperature sensor
|
||||
ushort miscellaneousControlDeviceId;
|
||||
_coreTemperature = new Sensor("CPU Cores", 0, SensorType.Temperature, this, new[] { new ParameterDescription("Offset [°C]", "Temperature offset.", 0) }, settings);
|
||||
_coreVoltage = new Sensor("CPU Cores", 0, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_coreVoltage);
|
||||
_northbridgeVoltage = new Sensor("Northbridge", 0, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_northbridgeVoltage);
|
||||
|
||||
_isSvi2 = (_family == 0x15 && _model >= 0x10) || _family == 0x16;
|
||||
|
||||
switch (_family)
|
||||
{
|
||||
case 0x10:
|
||||
miscellaneousControlDeviceId = FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x11:
|
||||
miscellaneousControlDeviceId = FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x12:
|
||||
miscellaneousControlDeviceId = FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x14:
|
||||
miscellaneousControlDeviceId = FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x15:
|
||||
switch (_model & 0xF0)
|
||||
{
|
||||
case 0x00:
|
||||
miscellaneousControlDeviceId = FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x10:
|
||||
miscellaneousControlDeviceId = FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x30:
|
||||
miscellaneousControlDeviceId = FAMILY_15H_MODEL_30_MISC_CONTROL_DEVICE_ID;
|
||||
break;
|
||||
case 0x70:
|
||||
miscellaneousControlDeviceId = FAMILY_15H_MODEL_70_MISC_CONTROL_DEVICE_ID;
|
||||
_hasSmuTemperatureRegister = true;
|
||||
break;
|
||||
case 0x60:
|
||||
miscellaneousControlDeviceId = FAMILY_15H_MODEL_60_MISC_CONTROL_DEVICE_ID;
|
||||
_hasSmuTemperatureRegister = true;
|
||||
break;
|
||||
default:
|
||||
miscellaneousControlDeviceId = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 0x16:
|
||||
miscellaneousControlDeviceId = (_model & 0xF0) switch
|
||||
{
|
||||
0x00 => FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID,
|
||||
0x30 => FAMILY_16H_MODEL_30_MISC_CONTROL_DEVICE_ID,
|
||||
_ => 0
|
||||
};
|
||||
break;
|
||||
default:
|
||||
miscellaneousControlDeviceId = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// get the pci address for the Miscellaneous Control registers
|
||||
_miscellaneousControlAddress = GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);
|
||||
_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)
|
||||
ActivateSensor(_coreClocks[i]);
|
||||
}
|
||||
|
||||
bool corePerformanceBoostSupport = (cpuId[0][0].ExtData[7, 3] & (1 << 9)) > 0;
|
||||
|
||||
// set affinity to the first thread for all frequency estimations
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId[0][0].Affinity);
|
||||
|
||||
// disable core performance boost
|
||||
Ring0.ReadMsr(HWCR, out uint hwcrEax, out uint hwcrEdx);
|
||||
if (corePerformanceBoostSupport)
|
||||
Ring0.WriteMsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
|
||||
|
||||
Ring0.ReadMsr(PERF_CTL_0, out uint ctlEax, out uint ctlEdx);
|
||||
Ring0.ReadMsr(PERF_CTR_0, out uint ctrEax, out uint ctrEdx);
|
||||
|
||||
_timeStampCounterMultiplier = EstimateTimeStampCounterMultiplier();
|
||||
|
||||
// restore the performance counter registers
|
||||
Ring0.WriteMsr(PERF_CTL_0, ctlEax, ctlEdx);
|
||||
Ring0.WriteMsr(PERF_CTR_0, ctrEax, ctrEdx);
|
||||
|
||||
// restore core performance boost
|
||||
if (corePerformanceBoostSupport)
|
||||
Ring0.WriteMsr(HWCR, hwcrEax, hwcrEdx);
|
||||
|
||||
// restore the thread affinity.
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
|
||||
// the file reader for lm-sensors support on Linux
|
||||
_temperatureStream = null;
|
||||
|
||||
if (Software.OperatingSystem.IsUnix)
|
||||
{
|
||||
foreach (string path in Directory.GetDirectories("/sys/class/hwmon/"))
|
||||
{
|
||||
string name = null;
|
||||
try
|
||||
{
|
||||
using StreamReader reader = new(path + "/device/name");
|
||||
|
||||
name = reader.ReadLine();
|
||||
}
|
||||
catch (IOException)
|
||||
{ }
|
||||
|
||||
_temperatureStream = name switch
|
||||
{
|
||||
"k10temp" => new FileStream(path + "/device/temp1_input", FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
|
||||
_ => _temperatureStream
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
uint addr = Ring0.GetPciAddress(0, 20, 0);
|
||||
if (Ring0.ReadPciConfig(addr, 0, out uint dev))
|
||||
{
|
||||
Ring0.ReadPciConfig(addr, 8, out uint rev);
|
||||
|
||||
_cStatesIoOffset = dev switch
|
||||
{
|
||||
0x43851002 => (byte)((rev & 0xFF) < 0x40 ? 0xB3 : 0x9C),
|
||||
0x780B1022 or 0x790B1022 => 0x9C,
|
||||
_ => _cStatesIoOffset
|
||||
};
|
||||
}
|
||||
|
||||
if (_cStatesIoOffset != 0)
|
||||
{
|
||||
_cStatesResidency = new[] { new Sensor("CPU Package C2", 0, SensorType.Level, this, settings), new Sensor("CPU Package C3", 1, SensorType.Level, this, settings) };
|
||||
ActivateSensor(_cStatesResidency[0]);
|
||||
ActivateSensor(_cStatesResidency[1]);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
private double EstimateTimeStampCounterMultiplier()
|
||||
{
|
||||
// preload the function
|
||||
EstimateTimeStampCounterMultiplier(0);
|
||||
EstimateTimeStampCounterMultiplier(0);
|
||||
|
||||
// estimate the multiplier
|
||||
List<double> estimate = new(3);
|
||||
for (int i = 0; i < 3; i++)
|
||||
estimate.Add(EstimateTimeStampCounterMultiplier(0.025));
|
||||
|
||||
estimate.Sort();
|
||||
return estimate[1];
|
||||
}
|
||||
|
||||
private double EstimateTimeStampCounterMultiplier(double timeWindow)
|
||||
{
|
||||
// select event "076h CPU Clocks not Halted" and enable the counter
|
||||
Ring0.WriteMsr(PERF_CTL_0,
|
||||
(1 << 22) | // enable performance counter
|
||||
(1 << 17) | // count events in user mode
|
||||
(1 << 16) | // count events in operating-system mode
|
||||
0x76,
|
||||
0x00000000);
|
||||
|
||||
// set the counter to 0
|
||||
Ring0.WriteMsr(PERF_CTR_0, 0, 0);
|
||||
|
||||
long ticks = (long)(timeWindow * Stopwatch.Frequency);
|
||||
|
||||
long timeBegin = Stopwatch.GetTimestamp() +
|
||||
(long)Math.Ceiling(0.001 * ticks);
|
||||
|
||||
long timeEnd = timeBegin + ticks;
|
||||
while (Stopwatch.GetTimestamp() < timeBegin)
|
||||
{ }
|
||||
|
||||
Ring0.ReadMsr(PERF_CTR_0, out uint lsbBegin, out uint msbBegin);
|
||||
|
||||
while (Stopwatch.GetTimestamp() < timeEnd)
|
||||
{ }
|
||||
|
||||
Ring0.ReadMsr(PERF_CTR_0, out uint lsbEnd, out uint msbEnd);
|
||||
Ring0.ReadMsr(COFVID_STATUS, out uint eax, out uint _);
|
||||
double coreMultiplier = GetCoreMultiplier(eax);
|
||||
|
||||
ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
|
||||
ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
|
||||
|
||||
double coreFrequency = 1e-6 * ((double)(countEnd - countBegin) * Stopwatch.Frequency) / (timeEnd - timeBegin);
|
||||
double busFrequency = coreFrequency / coreMultiplier;
|
||||
return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
|
||||
}
|
||||
|
||||
protected override uint[] GetMsrs()
|
||||
{
|
||||
return new[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0, COFVID_STATUS };
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.Append(base.GetReport());
|
||||
r.Append("Miscellaneous Control Address: 0x");
|
||||
r.AppendLine(_miscellaneousControlAddress.ToString("X", CultureInfo.InvariantCulture));
|
||||
r.Append("Time Stamp Counter Multiplier: ");
|
||||
r.AppendLine(_timeStampCounterMultiplier.ToString(CultureInfo.InvariantCulture));
|
||||
if (_family == 0x14)
|
||||
{
|
||||
Ring0.ReadPciConfig(_miscellaneousControlAddress, CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out uint value);
|
||||
r.Append("PCI Register D18F3xD4: ");
|
||||
r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
private double GetCoreMultiplier(uint cofVidEax)
|
||||
{
|
||||
uint cpuDid;
|
||||
uint cpuFid;
|
||||
|
||||
switch (_family)
|
||||
{
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
// 8:6 CpuDid: current core divisor ID
|
||||
// 5:0 CpuFid: current core frequency ID
|
||||
cpuDid = (cofVidEax >> 6) & 7;
|
||||
cpuFid = cofVidEax & 0x1F;
|
||||
return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
|
||||
|
||||
case 0x12:
|
||||
// 8:4 CpuFid: current CPU core frequency ID
|
||||
// 3:0 CpuDid: current CPU core divisor ID
|
||||
cpuFid = (cofVidEax >> 4) & 0x1F;
|
||||
cpuDid = cofVidEax & 0xF;
|
||||
double divisor = cpuDid switch
|
||||
{
|
||||
0 => 1,
|
||||
1 => 1.5,
|
||||
2 => 2,
|
||||
3 => 3,
|
||||
4 => 4,
|
||||
5 => 6,
|
||||
6 => 8,
|
||||
7 => 12,
|
||||
8 => 16,
|
||||
_ => 1
|
||||
};
|
||||
return (cpuFid + 0x10) / divisor;
|
||||
|
||||
case 0x14:
|
||||
// 8:4: current CPU core divisor ID most significant digit
|
||||
// 3:0: current CPU core divisor ID least significant digit
|
||||
uint divisorIdMsd = (cofVidEax >> 4) & 0x1F;
|
||||
uint divisorIdLsd = cofVidEax & 0xF;
|
||||
Ring0.ReadPciConfig(_miscellaneousControlAddress, CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out uint value);
|
||||
uint frequencyId = value & 0x1F;
|
||||
return (frequencyId + 0x10) / (divisorIdMsd + (divisorIdLsd * 0.25) + 1);
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadFirstLine(Stream stream)
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
|
||||
try
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
int b = stream.ReadByte();
|
||||
while (b is not -1 and not 10)
|
||||
{
|
||||
stringBuilder.Append((char)b);
|
||||
b = stream.ReadByte();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (_temperatureStream == null)
|
||||
{
|
||||
if (_miscellaneousControlAddress != Interop.Ring0.INVALID_PCI_ADDRESS)
|
||||
{
|
||||
bool isValueValid = _hasSmuTemperatureRegister
|
||||
? ReadSmuRegister(SMU_REPORTED_TEMP_CTRL_OFFSET, out uint value)
|
||||
: Ring0.ReadPciConfig(_miscellaneousControlAddress, REPORTED_TEMPERATURE_CONTROL_REGISTER, out value);
|
||||
|
||||
if (isValueValid)
|
||||
{
|
||||
if ((_family == 0x15 || _family == 0x16) && (value & 0x30000) == 0x3000)
|
||||
{
|
||||
if (_family == 0x15 && (_model & 0xF0) == 0x00)
|
||||
{
|
||||
_coreTemperature.Value = (((value >> 21) & 0x7FC) / 8.0f) + _coreTemperature.Parameters[0].Value - 49;
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreTemperature.Value = (((value >> 21) & 0x7FF) / 8.0f) + _coreTemperature.Parameters[0].Value - 49;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreTemperature.Value = (((value >> 21) & 0x7FF) / 8.0f) + _coreTemperature.Parameters[0].Value;
|
||||
}
|
||||
|
||||
ActivateSensor(_coreTemperature);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_coreTemperature);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_coreTemperature);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string s = ReadFirstLine(_temperatureStream);
|
||||
try
|
||||
{
|
||||
_coreTemperature.Value = 0.001f * long.Parse(s, CultureInfo.InvariantCulture);
|
||||
ActivateSensor(_coreTemperature);
|
||||
}
|
||||
catch
|
||||
{
|
||||
DeactivateSensor(_coreTemperature);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasTimeStampCounter)
|
||||
{
|
||||
double newBusClock = 0;
|
||||
float maxCoreVoltage = 0, maxNbVoltage = 0;
|
||||
|
||||
for (int i = 0; i < _coreClocks.Length; i++)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
|
||||
if (Ring0.ReadMsr(COFVID_STATUS, out uint curEax, out uint _, _cpuId[i][0].Affinity))
|
||||
{
|
||||
double multiplier = GetCoreMultiplier(curEax);
|
||||
|
||||
_coreClocks[i].Value = (float)(multiplier * TimeStampCounterFrequency / _timeStampCounterMultiplier);
|
||||
newBusClock = (float)(TimeStampCounterFrequency / _timeStampCounterMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreClocks[i].Value = (float)TimeStampCounterFrequency;
|
||||
}
|
||||
|
||||
float SVI2Volt(uint vid) => vid < 0b1111_1000 ? 1.5500f - (0.00625f * vid) : 0;
|
||||
|
||||
float SVI1Volt(uint vid) => vid < 0x7C ? 1.550f - (0.0125f * vid) : 0;
|
||||
|
||||
float newCoreVoltage, newNbVoltage;
|
||||
uint coreVid60 = (curEax >> 9) & 0x7F;
|
||||
if (_isSvi2)
|
||||
{
|
||||
newCoreVoltage = SVI2Volt((curEax >> 13 & 0x80) | coreVid60);
|
||||
newNbVoltage = SVI2Volt(curEax >> 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
newCoreVoltage = SVI1Volt(coreVid60);
|
||||
newNbVoltage = SVI1Volt(curEax >> 25);
|
||||
}
|
||||
|
||||
if (newCoreVoltage > maxCoreVoltage)
|
||||
maxCoreVoltage = newCoreVoltage;
|
||||
|
||||
if (newNbVoltage > maxNbVoltage)
|
||||
maxNbVoltage = newNbVoltage;
|
||||
}
|
||||
|
||||
_coreVoltage.Value = maxCoreVoltage;
|
||||
_northbridgeVoltage.Value = maxNbVoltage;
|
||||
|
||||
if (newBusClock > 0)
|
||||
{
|
||||
_busClock.Value = (float)newBusClock;
|
||||
ActivateSensor(_busClock);
|
||||
}
|
||||
}
|
||||
|
||||
if (_cStatesResidency != null)
|
||||
{
|
||||
for (int i = 0; i < _cStatesResidency.Length; i++)
|
||||
{
|
||||
Ring0.WriteIoPort(CSTATES_IO_PORT, (byte)(_cStatesIoOffset + i));
|
||||
_cStatesResidency[i].Value = Ring0.ReadIoPort(CSTATES_IO_PORT + 1) / 256f * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ReadSmuRegister(uint address, out uint value)
|
||||
{
|
||||
if (Mutexes.WaitPciBus(10))
|
||||
{
|
||||
if (!Ring0.WritePciConfig(0, 0xB8, address))
|
||||
{
|
||||
value = 0;
|
||||
|
||||
Mutexes.ReleasePciBus();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = Ring0.ReadPciConfig(0, 0xBC, out value);
|
||||
|
||||
Mutexes.ReleasePciBus();
|
||||
return result;
|
||||
}
|
||||
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_temperatureStream?.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
|
||||
private const uint COFVID_STATUS = 0xC0010071;
|
||||
private const uint CSTATES_IO_PORT = 0xCD6;
|
||||
private const uint SMU_REPORTED_TEMP_CTRL_OFFSET = 0xD8200CA4;
|
||||
private const uint HWCR = 0xC0010015;
|
||||
private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
|
||||
private const uint P_STATE_0 = 0xC0010064;
|
||||
private const uint PERF_CTL_0 = 0xC0010000;
|
||||
private const uint PERF_CTR_0 = 0xC0010004;
|
||||
private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
|
||||
|
||||
private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
|
||||
private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
|
||||
private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
|
||||
private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
|
||||
private const ushort FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1603;
|
||||
private const ushort FAMILY_15H_MODEL_10_MISC_CONTROL_DEVICE_ID = 0x1403;
|
||||
private const ushort FAMILY_15H_MODEL_30_MISC_CONTROL_DEVICE_ID = 0x141D;
|
||||
private const ushort FAMILY_15H_MODEL_60_MISC_CONTROL_DEVICE_ID = 0x1573;
|
||||
private const ushort FAMILY_15H_MODEL_70_MISC_CONTROL_DEVICE_ID = 0x15B3;
|
||||
private const ushort FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID = 0x1533;
|
||||
private const ushort FAMILY_16H_MODEL_30_MISC_CONTROL_DEVICE_ID = 0x1583;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
// 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.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal sealed class Amd17Cpu : AmdCpu
|
||||
{
|
||||
private readonly Processor _processor;
|
||||
private readonly Dictionary<SensorType, int> _sensorTypeIndex;
|
||||
private readonly RyzenSMU _smu;
|
||||
|
||||
public Amd17Cpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
|
||||
{
|
||||
_sensorTypeIndex = new Dictionary<SensorType, int>();
|
||||
foreach (SensorType type in Enum.GetValues(typeof(SensorType)))
|
||||
{
|
||||
_sensorTypeIndex.Add(type, 0);
|
||||
}
|
||||
|
||||
_sensorTypeIndex[SensorType.Load] = _active.Count(x => x.SensorType == SensorType.Load);
|
||||
|
||||
_smu = new RyzenSMU(_family, _model, _packageType);
|
||||
|
||||
// Add all numa nodes.
|
||||
// Register ..1E_2, [10:8] + 1
|
||||
_processor = new Processor(this);
|
||||
|
||||
// Add all numa nodes.
|
||||
int coreId = 0;
|
||||
int lastCoreId = -1; // Invalid id.
|
||||
|
||||
// Ryzen 3000's skip some core ids.
|
||||
// So start at 1 and count upwards when the read core changes.
|
||||
foreach (CpuId[] cpu in cpuId.OrderBy(x => x[0].ExtData[0x1e, 1] & 0xFF))
|
||||
{
|
||||
CpuId thread = cpu[0];
|
||||
|
||||
// CPUID_Fn8000001E_EBX, Register ..1E_1, [7:0]
|
||||
// threads per core = CPUID_Fn8000001E_EBX[15:8] + 1
|
||||
// CoreId: core ID = CPUID_Fn8000001E_EBX[7:0]
|
||||
int coreIdRead = (int)(thread.ExtData[0x1e, 1] & 0xff);
|
||||
|
||||
// CPUID_Fn8000001E_ECX, Node Identifiers, Register ..1E_2
|
||||
// NodesPerProcessor = CPUID_Fn8000001E_ECX[10:8]
|
||||
// nodeID = CPUID_Fn8000001E_ECX[7:0]
|
||||
int nodeId = (int)(thread.ExtData[0x1e, 2] & 0xff);
|
||||
|
||||
if (coreIdRead != lastCoreId)
|
||||
{
|
||||
coreId++;
|
||||
}
|
||||
|
||||
lastCoreId = coreIdRead;
|
||||
|
||||
_processor.AppendThread(thread, nodeId, coreId);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
protected override uint[] GetMsrs()
|
||||
{
|
||||
return new[] { PERF_CTL_0, PERF_CTR_0, HWCR, MSR_PSTATE_0, COFVID_STATUS };
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.Append(base.GetReport());
|
||||
r.Append(_smu.GetReport());
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
_processor.UpdateSensors();
|
||||
|
||||
foreach (NumaNode node in _processor.Nodes)
|
||||
{
|
||||
NumaNode.UpdateSensors();
|
||||
|
||||
foreach (Core c in node.Cores)
|
||||
{
|
||||
c.UpdateSensors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Processor
|
||||
{
|
||||
private readonly Sensor _busClock;
|
||||
private readonly Sensor[] _ccdTemperatures;
|
||||
private readonly Sensor _coreTemperatureTctl;
|
||||
private readonly Sensor _coreTemperatureTctlTdie;
|
||||
private readonly Sensor _coreTemperatureTdie;
|
||||
private readonly Sensor _coreVoltage;
|
||||
private readonly Amd17Cpu _cpu;
|
||||
private readonly Sensor _packagePower;
|
||||
private readonly Dictionary<KeyValuePair<uint, RyzenSMU.SmuSensorType>, Sensor> _smuSensors = new();
|
||||
private readonly Sensor _socVoltage;
|
||||
|
||||
private Sensor _ccdsAverageTemperature;
|
||||
private Sensor _ccdsMaxTemperature;
|
||||
private DateTime _lastPwrTime = new(0);
|
||||
private uint _lastPwrValue;
|
||||
|
||||
public Processor(Hardware hardware)
|
||||
{
|
||||
_cpu = (Amd17Cpu)hardware;
|
||||
|
||||
_packagePower = new Sensor("Package", _cpu._sensorTypeIndex[SensorType.Power]++, SensorType.Power, _cpu, _cpu._settings);
|
||||
_coreTemperatureTctl = new Sensor("Core (Tctl)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_coreTemperatureTdie = new Sensor("Core (Tdie)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_coreTemperatureTctlTdie = new Sensor("Core (Tctl/Tdie)", _cpu._sensorTypeIndex[SensorType.Temperature]++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_ccdTemperatures = new Sensor[8]; // Hardcoded until there's a way to get max CCDs.
|
||||
_coreVoltage = new Sensor("Core (SVI2 TFN)", _cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, _cpu, _cpu._settings);
|
||||
_socVoltage = new Sensor("SoC (SVI2 TFN)", _cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, _cpu, _cpu._settings);
|
||||
_busClock = new Sensor("Bus Speed", _cpu._sensorTypeIndex[SensorType.Clock]++, SensorType.Clock, _cpu, _cpu._settings);
|
||||
|
||||
_cpu.ActivateSensor(_packagePower);
|
||||
|
||||
foreach (KeyValuePair<uint, RyzenSMU.SmuSensorType> sensor in _cpu._smu.GetPmTableStructure())
|
||||
{
|
||||
_smuSensors.Add(sensor, new Sensor(sensor.Value.Name, _cpu._sensorTypeIndex[sensor.Value.Type]++, sensor.Value.Type, _cpu, _cpu._settings));
|
||||
}
|
||||
}
|
||||
|
||||
public List<NumaNode> Nodes { get; } = new();
|
||||
|
||||
public void UpdateSensors()
|
||||
{
|
||||
NumaNode node = Nodes[0];
|
||||
Core core = node?.Cores[0];
|
||||
CpuId cpuId = core?.Threads[0];
|
||||
|
||||
if (cpuId == null)
|
||||
return;
|
||||
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId.Affinity);
|
||||
|
||||
// MSRC001_0299
|
||||
// TU [19:16]
|
||||
// ESU [12:8] -> Unit 15.3 micro Joule per increment
|
||||
// PU [3:0]
|
||||
Ring0.ReadMsr(MSR_PWR_UNIT, out uint _, out uint _);
|
||||
|
||||
// MSRC001_029B
|
||||
// total_energy [31:0]
|
||||
DateTime sampleTime = DateTime.Now;
|
||||
Ring0.ReadMsr(MSR_PKG_ENERGY_STAT, out uint eax, out _);
|
||||
|
||||
uint totalEnergy = eax;
|
||||
|
||||
uint smuSvi0Tfn = 0;
|
||||
uint smuSvi0TelPlane0 = 0;
|
||||
uint smuSvi0TelPlane1 = 0;
|
||||
|
||||
if (Mutexes.WaitPciBus(10))
|
||||
{
|
||||
// THM_TCON_CUR_TMP
|
||||
// CUR_TEMP [31:21]
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_THM_TCON_CUR_TMP);
|
||||
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out uint temperature);
|
||||
|
||||
// SVI0_TFN_PLANE0 [0]
|
||||
// SVI0_TFN_PLANE1 [1]
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_SVI + 0x8);
|
||||
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0Tfn);
|
||||
|
||||
bool supportsPerCcdTemperatures = false;
|
||||
|
||||
// TODO: find a better way because these will probably keep changing in the future.
|
||||
|
||||
uint sviPlane0Offset;
|
||||
uint sviPlane1Offset;
|
||||
switch (cpuId.Model)
|
||||
{
|
||||
case 0x31: // Threadripper 3000.
|
||||
sviPlane0Offset = F17H_M01H_SVI + 0x14;
|
||||
sviPlane1Offset = F17H_M01H_SVI + 0x10;
|
||||
supportsPerCcdTemperatures = true;
|
||||
break;
|
||||
|
||||
case 0x71: // Zen 2.
|
||||
case 0x21: // Zen 3.
|
||||
sviPlane0Offset = F17H_M01H_SVI + 0x10;
|
||||
sviPlane1Offset = F17H_M01H_SVI + 0xC;
|
||||
supportsPerCcdTemperatures = true;
|
||||
break;
|
||||
|
||||
case 0x61: //Zen 4
|
||||
case 0x44: //Zen 5
|
||||
sviPlane0Offset = F17H_M01H_SVI + 0x10;
|
||||
sviPlane1Offset = F17H_M01H_SVI + 0xC;
|
||||
supportsPerCcdTemperatures = true;
|
||||
break;
|
||||
|
||||
default: // Zen and Zen+.
|
||||
sviPlane0Offset = F17H_M01H_SVI + 0xC;
|
||||
sviPlane1Offset = F17H_M01H_SVI + 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
// SVI0_PLANE0_VDDCOR [24:16]
|
||||
// SVI0_PLANE0_IDDCOR [7:0]
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, sviPlane0Offset);
|
||||
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0TelPlane0);
|
||||
|
||||
// SVI0_PLANE1_VDDCOR [24:16]
|
||||
// SVI0_PLANE1_IDDCOR [7:0]
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, sviPlane1Offset);
|
||||
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smuSvi0TelPlane1);
|
||||
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
|
||||
// power consumption
|
||||
// power.Value = (float) ((double)pu * 0.125);
|
||||
// esu = 15.3 micro Joule per increment
|
||||
if (_lastPwrTime.Ticks == 0)
|
||||
{
|
||||
_lastPwrTime = sampleTime;
|
||||
_lastPwrValue = totalEnergy;
|
||||
}
|
||||
|
||||
// ticks diff
|
||||
TimeSpan time = sampleTime - _lastPwrTime;
|
||||
long pwr;
|
||||
if (_lastPwrValue <= totalEnergy)
|
||||
pwr = totalEnergy - _lastPwrValue;
|
||||
else
|
||||
pwr = (0xffffffff - _lastPwrValue) + totalEnergy;
|
||||
|
||||
// update for next sample
|
||||
_lastPwrTime = sampleTime;
|
||||
_lastPwrValue = totalEnergy;
|
||||
|
||||
double energy = 15.3e-6 * pwr;
|
||||
energy /= time.TotalSeconds;
|
||||
|
||||
if (!double.IsNaN(energy))
|
||||
_packagePower.Value = (float)energy;
|
||||
|
||||
// current temp Bit [31:21]
|
||||
// If bit 19 of the Temperature Control register is set, there is an additional offset of 49 degrees C.
|
||||
bool tempOffsetFlag = (temperature & F17H_TEMP_OFFSET_FLAG) != 0;
|
||||
temperature = (temperature >> 21) * 125;
|
||||
|
||||
float offset = 0.0f;
|
||||
|
||||
// Offset table: https://github.com/torvalds/linux/blob/master/drivers/hwmon/k10temp.c#L78
|
||||
if (string.IsNullOrWhiteSpace(cpuId.Name))
|
||||
offset = 0;
|
||||
else if (cpuId.Name.Contains("1600X") || cpuId.Name.Contains("1700X") || cpuId.Name.Contains("1800X"))
|
||||
offset = -20.0f;
|
||||
else if (cpuId.Name.Contains("Threadripper 19") || cpuId.Name.Contains("Threadripper 29"))
|
||||
offset = -27.0f;
|
||||
else if (cpuId.Name.Contains("2700X"))
|
||||
offset = -10.0f;
|
||||
|
||||
float t = temperature * 0.001f;
|
||||
if (tempOffsetFlag)
|
||||
t += -49.0f;
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
_coreTemperatureTctl.Value = t;
|
||||
_coreTemperatureTdie.Value = t + offset;
|
||||
|
||||
_cpu.ActivateSensor(_coreTemperatureTctl);
|
||||
_cpu.ActivateSensor(_coreTemperatureTdie);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Zen 2 doesn't have an offset so Tdie and Tctl are the same.
|
||||
_coreTemperatureTctlTdie.Value = t;
|
||||
_cpu.ActivateSensor(_coreTemperatureTctlTdie);
|
||||
}
|
||||
|
||||
// Tested only on R5 3600 & Threadripper 3960X, 5900X, 7900X
|
||||
if (supportsPerCcdTemperatures)
|
||||
{
|
||||
for (uint i = 0; i < _ccdTemperatures.Length; i++)
|
||||
{
|
||||
if (cpuId.Model is 0x61 or 0x44) // Raphael or GraniteRidge
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M61H_CCD1_TEMP + (i * 0x4));
|
||||
else
|
||||
Ring0.WritePciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M70H_CCD1_TEMP + (i * 0x4));
|
||||
Ring0.ReadPciConfig(0x00, FAMILY_17H_PCI_CONTROL_REGISTER + 4, out uint ccdRawTemp);
|
||||
|
||||
ccdRawTemp &= 0xFFF;
|
||||
float ccdTemp = ((ccdRawTemp * 125) - 305000) * 0.001f;
|
||||
if (ccdRawTemp > 0 && ccdTemp < 125) // Zen 2 reports 95 degrees C max, but it might exceed that.
|
||||
{
|
||||
if (_ccdTemperatures[i] == null)
|
||||
{
|
||||
_cpu.ActivateSensor(_ccdTemperatures[i] = new Sensor($"CCD{i + 1} (Tdie)",
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
_ccdTemperatures[i].Value = ccdTemp;
|
||||
}
|
||||
}
|
||||
|
||||
Sensor[] activeCcds = _ccdTemperatures.Where(x => x != null).ToArray();
|
||||
if (activeCcds.Length > 1)
|
||||
{
|
||||
// No need to get the max / average ccds temp if there is only one CCD.
|
||||
|
||||
if (_ccdsMaxTemperature == null)
|
||||
{
|
||||
_cpu.ActivateSensor(_ccdsMaxTemperature = new Sensor("CCDs Max (Tdie)",
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
if (_ccdsAverageTemperature == null)
|
||||
{
|
||||
_cpu.ActivateSensor(_ccdsAverageTemperature = new Sensor("CCDs Average (Tdie)",
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
_ccdsMaxTemperature.Value = activeCcds.Max(x => x.Value);
|
||||
_ccdsAverageTemperature.Value = activeCcds.Average(x => x.Value);
|
||||
}
|
||||
}
|
||||
|
||||
Mutexes.ReleasePciBus();
|
||||
}
|
||||
|
||||
// voltage
|
||||
const double vidStep = 0.00625;
|
||||
double vcc;
|
||||
uint svi0PlaneXVddCor;
|
||||
|
||||
if (cpuId.Model is 0x61 or 0x44) // Readout not working for Ryzen 7000/9000.
|
||||
smuSvi0Tfn |= 0x01 | 0x02;
|
||||
|
||||
// Core (0x01).
|
||||
if ((smuSvi0Tfn & 0x01) == 0)
|
||||
{
|
||||
svi0PlaneXVddCor = (smuSvi0TelPlane0 >> 16) & 0xff;
|
||||
vcc = 1.550 - (vidStep * svi0PlaneXVddCor);
|
||||
_coreVoltage.Value = (float)vcc;
|
||||
|
||||
_cpu.ActivateSensor(_coreVoltage);
|
||||
}
|
||||
|
||||
// SoC (0x02), not every Zen cpu has this voltage.
|
||||
if (cpuId.Model is 0x11 or 0x21 or 0x71 or 0x31 || (smuSvi0Tfn & 0x02) == 0)
|
||||
{
|
||||
svi0PlaneXVddCor = (smuSvi0TelPlane1 >> 16) & 0xff;
|
||||
vcc = 1.550 - (vidStep * svi0PlaneXVddCor);
|
||||
_socVoltage.Value = (float)vcc;
|
||||
|
||||
_cpu.ActivateSensor(_socVoltage);
|
||||
}
|
||||
|
||||
double timeStampCounterMultiplier = GetTimeStampCounterMultiplier();
|
||||
if (timeStampCounterMultiplier > 0)
|
||||
{
|
||||
_busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier);
|
||||
_cpu.ActivateSensor(_busClock);
|
||||
}
|
||||
|
||||
if (_cpu._smu.IsPmTableLayoutDefined())
|
||||
{
|
||||
float[] smuData = _cpu._smu.GetPmTable();
|
||||
|
||||
foreach (KeyValuePair<KeyValuePair<uint, RyzenSMU.SmuSensorType>, Sensor> sensor in _smuSensors)
|
||||
{
|
||||
if (smuData.Length > sensor.Key.Key)
|
||||
{
|
||||
sensor.Value.Value = smuData[sensor.Key.Key] * sensor.Key.Value.Scale;
|
||||
if (sensor.Value.Value != 0)
|
||||
_cpu.ActivateSensor(sensor.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double GetTimeStampCounterMultiplier()
|
||||
{
|
||||
Ring0.ReadMsr(MSR_PSTATE_0, out uint eax, out _);
|
||||
uint cpuDfsId = (eax >> 8) & 0x3f;
|
||||
uint cpuFid = eax & 0xff;
|
||||
return 2.0 * cpuFid / cpuDfsId;
|
||||
}
|
||||
|
||||
public void AppendThread(CpuId thread, int numaId, int coreId)
|
||||
{
|
||||
NumaNode node = null;
|
||||
foreach (NumaNode n in Nodes)
|
||||
{
|
||||
if (n.NodeId == numaId)
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
node = new NumaNode(_cpu, numaId);
|
||||
Nodes.Add(node);
|
||||
}
|
||||
|
||||
if (thread != null)
|
||||
node.AppendThread(thread, coreId);
|
||||
}
|
||||
}
|
||||
|
||||
private class NumaNode
|
||||
{
|
||||
private readonly Amd17Cpu _cpu;
|
||||
|
||||
public NumaNode(Amd17Cpu cpu, int id)
|
||||
{
|
||||
Cores = new List<Core>();
|
||||
NodeId = id;
|
||||
_cpu = cpu;
|
||||
}
|
||||
|
||||
public List<Core> Cores { get; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public void AppendThread(CpuId thread, int coreId)
|
||||
{
|
||||
Core core = null;
|
||||
foreach (Core c in Cores)
|
||||
{
|
||||
if (c.CoreId == coreId)
|
||||
core = c;
|
||||
}
|
||||
|
||||
if (core == null)
|
||||
{
|
||||
core = new Core(_cpu, coreId);
|
||||
Cores.Add(core);
|
||||
}
|
||||
|
||||
if (thread != null)
|
||||
core.Threads.Add(thread);
|
||||
}
|
||||
|
||||
public static void UpdateSensors()
|
||||
{ }
|
||||
}
|
||||
|
||||
private class Core
|
||||
{
|
||||
private readonly Sensor _clock;
|
||||
private readonly Amd17Cpu _cpu;
|
||||
private readonly Sensor _multiplier;
|
||||
private readonly Sensor _power;
|
||||
private readonly Sensor _vcore;
|
||||
private ISensor _busSpeed;
|
||||
private DateTime _lastPwrTime = new(0);
|
||||
private uint _lastPwrValue;
|
||||
|
||||
public Core(Amd17Cpu cpu, int id)
|
||||
{
|
||||
_cpu = cpu;
|
||||
Threads = new List<CpuId>();
|
||||
CoreId = id;
|
||||
_clock = new Sensor("Core #" + CoreId, _cpu._sensorTypeIndex[SensorType.Clock]++, SensorType.Clock, cpu, cpu._settings);
|
||||
_multiplier = new Sensor("Core #" + CoreId, cpu._sensorTypeIndex[SensorType.Factor]++, SensorType.Factor, cpu, cpu._settings);
|
||||
_power = new Sensor("Core #" + CoreId + " (SMU)", cpu._sensorTypeIndex[SensorType.Power]++, SensorType.Power, cpu, cpu._settings);
|
||||
_vcore = new Sensor("Core #" + CoreId + " VID", cpu._sensorTypeIndex[SensorType.Voltage]++, SensorType.Voltage, cpu, cpu._settings);
|
||||
|
||||
cpu.ActivateSensor(_clock);
|
||||
cpu.ActivateSensor(_multiplier);
|
||||
cpu.ActivateSensor(_power);
|
||||
cpu.ActivateSensor(_vcore);
|
||||
}
|
||||
|
||||
public int CoreId { get; }
|
||||
|
||||
public List<CpuId> Threads { get; }
|
||||
|
||||
public void UpdateSensors()
|
||||
{
|
||||
// CPUID cpu = threads.FirstOrDefault();
|
||||
CpuId cpu = Threads[0];
|
||||
if (cpu == null)
|
||||
return;
|
||||
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(cpu.Affinity);
|
||||
|
||||
// MSRC001_0299
|
||||
// TU [19:16]
|
||||
// ESU [12:8] -> Unit 15.3 micro Joule per increment
|
||||
// PU [3:0]
|
||||
Ring0.ReadMsr(MSR_PWR_UNIT, out _, out _);
|
||||
|
||||
// MSRC001_029A
|
||||
// total_energy [31:0]
|
||||
DateTime sampleTime = DateTime.Now;
|
||||
Ring0.ReadMsr(MSR_CORE_ENERGY_STAT, out uint eax, out _);
|
||||
uint totalEnergy = eax;
|
||||
|
||||
// MSRC001_0293
|
||||
// CurHwPstate [24:22]
|
||||
// CurCpuVid [21:14]
|
||||
// CurCpuDfsId [13:8]
|
||||
// CurCpuFid [7:0]
|
||||
Ring0.ReadMsr(MSR_HARDWARE_PSTATE_STATUS, out eax, out _);
|
||||
int curCpuVid = (int)((eax >> 14) & 0xff);
|
||||
int curCpuDfsId = (int)((eax >> 8) & 0x3f);
|
||||
int curCpuFid = (int)(eax & 0xff);
|
||||
|
||||
// MSRC001_0064 + x
|
||||
// IddDiv [31:30]
|
||||
// IddValue [29:22]
|
||||
// CpuVid [21:14]
|
||||
// CpuDfsId [13:8]
|
||||
// CpuFid [7:0]
|
||||
// Ring0.ReadMsr(MSR_PSTATE_0 + (uint)CurHwPstate, out eax, out edx);
|
||||
// int IddDiv = (int)((eax >> 30) & 0x03);
|
||||
// int IddValue = (int)((eax >> 22) & 0xff);
|
||||
// int CpuVid = (int)((eax >> 14) & 0xff);
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
|
||||
// clock
|
||||
// CoreCOF is (Core::X86::Msr::PStateDef[CpuFid[7:0]] / Core::X86::Msr::PStateDef[CpuDfsId]) * 200
|
||||
double clock = 200.0;
|
||||
_busSpeed ??= _cpu.Sensors.FirstOrDefault(x => x.Name == "Bus Speed");
|
||||
if (_busSpeed?.Value.HasValue == true && _busSpeed.Value > 0)
|
||||
clock = (double)(_busSpeed.Value * 2);
|
||||
|
||||
_clock.Value = (float)(curCpuFid / (double)curCpuDfsId * clock);
|
||||
|
||||
// multiplier
|
||||
_multiplier.Value = (float)(curCpuFid / (double)curCpuDfsId * 2.0);
|
||||
|
||||
// Voltage
|
||||
const double vidStep = 0.00625;
|
||||
double vcc = 1.550 - (vidStep * curCpuVid);
|
||||
_vcore.Value = (float)vcc;
|
||||
|
||||
// power consumption
|
||||
// power.Value = (float) ((double)pu * 0.125);
|
||||
// esu = 15.3 micro Joule per increment
|
||||
if (_lastPwrTime.Ticks == 0)
|
||||
{
|
||||
_lastPwrTime = sampleTime;
|
||||
_lastPwrValue = totalEnergy;
|
||||
}
|
||||
|
||||
// ticks diff
|
||||
TimeSpan time = sampleTime - _lastPwrTime;
|
||||
long pwr;
|
||||
if (_lastPwrValue <= totalEnergy)
|
||||
pwr = totalEnergy - _lastPwrValue;
|
||||
else
|
||||
pwr = (0xffffffff - _lastPwrValue) + totalEnergy;
|
||||
|
||||
// update for next sample
|
||||
_lastPwrTime = sampleTime;
|
||||
_lastPwrValue = totalEnergy;
|
||||
|
||||
double energy = 15.3e-6 * pwr;
|
||||
energy /= time.TotalSeconds;
|
||||
|
||||
if (!double.IsNaN(energy))
|
||||
_power.Value = (float)energy;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private const uint COFVID_STATUS = 0xC0010071;
|
||||
private const uint F17H_M01H_SVI = 0x0005A000;
|
||||
private const uint F17H_M01H_THM_TCON_CUR_TMP = 0x00059800;
|
||||
private const uint F17H_M70H_CCD1_TEMP = 0x00059954;
|
||||
private const uint F17H_M61H_CCD1_TEMP = 0x00059b08;
|
||||
private const uint F17H_TEMP_OFFSET_FLAG = 0x80000;
|
||||
private const uint FAMILY_17H_PCI_CONTROL_REGISTER = 0x60;
|
||||
private const uint HWCR = 0xC0010015;
|
||||
private const uint MSR_CORE_ENERGY_STAT = 0xC001029A;
|
||||
private const uint MSR_HARDWARE_PSTATE_STATUS = 0xC0010293;
|
||||
private const uint MSR_PKG_ENERGY_STAT = 0xC001029B;
|
||||
private const uint MSR_PSTATE_0 = 0xC0010064;
|
||||
private const uint MSR_PWR_UNIT = 0xC0010299;
|
||||
private const uint PERF_CTL_0 = 0xC0010000;
|
||||
private const uint PERF_CTR_0 = 0xC0010004;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal abstract class AmdCpu : GenericCpu
|
||||
{
|
||||
protected AmdCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
|
||||
{ }
|
||||
|
||||
protected uint GetPciAddress(byte function, ushort deviceId)
|
||||
{
|
||||
// assemble the pci address
|
||||
uint address = Ring0.GetPciAddress(PCI_BUS, (byte)(PCI_BASE_DEVICE + Index), function);
|
||||
|
||||
// verify that we have the correct bus, device and function
|
||||
if (!Ring0.ReadPciConfig(address, DEVICE_VENDOR_ID_REGISTER, out uint deviceVendor))
|
||||
return Interop.Ring0.INVALID_PCI_ADDRESS;
|
||||
|
||||
if (deviceVendor != (deviceId << 16 | AMD_VENDOR_ID))
|
||||
return Interop.Ring0.INVALID_PCI_ADDRESS;
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private const ushort AMD_VENDOR_ID = 0x1022;
|
||||
private const byte DEVICE_VENDOR_ID_REGISTER = 0;
|
||||
private const byte PCI_BASE_DEVICE = 0x18;
|
||||
|
||||
private const byte PCI_BUS = 0;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal class CpuGroup : IGroup
|
||||
{
|
||||
private readonly List<GenericCpu> _hardware = new();
|
||||
private readonly CpuId[][][] _threads;
|
||||
|
||||
public CpuGroup(ISettings settings)
|
||||
{
|
||||
CpuId[][] processorThreads = GetProcessorThreads();
|
||||
_threads = new CpuId[processorThreads.Length][][];
|
||||
|
||||
int index = 0;
|
||||
foreach (CpuId[] threads in processorThreads)
|
||||
{
|
||||
if (threads.Length == 0)
|
||||
continue;
|
||||
|
||||
CpuId[][] coreThreads = GroupThreadsByCore(threads);
|
||||
_threads[index] = coreThreads;
|
||||
|
||||
switch (threads[0].Vendor)
|
||||
{
|
||||
case Vendor.Intel:
|
||||
_hardware.Add(new IntelCpu(index, coreThreads, settings));
|
||||
break;
|
||||
case Vendor.AMD:
|
||||
switch (threads[0].Family)
|
||||
{
|
||||
case 0x0F:
|
||||
_hardware.Add(new Amd0FCpu(index, coreThreads, settings));
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
_hardware.Add(new Amd10Cpu(index, coreThreads, settings));
|
||||
break;
|
||||
case 0x17:
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
_hardware.Add(new Amd17Cpu(index, coreThreads, settings));
|
||||
break;
|
||||
default:
|
||||
_hardware.Add(new GenericCpu(index, coreThreads, settings));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
_hardware.Add(new GenericCpu(index, coreThreads, settings));
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
if (_threads == null)
|
||||
return null;
|
||||
|
||||
StringBuilder r = new();
|
||||
r.AppendLine("CPUID");
|
||||
r.AppendLine();
|
||||
for (int i = 0; i < _threads.Length; i++)
|
||||
{
|
||||
r.AppendLine("Processor " + i);
|
||||
r.AppendLine();
|
||||
r.AppendFormat("Processor Vendor: {0}{1}", _threads[i][0][0].Vendor, Environment.NewLine);
|
||||
r.AppendFormat("Processor Brand: {0}{1}", _threads[i][0][0].BrandString, Environment.NewLine);
|
||||
r.AppendFormat("Family: 0x{0}{1}", _threads[i][0][0].Family.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
|
||||
r.AppendFormat("Model: 0x{0}{1}", _threads[i][0][0].Model.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
|
||||
r.AppendFormat("Stepping: 0x{0}{1}", _threads[i][0][0].Stepping.ToString("X", CultureInfo.InvariantCulture), Environment.NewLine);
|
||||
r.AppendLine();
|
||||
|
||||
r.AppendLine("CPUID Return Values");
|
||||
r.AppendLine();
|
||||
for (int j = 0; j < _threads[i].Length; j++)
|
||||
{
|
||||
for (int k = 0; k < _threads[i][j].Length; k++)
|
||||
{
|
||||
r.AppendLine(" CPU Group: " + _threads[i][j][k].Group);
|
||||
r.AppendLine(" CPU Thread: " + _threads[i][j][k].Thread);
|
||||
r.AppendLine(" APIC ID: " + _threads[i][j][k].ApicId);
|
||||
r.AppendLine(" Processor ID: " + _threads[i][j][k].ProcessorId);
|
||||
r.AppendLine(" Core ID: " + _threads[i][j][k].CoreId);
|
||||
r.AppendLine(" Thread ID: " + _threads[i][j][k].ThreadId);
|
||||
r.AppendLine();
|
||||
r.AppendLine(" Function EAX EBX ECX EDX");
|
||||
AppendCpuidData(r, _threads[i][j][k].Data, CpuId.CPUID_0);
|
||||
AppendCpuidData(r, _threads[i][j][k].ExtData, CpuId.CPUID_EXT);
|
||||
r.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (GenericCpu cpu in _hardware)
|
||||
{
|
||||
cpu.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static CpuId[][] GetProcessorThreads()
|
||||
{
|
||||
List<CpuId> threads = new();
|
||||
|
||||
for (int i = 0; i < ThreadAffinity.ProcessorGroupCount; i++)
|
||||
{
|
||||
for (int j = 0; j < 192; j++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cpuid = CpuId.Get(i, j);
|
||||
if (cpuid != null)
|
||||
threads.Add(cpuid);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
// Continue...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortedDictionary<uint, List<CpuId>> processors = new();
|
||||
foreach (CpuId thread in threads)
|
||||
{
|
||||
processors.TryGetValue(thread.ProcessorId, out List<CpuId> list);
|
||||
if (list == null)
|
||||
{
|
||||
list = new List<CpuId>();
|
||||
processors.Add(thread.ProcessorId, list);
|
||||
}
|
||||
|
||||
list.Add(thread);
|
||||
}
|
||||
|
||||
CpuId[][] processorThreads = new CpuId[processors.Count][];
|
||||
int index = 0;
|
||||
foreach (List<CpuId> list in processors.Values)
|
||||
{
|
||||
processorThreads[index] = list.ToArray();
|
||||
index++;
|
||||
}
|
||||
|
||||
return processorThreads;
|
||||
}
|
||||
|
||||
private static CpuId[][] GroupThreadsByCore(IEnumerable<CpuId> threads)
|
||||
{
|
||||
SortedDictionary<uint, List<CpuId>> cores = new();
|
||||
foreach (CpuId thread in threads)
|
||||
{
|
||||
cores.TryGetValue(thread.CoreId, out List<CpuId> coreList);
|
||||
if (coreList == null)
|
||||
{
|
||||
coreList = new List<CpuId>();
|
||||
cores.Add(thread.CoreId, coreList);
|
||||
}
|
||||
|
||||
coreList.Add(thread);
|
||||
}
|
||||
|
||||
CpuId[][] coreThreads = new CpuId[cores.Count][];
|
||||
int index = 0;
|
||||
foreach (List<CpuId> list in cores.Values)
|
||||
{
|
||||
coreThreads[index] = list.ToArray();
|
||||
index++;
|
||||
}
|
||||
|
||||
return coreThreads;
|
||||
}
|
||||
|
||||
private static void AppendCpuidData(StringBuilder r, uint[,] data, uint offset)
|
||||
{
|
||||
for (int i = 0; i < data.GetLength(0); i++)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append((i + offset).ToString("X8", CultureInfo.InvariantCulture));
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append(data[i, j].ToString("X8", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
// 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.Text;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
public enum Vendor
|
||||
{
|
||||
Unknown,
|
||||
Intel,
|
||||
AMD
|
||||
}
|
||||
|
||||
public class CpuId
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CpuId" /> class.
|
||||
/// </summary>
|
||||
/// <param name="group">The group.</param>
|
||||
/// <param name="thread">The thread.</param>
|
||||
/// <param name="affinity">The affinity.</param>
|
||||
private CpuId(int group, int thread, GroupAffinity affinity)
|
||||
{
|
||||
Thread = thread;
|
||||
Group = group;
|
||||
Affinity = affinity;
|
||||
|
||||
uint threadMaskWith;
|
||||
uint coreMaskWith;
|
||||
uint maxCpuidExt;
|
||||
|
||||
if (thread >= 64)
|
||||
throw new ArgumentOutOfRangeException(nameof(thread));
|
||||
|
||||
uint maxCpuid;
|
||||
if (OpCode.CpuId(CPUID_0, 0, out uint eax, out uint ebx, out uint ecx, out uint edx))
|
||||
{
|
||||
if (eax > 0)
|
||||
maxCpuid = eax;
|
||||
else
|
||||
return;
|
||||
|
||||
StringBuilder vendorBuilder = new();
|
||||
AppendRegister(vendorBuilder, ebx);
|
||||
AppendRegister(vendorBuilder, edx);
|
||||
AppendRegister(vendorBuilder, ecx);
|
||||
|
||||
Vendor = vendorBuilder.ToString() switch
|
||||
{
|
||||
"GenuineIntel" => Vendor.Intel,
|
||||
"AuthenticAMD" => Vendor.AMD,
|
||||
_ => Vendor.Unknown
|
||||
};
|
||||
|
||||
if (OpCode.CpuId(CPUID_EXT, 0, out eax, out _, out _, out _))
|
||||
{
|
||||
if (eax > CPUID_EXT)
|
||||
maxCpuidExt = eax - CPUID_EXT;
|
||||
else
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(thread));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(thread));
|
||||
}
|
||||
|
||||
maxCpuid = Math.Min(maxCpuid, 1024);
|
||||
maxCpuidExt = Math.Min(maxCpuidExt, 1024);
|
||||
|
||||
Data = new uint[maxCpuid + 1, 4];
|
||||
for (uint i = 0; i < maxCpuid + 1; i++)
|
||||
{
|
||||
OpCode.CpuId(CPUID_0 + i, 0, out Data[i, 0], out Data[i, 1], out Data[i, 2], out Data[i, 3]);
|
||||
}
|
||||
|
||||
ExtData = new uint[maxCpuidExt + 1, 4];
|
||||
for (uint i = 0; i < maxCpuidExt + 1; i++)
|
||||
{
|
||||
OpCode.CpuId(CPUID_EXT + i, 0, out ExtData[i, 0], out ExtData[i, 1], out ExtData[i, 2], out ExtData[i, 3]);
|
||||
}
|
||||
|
||||
StringBuilder nameBuilder = new();
|
||||
for (uint i = 2; i <= 4; i++)
|
||||
{
|
||||
if (OpCode.CpuId(CPUID_EXT + i, 0, out eax, out ebx, out ecx, out edx))
|
||||
{
|
||||
AppendRegister(nameBuilder, eax);
|
||||
AppendRegister(nameBuilder, ebx);
|
||||
AppendRegister(nameBuilder, ecx);
|
||||
AppendRegister(nameBuilder, edx);
|
||||
}
|
||||
}
|
||||
|
||||
nameBuilder.Replace('\0', ' ');
|
||||
BrandString = nameBuilder.ToString().Trim();
|
||||
nameBuilder.Replace("(R)", string.Empty);
|
||||
nameBuilder.Replace("(TM)", string.Empty);
|
||||
nameBuilder.Replace("(tm)", string.Empty);
|
||||
nameBuilder.Replace("CPU", string.Empty);
|
||||
nameBuilder.Replace("Dual-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("Triple-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("Quad-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("Six-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("Eight-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("64-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("32-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("24-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("16-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("12-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("8-Core Processor", string.Empty);
|
||||
nameBuilder.Replace("6-Core Processor", string.Empty);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
nameBuilder.Replace(" ", " ");
|
||||
|
||||
Name = nameBuilder.ToString();
|
||||
if (Name.Contains("@"))
|
||||
Name = Name.Remove(Name.LastIndexOf('@'));
|
||||
|
||||
Name = Name.Trim();
|
||||
Family = ((Data[1, 0] & 0x0FF00000) >> 20) + ((Data[1, 0] & 0x0F00) >> 8);
|
||||
Model = ((Data[1, 0] & 0x0F0000) >> 12) + ((Data[1, 0] & 0xF0) >> 4);
|
||||
Stepping = Data[1, 0] & 0x0F;
|
||||
ApicId = (Data[1, 1] >> 24) & 0xFF;
|
||||
PkgType = (ExtData[1, 1] >> 28) & 0xFF;
|
||||
|
||||
switch (Vendor)
|
||||
{
|
||||
case Vendor.Intel:
|
||||
uint maxCoreAndThreadIdPerPackage = (Data[1, 1] >> 16) & 0xFF;
|
||||
uint maxCoreIdPerPackage;
|
||||
if (maxCpuid >= 4)
|
||||
maxCoreIdPerPackage = ((Data[4, 0] >> 26) & 0x3F) + 1;
|
||||
else
|
||||
maxCoreIdPerPackage = 1;
|
||||
|
||||
threadMaskWith = NextLog2(maxCoreAndThreadIdPerPackage / maxCoreIdPerPackage);
|
||||
coreMaskWith = NextLog2(maxCoreIdPerPackage);
|
||||
break;
|
||||
case Vendor.AMD:
|
||||
uint corePerPackage;
|
||||
if (maxCpuidExt >= 8)
|
||||
corePerPackage = (ExtData[8, 2] & 0xFF) + 1;
|
||||
else
|
||||
corePerPackage = 1;
|
||||
|
||||
threadMaskWith = 0;
|
||||
coreMaskWith = NextLog2(corePerPackage);
|
||||
|
||||
if (Family is 0x17 or 0x19)
|
||||
{
|
||||
// ApicIdCoreIdSize: APIC ID size.
|
||||
// cores per DIE
|
||||
// we need this for Ryzen 5 (4 cores, 8 threads) ans Ryzen 6 (6 cores, 12 threads)
|
||||
// Ryzen 5: [core0][core1][dummy][dummy][core2][core3] (Core0 EBX = 00080800, Core2 EBX = 08080800)
|
||||
coreMaskWith = ((ExtData[8, 2] >> 12) & 0xF) switch
|
||||
{
|
||||
0x04 => NextLog2(16), // Ryzen
|
||||
0x05 => NextLog2(32), // Threadripper
|
||||
0x06 => NextLog2(64), // Epic
|
||||
_ => coreMaskWith
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
threadMaskWith = 0;
|
||||
coreMaskWith = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessorId = ApicId >> (int)(coreMaskWith + threadMaskWith);
|
||||
CoreId = (ApicId >> (int)threadMaskWith) - (ProcessorId << (int)coreMaskWith);
|
||||
ThreadId = ApicId - (ProcessorId << (int)(coreMaskWith + threadMaskWith)) - (CoreId << (int)threadMaskWith);
|
||||
}
|
||||
|
||||
public GroupAffinity Affinity { get; }
|
||||
|
||||
public uint ApicId { get; }
|
||||
|
||||
public string BrandString { get; } = string.Empty;
|
||||
|
||||
public uint CoreId { get; }
|
||||
|
||||
public uint[,] Data { get; } = new uint[0, 0];
|
||||
|
||||
public uint[,] ExtData { get; } = new uint[0, 0];
|
||||
|
||||
public uint Family { get; }
|
||||
|
||||
public int Group { get; }
|
||||
|
||||
public uint Model { get; }
|
||||
|
||||
public string Name { get; } = string.Empty;
|
||||
|
||||
public uint PkgType { get; }
|
||||
|
||||
public uint ProcessorId { get; }
|
||||
|
||||
public uint Stepping { get; }
|
||||
|
||||
public int Thread { get; }
|
||||
|
||||
public uint ThreadId { get; }
|
||||
|
||||
public Vendor Vendor { get; } = Vendor.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="CpuId" />.
|
||||
/// </summary>
|
||||
/// <param name="group">The group.</param>
|
||||
/// <param name="thread">The thread.</param>
|
||||
/// <returns><see cref="CpuId" />.</returns>
|
||||
public static CpuId Get(int group, int thread)
|
||||
{
|
||||
if (thread >= 64)
|
||||
return null;
|
||||
|
||||
var affinity = GroupAffinity.Single((ushort)group, thread);
|
||||
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(affinity);
|
||||
if (previousAffinity == GroupAffinity.Undefined)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return new CpuId(group, thread, affinity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendRegister(StringBuilder b, uint value)
|
||||
{
|
||||
b.Append((char)(value & 0xff));
|
||||
b.Append((char)((value >> 8) & 0xff));
|
||||
b.Append((char)((value >> 16) & 0xff));
|
||||
b.Append((char)((value >> 24) & 0xff));
|
||||
}
|
||||
|
||||
private static uint NextLog2(long x)
|
||||
{
|
||||
if (x <= 0)
|
||||
return 0;
|
||||
|
||||
x--;
|
||||
uint count = 0;
|
||||
while (x > 0)
|
||||
{
|
||||
x >>= 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
public const uint CPUID_0 = 0;
|
||||
public const uint CPUID_EXT = 0x80000000;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
internal class CpuLoad
|
||||
{
|
||||
private readonly float[] _threadLoads;
|
||||
|
||||
private long[] _idleTimes;
|
||||
private float _totalLoad;
|
||||
private long[] _totalTimes;
|
||||
|
||||
public CpuLoad(CpuId[][] cpuid)
|
||||
{
|
||||
_threadLoads = new float[cpuid.Sum(x => x.Length)];
|
||||
_totalLoad = 0;
|
||||
|
||||
try
|
||||
{
|
||||
GetTimes(out _idleTimes, out _totalTimes);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_idleTimes = null;
|
||||
_totalTimes = null;
|
||||
}
|
||||
|
||||
if (_idleTimes != null)
|
||||
IsAvailable = true;
|
||||
}
|
||||
|
||||
public bool IsAvailable { get; }
|
||||
|
||||
private static bool GetTimes(out long[] idle, out long[] total)
|
||||
{
|
||||
return !Software.OperatingSystem.IsUnix ? GetWindowsTimes(out idle, out total) : GetUnixTimes(out idle, out total);
|
||||
}
|
||||
|
||||
private static bool GetWindowsTimes(out long[] idle, out long[] total)
|
||||
{
|
||||
idle = null;
|
||||
total = null;
|
||||
|
||||
//Query processor idle information
|
||||
Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION[] idleInformation = new Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION[64];
|
||||
int idleSize = Marshal.SizeOf(typeof(Interop.NtDll.SYSTEM_PROCESSOR_IDLE_INFORMATION));
|
||||
if (Interop.NtDll.NtQuerySystemInformation(Interop.NtDll.SYSTEM_INFORMATION_CLASS.SystemProcessorIdleInformation, idleInformation, idleInformation.Length * idleSize, out int idleReturn) != 0)
|
||||
return false;
|
||||
|
||||
//Query processor performance information
|
||||
Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] perfInformation = new Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[64];
|
||||
int perfSize = Marshal.SizeOf(typeof(Interop.NtDll.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
|
||||
if (Interop.NtDll.NtQuerySystemInformation(Interop.NtDll.SYSTEM_INFORMATION_CLASS.SystemProcessorPerformanceInformation,
|
||||
perfInformation,
|
||||
perfInformation.Length * perfSize,
|
||||
out int perfReturn) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
idle = new long[idleReturn / idleSize];
|
||||
for (int i = 0; i < idle.Length; i++)
|
||||
idle[i] = idleInformation[i].IdleTime;
|
||||
|
||||
total = new long[perfReturn / perfSize];
|
||||
for (int i = 0; i < total.Length; i++)
|
||||
total[i] = perfInformation[i].KernelTime + perfInformation[i].UserTime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool GetUnixTimes(out long[] idle, out long[] total)
|
||||
{
|
||||
idle = null;
|
||||
total = null;
|
||||
|
||||
List<long> idleList = new();
|
||||
List<long> totalList = new();
|
||||
|
||||
if (!File.Exists("/proc/stat"))
|
||||
return false;
|
||||
|
||||
string[] cpuInfos = File.ReadAllLines("/proc/stat");
|
||||
|
||||
// currently parse the OverAll CPU info
|
||||
// cpu 1583083 737 452845 36226266 723316 63685 31896 0 0 0
|
||||
// cpu0 397468 189 109728 9040007 191429 16939 14954 0 0 0
|
||||
// 0=cpu 1=user 2=nice 3=system 4=idle 5=iowait 6=irq 7=softirq 8=steal 9=guest 10=guest_nice
|
||||
foreach (string cpuInfo in cpuInfos.Where(s => s.StartsWith("cpu") && s.Length > 3 && s[3] != ' '))
|
||||
{
|
||||
string[] overall = cpuInfo.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
try
|
||||
{
|
||||
// Parse idle information.
|
||||
idleList.Add(long.Parse(overall[4]));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
|
||||
// Parse total information = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice.
|
||||
long currentTotal = 0;
|
||||
foreach (string item in overall.Skip(1))
|
||||
{
|
||||
try
|
||||
{
|
||||
currentTotal += long.Parse(item);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
|
||||
totalList.Add(currentTotal);
|
||||
}
|
||||
|
||||
idle = idleList.ToArray();
|
||||
total = totalList.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
public float GetTotalLoad()
|
||||
{
|
||||
return _totalLoad;
|
||||
}
|
||||
|
||||
public float GetThreadLoad(int thread)
|
||||
{
|
||||
return _threadLoads[thread];
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_idleTimes == null || !GetTimes(out long[] newIdleTimes, out long[] newTotalTimes))
|
||||
return;
|
||||
|
||||
int minDiff = Software.OperatingSystem.IsUnix ? 100 : 100000;
|
||||
for (int i = 0; i < Math.Min(newTotalTimes.Length, _totalTimes.Length); i++)
|
||||
{
|
||||
if (newTotalTimes[i] - _totalTimes[i] < minDiff)
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIdleTimes == null)
|
||||
return;
|
||||
|
||||
float total = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < _threadLoads.Length && i < _idleTimes.Length && i < newIdleTimes.Length; i++)
|
||||
{
|
||||
float idle = (newIdleTimes[i] - _idleTimes[i]) / (float)(newTotalTimes[i] - _totalTimes[i]);
|
||||
_threadLoads[i] = 100f * (1.0f - Math.Min(idle, 1.0f));
|
||||
total += idle;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
total = 1.0f - (total / count);
|
||||
total = total < 0 ? 0 : total;
|
||||
}
|
||||
else
|
||||
{
|
||||
total = 0;
|
||||
}
|
||||
|
||||
_totalLoad = total * 100;
|
||||
_totalTimes = newTotalTimes;
|
||||
_idleTimes = newIdleTimes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
// 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;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Cpu;
|
||||
|
||||
public class GenericCpu : Hardware
|
||||
{
|
||||
protected readonly int _coreCount;
|
||||
protected readonly CpuId[][] _cpuId;
|
||||
protected readonly uint _family;
|
||||
protected readonly uint _model;
|
||||
protected readonly uint _packageType;
|
||||
protected readonly uint _stepping;
|
||||
protected readonly int _threadCount;
|
||||
|
||||
private readonly CpuLoad _cpuLoad;
|
||||
private readonly double _estimatedTimeStampCounterFrequency;
|
||||
private readonly double _estimatedTimeStampCounterFrequencyError;
|
||||
private readonly bool _isInvariantTimeStampCounter;
|
||||
private readonly Sensor[] _threadLoads;
|
||||
|
||||
private readonly Sensor _totalLoad;
|
||||
private readonly Sensor _maxLoad;
|
||||
private readonly Vendor _vendor;
|
||||
private long _lastTime;
|
||||
private ulong _lastTimeStampCount;
|
||||
|
||||
public GenericCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(cpuId[0][0].Name, CreateIdentifier(cpuId[0][0].Vendor, processorIndex), settings)
|
||||
{
|
||||
_cpuId = cpuId;
|
||||
_vendor = cpuId[0][0].Vendor;
|
||||
_family = cpuId[0][0].Family;
|
||||
_model = cpuId[0][0].Model;
|
||||
_stepping = cpuId[0][0].Stepping;
|
||||
_packageType = cpuId[0][0].PkgType;
|
||||
|
||||
Index = processorIndex;
|
||||
_coreCount = cpuId.Length;
|
||||
_threadCount = cpuId.Sum(x => x.Length);
|
||||
|
||||
// Check if processor has MSRs.
|
||||
HasModelSpecificRegisters = cpuId[0][0].Data.GetLength(0) > 1 && (cpuId[0][0].Data[1, 3] & 0x20) != 0;
|
||||
|
||||
// Check if processor has a TSC.
|
||||
HasTimeStampCounter = cpuId[0][0].Data.GetLength(0) > 1 && (cpuId[0][0].Data[1, 3] & 0x10) != 0;
|
||||
|
||||
// Check if processor supports an invariant TSC.
|
||||
_isInvariantTimeStampCounter = cpuId[0][0].ExtData.GetLength(0) > 7 && (cpuId[0][0].ExtData[7, 3] & 0x100) != 0;
|
||||
|
||||
_totalLoad = _coreCount > 1 ? new Sensor("CPU Total", 0, SensorType.Load, this, settings) : null;
|
||||
_maxLoad = _coreCount > 1 ? new Sensor("CPU Core Max", 1, SensorType.Load, this, settings) : null;
|
||||
|
||||
_cpuLoad = new CpuLoad(cpuId);
|
||||
if (_cpuLoad.IsAvailable)
|
||||
{
|
||||
_threadLoads = new Sensor[_threadCount];
|
||||
for (int coreIdx = 0; coreIdx < cpuId.Length; coreIdx++)
|
||||
{
|
||||
for (int threadIdx = 0; threadIdx < cpuId[coreIdx].Length; threadIdx++)
|
||||
{
|
||||
int thread = cpuId[coreIdx][threadIdx].Thread;
|
||||
if (thread < _threadLoads.Length)
|
||||
{
|
||||
// Some cores may have 2 threads while others have only one (e.g. P-cores vs E-cores on Intel 12th gen).
|
||||
string sensorName = CoreString(coreIdx) + (cpuId[coreIdx].Length > 1 ? $" Thread #{threadIdx + 1}" : string.Empty);
|
||||
_threadLoads[thread] = new Sensor(sensorName, thread + 2, SensorType.Load, this, settings);
|
||||
|
||||
ActivateSensor(_threadLoads[thread]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_totalLoad != null)
|
||||
{
|
||||
ActivateSensor(_totalLoad);
|
||||
}
|
||||
|
||||
if (_maxLoad != null)
|
||||
{
|
||||
ActivateSensor(_maxLoad);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasTimeStampCounter)
|
||||
{
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(cpuId[0][0].Affinity);
|
||||
EstimateTimeStampCounterFrequency(out _estimatedTimeStampCounterFrequency, out _estimatedTimeStampCounterFrequencyError);
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_estimatedTimeStampCounterFrequency = 0;
|
||||
}
|
||||
|
||||
TimeStampCounterFrequency = _estimatedTimeStampCounterFrequency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CPUID.
|
||||
/// </summary>
|
||||
public CpuId[][] CpuId => _cpuId;
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Cpu;
|
||||
|
||||
public bool HasModelSpecificRegisters { get; }
|
||||
|
||||
public bool HasTimeStampCounter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CPU index.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
|
||||
public double TimeStampCounterFrequency { get; private set; }
|
||||
|
||||
protected string CoreString(int i)
|
||||
{
|
||||
if (_coreCount == 1)
|
||||
return "CPU Core";
|
||||
|
||||
return "CPU Core #" + (i + 1);
|
||||
}
|
||||
|
||||
private static Identifier CreateIdentifier(Vendor vendor, int processorIndex)
|
||||
{
|
||||
string s = vendor switch
|
||||
{
|
||||
Vendor.AMD => "amdcpu",
|
||||
Vendor.Intel => "intelcpu",
|
||||
_ => "genericcpu"
|
||||
};
|
||||
|
||||
return new Identifier(s, processorIndex.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private static void EstimateTimeStampCounterFrequency(out double frequency, out double error)
|
||||
{
|
||||
// preload the function
|
||||
EstimateTimeStampCounterFrequency(0, out double f, out double e);
|
||||
EstimateTimeStampCounterFrequency(0, out f, out e);
|
||||
|
||||
// estimate the frequency
|
||||
error = double.MaxValue;
|
||||
frequency = 0;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
EstimateTimeStampCounterFrequency(0.025, out f, out e);
|
||||
if (e < error)
|
||||
{
|
||||
error = e;
|
||||
frequency = f;
|
||||
}
|
||||
|
||||
if (error < 1e-4)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EstimateTimeStampCounterFrequency(double timeWindow, out double frequency, out double error)
|
||||
{
|
||||
long ticks = (long)(timeWindow * Stopwatch.Frequency);
|
||||
|
||||
long timeBegin = Stopwatch.GetTimestamp() + (long)Math.Ceiling(0.001 * ticks);
|
||||
long timeEnd = timeBegin + ticks;
|
||||
|
||||
while (Stopwatch.GetTimestamp() < timeBegin)
|
||||
{ }
|
||||
|
||||
ulong countBegin = OpCode.Rdtsc();
|
||||
long afterBegin = Stopwatch.GetTimestamp();
|
||||
|
||||
while (Stopwatch.GetTimestamp() < timeEnd)
|
||||
{ }
|
||||
|
||||
ulong countEnd = OpCode.Rdtsc();
|
||||
long afterEnd = Stopwatch.GetTimestamp();
|
||||
|
||||
double delta = timeEnd - timeBegin;
|
||||
frequency = 1e-6 * ((double)(countEnd - countBegin) * Stopwatch.Frequency) / delta;
|
||||
|
||||
double beginError = (afterBegin - timeBegin) / delta;
|
||||
double endError = (afterEnd - timeEnd) / delta;
|
||||
error = beginError + endError;
|
||||
}
|
||||
|
||||
private static void AppendMsrData(StringBuilder r, uint msr, GroupAffinity affinity)
|
||||
{
|
||||
if (Ring0.ReadMsr(msr, out uint eax, out uint edx, affinity))
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append(msr.ToString("X8", CultureInfo.InvariantCulture));
|
||||
r.Append(" ");
|
||||
r.Append(edx.ToString("X8", CultureInfo.InvariantCulture));
|
||||
r.Append(" ");
|
||||
r.Append(eax.ToString("X8", CultureInfo.InvariantCulture));
|
||||
r.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual uint[] GetMsrs()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
|
||||
switch (_vendor)
|
||||
{
|
||||
case Vendor.AMD:
|
||||
r.AppendLine("AMD CPU");
|
||||
break;
|
||||
case Vendor.Intel:
|
||||
r.AppendLine("Intel CPU");
|
||||
break;
|
||||
default:
|
||||
r.AppendLine("Generic CPU");
|
||||
break;
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
r.AppendFormat("Name: {0}{1}", _name, Environment.NewLine);
|
||||
r.AppendFormat("Number of Cores: {0}{1}", _coreCount, Environment.NewLine);
|
||||
r.AppendFormat("Threads per Core: {0}{1}", _cpuId[0].Length, Environment.NewLine);
|
||||
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
|
||||
r.AppendLine("Time Stamp Counter: " + (HasTimeStampCounter ? _isInvariantTimeStampCounter ? "Invariant" : "Not Invariant" : "None"));
|
||||
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Estimated Time Stamp Counter Frequency: {0} MHz", Math.Round(_estimatedTimeStampCounterFrequency * 100) * 0.01));
|
||||
r.AppendLine(string.Format(CultureInfo.InvariantCulture,
|
||||
"Estimated Time Stamp Counter Frequency Error: {0} Mhz",
|
||||
Math.Round(_estimatedTimeStampCounterFrequency * _estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
|
||||
|
||||
r.AppendLine(string.Format(CultureInfo.InvariantCulture, "Time Stamp Counter Frequency: {0} MHz", Math.Round(TimeStampCounterFrequency * 100) * 0.01));
|
||||
r.AppendLine();
|
||||
|
||||
uint[] msrArray = GetMsrs();
|
||||
if (msrArray is { Length: > 0 })
|
||||
{
|
||||
for (int i = 0; i < _cpuId.Length; i++)
|
||||
{
|
||||
r.AppendLine("MSR Core #" + (i + 1));
|
||||
r.AppendLine();
|
||||
r.AppendLine(" MSR EDX EAX");
|
||||
foreach (uint msr in msrArray)
|
||||
AppendMsrData(r, msr, _cpuId[i][0].Affinity);
|
||||
|
||||
r.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (HasTimeStampCounter && _isInvariantTimeStampCounter)
|
||||
{
|
||||
// make sure always the same thread is used
|
||||
GroupAffinity previousAffinity = ThreadAffinity.Set(_cpuId[0][0].Affinity);
|
||||
|
||||
// read time before and after getting the TSC to estimate the error
|
||||
long firstTime = Stopwatch.GetTimestamp();
|
||||
ulong timeStampCount = OpCode.Rdtsc();
|
||||
long time = Stopwatch.GetTimestamp();
|
||||
|
||||
// restore the thread affinity mask
|
||||
ThreadAffinity.Set(previousAffinity);
|
||||
|
||||
double delta = (double)(time - _lastTime) / Stopwatch.Frequency;
|
||||
double error = (double)(time - firstTime) / Stopwatch.Frequency;
|
||||
|
||||
// only use data if they are measured accurate enough (max 0.1ms delay)
|
||||
if (error < 0.0001)
|
||||
{
|
||||
// ignore the first reading because there are no initial values
|
||||
// ignore readings with too large or too small time window
|
||||
if (_lastTime != 0 && delta is > 0.5 and < 2)
|
||||
{
|
||||
// update the TSC frequency with the new value
|
||||
TimeStampCounterFrequency = (timeStampCount - _lastTimeStampCount) / (1e6 * delta);
|
||||
}
|
||||
|
||||
_lastTimeStampCount = timeStampCount;
|
||||
_lastTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
if (_cpuLoad.IsAvailable)
|
||||
{
|
||||
_cpuLoad.Update();
|
||||
|
||||
float maxLoad = 0;
|
||||
if (_threadLoads != null)
|
||||
{
|
||||
for (int i = 0; i < _threadLoads.Length; i++)
|
||||
{
|
||||
if (_threadLoads[i] != null)
|
||||
{
|
||||
_threadLoads[i].Value = _cpuLoad.GetThreadLoad(i);
|
||||
maxLoad = Math.Max(maxLoad, _threadLoads[i].Value ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_totalLoad != null)
|
||||
_totalLoad.Value = _cpuLoad.GetTotalLoad();
|
||||
|
||||
if (_maxLoad != null)
|
||||
_maxLoad.Value = maxLoad;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,731 @@
|
||||
// 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[] _coreVIDs;
|
||||
private readonly Sensor _coreVoltage;
|
||||
private readonly Sensor[] _distToTjMaxTemperatures;
|
||||
|
||||
private readonly uint[] _energyStatusMsrs = { MSR_PKG_ENERGY_STATUS, MSR_PP0_ENERGY_STATUS, MSR_PP1_ENERGY_STATUS, MSR_DRAM_ENERGY_STATUS, MSR_PLATFORM_ENERGY_STATUS };
|
||||
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 IntelCpu(int processorIndex, CpuId[][] cpuId, ISettings settings) : base(processorIndex, cpuId, settings)
|
||||
{
|
||||
uint eax;
|
||||
|
||||
// set tjMax
|
||||
float[] tjMax;
|
||||
switch (_family)
|
||||
{
|
||||
case 0x06:
|
||||
{
|
||||
switch (_model)
|
||||
{
|
||||
case 0x0F: // Intel Core 2 (65nm)
|
||||
_microArchitecture = MicroArchitecture.Core;
|
||||
tjMax = _stepping switch
|
||||
{
|
||||
// B2
|
||||
0x06 => _coreCount switch
|
||||
{
|
||||
2 => Floats(80 + 10),
|
||||
4 => Floats(90 + 10),
|
||||
_ => Floats(85 + 10)
|
||||
},
|
||||
// G0
|
||||
0x0B => Floats(90 + 10),
|
||||
// M0
|
||||
0x0D => Floats(85 + 10),
|
||||
_ => Floats(85 + 10)
|
||||
};
|
||||
break;
|
||||
|
||||
case 0x17: // Intel Core 2 (45nm)
|
||||
_microArchitecture = MicroArchitecture.Core;
|
||||
tjMax = Floats(100);
|
||||
break;
|
||||
|
||||
case 0x1C: // Intel Atom (45nm)
|
||||
_microArchitecture = MicroArchitecture.Atom;
|
||||
tjMax = _stepping switch
|
||||
{
|
||||
// C0
|
||||
0x02 => Floats(90),
|
||||
// A0, B0
|
||||
0x0A => Floats(100),
|
||||
_ => Floats(90)
|
||||
};
|
||||
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 (Intel 10 nm SuperFin, Gen. 11)
|
||||
case 0x8D:
|
||||
_microArchitecture = MicroArchitecture.TigerLake;
|
||||
tjMax = GetTjMaxFromMsr();
|
||||
break;
|
||||
|
||||
case 0x97: // Alder Lake (Intel 7 (10ESF), Gen. 12)
|
||||
case 0x9A: // Alder Lake-L (Intel 7 (10ESF), Gen. 12)
|
||||
case 0xBE: // Alder Lake-N (Intel 7 (10ESF), Gen. 12)
|
||||
_microArchitecture = MicroArchitecture.AlderLake;
|
||||
tjMax = GetTjMaxFromMsr();
|
||||
break;
|
||||
|
||||
case 0xB7: // Raptor Lake (Intel 7 (10ESF), Gen. 13)
|
||||
case 0xBA: // Raptor Lake-P (Intel 7 (10ESF), Gen. 13)
|
||||
case 0xBF: // Raptor Lake-N (Intel 7 (10ESF), Gen. 13)
|
||||
_microArchitecture = MicroArchitecture.RaptorLake;
|
||||
tjMax = GetTjMaxFromMsr();
|
||||
break;
|
||||
|
||||
case 0xAC: // Meteor Lake (Intel 4, TSMC N5/N6, Gen. 14)
|
||||
case 0xAA: // Meteor Lake-L (Intel 4, TSMC N5/N6, Gen. 14)
|
||||
_microArchitecture = MicroArchitecture.MeteorLake;
|
||||
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;
|
||||
|
||||
case 0xC6: // Intel Core Ultra 7 200 Series ArrowLake
|
||||
_microArchitecture = MicroArchitecture.ArrowLake;
|
||||
tjMax = GetTjMaxFromMsr();
|
||||
break;
|
||||
|
||||
case 0xBD: // Intel Core Ultra 5/7 200 Series LunarLake
|
||||
_microArchitecture = MicroArchitecture.LunarLake;
|
||||
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.ArrowLake:
|
||||
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.LunarLake:
|
||||
case MicroArchitecture.Nehalem:
|
||||
case MicroArchitecture.MeteorLake:
|
||||
case MicroArchitecture.RaptorLake:
|
||||
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 eax, out uint _))
|
||||
_timeStampCounterMultiplier = (eax >> 8) & 0xff;
|
||||
|
||||
break;
|
||||
default:
|
||||
_timeStampCounterMultiplier = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
int coreSensorId = 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);
|
||||
coreSensorId++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreMax = null;
|
||||
_coreAvg = null;
|
||||
}
|
||||
|
||||
// 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 = Array.Empty<Sensor>();
|
||||
|
||||
// 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 = Array.Empty<Sensor>();
|
||||
|
||||
_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 is MicroArchitecture.Airmont or
|
||||
MicroArchitecture.AlderLake or
|
||||
MicroArchitecture.ArrowLake or
|
||||
MicroArchitecture.Broadwell or
|
||||
MicroArchitecture.CannonLake or
|
||||
MicroArchitecture.CometLake or
|
||||
MicroArchitecture.Goldmont or
|
||||
MicroArchitecture.GoldmontPlus or
|
||||
MicroArchitecture.Haswell or
|
||||
MicroArchitecture.IceLake or
|
||||
MicroArchitecture.IvyBridge or
|
||||
MicroArchitecture.JasperLake or
|
||||
MicroArchitecture.KabyLake or
|
||||
MicroArchitecture.LunarLake or
|
||||
MicroArchitecture.MeteorLake or
|
||||
MicroArchitecture.RaptorLake or
|
||||
MicroArchitecture.RocketLake or
|
||||
MicroArchitecture.SandyBridge or
|
||||
MicroArchitecture.Silvermont or
|
||||
MicroArchitecture.Skylake or
|
||||
MicroArchitecture.TigerLake or
|
||||
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 eax, out uint _))
|
||||
{
|
||||
EnergyUnitsMultiplier = _microArchitecture switch
|
||||
{
|
||||
MicroArchitecture.Silvermont or MicroArchitecture.Airmont => 1.0e-6f * (1 << (int)((eax >> 8) & 0x1F)),
|
||||
_ => 1.0f / (1 << (int)((eax >> 8) & 0x1F))
|
||||
};
|
||||
}
|
||||
|
||||
if (EnergyUnitsMultiplier != 0)
|
||||
{
|
||||
string[] powerSensorLabels = { "CPU Package", "CPU Cores", "CPU Graphics", "CPU Memory", "CPU Platform" };
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Ring0.ReadMsr(IA32_PERF_STATUS, out eax, out uint _) && ((eax >> 32) & 0xFFFF) > 0)
|
||||
{
|
||||
_coreVoltage = new Sensor("CPU Core", 0, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_coreVoltage);
|
||||
}
|
||||
|
||||
_coreVIDs = new Sensor[_coreCount];
|
||||
for (int i = 0; i < _coreVIDs.Length; i++)
|
||||
{
|
||||
_coreVIDs[i] = new Sensor(CoreString(i), i + 1, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_coreVIDs[i]);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
public float EnergyUnitsMultiplier { get; }
|
||||
|
||||
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_ENERGY_STATUS,
|
||||
MSR_DRAM_ENERGY_STATUS,
|
||||
MSR_PP0_ENERGY_STATUS,
|
||||
MSR_PP1_ENERGY_STATUS,
|
||||
MSR_PLATFORM_ENERGY_STATUS,
|
||||
};
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
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;
|
||||
uint eax;
|
||||
|
||||
for (int i = 0; i < _coreTemperatures.Length; i++)
|
||||
{
|
||||
// if reading is valid
|
||||
if (Ring0.ReadMsr(IA32_THERM_STATUS_MSR, out eax, out _, _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 eax, out _, _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 eax, out _, _cpuId[i][0].Affinity))
|
||||
{
|
||||
newBusClock = TimeStampCounterFrequency / _timeStampCounterMultiplier;
|
||||
switch (_microArchitecture)
|
||||
{
|
||||
case MicroArchitecture.Nehalem:
|
||||
_coreClocks[i].Value = (float)((eax & 0xff) * newBusClock);
|
||||
break;
|
||||
case MicroArchitecture.Airmont:
|
||||
case MicroArchitecture.AlderLake:
|
||||
case MicroArchitecture.ArrowLake:
|
||||
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.LunarLake:
|
||||
case MicroArchitecture.MeteorLake:
|
||||
case MicroArchitecture.RaptorLake:
|
||||
case MicroArchitecture.RocketLake:
|
||||
case MicroArchitecture.SandyBridge:
|
||||
case MicroArchitecture.Silvermont:
|
||||
case MicroArchitecture.Skylake:
|
||||
case MicroArchitecture.TigerLake:
|
||||
case MicroArchitecture.Tremont:
|
||||
_coreClocks[i].Value = (float)(((eax >> 8) & 0xff) * newBusClock);
|
||||
break;
|
||||
default:
|
||||
_coreClocks[i].Value = (float)((((eax >> 8) & 0x1f) + (0.5 * ((eax >> 14) & 1))) * 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 eax, out _))
|
||||
continue;
|
||||
|
||||
DateTime time = DateTime.UtcNow;
|
||||
uint energyConsumed = eax;
|
||||
float deltaTime = (float)(time - _lastEnergyTime[sensor.Index]).TotalSeconds;
|
||||
if (deltaTime < 0.01)
|
||||
continue;
|
||||
|
||||
sensor.Value = EnergyUnitsMultiplier * unchecked(energyConsumed - _lastEnergyConsumed[sensor.Index]) / deltaTime;
|
||||
_lastEnergyTime[sensor.Index] = time;
|
||||
_lastEnergyConsumed[sensor.Index] = energyConsumed;
|
||||
}
|
||||
}
|
||||
|
||||
if (_coreVoltage != null && Ring0.ReadMsr(IA32_PERF_STATUS, out _, out uint edx))
|
||||
{
|
||||
_coreVoltage.Value = ((edx >> 32) & 0xFFFF) / (float)(1 << 13);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _coreVIDs.Length; i++)
|
||||
{
|
||||
if (Ring0.ReadMsr(IA32_PERF_STATUS, out _, out edx, _cpuId[i][0].Affinity) && ((edx >> 32) & 0xFFFF) > 0)
|
||||
{
|
||||
_coreVIDs[i].Value = ((edx >> 32) & 0xFFFF) / (float)(1 << 13);
|
||||
ActivateSensor(_coreVIDs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_coreVIDs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "IdentifierTypo")]
|
||||
private enum MicroArchitecture
|
||||
{
|
||||
Airmont,
|
||||
AlderLake,
|
||||
Atom,
|
||||
ArrowLake, // Gen. 15 (0xC6, -H = 0xC5)
|
||||
Broadwell,
|
||||
CannonLake,
|
||||
CometLake,
|
||||
Core,
|
||||
Goldmont,
|
||||
GoldmontPlus,
|
||||
Haswell,
|
||||
IceLake,
|
||||
IvyBridge,
|
||||
JasperLake,
|
||||
KabyLake,
|
||||
LunarLake,
|
||||
Nehalem,
|
||||
NetBurst,
|
||||
MeteorLake,
|
||||
RocketLake,
|
||||
SandyBridge,
|
||||
Silvermont,
|
||||
Skylake,
|
||||
TigerLake,
|
||||
Tremont,
|
||||
RaptorLake,
|
||||
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_ENERGY_STATUS = 0x611;
|
||||
private const uint MSR_PLATFORM_INFO = 0xCE;
|
||||
private const uint MSR_PP0_ENERGY_STATUS = 0x639;
|
||||
private const uint MSR_PP1_ENERGY_STATUS = 0x641;
|
||||
private const uint MSR_PLATFORM_ENERGY_STATUS = 0x64D;
|
||||
|
||||
private const uint MSR_RAPL_POWER_UNIT = 0x606;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
Reference in New Issue
Block a user