+2
-2
@@ -28,8 +28,8 @@ csharp_new_line_between_query_expression_clauses = true
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_case_contents = false
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 639 B |
@@ -13,16 +13,21 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
internal sealed class Amd17Cpu : AmdCpu
|
||||
{
|
||||
private readonly Processor _processor;
|
||||
private int _sensorClock;
|
||||
private int _sensorMulti;
|
||||
private int _sensorPower;
|
||||
|
||||
// counter, to create sensor index values
|
||||
private int _sensorTemperatures;
|
||||
private int _sensorVoltage;
|
||||
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);
|
||||
@@ -51,6 +56,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
{
|
||||
coreId++;
|
||||
}
|
||||
|
||||
lastCoreId = coreIdRead;
|
||||
|
||||
_processor.AppendThread(thread, nodeId, coreId);
|
||||
@@ -66,9 +72,9 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new StringBuilder();
|
||||
StringBuilder r = new();
|
||||
r.Append(base.GetReport());
|
||||
r.Append("Ryzen");
|
||||
r.Append(_smu.GetReport());
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
@@ -77,6 +83,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
base.Update();
|
||||
|
||||
_processor.UpdateSensors();
|
||||
|
||||
foreach (NumaNode node in _processor.Nodes)
|
||||
{
|
||||
NumaNode.UpdateSensors();
|
||||
@@ -90,37 +97,44 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
|
||||
private class Processor
|
||||
{
|
||||
private readonly Amd17Cpu _cpu;
|
||||
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 DateTime(0);
|
||||
private DateTime _lastPwrTime = new(0);
|
||||
private uint _lastPwrValue;
|
||||
|
||||
public Processor(Hardware hardware)
|
||||
{
|
||||
_cpu = (Amd17Cpu)hardware;
|
||||
|
||||
_packagePower = new Sensor("Package Power", _cpu._sensorPower++, SensorType.Power, _cpu, _cpu._settings);
|
||||
_coreTemperatureTctl = new Sensor("Core (Tctl)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_coreTemperatureTdie = new Sensor("Core (Tdie)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_coreTemperatureTctlTdie = new Sensor("Core (Tctl/Tdie)", _cpu._sensorTemperatures++, SensorType.Temperature, _cpu, _cpu._settings);
|
||||
_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._sensorVoltage++, SensorType.Voltage, _cpu, _cpu._settings);
|
||||
_socVoltage = new Sensor("SoC (SVI2 TFN)", _cpu._sensorVoltage++, SensorType.Voltage, _cpu, _cpu._settings);
|
||||
_busClock = new Sensor("Bus Speed", _cpu._sensorClock++, SensorType.Clock, _cpu, _cpu._settings);
|
||||
_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 List<NumaNode>();
|
||||
public List<NumaNode> Nodes { get; } = new();
|
||||
|
||||
public void UpdateSensors()
|
||||
{
|
||||
@@ -279,16 +293,17 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
|
||||
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 (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._sensorTemperatures++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
_ccdTemperatures[i].Value = ccdTemp;
|
||||
}
|
||||
}
|
||||
@@ -301,25 +316,26 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
if (_ccdsMaxTemperature == null)
|
||||
{
|
||||
_cpu.ActivateSensor(_ccdsMaxTemperature = new Sensor("CCDs Max (Tdie)",
|
||||
_cpu._sensorTemperatures++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
if (_ccdsAverageTemperature == null)
|
||||
{
|
||||
_cpu.ActivateSensor(_ccdsAverageTemperature = new Sensor("CCDs Average (Tdie)",
|
||||
_cpu._sensorTemperatures++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
_cpu._sensorTypeIndex[SensorType.Temperature]++,
|
||||
SensorType.Temperature,
|
||||
_cpu,
|
||||
_cpu._settings));
|
||||
}
|
||||
|
||||
_ccdsMaxTemperature.Value = activeCcds.Max(x => x.Value);
|
||||
_ccdsAverageTemperature.Value = activeCcds.Average(x => x.Value);
|
||||
}
|
||||
}
|
||||
|
||||
Ring0.ReleasePciBusMutex();
|
||||
}
|
||||
|
||||
@@ -354,6 +370,21 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
_busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier);
|
||||
_cpu.ActivateSensor(_busClock);
|
||||
}
|
||||
|
||||
if (_cpu._smu.IsPmTableLayoutDefined())
|
||||
{
|
||||
float[] smuData = _cpu._smu.GetPmTable();
|
||||
|
||||
foreach (var 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()
|
||||
@@ -427,13 +458,13 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
|
||||
private class Core
|
||||
{
|
||||
private readonly Amd17Cpu _cpu;
|
||||
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 DateTime(0);
|
||||
private DateTime _lastPwrTime = new(0);
|
||||
private uint _lastPwrValue;
|
||||
|
||||
public Core(Amd17Cpu cpu, int id)
|
||||
@@ -441,10 +472,10 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
_cpu = cpu;
|
||||
Threads = new List<CpuId>();
|
||||
CoreId = id;
|
||||
_clock = new Sensor("Core #" + CoreId, cpu._sensorClock++, SensorType.Clock, cpu, cpu._settings);
|
||||
_multiplier = new Sensor("Core #" + CoreId, cpu._sensorMulti++, SensorType.Factor, cpu, cpu._settings);
|
||||
_power = new Sensor("Core #" + CoreId + " (SMU)", cpu._sensorPower++, SensorType.Power, cpu, cpu._settings);
|
||||
_vcore = new Sensor("Core #" + CoreId + " VID", cpu._sensorVoltage++, SensorType.Voltage, cpu, cpu._settings);
|
||||
_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);
|
||||
@@ -506,7 +537,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
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 = (double)(_busSpeed.Value * 2);
|
||||
|
||||
_clock.Value = (float)(curCpuFid / (double)curCpuDfsId * clock);
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
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)
|
||||
{
|
||||
@@ -240,6 +241,8 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
|
||||
public Vendor Vendor { get; } = Vendor.Unknown;
|
||||
|
||||
public uint PkgType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="CpuId" />.
|
||||
/// </summary>
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
protected readonly uint _model;
|
||||
protected readonly int _processorIndex;
|
||||
protected readonly uint _stepping;
|
||||
protected readonly uint _packageType;
|
||||
|
||||
private readonly Sensor[] _coreLoads;
|
||||
private readonly CpuLoad _cpuLoad;
|
||||
@@ -39,6 +40,7 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
_family = cpuId[0][0].Family;
|
||||
_model = cpuId[0][0].Model;
|
||||
_stepping = cpuId[0][0].Stepping;
|
||||
_packageType = cpuId[0][0].PkgType;
|
||||
|
||||
_processorIndex = processorIndex;
|
||||
_coreCount = cpuId.Length;
|
||||
@@ -71,10 +73,14 @@ namespace LibreHardwareMonitor.Hardware.CPU
|
||||
if (_cpuLoad.IsAvailable)
|
||||
{
|
||||
foreach (Sensor sensor in _coreLoads)
|
||||
{
|
||||
ActivateSensor(sensor);
|
||||
}
|
||||
|
||||
if (_totalLoad != null)
|
||||
{
|
||||
ActivateSensor(_totalLoad);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasTimeStampCounter)
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace LibreHardwareMonitor.Hardware
|
||||
{
|
||||
Voltage, // V
|
||||
Current, // A
|
||||
Power, // W
|
||||
Clock, // MHz
|
||||
Temperature, // °C
|
||||
Load, // %
|
||||
@@ -25,7 +26,6 @@ namespace LibreHardwareMonitor.Hardware
|
||||
Control, // %
|
||||
Level, // %
|
||||
Factor, // 1
|
||||
Power, // W
|
||||
Data, // GB = 2^30 Bytes
|
||||
SmallData, // MB = 2^20 Bytes
|
||||
Throughput // B/s
|
||||
|
||||
@@ -137,6 +137,26 @@ namespace LibreHardwareMonitor.Hardware
|
||||
return b;
|
||||
}
|
||||
|
||||
public bool DeviceIOControl<T>(Kernel32.IOControlCode ioControlCode, object inBuffer, ref T[] outBuffer)
|
||||
{
|
||||
if (_device == null)
|
||||
return false;
|
||||
|
||||
|
||||
object boxedOutBuffer = outBuffer;
|
||||
bool b = Kernel32.DeviceIoControl(_device,
|
||||
ioControlCode,
|
||||
inBuffer,
|
||||
inBuffer == null ? 0 : (uint)Marshal.SizeOf(inBuffer),
|
||||
boxedOutBuffer,
|
||||
(uint)(Marshal.SizeOf(typeof(T)) * outBuffer.Length),
|
||||
out uint _,
|
||||
IntPtr.Zero);
|
||||
|
||||
outBuffer = (T[])boxedOutBuffer;
|
||||
return b;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_device != null)
|
||||
|
||||
@@ -466,6 +466,15 @@ namespace LibreHardwareMonitor.Hardware
|
||||
return _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_READ_MEMORY, input, ref buffer);
|
||||
}
|
||||
|
||||
public static bool ReadMemory<T>(ulong address, ref T[] buffer)
|
||||
{
|
||||
if (_driver == null)
|
||||
return false;
|
||||
|
||||
ReadMemoryInput input = new ReadMemoryInput { Address = address, UnitSize = (uint)Marshal.SizeOf(typeof(T)), Count = (uint)buffer.Length };
|
||||
return _driver.DeviceIOControl(Interop.Ring0.IOCTL_OLS_READ_MEMORY, input, ref buffer);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct WriteMsrInput
|
||||
{
|
||||
|
||||
@@ -0,0 +1,819 @@
|
||||
// ported from: https://gitlab.com/leogx9r/ryzen_smu
|
||||
// and: https://github.com/irusanov/SMUDebugTool
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware
|
||||
{
|
||||
internal class RyzenSMU
|
||||
{
|
||||
private const byte SMU_PCI_ADDR_REG = 0xC4;
|
||||
private const byte SMU_PCI_DATA_REG = 0xC8;
|
||||
private const uint SMU_REQ_MAX_ARGS = 6;
|
||||
private const uint SMU_RETRIES_MAX = 8096;
|
||||
|
||||
private readonly CpuCodeName _cpuCodeName;
|
||||
private readonly Mutex _mutex = new();
|
||||
private readonly bool _supportedCPU;
|
||||
|
||||
private readonly Dictionary<uint, Dictionary<uint, SmuSensorType>> _supportedPmTableVersions = new()
|
||||
{
|
||||
{
|
||||
// Zen Raven Ridge APU.
|
||||
0x001E0004, new Dictionary<uint, SmuSensorType>
|
||||
{
|
||||
{ 28, new SmuSensorType { Name = "TDC", Type = SensorType.Current, Scale = 1}},
|
||||
{ 44, new SmuSensorType { Name = "EDC", Type = SensorType.Current, Scale = 1}},
|
||||
//{ 61, new SmuSensorType { Name = "Core", Type = SensorType.Voltage } },
|
||||
//{ 62, new SmuSensorType { Name = "Core", Type = SensorType.Current, Scale = 1} },
|
||||
//{ 63, new SmuSensorType { Name = "Core", Type = SensorType.Power, Scale = 1 } },
|
||||
//{ 65, new SmuSensorType { Name = "SoC", Type = SensorType.Voltage } },
|
||||
{ 66, new SmuSensorType { Name = "SoC", Type = SensorType.Current, Scale = 1 } },
|
||||
{ 67, new SmuSensorType { Name = "SoC", Type = SensorType.Power, Scale = 1 } },
|
||||
//{ 96, new SmuSensorType { Name = "Core #1", Type = SensorType.Power } },
|
||||
//{ 97, new SmuSensorType { Name = "Core #2", Type = SensorType.Power } },
|
||||
//{ 98, new SmuSensorType { Name = "Core #3", Type = SensorType.Power } },
|
||||
//{ 99, new SmuSensorType { Name = "Core #4", Type = SensorType.Power } },
|
||||
{ 108, new SmuSensorType { Name = "Core #1", Type = SensorType.Temperature, Scale = 1 } },
|
||||
{ 109, new SmuSensorType { Name = "Core #2", Type = SensorType.Temperature, Scale = 1 } },
|
||||
{ 110, new SmuSensorType { Name = "Core #3", Type = SensorType.Temperature, Scale = 1 } },
|
||||
{ 111, new SmuSensorType { Name = "Core #4", Type = SensorType.Temperature, Scale = 1 } },
|
||||
{ 150, new SmuSensorType { Name = "GFX", Type = SensorType.Voltage, Scale = 1 } },
|
||||
{ 151, new SmuSensorType { Name = "GFX", Type = SensorType.Temperature, Scale = 1 } },
|
||||
{ 154, new SmuSensorType { Name = "GFX", Type = SensorType.Clock, Scale = 1 } },
|
||||
{ 156, new SmuSensorType { Name = "GFX", Type = SensorType.Load, Scale = 1 } },
|
||||
{ 166, new SmuSensorType { Name = "Fabric", Type = SensorType.Clock, Scale = 1 } },
|
||||
{ 177, new SmuSensorType { Name = "Uncore", Type = SensorType.Clock, Scale = 1 } },
|
||||
{ 178, new SmuSensorType { Name = "Memory", Type = SensorType.Clock, Scale = 1 } },
|
||||
{ 342, new SmuSensorType { Name = "Displays", Type = SensorType.Factor, Scale = 1 } },
|
||||
}
|
||||
},
|
||||
{
|
||||
// Zen 2.
|
||||
0x00240903, new Dictionary<uint, SmuSensorType>
|
||||
{
|
||||
{ 15, new SmuSensorType { Name = "TDC", Type = SensorType.Current, Scale = 1}},
|
||||
{ 21, new SmuSensorType { Name = "EDC", Type = SensorType.Current, Scale = 1}},
|
||||
{ 48, new SmuSensorType { Name = "Fabric", Type = SensorType.Clock, Scale = 1} },
|
||||
{ 50, new SmuSensorType { Name = "Uncore", Type = SensorType.Clock, Scale = 1} },
|
||||
{ 51, new SmuSensorType { Name = "Memory", Type = SensorType.Clock, Scale = 1} },
|
||||
{ 115, new SmuSensorType { Name = "SoC", Type = SensorType.Temperature, Scale = 1} },
|
||||
//{ 66, new SmuSensorType { Name = "Bus Speed", Type = SensorType.Clock, Scale = 1 } },
|
||||
//{ 188, new SmuSensorType { Name = "Core #1", Type = SensorType.Clock, Scale = 1000 } },
|
||||
//{ 189, new SmuSensorType { Name = "Core #2", Type = SensorType.Clock, Scale = 1000 } },
|
||||
//{ 190, new SmuSensorType { Name = "Core #3", Type = SensorType.Clock, Scale = 1000 } },
|
||||
//{ 191, new SmuSensorType { Name = "Core #4", Type = SensorType.Clock, Scale = 1000 } },
|
||||
//{ 192, new SmuSensorType { Name = "Core #5", Type = SensorType.Clock, Scale = 1000 } },
|
||||
//{ 193, new SmuSensorType { Name = "Core #6", Type = SensorType.Clock, Scale = 1000 } },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private uint _argsAddr;
|
||||
private uint _cmdAddr;
|
||||
private uint _dramBaseAddr;
|
||||
private uint _pmTableSize;
|
||||
private uint _pmTableSizeAlt;
|
||||
private uint _pmTableVersion;
|
||||
private uint _rspAddr;
|
||||
|
||||
public RyzenSMU(uint family, uint model, uint packageType)
|
||||
{
|
||||
_cpuCodeName = GetCpuCodeName(family, model, packageType);
|
||||
|
||||
_supportedCPU = SetAddresses(_cpuCodeName);
|
||||
|
||||
SetupPmTableAddrAndSize();
|
||||
}
|
||||
|
||||
private static CpuCodeName GetCpuCodeName(uint family, uint model, uint packageType)
|
||||
{
|
||||
if (family == 0x17)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case 0x01:
|
||||
{
|
||||
return packageType == 7 ? CpuCodeName.Threadripper : CpuCodeName.SummitRidge;
|
||||
}
|
||||
case 0x08:
|
||||
{
|
||||
return packageType == 7 ? CpuCodeName.Colfax : CpuCodeName.PinnacleRidge;
|
||||
}
|
||||
case 0x11:
|
||||
{
|
||||
return CpuCodeName.RavenRidge;
|
||||
}
|
||||
case 0x18:
|
||||
{
|
||||
return packageType == 2 ? CpuCodeName.RavenRidge2 : CpuCodeName.Picasso;
|
||||
}
|
||||
case 0x20:
|
||||
{
|
||||
return CpuCodeName.Dali;
|
||||
}
|
||||
case 0x31:
|
||||
{
|
||||
return CpuCodeName.CastlePeak;
|
||||
}
|
||||
case 0x60:
|
||||
{
|
||||
return CpuCodeName.Renoir;
|
||||
}
|
||||
case 0x71:
|
||||
{
|
||||
return CpuCodeName.Matisse;
|
||||
}
|
||||
case 0x90:
|
||||
{
|
||||
return CpuCodeName.Vangogh;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return CpuCodeName.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (family == 0x19)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case 0x00:
|
||||
{
|
||||
return CpuCodeName.Milan;
|
||||
}
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
{
|
||||
return CpuCodeName.Vermeer;
|
||||
}
|
||||
case 0x40:
|
||||
{
|
||||
return CpuCodeName.Rembrandt;
|
||||
}
|
||||
case 0x50:
|
||||
{
|
||||
return CpuCodeName.Cezanne;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return CpuCodeName.Undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CpuCodeName.Undefined;
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
|
||||
r.AppendLine("Ryzen SMU");
|
||||
r.AppendLine();
|
||||
r.AppendLine($" PM table version: 0x{_pmTableVersion:X8}");
|
||||
r.AppendLine($" PM table supported: {_supportedCPU}");
|
||||
r.AppendLine($" PM table layout defined: {IsPmTableLayoutDefined()}");
|
||||
|
||||
if (_supportedCPU)
|
||||
{
|
||||
r.AppendLine($" PM table size: 0x{_pmTableSize:X3}");
|
||||
r.AppendLine($" PM table start address: 0x{_dramBaseAddr:X8}");
|
||||
r.AppendLine();
|
||||
r.AppendLine(" PM table dump:");
|
||||
r.AppendLine(" Idx Offset Value");
|
||||
r.AppendLine(" ------------------------");
|
||||
|
||||
float[] pm_values = GetPmTable();
|
||||
for (int i = 0; i < pm_values.Length; i++)
|
||||
{
|
||||
r.AppendLine($" {i,4} 0x{i * 4:X3} {pm_values[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
private bool SetAddresses(CpuCodeName codeName)
|
||||
{
|
||||
switch (codeName)
|
||||
{
|
||||
case CpuCodeName.CastlePeak:
|
||||
case CpuCodeName.Matisse:
|
||||
case CpuCodeName.Vermeer:
|
||||
{
|
||||
_cmdAddr = 0x3B10524;
|
||||
_rspAddr = 0x3B10570;
|
||||
_argsAddr = 0x3B10A40;
|
||||
|
||||
return true;
|
||||
}
|
||||
case CpuCodeName.Colfax:
|
||||
case CpuCodeName.SummitRidge:
|
||||
case CpuCodeName.Threadripper:
|
||||
case CpuCodeName.PinnacleRidge:
|
||||
{
|
||||
_cmdAddr = 0x3B1051C;
|
||||
_rspAddr = 0x3B10568;
|
||||
_argsAddr = 0x3B10590;
|
||||
|
||||
return true;
|
||||
}
|
||||
case CpuCodeName.Renoir:
|
||||
case CpuCodeName.Picasso:
|
||||
case CpuCodeName.RavenRidge:
|
||||
case CpuCodeName.RavenRidge2:
|
||||
case CpuCodeName.Dali:
|
||||
{
|
||||
_cmdAddr = 0x3B10A20;
|
||||
_rspAddr = 0x3B10A80;
|
||||
_argsAddr = 0x3B10A88;
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint GetSmuVersion()
|
||||
{
|
||||
uint[] args = { 1 };
|
||||
|
||||
if (SendCommand(0x02, ref args))
|
||||
{
|
||||
return args[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Dictionary<uint, SmuSensorType> GetPmTableStructure()
|
||||
{
|
||||
if (!IsPmTableLayoutDefined())
|
||||
return new Dictionary<uint, SmuSensorType>();
|
||||
|
||||
|
||||
return _supportedPmTableVersions[_pmTableVersion];
|
||||
}
|
||||
|
||||
public bool IsPmTableLayoutDefined()
|
||||
{
|
||||
return _supportedPmTableVersions.ContainsKey(_pmTableVersion);
|
||||
}
|
||||
|
||||
public float[] GetPmTable()
|
||||
{
|
||||
if (!_supportedCPU)
|
||||
return new float[] { 0 };
|
||||
|
||||
if (!SetupPmTableAddrAndSize())
|
||||
return new float[] { 0 };
|
||||
|
||||
if (!TransferTableToDram())
|
||||
return new float[] { 0 };
|
||||
|
||||
|
||||
float[] table = ReadDramToArray();
|
||||
if (table[0] == 0) /* Fix for Zen+ empty values at first call bug */
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
TransferTableToDram();
|
||||
table = ReadDramToArray();
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private float[] ReadDramToArray()
|
||||
{
|
||||
float[] table = new float[_pmTableSize / 4];
|
||||
|
||||
Ring0.ReadMemory(_dramBaseAddr, ref table);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private bool SetupPmTableAddrAndSize()
|
||||
{
|
||||
if (_pmTableSize == 0)
|
||||
{
|
||||
SetupPmTableSize();
|
||||
}
|
||||
|
||||
if (_dramBaseAddr == 0)
|
||||
{
|
||||
SetupDramBaseAddr();
|
||||
}
|
||||
|
||||
return _dramBaseAddr != 0 && _pmTableSize != 0;
|
||||
}
|
||||
|
||||
private void SetupPmTableSize()
|
||||
{
|
||||
if (!GetPmTableVersion(ref _pmTableVersion))
|
||||
return;
|
||||
|
||||
|
||||
switch (_cpuCodeName)
|
||||
{
|
||||
case CpuCodeName.Matisse:
|
||||
{
|
||||
switch (_pmTableVersion)
|
||||
{
|
||||
case 0x240902:
|
||||
{
|
||||
_pmTableSize = 0x514;
|
||||
break;
|
||||
}
|
||||
case 0x240903:
|
||||
{
|
||||
_pmTableSize = 0x518;
|
||||
break;
|
||||
}
|
||||
case 0x240802:
|
||||
{
|
||||
_pmTableSize = 0x7E0;
|
||||
break;
|
||||
}
|
||||
case 0x240803:
|
||||
{
|
||||
_pmTableSize = 0x7E4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Vermeer:
|
||||
{
|
||||
switch (_pmTableVersion)
|
||||
{
|
||||
case 0x2D0903:
|
||||
{
|
||||
_pmTableSize = 0x594;
|
||||
break;
|
||||
}
|
||||
case 0x380904:
|
||||
{
|
||||
_pmTableSize = 0x5A4;
|
||||
break;
|
||||
}
|
||||
case 0x380905:
|
||||
{
|
||||
_pmTableSize = 0x5D0;
|
||||
break;
|
||||
}
|
||||
case 0x2D0803:
|
||||
{
|
||||
_pmTableSize = 0x894;
|
||||
break;
|
||||
}
|
||||
case 0x380804:
|
||||
{
|
||||
_pmTableSize = 0x8A4;
|
||||
break;
|
||||
}
|
||||
case 0x380805:
|
||||
{
|
||||
_pmTableSize = 0x8F0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Renoir:
|
||||
{
|
||||
switch (_pmTableVersion)
|
||||
{
|
||||
case 0x370000:
|
||||
{
|
||||
_pmTableSize = 0x794;
|
||||
break;
|
||||
}
|
||||
case 0x370001:
|
||||
{
|
||||
_pmTableSize = 0x884;
|
||||
break;
|
||||
}
|
||||
case 0x370002:
|
||||
case 0x370003:
|
||||
{
|
||||
_pmTableSize = 0x88C;
|
||||
break;
|
||||
}
|
||||
case 0x370004:
|
||||
{
|
||||
_pmTableSize = 0x8AC;
|
||||
break;
|
||||
}
|
||||
case 0x370005:
|
||||
{
|
||||
_pmTableSize = 0x8C8;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Cezanne:
|
||||
{
|
||||
switch (_pmTableVersion)
|
||||
{
|
||||
case 0x400005:
|
||||
{
|
||||
_pmTableSize = 0x944;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Picasso:
|
||||
case CpuCodeName.RavenRidge:
|
||||
case CpuCodeName.RavenRidge2:
|
||||
{
|
||||
_pmTableSizeAlt = 0xA4;
|
||||
_pmTableSize = 0x608 + _pmTableSizeAlt;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetPmTableVersion(ref uint version)
|
||||
{
|
||||
uint[] args = { 0 };
|
||||
uint fn;
|
||||
|
||||
switch (_cpuCodeName)
|
||||
{
|
||||
case CpuCodeName.RavenRidge:
|
||||
case CpuCodeName.Picasso:
|
||||
{
|
||||
fn = 0x0c;
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Matisse:
|
||||
case CpuCodeName.Vermeer:
|
||||
{
|
||||
fn = 0x08;
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Renoir:
|
||||
{
|
||||
fn = 0x06;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ret = SendCommand(fn, ref args);
|
||||
version = args[0];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void SetupAddrClass1(uint[] fn)
|
||||
{
|
||||
uint[] args = { 1, 1 };
|
||||
|
||||
bool command = SendCommand(fn[0], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
_dramBaseAddr = args[0] | (args[1] << 32);
|
||||
}
|
||||
|
||||
private void SetupAddrClass2(uint[] fn)
|
||||
{
|
||||
uint[] args = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
bool command = SendCommand(fn[0], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
args = new uint[] { 0 };
|
||||
command = SendCommand(fn[1], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
_dramBaseAddr = args[0];
|
||||
}
|
||||
|
||||
private void SetupAddrClass3(uint[] fn)
|
||||
{
|
||||
uint[] parts = { 0, 0 };
|
||||
|
||||
// == Part 1 ==
|
||||
uint[] args = { 3 };
|
||||
bool command = SendCommand(fn[0], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
args = new uint[] { 3 };
|
||||
command = SendCommand(fn[2], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
// 1st Base.
|
||||
parts[0] = args[0];
|
||||
// == Part 1 End ==
|
||||
|
||||
// == Part 2 ==
|
||||
args = new uint[] { 3 };
|
||||
command = SendCommand(fn[1], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
args = new uint[] { 5 };
|
||||
command = SendCommand(fn[0], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
args = new uint[] { 5 };
|
||||
command = SendCommand(fn[2], ref args);
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
|
||||
// 2nd base.
|
||||
parts[1] = args[0];
|
||||
// == Part 2 End ==
|
||||
|
||||
_dramBaseAddr = parts[0] & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
private void SetupDramBaseAddr()
|
||||
{
|
||||
uint[] fn = { 0, 0, 0 };
|
||||
|
||||
switch (_cpuCodeName)
|
||||
{
|
||||
case CpuCodeName.Vermeer:
|
||||
case CpuCodeName.Matisse:
|
||||
case CpuCodeName.CastlePeak:
|
||||
{
|
||||
fn[0] = 0x06;
|
||||
SetupAddrClass1(fn);
|
||||
|
||||
return;
|
||||
}
|
||||
case CpuCodeName.Renoir:
|
||||
{
|
||||
fn[0] = 0x66;
|
||||
SetupAddrClass1(fn);
|
||||
|
||||
return;
|
||||
}
|
||||
case CpuCodeName.Colfax:
|
||||
case CpuCodeName.PinnacleRidge:
|
||||
{
|
||||
fn[0] = 0x0b;
|
||||
fn[1] = 0x0c;
|
||||
SetupAddrClass2(fn);
|
||||
|
||||
return;
|
||||
}
|
||||
case CpuCodeName.Dali:
|
||||
case CpuCodeName.Picasso:
|
||||
case CpuCodeName.RavenRidge:
|
||||
case CpuCodeName.RavenRidge2:
|
||||
{
|
||||
fn[0] = 0x0a;
|
||||
fn[1] = 0x3d;
|
||||
fn[2] = 0x0b;
|
||||
SetupAddrClass3(fn);
|
||||
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TransferTableToDram()
|
||||
{
|
||||
uint[] args = { 0 };
|
||||
uint fn;
|
||||
|
||||
switch (_cpuCodeName)
|
||||
{
|
||||
case CpuCodeName.Matisse:
|
||||
case CpuCodeName.Vermeer:
|
||||
{
|
||||
fn = 0x05;
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Renoir:
|
||||
{
|
||||
args[0] = 3;
|
||||
fn = 0x65;
|
||||
break;
|
||||
}
|
||||
case CpuCodeName.Picasso:
|
||||
case CpuCodeName.RavenRidge:
|
||||
case CpuCodeName.RavenRidge2:
|
||||
{
|
||||
args[0] = 3;
|
||||
fn = 0x3d;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return SendCommand(fn, ref args);
|
||||
}
|
||||
|
||||
private bool SendCommand(uint msg, ref uint[] args)
|
||||
{
|
||||
uint[] cmdArgs = new uint[SMU_REQ_MAX_ARGS];
|
||||
int argsLength = Math.Min(args.Length, cmdArgs.Length);
|
||||
|
||||
for (int i = 0; i < argsLength; ++i)
|
||||
cmdArgs[i] = args[i];
|
||||
|
||||
uint tmp = 0;
|
||||
if (_mutex.WaitOne(5000))
|
||||
{
|
||||
// Step 1: Wait until the RSP register is non-zero.
|
||||
|
||||
tmp = 0;
|
||||
uint retries = SMU_RETRIES_MAX;
|
||||
do
|
||||
{
|
||||
if (!ReadReg(_rspAddr, ref tmp))
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (tmp == 0 && 0 != retries--);
|
||||
|
||||
// Step 1.b: A command is still being processed meaning a new command cannot be issued.
|
||||
|
||||
if (retries == 0 && tmp == 0)
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Write zero (0) to the RSP register
|
||||
WriteReg(_rspAddr, 0);
|
||||
|
||||
// Step 3: Write the argument(s) into the argument register(s)
|
||||
for (int i = 0; i < cmdArgs.Length; ++i)
|
||||
WriteReg(_argsAddr + (uint)(i * 4), cmdArgs[i]);
|
||||
|
||||
// Step 4: Write the message Id into the Message ID register
|
||||
WriteReg(_cmdAddr, msg);
|
||||
|
||||
// Step 5: Wait until the Response register is non-zero.
|
||||
tmp = 0;
|
||||
retries = SMU_RETRIES_MAX;
|
||||
do
|
||||
{
|
||||
if (!ReadReg(_rspAddr, ref tmp))
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (tmp == 0 && 0 != retries--);
|
||||
|
||||
if (retries == 0 && tmp != (uint)Status.OK)
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6: If the Response register contains OK, then SMU has finished processing the message.
|
||||
|
||||
args = new uint[SMU_REQ_MAX_ARGS];
|
||||
for (byte i = 0; i < SMU_REQ_MAX_ARGS; i++)
|
||||
{
|
||||
if (!ReadReg(_argsAddr + (uint)(i * 4), ref args[i]))
|
||||
{
|
||||
_mutex.ReleaseMutex();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ReadReg(_rspAddr, ref tmp);
|
||||
_mutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
return tmp == (uint)Status.OK;
|
||||
}
|
||||
|
||||
private void WriteReg(uint addr, uint data)
|
||||
{
|
||||
if (Ring0.WaitPciBusMutex(10))
|
||||
{
|
||||
if (Ring0.WritePciConfig(0x00, SMU_PCI_ADDR_REG, addr))
|
||||
{
|
||||
Ring0.WritePciConfig(0x00, SMU_PCI_DATA_REG, data);
|
||||
}
|
||||
|
||||
Ring0.ReleasePciBusMutex();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadReg(uint addr, ref uint data)
|
||||
{
|
||||
bool read = false;
|
||||
|
||||
if (Ring0.WaitPciBusMutex(10))
|
||||
{
|
||||
if (Ring0.WritePciConfig(0x00, SMU_PCI_ADDR_REG, addr))
|
||||
{
|
||||
read = Ring0.ReadPciConfig(0x00, SMU_PCI_DATA_REG, out data);
|
||||
}
|
||||
|
||||
Ring0.ReleasePciBusMutex();
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
public struct SmuSensorType
|
||||
{
|
||||
public string Name;
|
||||
public SensorType Type;
|
||||
public float Scale;
|
||||
}
|
||||
|
||||
private enum Status : uint
|
||||
{
|
||||
OK = 0x01,
|
||||
Failed = 0xFF,
|
||||
UnknownCmd = 0xFE,
|
||||
CmdRejectedPrereq = 0xFD,
|
||||
CmdRejectedBusy = 0xFC
|
||||
}
|
||||
|
||||
private enum CpuCodeName
|
||||
{
|
||||
Undefined,
|
||||
Colfax,
|
||||
Renoir,
|
||||
Picasso,
|
||||
Matisse,
|
||||
Threadripper,
|
||||
CastlePeak,
|
||||
RavenRidge,
|
||||
RavenRidge2,
|
||||
SummitRidge,
|
||||
PinnacleRidge,
|
||||
Rembrandt,
|
||||
Vermeer,
|
||||
Vangogh,
|
||||
Cezanne,
|
||||
Milan,
|
||||
Dali
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user