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 @@ - +