mirror of
https://github.com/openhardwaremonitor/openhardwaremonitor
synced 2025-08-29 05:18:14 +00:00
353 lines
12 KiB
C#
353 lines
12 KiB
C#
/*
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public
|
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
Copyright (C) 2020 Michael Möller <mmoeller@openhardwaremonitor.org>
|
|
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
|
|
namespace OpenHardwareMonitor.Hardware.CPU {
|
|
|
|
internal sealed class AMD17CPU : AMDCPU {
|
|
|
|
private readonly Core[] cores;
|
|
|
|
private readonly Sensor coreTemperature;
|
|
private readonly Sensor tctlTemperature;
|
|
private readonly Sensor ccdMaxTemperature;
|
|
private readonly Sensor ccdAvgTemperature;
|
|
private readonly Sensor[] ccdTemperatures;
|
|
private readonly Sensor packagePowerSensor;
|
|
private readonly Sensor coresPowerSensor;
|
|
private readonly Sensor busClock;
|
|
|
|
private const uint FAMILY_17H_M01H_THM_TCON_TEMP = 0x00059800;
|
|
private const uint FAMILY_17H_M01H_THM_TCON_TEMP_RANGE_SEL = 0x80000;
|
|
private uint FAMILY_17H_M70H_CCD_TEMP(uint i) { return 0x00059954 + i * 4; }
|
|
private const uint FAMILY_17H_M70H_CCD_TEMP_VALID = 0x800;
|
|
private uint maxCcdCount;
|
|
|
|
private const uint MSR_RAPL_PWR_UNIT = 0xC0010299;
|
|
private const uint MSR_CORE_ENERGY_STAT = 0xC001029A;
|
|
private const uint MSR_PKG_ENERGY_STAT = 0xC001029B;
|
|
private const uint MSR_P_STATE_0 = 0xC0010064;
|
|
private const uint MSR_FAMILY_17H_P_STATE = 0xc0010293;
|
|
|
|
private float energyUnitMultiplier = 0;
|
|
private uint lastEnergyConsumed;
|
|
private DateTime lastEnergyTime;
|
|
|
|
private readonly double timeStampCounterMultiplier;
|
|
|
|
private struct TctlOffsetItem {
|
|
public string Name { get; set; }
|
|
public float Offset { get; set; }
|
|
}
|
|
private IEnumerable<TctlOffsetItem> tctlOffsetItems = new[] {
|
|
new TctlOffsetItem { Name = "AMD Ryzen 5 1600X", Offset = 20.0f },
|
|
new TctlOffsetItem { Name = "AMD Ryzen 7 1700X", Offset = 20.0f },
|
|
new TctlOffsetItem { Name = "AMD Ryzen 7 1800X", Offset = 20.0f },
|
|
new TctlOffsetItem { Name = "AMD Ryzen 7 2700X", Offset = 10.0f },
|
|
new TctlOffsetItem { Name = "AMD Ryzen Threadripper 19", Offset = 27.0f },
|
|
new TctlOffsetItem { Name = "AMD Ryzen Threadripper 29", Offset = 27.0f }
|
|
};
|
|
private readonly float tctlOffset = 0.0f;
|
|
|
|
public AMD17CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
|
|
: base(processorIndex, cpuid, settings)
|
|
{
|
|
string cpuName = cpuid[0][0].BrandString;
|
|
if (!string.IsNullOrEmpty(cpuName)) {
|
|
foreach (var item in tctlOffsetItems) {
|
|
if (cpuName.StartsWith(item.Name)) {
|
|
tctlOffset = item.Offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
coreTemperature = new Sensor(
|
|
"CPU Package", 0, SensorType.Temperature, this, new[] {
|
|
new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
|
|
}, this.settings);
|
|
|
|
if (tctlOffset != 0.0f)
|
|
tctlTemperature = new Sensor(
|
|
"CPU Tctl", 1, true, SensorType.Temperature, this, new[] {
|
|
new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
|
|
}, this.settings);
|
|
|
|
ccdMaxTemperature = new Sensor(
|
|
"CPU CCD Max", 2, SensorType.Temperature, this, this.settings);
|
|
|
|
ccdAvgTemperature = new Sensor(
|
|
"CPU CCD Average", 3, SensorType.Temperature, this, this.settings);
|
|
|
|
switch (model & 0xf0) {
|
|
case 0x30:
|
|
case 0x70:
|
|
maxCcdCount = 8; break;
|
|
default:
|
|
maxCcdCount = 4; break;
|
|
}
|
|
|
|
ccdTemperatures = new Sensor[maxCcdCount];
|
|
for (int i = 0; i < ccdTemperatures.Length; i++) {
|
|
ccdTemperatures[i] = new Sensor(
|
|
"CPU CCD #" + (i + 1), i + 4, SensorType.Temperature, this,
|
|
new[] {
|
|
new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
|
|
}, this.settings);
|
|
}
|
|
|
|
if (Ring0.Rdmsr(MSR_RAPL_PWR_UNIT, out uint eax, out _)) {
|
|
energyUnitMultiplier = 1.0f / (1 << (int)((eax >> 8) & 0x1F));
|
|
}
|
|
|
|
if (energyUnitMultiplier != 0) {
|
|
if (Ring0.Rdmsr(MSR_PKG_ENERGY_STAT, out uint energyConsumed, out _)) {
|
|
lastEnergyTime = DateTime.UtcNow;
|
|
lastEnergyConsumed = energyConsumed;
|
|
packagePowerSensor = new Sensor(
|
|
"CPU Package", 0, SensorType.Power, this, settings);
|
|
ActivateSensor(packagePowerSensor);
|
|
}
|
|
}
|
|
coresPowerSensor = new Sensor("CPU Cores", 1, SensorType.Power, this,
|
|
settings);
|
|
|
|
busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
|
|
timeStampCounterMultiplier = GetTimeStampCounterMultiplier();
|
|
if (timeStampCounterMultiplier > 0) {
|
|
busClock.Value = (float)(TimeStampCounterFrequency /
|
|
timeStampCounterMultiplier);
|
|
ActivateSensor(busClock);
|
|
}
|
|
|
|
this.cores = new Core[coreCount];
|
|
for (int i = 0; i < this.cores.Length; i++) {
|
|
this.cores[i] = new Core(i, cpuid[i], this, settings);
|
|
}
|
|
}
|
|
|
|
protected override uint[] GetMSRs() {
|
|
return new uint[] { MSR_P_STATE_0, MSR_FAMILY_17H_P_STATE,
|
|
MSR_RAPL_PWR_UNIT, MSR_CORE_ENERGY_STAT, MSR_PKG_ENERGY_STAT };
|
|
}
|
|
|
|
private IList<uint> GetSmnRegisters() {
|
|
var registers = new List<uint>();
|
|
registers.Add(FAMILY_17H_M01H_THM_TCON_TEMP);
|
|
for (uint i = 0; i < maxCcdCount; i++) {
|
|
registers.Add(FAMILY_17H_M70H_CCD_TEMP(i));
|
|
}
|
|
return registers;
|
|
}
|
|
|
|
public override string GetReport() {
|
|
StringBuilder r = new StringBuilder();
|
|
r.Append(base.GetReport());
|
|
|
|
r.Append("Time Stamp Counter Multiplier: ");
|
|
r.AppendLine(timeStampCounterMultiplier.ToString(
|
|
CultureInfo.InvariantCulture));
|
|
r.AppendLine();
|
|
|
|
r.AppendLine("SMN Registers");
|
|
r.AppendLine();
|
|
r.AppendLine(" Register Value");
|
|
var registers = GetSmnRegisters();
|
|
for (int i = 0; i < registers.Count; i++)
|
|
if (ReadSmnRegister(registers[i], out uint value)) {
|
|
r.Append(" ");
|
|
r.Append(registers[i].ToString("X8", CultureInfo.InvariantCulture));
|
|
r.Append(" ");
|
|
r.Append(value.ToString("X8", CultureInfo.InvariantCulture));
|
|
r.AppendLine();
|
|
}
|
|
r.AppendLine();
|
|
|
|
return r.ToString();
|
|
}
|
|
|
|
private double GetTimeStampCounterMultiplier() {
|
|
Ring0.Rdmsr(MSR_P_STATE_0, out uint eax, out _);
|
|
uint cpuDfsId = (eax >> 8) & 0x3f;
|
|
uint cpuFid = eax & 0xff;
|
|
return 2.0 * cpuFid / cpuDfsId;
|
|
}
|
|
|
|
private bool ReadSmnRegister(uint address, out uint value) {
|
|
if (!Ring0.WritePciConfig(0, 0x60, address)) {
|
|
value = 0;
|
|
return false;
|
|
}
|
|
return Ring0.ReadPciConfig(0, 0x64, out value);
|
|
}
|
|
|
|
public override void Update() {
|
|
base.Update();
|
|
|
|
uint value;
|
|
if (ReadSmnRegister(FAMILY_17H_M01H_THM_TCON_TEMP, out value)) {
|
|
float temperature = ((value >> 21) & 0x7FF) / 8.0f;
|
|
if ((value & FAMILY_17H_M01H_THM_TCON_TEMP_RANGE_SEL) != 0)
|
|
temperature -= 49;
|
|
|
|
if (tctlTemperature != null) {
|
|
tctlTemperature.Value = temperature +
|
|
tctlTemperature.Parameters[0].Value;
|
|
ActivateSensor(tctlTemperature);
|
|
}
|
|
|
|
temperature -= tctlOffset;
|
|
|
|
coreTemperature.Value = temperature +
|
|
coreTemperature.Parameters[0].Value;
|
|
ActivateSensor(coreTemperature);
|
|
}
|
|
|
|
float maxTemperature = float.MinValue;
|
|
int ccdCount = 0;
|
|
float ccdTemperatureSum = 0;
|
|
for (uint i = 0; i < ccdTemperatures.Length; i++) {
|
|
if (ReadSmnRegister(FAMILY_17H_M70H_CCD_TEMP(i), out value)) {
|
|
if ((value & FAMILY_17H_M70H_CCD_TEMP_VALID) == 0)
|
|
continue;
|
|
|
|
float temperature = (value & 0x7FF) / 8.0f - 49;
|
|
temperature += ccdTemperatures[i].Parameters[0].Value;
|
|
|
|
if (temperature > maxTemperature)
|
|
maxTemperature = temperature;
|
|
ccdCount++;
|
|
ccdTemperatureSum += temperature;
|
|
|
|
ccdTemperatures[i].Value = temperature;
|
|
ActivateSensor(ccdTemperatures[i]);
|
|
}
|
|
}
|
|
|
|
if (ccdCount > 1) {
|
|
ccdMaxTemperature.Value = maxTemperature;
|
|
ActivateSensor(ccdMaxTemperature);
|
|
|
|
ccdAvgTemperature.Value = ccdTemperatureSum / ccdCount;
|
|
ActivateSensor(ccdAvgTemperature);
|
|
}
|
|
|
|
if (energyUnitMultiplier != 0 &&
|
|
Ring0.Rdmsr(MSR_PKG_ENERGY_STAT, out uint energyConsumed, out _))
|
|
{
|
|
DateTime time = DateTime.UtcNow;
|
|
float deltaTime = (float)(time - lastEnergyTime).TotalSeconds;
|
|
if (deltaTime > 0.01) {
|
|
|
|
packagePowerSensor.Value = energyUnitMultiplier * unchecked(
|
|
energyConsumed - lastEnergyConsumed) / deltaTime;
|
|
lastEnergyTime = time;
|
|
lastEnergyConsumed = energyConsumed;
|
|
}
|
|
}
|
|
|
|
float? coresPower = 0f;
|
|
for (int i = 0; i < cores.Length; i++) {
|
|
cores[i].Update();
|
|
coresPower += cores[i].Power;
|
|
}
|
|
coresPowerSensor.Value = coresPower;
|
|
|
|
if (coresPower.HasValue) {
|
|
ActivateSensor(coresPowerSensor);
|
|
}
|
|
}
|
|
|
|
private class Core {
|
|
|
|
private readonly AMD17CPU cpu;
|
|
private readonly GroupAffinity affinity;
|
|
|
|
private readonly Sensor powerSensor;
|
|
private readonly Sensor clockSensor;
|
|
|
|
private DateTime lastEnergyTime;
|
|
private uint lastEnergyConsumed;
|
|
private float? power = null;
|
|
|
|
public Core(int index, CPUID[] threads, AMD17CPU cpu, ISettings settings)
|
|
{
|
|
this.cpu = cpu;
|
|
this.affinity = threads[0].Affinity;
|
|
|
|
string coreString = cpu.CoreString(index);
|
|
this.powerSensor =
|
|
new Sensor(coreString, index + 2, SensorType.Power, cpu, settings);
|
|
this.clockSensor =
|
|
new Sensor(coreString, index + 1, SensorType.Clock, cpu, settings);
|
|
|
|
if (cpu.energyUnitMultiplier != 0) {
|
|
if (Ring0.RdmsrTx(MSR_CORE_ENERGY_STAT, out uint energyConsumed,
|
|
out _, affinity))
|
|
{
|
|
lastEnergyTime = DateTime.UtcNow;
|
|
lastEnergyConsumed = energyConsumed;
|
|
cpu.ActivateSensor(powerSensor);
|
|
}
|
|
}
|
|
}
|
|
|
|
private double? GetMultiplier() {
|
|
if (Ring0.Rdmsr(MSR_FAMILY_17H_P_STATE, out uint eax, out _)) {
|
|
uint cpuDfsId = (eax >> 8) & 0x3f;
|
|
uint cpuFid = eax & 0xff;
|
|
return 2.0 * cpuFid / cpuDfsId;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public float? Power { get { return power; } }
|
|
|
|
public void Update() {
|
|
DateTime energyTime = DateTime.MinValue;
|
|
double? multiplier = null;
|
|
|
|
var previousAffinity = ThreadAffinity.Set(affinity);
|
|
if (Ring0.Rdmsr(MSR_CORE_ENERGY_STAT, out uint energyConsumed, out _)) {
|
|
energyTime = DateTime.UtcNow;
|
|
}
|
|
|
|
multiplier = GetMultiplier();
|
|
ThreadAffinity.Set(previousAffinity);
|
|
|
|
if (cpu.energyUnitMultiplier != 0) {
|
|
float deltaTime = (float)(energyTime - lastEnergyTime).TotalSeconds;
|
|
if (deltaTime > 0.01) {
|
|
power = cpu.energyUnitMultiplier *
|
|
unchecked(energyConsumed - lastEnergyConsumed) / deltaTime;
|
|
powerSensor.Value = power;
|
|
lastEnergyTime = energyTime;
|
|
lastEnergyConsumed = energyConsumed;
|
|
}
|
|
}
|
|
|
|
if (multiplier.HasValue) {
|
|
float? clock = (float?)(multiplier * cpu.busClock.Value);
|
|
clockSensor.Value = clock;
|
|
if (clock.HasValue)
|
|
cpu.ActivateSensor(clockSensor);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|