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