Hardware/CPU/AMD10CPU.cs
author sl
Sun, 03 Feb 2013 18:01:50 +0100
changeset 391 ca4c0e7ae75d
parent 329 756af5ee409e
permissions -rw-r--r--
Converted project to VisualStudio 2012.
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2009-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Collections.Generic;
    13 using System.Diagnostics;
    14 using System.Globalization;
    15 using System.IO;
    16 using System.Runtime.InteropServices;
    17 using System.Text;
    18 using System.Threading;
    19 
    20 namespace OpenHardwareMonitor.Hardware.CPU {
    21 
    22   internal sealed class AMD10CPU : AMDCPU {
    23 
    24     private readonly Sensor coreTemperature;
    25     private readonly Sensor[] coreClocks;
    26     private readonly Sensor busClock;
    27       
    28     private const uint PERF_CTL_0 = 0xC0010000;
    29     private const uint PERF_CTR_0 = 0xC0010004;
    30     private const uint HWCR = 0xC0010015;
    31     private const uint P_STATE_0 = 0xC0010064;
    32     private const uint COFVID_STATUS = 0xC0010071;
    33 
    34     private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    35     private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    36     private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
    37     private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
    38     private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
    39     private const ushort FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1603; 
    40     private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    41     private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
    42 
    43     private readonly uint miscellaneousControlAddress;
    44     private readonly ushort miscellaneousControlDeviceId;
    45 
    46     private readonly FileStream temperatureStream;
    47 
    48     private readonly double timeStampCounterMultiplier;
    49     private readonly bool corePerformanceBoostSupport;
    50 
    51     public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    52       : base(processorIndex, cpuid, settings) 
    53     {            
    54       // AMD family 1Xh processors support only one temperature sensor
    55       coreTemperature = new Sensor(
    56         "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
    57         SensorType.Temperature, this, new [] {
    58             new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
    59           }, settings);
    60 
    61       switch (family) {
    62         case 0x10: miscellaneousControlDeviceId =
    63           FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    64         case 0x11: miscellaneousControlDeviceId =
    65           FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    66         case 0x12: miscellaneousControlDeviceId =
    67           FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    68         case 0x14: miscellaneousControlDeviceId = 
    69           FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    70         case 0x15: miscellaneousControlDeviceId =
    71           FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    72         default: miscellaneousControlDeviceId = 0; break;
    73       }
    74 
    75       // get the pci address for the Miscellaneous Control registers 
    76       miscellaneousControlAddress = GetPciAddress(
    77         MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);        
    78 
    79       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
    80       coreClocks = new Sensor[coreCount];
    81       for (int i = 0; i < coreClocks.Length; i++) {
    82         coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
    83           this, settings);
    84         if (HasTimeStampCounter)
    85           ActivateSensor(coreClocks[i]);
    86       }
    87 
    88       corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
    89 
    90       // set affinity to the first thread for all frequency estimations     
    91       ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
    92 
    93       // disable core performance boost  
    94       uint hwcrEax, hwcrEdx;
    95       Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
    96       if (corePerformanceBoostSupport) 
    97         Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
    98 
    99       uint ctlEax, ctlEdx;
   100       Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
   101       uint ctrEax, ctrEdx;
   102       Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
   103 
   104       timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
   105 
   106       // restore the performance counter registers
   107       Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
   108       Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
   109 
   110       // restore core performance boost
   111       if (corePerformanceBoostSupport)     
   112         Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
   113 
   114       // restore the thread affinity.
   115       ThreadAffinity.Set(mask);
   116 
   117       // the file reader for lm-sensors support on Linux
   118       temperatureStream = null;
   119       int p = (int)Environment.OSVersion.Platform;
   120       if ((p == 4) || (p == 128)) {
   121         string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
   122         foreach (string path in devicePaths) {
   123           string name = null;
   124           try {
   125             using (StreamReader reader = new StreamReader(path + "/device/name"))
   126               name = reader.ReadLine();
   127           } catch (IOException) { }
   128           switch (name) {
   129             case "k10temp":
   130               temperatureStream = new FileStream(path + "/device/temp1_input", 
   131                 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
   132               break;
   133           }
   134         }
   135       }
   136 
   137       Update();                   
   138     }
   139 
   140     private double estimateTimeStampCounterMultiplier() {
   141       // preload the function
   142       estimateTimeStampCounterMultiplier(0);
   143       estimateTimeStampCounterMultiplier(0);
   144 
   145       // estimate the multiplier
   146       List<double> estimate = new List<double>(3);
   147       for (int i = 0; i < 3; i++)
   148         estimate.Add(estimateTimeStampCounterMultiplier(0.025));
   149       estimate.Sort();
   150       return estimate[1];
   151     }
   152 
   153     private double estimateTimeStampCounterMultiplier(double timeWindow) {
   154       uint eax, edx;
   155      
   156       // select event "076h CPU Clocks not Halted" and enable the counter
   157       Ring0.Wrmsr(PERF_CTL_0,
   158         (1 << 22) | // enable performance counter
   159         (1 << 17) | // count events in user mode
   160         (1 << 16) | // count events in operating-system mode
   161         0x76, 0x00000000);
   162 
   163       // set the counter to 0
   164       Ring0.Wrmsr(PERF_CTR_0, 0, 0);
   165 
   166       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   167       uint lsbBegin, msbBegin, lsbEnd, msbEnd;      
   168 
   169       long timeBegin = Stopwatch.GetTimestamp() +
   170         (long)Math.Ceiling(0.001 * ticks);
   171       long timeEnd = timeBegin + ticks;
   172       while (Stopwatch.GetTimestamp() < timeBegin) { }
   173       Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   174 
   175       while (Stopwatch.GetTimestamp() < timeEnd) { }
   176       Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   177       Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
   178       double coreMultiplier = GetCoreMultiplier(eax);
   179 
   180       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   181       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   182 
   183       double coreFrequency = 1e-6 * 
   184         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   185         (timeEnd - timeBegin);
   186 
   187       double busFrequency = coreFrequency / coreMultiplier;
   188 
   189       return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
   190     }
   191 
   192     protected override uint[] GetMSRs() {
   193       return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0, 
   194         COFVID_STATUS };
   195     }
   196 
   197     public override string GetReport() {
   198       StringBuilder r = new StringBuilder();
   199       r.Append(base.GetReport());
   200 
   201       r.Append("Miscellaneous Control Address: 0x");
   202       r.AppendLine((miscellaneousControlAddress).ToString("X",
   203         CultureInfo.InvariantCulture));
   204       r.Append("Time Stamp Counter Multiplier: ");
   205       r.AppendLine(timeStampCounterMultiplier.ToString(
   206         CultureInfo.InvariantCulture));
   207       if (family == 0x14) {
   208         uint value = 0;
   209         Ring0.ReadPciConfig(miscellaneousControlAddress,
   210           CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
   211         r.Append("PCI Register D18F3xD4: ");
   212         r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
   213       }
   214       r.AppendLine();
   215 
   216       return r.ToString();
   217     }
   218 
   219     private double GetCoreMultiplier(uint cofvidEax) {
   220       switch (family) {
   221         case 0x10:
   222         case 0x11: 
   223         case 0x15: {
   224             // 8:6 CpuDid: current core divisor ID
   225             // 5:0 CpuFid: current core frequency ID
   226             uint cpuDid = (cofvidEax >> 6) & 7;
   227             uint cpuFid = cofvidEax & 0x1F;
   228             return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
   229           }
   230         case 0x12: {
   231             // 8:4 CpuFid: current CPU core frequency ID
   232             // 3:0 CpuDid: current CPU core divisor ID
   233             uint cpuFid = (cofvidEax >> 4) & 0x1F;
   234             uint cpuDid = cofvidEax & 0xF;
   235             double divisor;
   236             switch (cpuDid) {
   237               case 0: divisor = 1; break;
   238               case 1: divisor = 1.5; break;
   239               case 2: divisor = 2; break;
   240               case 3: divisor = 3; break;
   241               case 4: divisor = 4; break;
   242               case 5: divisor = 6; break;
   243               case 6: divisor = 8; break;
   244               case 7: divisor = 12; break;
   245               case 8: divisor = 16; break;
   246               default: divisor = 1; break;
   247             }
   248             return (cpuFid + 0x10) / divisor;
   249           }
   250         case 0x14: {
   251             // 8:4: current CPU core divisor ID most significant digit
   252             // 3:0: current CPU core divisor ID least significant digit
   253             uint divisorIdMSD = (cofvidEax >> 4) & 0x1F;
   254             uint divisorIdLSD = cofvidEax & 0xF;
   255             uint value = 0;
   256             Ring0.ReadPciConfig(miscellaneousControlAddress,
   257               CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
   258             uint frequencyId = value & 0x1F;
   259             return (frequencyId + 0x10) /
   260               (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
   261           }
   262         default:
   263           return 1;
   264       }
   265     }
   266 
   267     private string ReadFirstLine(Stream stream) {
   268       StringBuilder sb = new StringBuilder();
   269       try {
   270         stream.Seek(0, SeekOrigin.Begin);
   271         int b = stream.ReadByte();
   272         while (b != -1 && b != 10) {
   273           sb.Append((char)b);
   274           b = stream.ReadByte();
   275         }
   276       } catch { }
   277       return sb.ToString();
   278     }
   279 
   280     public override void Update() {
   281       base.Update();
   282 
   283       if (temperatureStream == null) {
   284         if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
   285           uint value;
   286           if (Ring0.ReadPciConfig(miscellaneousControlAddress,
   287             REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
   288             if (family == 0x15 && (value & 0x30000) == 0x30000) {
   289               coreTemperature.Value = ((value >> 21) & 0x7FC) / 8.0f +
   290                 coreTemperature.Parameters[0].Value - 49;
   291             } else {
   292               coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
   293                 coreTemperature.Parameters[0].Value;
   294             }
   295             ActivateSensor(coreTemperature);
   296           } else {
   297             DeactivateSensor(coreTemperature);
   298           }
   299         }
   300       } else {
   301         string s = ReadFirstLine(temperatureStream);
   302         try {
   303           coreTemperature.Value = 0.001f *
   304             long.Parse(s, CultureInfo.InvariantCulture);
   305           ActivateSensor(coreTemperature);
   306         } catch {
   307           DeactivateSensor(coreTemperature);
   308         }        
   309       }
   310 
   311       if (HasTimeStampCounter) {
   312         double newBusClock = 0;
   313 
   314         for (int i = 0; i < coreClocks.Length; i++) {
   315           Thread.Sleep(1);
   316 
   317           uint curEax, curEdx;
   318           if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   319             1UL << cpuid[i][0].Thread)) 
   320           {
   321             double multiplier;
   322             multiplier = GetCoreMultiplier(curEax);
   323 
   324             coreClocks[i].Value = 
   325               (float)(multiplier * TimeStampCounterFrequency / 
   326               timeStampCounterMultiplier);
   327             newBusClock = 
   328               (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   329           } else {
   330             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   331           }
   332         }
   333 
   334         if (newBusClock > 0) {
   335           this.busClock.Value = (float)newBusClock;
   336           ActivateSensor(this.busClock);
   337         }
   338       }
   339     }
   340 
   341     public override void Close() {      
   342       if (temperatureStream != null) {
   343         temperatureStream.Close();
   344       }
   345       base.Close();
   346     }
   347   }
   348 }