From 3f8294898dd787270fc939379fe7d48bd87965bf Mon Sep 17 00:00:00 2001 From: Blacktempel <6657796+Blacktempel@users.noreply.github.com> Date: Mon, 8 Sep 2025 13:14:32 +0200 Subject: [PATCH] RAMSPDToolkit Update + timings added (#1835) * Workaround for page changing on DDR5 systems with SPD Write Protection active has been implemented in new version of RAMSPDToolkit. Code for LHM has been adjusted accordingly. Added more sensors (timings). * Move abbreviation of timings to start of string. * Simplify names for timings. * Update version. * Remove extra padding. --- LibreHardwareMonitor/UI/SensorGadget.cs | 3 + LibreHardwareMonitor/UI/SensorNode.cs | 3 + LibreHardwareMonitor/UI/SensorNotifyIcon.cs | 3 + LibreHardwareMonitor/UI/TypeNode.cs | 4 + LibreHardwareMonitorLib/Hardware/ISensor.cs | 3 +- .../Hardware/Memory/DimmMemory.cs | 119 ++++++++++++++---- .../Hardware/Memory/MemoryGroup.cs | 7 +- .../LibreHardwareMonitorLib.csproj | 4 +- 8 files changed, 114 insertions(+), 32 deletions(-) diff --git a/LibreHardwareMonitor/UI/SensorGadget.cs b/LibreHardwareMonitor/UI/SensorGadget.cs index 7d97d90..ac99f87 100644 --- a/LibreHardwareMonitor/UI/SensorGadget.cs +++ b/LibreHardwareMonitor/UI/SensorGadget.cs @@ -646,6 +646,9 @@ public class SensorGadget : Gadget case SensorType.TimeSpan: format = "{0:g}"; break; + case SensorType.Timing: + format = "{0:F3} ns"; + break; case SensorType.Energy: format = "{0:F0} mWh"; break; diff --git a/LibreHardwareMonitor/UI/SensorNode.cs b/LibreHardwareMonitor/UI/SensorNode.cs index bf2ab7f..3cd878e 100644 --- a/LibreHardwareMonitor/UI/SensorNode.cs +++ b/LibreHardwareMonitor/UI/SensorNode.cs @@ -76,6 +76,9 @@ public class SensorNode : Node case SensorType.TimeSpan: Format = "{0:g}"; break; + case SensorType.Timing: + Format = "{0:F3} ns"; + break; case SensorType.Energy: Format = "{0:F0} mWh"; break; diff --git a/LibreHardwareMonitor/UI/SensorNotifyIcon.cs b/LibreHardwareMonitor/UI/SensorNotifyIcon.cs index 35ac980..577b67a 100644 --- a/LibreHardwareMonitor/UI/SensorNotifyIcon.cs +++ b/LibreHardwareMonitor/UI/SensorNotifyIcon.cs @@ -165,6 +165,8 @@ public class SensorNotifyIcon : IDisposable return _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit ? $"{UnitManager.CelsiusToFahrenheit(Sensor.Value):F0}" : $"{Sensor.Value:F0}"; case SensorType.TimeSpan: return $"{TimeSpan.FromSeconds(Sensor.Value.Value):g}"; + case SensorType.Timing: + return $"{Sensor.Value.Value:F3}"; case SensorType.Clock: case SensorType.Fan: case SensorType.Flow: @@ -289,6 +291,7 @@ public class SensorNotifyIcon : IDisposable case SensorType.Noise: format = "\n{0}: {0:F0} dBA"; break; case SensorType.Conductivity: format = "\n{0}: {0:F1} µS/cm"; break; case SensorType.Humidity: format = "\n{0}: {0:F0} %"; break; + case SensorType.Timing: format = "\n{0}: {0:F3} ns"; break; } string formattedValue = string.Format(format, Sensor.Name, Sensor.Value); diff --git a/LibreHardwareMonitor/UI/TypeNode.cs b/LibreHardwareMonitor/UI/TypeNode.cs index f2a8178..0b2bf4e 100644 --- a/LibreHardwareMonitor/UI/TypeNode.cs +++ b/LibreHardwareMonitor/UI/TypeNode.cs @@ -91,6 +91,10 @@ public sealed class TypeNode : Node, IExpandPersistNode Image = Utilities.EmbeddedResources.GetImage("time.png"); Text = "Times"; break; + case SensorType.Timing: + Image = Utilities.EmbeddedResources.GetImage("time.png"); + Text = "Timings"; + break; case SensorType.Noise: Image = Utilities.EmbeddedResources.GetImage("loudspeaker.png"); Text = "Noise Levels"; diff --git a/LibreHardwareMonitorLib/Hardware/ISensor.cs b/LibreHardwareMonitorLib/Hardware/ISensor.cs index f0e9bcb..ba2116d 100644 --- a/LibreHardwareMonitorLib/Hardware/ISensor.cs +++ b/LibreHardwareMonitorLib/Hardware/ISensor.cs @@ -29,7 +29,8 @@ public enum SensorType Data, // GB = 2^30 Bytes SmallData, // MB = 2^20 Bytes Throughput, // B/s - TimeSpan, // Seconds + TimeSpan, // Seconds + Timing, // ns Energy, // milliwatt-hour (mWh) Noise, // dBA Conductivity, // µS/cm diff --git a/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs b/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs index f7f0e8c..0ec4de8 100644 --- a/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs +++ b/LibreHardwareMonitorLib/Hardware/Memory/DimmMemory.cs @@ -6,7 +6,6 @@ using LibreHardwareMonitor.Hardware.Memory.Sensors; using RAMSPDToolkit.SPD; -using RAMSPDToolkit.SPD.Enums; using RAMSPDToolkit.SPD.Interfaces; using RAMSPDToolkit.SPD.Interop.Shared; @@ -19,40 +18,36 @@ internal sealed class DimmMemory : Hardware public DimmMemory(SPDAccessor accessor, string name, Identifier identifier, ISettings settings) : base(name, identifier, settings) { - //Check which kind of RAM we have - switch (accessor.MemoryType()) + //Only add thermal sensor if present + if (accessor is IThermalSensor ts && ts.HasThermalSensor) { - case SPDMemoryType.SPD_DDR4_SDRAM: - case SPDMemoryType.SPD_DDR4E_SDRAM: - case SPDMemoryType.SPD_LPDDR4_SDRAM: - case SPDMemoryType.SPD_LPDDR4X_SDRAM: - _thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}", - accessor.Index, - SensorType.Temperature, - this, - settings, - accessor as IThermalSensor); - - break; - case SPDMemoryType.SPD_DDR5_SDRAM: - case SPDMemoryType.SPD_LPDDR5_SDRAM: - //Check if we are on correct page or if write protection is not enabled - if (accessor.PageData.HasFlag(PageData.ThermalData) || !accessor.HasSPDWriteProtection) - { + //Check which kind of RAM we have + switch (accessor.MemoryType()) + { + case SPDMemoryType.SPD_DDR4_SDRAM: + case SPDMemoryType.SPD_DDR4E_SDRAM: + case SPDMemoryType.SPD_LPDDR4_SDRAM: + case SPDMemoryType.SPD_LPDDR4X_SDRAM: + case SPDMemoryType.SPD_DDR5_SDRAM: + case SPDMemoryType.SPD_LPDDR5_SDRAM: _thermalSensor = new SpdThermalSensor($"DIMM #{accessor.Index}", accessor.Index, SensorType.Temperature, this, settings, accessor as IThermalSensor); - } - - break; + break; + } } + bool hasThermalSensor = _thermalSensor != null; + //Add thermal sensor - if (_thermalSensor != null) + if (hasThermalSensor) ActivateSensor(_thermalSensor); + + //Add other sensors + CreateSensors(accessor, hasThermalSensor); } public override HardwareType HardwareType => HardwareType.Memory; @@ -61,4 +56,80 @@ internal sealed class DimmMemory : Hardware { _thermalSensor?.UpdateSensor(); } + + private void CreateSensors(SPDAccessor accessor, bool hasThermalSensor) + { + if (accessor is DDR4Accessor ddr4) + { + CreateSensorsDDR4(ddr4, hasThermalSensor); + } + else if (accessor is DDR5Accessor ddr5) + { + CreateSensorsDDR5(ddr5, hasThermalSensor); + } + } + + private void CreateSensorsDDR4(DDR4Accessor accessor, bool hasThermalSensor) + { + if (hasThermalSensor) + { + //Temperature Resolution (fixed value) + AddSensor("Temperature Sensor Resolution", 0, false, SensorType.Temperature, accessor.TemperatureResolution); + } + + //Timings + AddSensor("tCKAVGmin (Minimum Cycle Time)", 1, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumCycleTime); + AddSensor("tCKAVGmax (Maximum Cycle Time)", 2, false, SensorType.Timing, (float)accessor.SDRAMTimings.MaximumCycleTime); + AddSensor("tAA (CAS Latency Time)", 3, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumCASLatencyTime); + AddSensor("tRCD (RAS to CAS Delay Time)", 4, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRASToCASDelayTime); + AddSensor("tRP (Row Precharge Delay Time)", 5, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRowPrechargeDelayTime); + AddSensor("tRAS (Active to Precharge Delay Time)", 6, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActiveToPrechargeDelayTime); + AddSensor("tRC (Active to Active/Refresh Delay Time)", 7, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActiveToActiveRefreshDelayTime); + AddSensor("tRFC1 (Refresh Recovery Delay Time)", 8, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRefreshRecoveryDelayTime1); + AddSensor("tRFC2 (Refresh Recovery Delay Time)", 9, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRefreshRecoveryDelayTime2); + AddSensor("tRFC4 (Refresh Recovery Delay Time)", 10, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRefreshRecoveryDelayTime4); + AddSensor("tFAW (Four Activate Window Time)", 11, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumFourActivateWindowTime); + AddSensor("tRRD_S (Activate to Activate Delay Time)", 12, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActivateToActivateDelay_DiffGroup); + AddSensor("tRRD_L (Activate to Activate Delay Time)", 13, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActivateToActivateDelay_SameGroup); + AddSensor("tCCD_L (CAS to CAS Delay Time)", 14, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumCASToCASDelay_SameGroup); + AddSensor("tWR (Write Recovery Time)", 15, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumWriteRecoveryTime); + AddSensor("tWTR_S (Write to Read Time)", 16, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumWriteToReadTime_DiffGroup); + AddSensor("tWTR_L (Write to Read Time)", 17, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumWriteToReadTime_SameGroup); + + } + + private void CreateSensorsDDR5(DDR5Accessor accessor, bool hasThermalSensor) + { + if (hasThermalSensor) + { + //Temperature Resolution (fixed value) + AddSensor("Temperature Sensor Resolution", 0, false, SensorType.Temperature, accessor.TemperatureResolution); + } + + //Timings + AddSensor("tCKAVGmin (Minimum Cycle Time)", 1, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumCycleTime); + AddSensor("tCKAVGmax (Maximum Cycle Time)", 2, false, SensorType.Timing, (float)accessor.SDRAMTimings.MaximumCycleTime); + AddSensor("tAA (CAS Latency Time)", 3, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumCASLatencyTime); + AddSensor("tRCD (RAS to CAS Delay Time)", 4, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRASToCASDelayTime); + AddSensor("tRP (Row Precharge Delay Time)", 5, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumRowPrechargeDelayTime); + AddSensor("tRAS (Active to Precharge Delay Time)", 6, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActiveToPrechargeDelayTime); + AddSensor("tRC (Active to Active/Refresh Delay Time)", 7, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumActiveToActiveRefreshDelayTime); + AddSensor("tWR (Write Recovery Time)", 8, false, SensorType.Timing, (float)accessor.SDRAMTimings.MinimumWriteRecoveryTime); + AddSensor("tRFC1 (Normal Refresh Recovery Time)", 9, false, SensorType.Timing, (float)accessor.SDRAMTimings.NormalRefreshRecoveryTime); + AddSensor("tRFC2 (Fine Granularity Refresh Recovery Time)", 10, false, SensorType.Timing, (float)accessor.SDRAMTimings.FineGranularityRefreshRecoveryTime); + AddSensor("tRFCsb (Same Bank Refresh Recovery Time)", 11, false, SensorType.Timing, (float)accessor.SDRAMTimings.SameBankRefreshRecoveryTime); + AddSensor("tRFC1_dlr (Normal Refresh Recovery Time 3DS)", 12, false, SensorType.Timing, (float)accessor.SDRAMTimings.NormalRefreshRecoveryTime_3DSDifferentLogicalRank); + AddSensor("tRFC2_dlr (Fine Granularity Refresh Recovery Time 3DS)", 13, false, SensorType.Timing, (float)accessor.SDRAMTimings.FineGranularityRefreshRecoveryTime_3DSDifferentLogicalRank); + AddSensor("tRFCsb_dlr (Same Bank Refresh Recovery Time 3DS)", 14, false, SensorType.Timing, (float)accessor.SDRAMTimings.SameBankRefreshRecoveryTime_3DSDifferentLogicalRank); + } + + private void AddSensor(string name, int index, bool defaultHidden, SensorType sensorType, float value) + { + var sensor = new Sensor(name, index, defaultHidden, sensorType, this, null, _settings) + { + Value = value, + }; + + ActivateSensor(sensor); + } } diff --git a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs index b9d5b4a..bd5dd40 100644 --- a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs @@ -7,14 +7,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using RAMSPDToolkit.I2CSMBus; using RAMSPDToolkit.SPD; using RAMSPDToolkit.SPD.Enums; -using RAMSPDToolkit.SPD.Interfaces; using RAMSPDToolkit.SPD.Interop.Shared; using RAMSPDToolkit.Windows.Driver; @@ -162,9 +160,8 @@ internal class MemoryGroup : IGroup, IHardwareChanged //RAM available and detected if (detector.Accessor != null) { - //We are only interested in modules with thermal sensor - if (detector.Accessor is IThermalSensor { HasThermalSensor: true }) - accessors.Add(detector.Accessor); + //Add all detected modules + accessors.Add(detector.Accessor); ramDetected = true; } diff --git a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj index ba3d8dc..8f33cd3 100644 --- a/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj +++ b/LibreHardwareMonitorLib/LibreHardwareMonitorLib.csproj @@ -1,4 +1,4 @@ - + net472;netstandard2.0;net6.0;net8.0 LibreHardwareMonitorLib @@ -41,7 +41,7 @@ - +