Skip to content

Commit 76b44d6

Browse files
committed
Add sensors for Astral GPUs
1 parent 3a7ea82 commit 76b44d6

2 files changed

Lines changed: 171 additions & 0 deletions

File tree

OpenHardwareMonitorLib/Hardware/Gpu/NvidiaGpu.cs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ internal sealed class NvidiaGpu : GenericGpu
3939
private readonly Sensor _coreVoltage;
4040
private readonly Sensor[] _temperatures;
4141
private readonly uint _thermalSensorsMask;
42+
private readonly Sensor[] _12VHPwrPinCurrentSensors;
43+
private readonly Sensor[] _12VHPwrPinVoltageSensors;
44+
private readonly Sensor[] _12VHPwrPinPowerSensors;
45+
private readonly Sensor _12VHPwrConnectorPowerSensor;
46+
private readonly Sensor _12VHPwrConnectorCurrentSensor;
47+
48+
// ASUS Astral subsystem IDs for 12VHPwr pin monitoring
49+
private static readonly uint[] AstralSubSystemIds =
50+
[
51+
0x89EA1043, // Astral 5090D OC
52+
0x8A611043, // Astral 5090 Matrix
53+
0x89EC1043, // Astral 5090 LC
54+
0x89E31043, // Astral 5090 OC
55+
0x89DE1043, // Astral 5080 OC
56+
];
4257

4358
public NvidiaGpu(int adapterIndex, NvApi.NvPhysicalGpuHandle handle, NvApi.NvDisplayHandle? displayHandle, ISettings settings)
4459
: base(GetName(handle),
@@ -428,6 +443,58 @@ public NvidiaGpu(int adapterIndex, NvApi.NvPhysicalGpuHandle handle, NvApi.NvDis
428443
_memoryTotal = new Sensor("GPU Memory Total", 2, SensorType.SmallData, this, settings);
429444
_memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings);
430445

446+
// Pin power sensors for NVIDIA RTX Astral series from ASUS
447+
if (NvApi.NvAPI_I2CReadEx != null && NvApi.NvAPI_GPU_GetPCIIdentifiers != null)
448+
{
449+
NvApi.NvStatus pciIdentifierStatus = NvApi.NvAPI_GPU_GetPCIIdentifiers(_handle, out _, out uint subSystemId, out _, out _);
450+
451+
if (pciIdentifierStatus == NvApi.NvStatus.OK && AstralSubSystemIds.Contains(subSystemId))
452+
{
453+
_12VHPwrPinVoltageSensors =
454+
[
455+
new Sensor("12VHPWR Pin 1", 0, SensorType.Voltage, this, settings),
456+
new Sensor("12VHPWR Pin 2", 1, SensorType.Voltage, this, settings),
457+
new Sensor("12VHPWR Pin 3", 2, SensorType.Voltage, this, settings),
458+
new Sensor("12VHPWR Pin 4", 3, SensorType.Voltage, this, settings),
459+
new Sensor("12VHPWR Pin 5", 4, SensorType.Voltage, this, settings),
460+
new Sensor("12VHPWR Pin 6", 5, SensorType.Voltage, this, settings),
461+
];
462+
_12VHPwrPinCurrentSensors =
463+
[
464+
new Sensor("12VHPWR Pin 1", 1, SensorType.Current, this, settings),
465+
new Sensor("12VHPWR Pin 2", 2, SensorType.Current, this, settings),
466+
new Sensor("12VHPWR Pin 3", 3, SensorType.Current, this, settings),
467+
new Sensor("12VHPWR Pin 4", 4, SensorType.Current, this, settings),
468+
new Sensor("12VHPWR Pin 5", 5, SensorType.Current, this, settings),
469+
new Sensor("12VHPWR Pin 6", 6, SensorType.Current, this, settings),
470+
];
471+
_12VHPwrPinPowerSensors =
472+
[
473+
new Sensor("12VHPWR Pin 1", 2, SensorType.Power, this, settings),
474+
new Sensor("12VHPWR Pin 2", 3, SensorType.Power, this, settings),
475+
new Sensor("12VHPWR Pin 3", 4, SensorType.Power, this, settings),
476+
new Sensor("12VHPWR Pin 4", 5, SensorType.Power, this, settings),
477+
new Sensor("12VHPWR Pin 5", 6, SensorType.Power, this, settings),
478+
new Sensor("12VHPWR Pin 6", 7, SensorType.Power, this, settings),
479+
];
480+
481+
foreach (Sensor sensor in _12VHPwrPinVoltageSensors)
482+
ActivateSensor(sensor);
483+
484+
foreach (Sensor sensor in _12VHPwrPinCurrentSensors)
485+
ActivateSensor(sensor);
486+
487+
foreach (Sensor sensor in _12VHPwrPinPowerSensors)
488+
ActivateSensor(sensor);
489+
490+
_12VHPwrConnectorPowerSensor = new Sensor("12VHPWR Connector", 1, SensorType.Power, this, settings);
491+
_12VHPwrConnectorCurrentSensor = new Sensor("12VHPWR Connector", 0, SensorType.Current, this, settings);
492+
493+
ActivateSensor(_12VHPwrConnectorPowerSensor);
494+
ActivateSensor(_12VHPwrConnectorCurrentSensor);
495+
}
496+
}
497+
431498
Update();
432499
}
433500

@@ -675,6 +742,75 @@ public override void Update()
675742
ActivateSensor(_pcieThroughputTx);
676743
}
677744
}
745+
746+
// Astral specific
747+
if (_12VHPwrPinCurrentSensors is { Length: > 0 } && TryReadAstral12VHPwrPinSensors(out ushort[] pinSensorValues))
748+
{
749+
float connectorCurrent = 0;
750+
float connectorPower = 0;
751+
for (int i = 0; i < 6; i++)
752+
{
753+
float current = pinSensorValues[i * 2] / 1000f;
754+
float voltage = pinSensorValues[i * 2 + 1] / 1000f;
755+
_12VHPwrPinVoltageSensors[i].Value = voltage;
756+
_12VHPwrPinCurrentSensors[i].Value = current;
757+
_12VHPwrPinPowerSensors[i].Value = voltage * current;
758+
759+
connectorCurrent += current;
760+
connectorPower += voltage * current;
761+
}
762+
763+
_12VHPwrConnectorCurrentSensor.Value = connectorCurrent;
764+
_12VHPwrConnectorPowerSensor.Value = connectorPower;
765+
}
766+
}
767+
768+
private bool TryReadAstral12VHPwrPinSensors(out ushort[] data)
769+
{
770+
data = null;
771+
772+
if (NvApi.NvAPI_I2CReadEx == null)
773+
return false;
774+
775+
byte[] dataBuffer = new byte[24];
776+
byte[] regAddress = [0x80];
777+
778+
unsafe
779+
{
780+
fixed (byte* pData = dataBuffer)
781+
fixed (byte* pRegAddr = regAddress)
782+
{
783+
NvApi.NvI2CInfo i2cInfo = new()
784+
{
785+
Version = (uint)NvApi.MAKE_NVAPI_VERSION<NvApi.NvI2CInfo>(3),
786+
DisplayMask = 0,
787+
IsDDCPort = 0,
788+
I2CDevAddress = 0x2B << 1,
789+
I2CRegAddress = (IntPtr)pRegAddr,
790+
RegAddrSize = 1,
791+
Data = (IntPtr)pData,
792+
Size = 24,
793+
I2CSpeed = NvApi.NVAPI_I2C_SPEED_DEPRECATED,
794+
I2CSpeedKhz = NvApi.NvI2CSpeed.Speed100Khz,
795+
PortId = 0x1,
796+
IsPortIdSet = 1
797+
};
798+
799+
uint readData = 0;
800+
if (NvApi.NvAPI_I2CReadEx(_handle, ref i2cInfo, ref readData) != NvApi.NvStatus.OK)
801+
return false;
802+
}
803+
}
804+
805+
// Parse 12 x u16 big-endian (as mV and mA) values in reverse order
806+
data = new ushort[12];
807+
for (int i = 0; i < 12; i++)
808+
{
809+
int pinIndex = 11 - i;
810+
data[i] = (ushort)((dataBuffer[pinIndex * 2] << 8) | dataBuffer[pinIndex * 2 + 1]);
811+
}
812+
813+
return true;
678814
}
679815

680816
public override string GetReport()

OpenHardwareMonitorLib/Interop/NvApi.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ internal static class NvApi
2222
public const int THERMAL_SENSOR_RESERVED_COUNT = 8;
2323
public const int THERMAL_SENSOR_TEMPERATURE_COUNT = 32;
2424

25+
public const uint NVAPI_I2C_SPEED_DEPRECATED = 0xFFFF;
26+
2527
private const string DllName = "nvapi.dll";
2628
private const string DllName64 = "nvapi64.dll";
2729

@@ -45,6 +47,7 @@ internal static class NvApi
4547
public static NvAPI_GPU_GetUsagesDelegate NvAPI_GPU_GetUsages { get; internal set; }
4648
public static NvAPI_GPU_SetCoolerLevelsDelegate NvAPI_GPU_SetCoolerLevels { get; internal set; }
4749
public static NvAPI_GPU_GetThermalSensorsDelegate NvAPI_GPU_GetThermalSensors { get; internal set; }
50+
public static NvAPI_I2CReadExDelegate NvAPI_I2CReadEx { get; internal set; }
4851
public static NvAPI_GPU_ClientVoltRailsGetStatusDelegate NvAPI_GPU_ClientVoltRailsGetStatus { get; internal set; }
4952

5053
private static NvAPI_GetInterfaceVersionStringDelegate _nvAPI_GetInterfaceVersionString;
@@ -90,6 +93,7 @@ public static void Initialize()
9093
NvAPI_GPU_ClientFanCoolersSetControl = GetDelegate<NvAPI_GPU_ClientFanCoolersSetControlDelegate>(0xA58971A5);
9194
NvAPI_GPU_ClientPowerTopologyGetStatus = GetDelegate<NvAPI_GPU_ClientPowerTopologyGetStatusDelegate>(0x0EDCF624E);
9295
NvAPI_GPU_GetThermalSensors = GetDelegate<NvAPI_GPU_GetThermalSensorsDelegate>(0x65FE3AAD);
96+
NvAPI_I2CReadEx = GetDelegate<NvAPI_I2CReadExDelegate>(0x4D7B0709);
9397
NvAPI_GPU_ClientVoltRailsGetStatus = GetDelegate<NvAPI_GPU_ClientVoltRailsGetStatusDelegate>(0x465F9BCF);
9498

9599
IsAvailable = true;
@@ -159,6 +163,9 @@ public static void Initialize()
159163
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
160164
public delegate NvStatus NvAPI_GPU_SetCoolerLevelsDelegate(NvPhysicalGpuHandle gpuHandle, int coolerIndex, ref NvCoolerLevels NvCoolerLevels);
161165

166+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
167+
public delegate NvStatus NvAPI_I2CReadExDelegate(NvPhysicalGpuHandle gpuHandle, ref NvI2CInfo i2cInfo, ref uint readData);
168+
162169
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
163170
public delegate NvStatus NvAPI_GPU_ClientVoltRailsGetStatusDelegate(NvPhysicalGpuHandle gpuHandle, ref NvGpuClientVoltRailsStatus status);
164171

@@ -193,6 +200,17 @@ public enum NvUtilizationDomain
193200
BusInterface // Bus
194201
}
195202

203+
public enum NvI2CSpeed : uint
204+
{
205+
Default = 0,
206+
Speed3Khz = 1,
207+
Speed10Khz = 2,
208+
Speed33Khz = 3,
209+
Speed100Khz = 4,
210+
Speed200Khz = 5,
211+
Speed400Khz = 6
212+
}
213+
196214
public static bool IsAvailable { get; private set; }
197215

198216
[DllImport(DllName, EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl, PreserveSig = true)]
@@ -627,6 +645,23 @@ internal struct NvGpuClockFrequenciesDomain
627645
public bool IsPresent => (_isPresent & 1) != 0;
628646
}
629647

648+
[StructLayout(LayoutKind.Sequential, Pack = 8)]
649+
internal struct NvI2CInfo
650+
{
651+
public uint Version;
652+
public uint DisplayMask;
653+
public byte IsDDCPort;
654+
public byte I2CDevAddress;
655+
public IntPtr I2CRegAddress;
656+
public uint RegAddrSize;
657+
public IntPtr Data;
658+
public uint Size;
659+
public uint I2CSpeed;
660+
public NvI2CSpeed I2CSpeedKhz;
661+
public byte PortId;
662+
public uint IsPortIdSet;
663+
}
664+
630665
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
631666
private delegate NvStatus NvAPI_InitializeDelegate();
632667

0 commit comments

Comments
 (0)