Hardware/CPU/AMD10CPU.cs
author moel.mich
Wed, 11 Jul 2012 17:35:30 +0000
changeset 360 c1a4377c11d1
parent 329 756af5ee409e
permissions -rw-r--r--
Added the option to enable/disable all hardware groups.
     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 }