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