diff --git a/Hardware/CPU/AMD17CPU.cs b/Hardware/CPU/AMD17CPU.cs new file mode 100644 index 0000000..e9dd58f --- /dev/null +++ b/Hardware/CPU/AMD17CPU.cs @@ -0,0 +1,478 @@ +// 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) 2016-2017 Sebastian Grams +// Copyright (C) 2016-2017 Aqua Computer + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; +using System.Threading; + +namespace OpenHardwareMonitor.Hardware.CPU +{ + internal sealed class AMD17CPU : AMDCPU + { + // counter, to create sensor index values + private int _sensorTemperatures = 0; + private int _sensorPower = 0; + private int _sensorVoltage = 0; + private int _sensorClock = 0; + private int _sensorMulti = 0; + + // register index names for CPUID[] + private const int EAX = 0; + private const int EBX = 1; + private const int ECX = 2; + private const int EDX = 3; + + #region amd zen registers + private const uint PERF_CTL_0 = 0xC0010000; + private const uint PERF_CTR_0 = 0xC0010004; + private const uint HWCR = 0xC0010015; + + private const uint MSR_PSTATE_L = 0xC0010061; + private const uint MSR_PSTATE_C = 0xC0010062; + private const uint MSR_PSTATE_S = 0xC0010063; + private const uint MSR_PSTATE_0 = 0xC0010064; + + private const uint MSR_PWR_UNIT = 0xC0010299; + private const uint MSR_CORE_ENERGY_STAT = 0xC001029A; + private const uint MSR_PKG_ENERGY_STAT = 0xC001029B; + private const uint MSR_HARDWARE_PSTATE_STATUS = 0xC0010293; + private const uint COFVID_STATUS = 0xC0010071; + private const uint FAMILY_17H_PCI_CONTROL_REGISTER = 0x60; + private const uint FAMILY_17H_MODEL_01_MISC_CONTROL_DEVICE_ID = 0x1463; + private const uint F17H_M01H_THM_TCON_CUR_TMP = 0x00059800; + private const uint F17H_M01H_SVI = 0x0005A000; + #endregion + + #region Processor + private class Processor + { + private AMD17CPU _hw = null; + private DateTime _lastPwrTime = new DateTime(0); + private uint _lastPwrValue = 0; + private Sensor _packagePower = null; + private Sensor _coreTemperatureTctl = null; + private Sensor _coreTemperatureTdie = null; + private Sensor _coreVoltage = null; + private Sensor _socVoltage = null; + public List Nodes { get; private set; } + + public Processor(Hardware hw) + { + this._hw = (AMD17CPU)hw; + Nodes = new List(); + + _packagePower = new Sensor("Package Power", this._hw._sensorPower++, SensorType.Power, this._hw, this._hw.settings); + _coreTemperatureTctl = new Sensor("Core (Tctl)", this._hw._sensorTemperatures++, SensorType.Temperature, this._hw, this._hw.settings); + _coreTemperatureTdie = new Sensor("Core (Tdie)", this._hw._sensorTemperatures++, SensorType.Temperature, this._hw, this._hw.settings); + _coreVoltage = new Sensor("Core (SVI2)", this._hw._sensorVoltage++, SensorType.Voltage, this._hw, this._hw.settings); + _socVoltage = new Sensor("SoC (SVI2)", this._hw._sensorVoltage++, SensorType.Voltage, this._hw, this._hw.settings); + + _hw.ActivateSensor(_packagePower); + _hw.ActivateSensor(_coreTemperatureTctl); + _hw.ActivateSensor(_coreTemperatureTdie); + _hw.ActivateSensor(_coreVoltage); + } + + #region UpdateSensors + public void UpdateSensors() + { + var node = Nodes[0]; + if (node == null) + return; + Core core = node.Cores[0]; + if (core == null) + return; + CPUID cpu = core.Threads[0]; + if (cpu == null) + return; + uint eax, edx; + + ulong mask = Ring0.ThreadAffinitySet(1UL << cpu.Thread); + + // MSRC001_0299 + // TU [19:16] + // ESU [12:8] -> Unit 15.3 micro Joule per increment + // PU [3:0] + Ring0.Rdmsr(MSR_PWR_UNIT, out eax, out edx); + int tu = (int)((eax >> 16) & 0xf); + int esu = (int)((eax >> 12) & 0xf); + int pu = (int)(eax & 0xf); + + // MSRC001_029B + // total_energy [31:0] + DateTime sample_time = DateTime.Now; + Ring0.Rdmsr(MSR_PKG_ENERGY_STAT, out eax, out edx); + uint total_energy = eax; + + // THM_TCON_CUR_TMP + // CUR_TEMP [31:21] + uint temperature = 0; + Ring0.WritePciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_THM_TCON_CUR_TMP); + Ring0.ReadPciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER + 4, out temperature); + + // SVI0_TFN_PLANE0 [0] + // SVI0_TFN_PLANE1 [1] + uint smusvi0_tfn = 0; + Ring0.WritePciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_SVI + 0x8); + Ring0.ReadPciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smusvi0_tfn); + + // SVI0_PLANE0_VDDCOR [24:16] + // SVI0_PLANE0_IDDCOR [7:0] + uint smusvi0_tel_plane0 = 0; + Ring0.WritePciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_SVI + 0xc); + Ring0.ReadPciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smusvi0_tel_plane0); + + // SVI0_PLANE1_VDDCOR [24:16] + // SVI0_PLANE1_IDDCOR [7:0] + uint smusvi0_tel_plane1 = 0; + Ring0.WritePciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER, F17H_M01H_SVI + 0x10); + Ring0.ReadPciConfig(Ring0.GetPciAddress(0, 0, 0), FAMILY_17H_PCI_CONTROL_REGISTER + 4, out smusvi0_tel_plane1); + + Ring0.ThreadAffinitySet(mask); + + // power consumption + // power.Value = (float) ((double)pu * 0.125); + // esu = 15.3 micro Joule per increment + if (_lastPwrTime.Ticks == 0) + { + _lastPwrTime = sample_time; + _lastPwrValue = total_energy; + } + // ticks diff + TimeSpan time = sample_time - _lastPwrTime; + long pwr; + if (_lastPwrValue <= total_energy) + pwr = total_energy - _lastPwrValue; + else + pwr = (0xffffffff - _lastPwrValue) + total_energy; + + // update for next sample + _lastPwrTime = sample_time; + _lastPwrValue = total_energy; + + double energy = 15.3e-6 * pwr; + energy /= time.TotalSeconds; + + _packagePower.Value = (float)energy; + + // current temp Bit [31:21] + temperature = (temperature >> 21) * 125; + float offset = 0.0f; + if (cpu.Name != null && (cpu.Name.Contains("1600X") || cpu.Name.Contains("1700X") || cpu.Name.Contains("1800X"))) + offset = -20.0f; + else if (cpu.Name != null && (cpu.Name.Contains("1920X") || cpu.Name.Contains("1950X"))) + offset = -27.0f; + else if (cpu.Name != null && (cpu.Name.Contains("1910") || cpu.Name.Contains("1920"))) + offset = -10.0f; + + _coreTemperatureTctl.Value = (temperature * 0.001f); + _coreTemperatureTdie.Value = (temperature * 0.001f) + offset; + + // voltage + double VIDStep = 0.00625; + double vcc; + uint svi0_plane_x_vddcor; + uint svi0_plane_x_iddcor; + + //Core + if ((smusvi0_tfn & 0x01) == 0) + { + svi0_plane_x_vddcor = (smusvi0_tel_plane0 >> 16) & 0xff; + svi0_plane_x_iddcor = smusvi0_tel_plane0 & 0xff; + vcc = 1.550 - (double)VIDStep * svi0_plane_x_vddcor; + _coreVoltage.Value = (float)vcc; + } + + // SoC + // not every zen cpu has this voltage + if ((smusvi0_tfn & 0x02) == 0) + { + svi0_plane_x_vddcor = (smusvi0_tel_plane1 >> 16) & 0xff; + svi0_plane_x_iddcor = smusvi0_tel_plane1 & 0xff; + vcc = 1.550 - (double)VIDStep * svi0_plane_x_vddcor; + _socVoltage.Value = (float)vcc; + _hw.ActivateSensor(_socVoltage); + } + + } + #endregion + + public void AppendThread(CPUID thread, int numa_id, int core_id) + { + NumaNode node = null; + foreach (var n in Nodes) + { + if (n.NodeId == numa_id) + node = n; + } + if (node == null) + { + node = new NumaNode(_hw, numa_id); + Nodes.Add(node); + } + if (thread != null) + node.AppendThread(thread, core_id); + } + } + #endregion + + #region NumaNode + private class NumaNode + { + private AMD17CPU _hw = null; + public int NodeId { get; private set; } + public List Cores { get; private set; } + + public NumaNode(Hardware hw, int id) + { + Cores = new List(); + NodeId = id; + _hw = (AMD17CPU)hw; + } + + public void AppendThread(CPUID thread, int core_id) + { + Core core = null; + foreach (var c in Cores) + { + if (c.CoreId == core_id) + core = c; + } + if (core == null) + { + core = new Core(_hw, core_id); + Cores.Add(core); + } + if (thread != null) + core.Threads.Add(thread); + } + + #region UpdateSensors + public void UpdateSensors() + { + } + #endregion + } + #endregion + + #region Core + private class Core + { + private DateTime _lastPwrTime = new DateTime(0); + private uint _lastPwrValue = 0; + private AMD17CPU _hw = null; + private Sensor _clock = null; + private Sensor _vcore = null; + private Sensor _power = null; + private Sensor _multiplier = null; + public int CoreId { get; private set; } + public List Threads { get; private set; } + + public Core(Hardware hw, int id) + { + Threads = new List(); + CoreId = id; + _hw = (AMD17CPU)hw; + _clock = new Sensor("Core #" + CoreId.ToString(), _hw._sensorClock++, SensorType.Clock, _hw, _hw.settings); + _multiplier = new Sensor("Core #" + CoreId.ToString(), _hw._sensorMulti++, SensorType.Factor, _hw, _hw.settings); + _power = new Sensor("Core #" + CoreId.ToString() + " (SMU)", _hw._sensorPower++, SensorType.Power, _hw, _hw.settings); + _vcore = new Sensor("Core #" + CoreId.ToString() + " VID", _hw._sensorVoltage++, SensorType.Voltage, _hw, _hw.settings); + + _hw.ActivateSensor(_clock); + _hw.ActivateSensor(_multiplier); + _hw.ActivateSensor(_power); + _hw.ActivateSensor(_vcore); + } + + #region UpdateSensors + public void UpdateSensors() + { + // CPUID cpu = threads.FirstOrDefault(); + CPUID cpu = Threads[0]; + if (cpu == null) + return; + uint eax, edx; + ulong mask = Ring0.ThreadAffinitySet(1UL << cpu.Thread); + + // MSRC001_0299 + // TU [19:16] + // ESU [12:8] -> Unit 15.3 micro Joule per increment + // PU [3:0] + Ring0.Rdmsr(MSR_PWR_UNIT, out eax, out edx); + int tu = (int)((eax >> 16) & 0xf); + int esu = (int)((eax >> 12) & 0xf); + int pu = (int)(eax & 0xf); + + // MSRC001_029A + // total_energy [31:0] + DateTime sample_time = DateTime.Now; + Ring0.Rdmsr(MSR_CORE_ENERGY_STAT, out eax, out edx); + uint total_energy = eax; + + // MSRC001_0293 + // CurHwPstate [24:22] + // CurCpuVid [21:14] + // CurCpuDfsId [13:8] + // CurCpuFid [7:0] + Ring0.Rdmsr(MSR_HARDWARE_PSTATE_STATUS, out eax, out edx); + int CurHwPstate = (int)((eax >> 22) & 0x3); + 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.Rdmsr(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); + Ring0.ThreadAffinitySet(mask); + + // clock + // CoreCOF is (Core::X86::Msr::PStateDef[CpuFid[7:0]] / Core::X86::Msr::PStateDef[CpuDfsId]) * 200 + _clock.Value = (float)((double)CurCpuFid / (double)CurCpuDfsId * 200.0); + + // multiplier + _multiplier.Value = (float)((double)CurCpuFid / (double)CurCpuDfsId * 2.0); + + // Voltage + double VIDStep = 0.00625; + double vcc = 1.550 - (double)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 = sample_time; + _lastPwrValue = total_energy; + } + // ticks diff + TimeSpan time = sample_time - _lastPwrTime; + long pwr; + if (_lastPwrValue <= total_energy) + pwr = total_energy - _lastPwrValue; + else + pwr = (0xffffffff - _lastPwrValue) + total_energy; + + // update for next sample + _lastPwrTime = sample_time; + _lastPwrValue = total_energy; + + double energy = 15.3e-6 * pwr; + energy /= time.TotalSeconds; + + _power.Value = (float)energy; + } + #endregion + } + #endregion + + private Processor _ryzen = null; + + public AMD17CPU(int processorIndex, CPUID[][] cpuid, ISettings settings) + : base(processorIndex, cpuid, settings) + { + // add all numa nodes + // Register ..1E_ECX, [10:8] + 1 + _ryzen = new Processor(this); + int NodesPerProcessor = 1 + (int)((cpuid[0][0].ExtData[0x1e, ECX] >> 8) & 0x7); + + // add all numa nodes + foreach (CPUID[] cpu in cpuid) + { + CPUID thread = cpu[0]; + + // coreID + // Register ..1E_EBX, [7:0] + int core_id = (int)(thread.ExtData[0x1e, EBX] & 0xff); + + // nodeID + // Register ..1E_ECX, [7:0] + int node_id = (int)(thread.ExtData[0x1e, ECX] & 0xff); + + _ryzen.AppendThread(null, node_id, core_id); + } + + // add all threads to numa nodes and specific core + foreach (CPUID[] cpu in cpuid) + { + CPUID thread = cpu[0]; + + // coreID + // Register ..1E_EBX, [7:0] + int core_id = (int)(thread.ExtData[0x1e, EBX] & 0xff); + + // nodeID + // Register ..1E_ECX, [7:0] + int node_id = (int)(thread.ExtData[0x1e, ECX] & 0xff); + + _ryzen.AppendThread(thread, node_id, core_id); + } + Update(); + } + + protected override uint[] GetMSRs() + { + return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, MSR_PSTATE_0, COFVID_STATUS }; + } + + public override string GetReport() + { + StringBuilder r = new StringBuilder(); + r.Append(base.GetReport()); + r.Append("Ryzen"); + return r.ToString(); + } + + private string ReadFirstLine(Stream stream) + { + StringBuilder sb = new StringBuilder(); + try + { + stream.Seek(0, SeekOrigin.Begin); + int b = stream.ReadByte(); + while (b != -1 && b != 10) + { + sb.Append((char)b); + b = stream.ReadByte(); + } + } + catch { } + return sb.ToString(); + } + + public override void Update() + { + base.Update(); + + _ryzen.UpdateSensors(); + foreach (NumaNode node in _ryzen.Nodes) + { + node.UpdateSensors(); + + foreach (Core c in node.Cores) + { + c.UpdateSensors(); + } + } + } + + public override void Close() + { + base.Close(); + } + } +} diff --git a/Hardware/CPU/CPUGroup.cs b/Hardware/CPU/CPUGroup.cs index 98829d2..7373a46 100644 --- a/Hardware/CPU/CPUGroup.cs +++ b/Hardware/CPU/CPUGroup.cs @@ -102,9 +102,11 @@ namespace OpenHardwareMonitor.Hardware.CPU { case 0x14: case 0x15: case 0x16: - case 0x17: hardware.Add(new AMD10CPU(index, coreThreads, settings)); break; + case 0x17: + hardware.Add(new AMD17CPU(index, coreThreads, settings)); + break; default: hardware.Add(new GenericCPU(index, coreThreads, settings)); break; diff --git a/Hardware/CPU/CPUID.cs b/Hardware/CPU/CPUID.cs index e418dc4..b219b6c 100644 --- a/Hardware/CPU/CPUID.cs +++ b/Hardware/CPU/CPUID.cs @@ -186,6 +186,27 @@ namespace OpenHardwareMonitor.Hardware.CPU { corePerPackage = 1; threadMaskWith = 0; coreMaskWith = NextLog2(corePerPackage); + + if (this.family == 0x17) + { + // 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) + uint max_cores_per_die = (cpuidExtData[8, 2] >> 12) & 0xF; + switch (max_cores_per_die) + { + case 0x04: // Ryzen + coreMaskWith = NextLog2(16); + break; + case 0x05:// Threadripper + coreMaskWith = NextLog2(32); + break; + case 0x06:// Epic + coreMaskWith = NextLog2(64); + break; + } + } break; default: threadMaskWith = 0; diff --git a/Hardware/Ring0.cs b/Hardware/Ring0.cs index 9f5a44c..c506c69 100644 --- a/Hardware/Ring0.cs +++ b/Hardware/Ring0.cs @@ -239,6 +239,10 @@ namespace OpenHardwareMonitor.Hardware { catch (UnauthorizedAccessException) { } } } + public static ulong ThreadAffinitySet(ulong mask) + { + return ThreadAffinity.Set(mask); + } public static string GetReport() { if (report.Length > 0) { diff --git a/OpenHardwareMonitorLib.csproj b/OpenHardwareMonitorLib.csproj index 9eb1e8d..fd6bf2f 100644 --- a/OpenHardwareMonitorLib.csproj +++ b/OpenHardwareMonitorLib.csproj @@ -62,6 +62,7 @@ +