Hardware/CPU/AMD10CPU.cs
author moel.mich
Sat, 12 Feb 2011 21:17:42 +0000
changeset 256 6dc6410489f4
parent 238 bddc6e01840a
child 266 2687ac753d90
permissions -rw-r--r--
Fixed Issue 164.
     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-2010
    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.Runtime.InteropServices;
    43 using System.Text;
    44 using System.Threading;
    45 
    46 namespace OpenHardwareMonitor.Hardware.CPU {
    47 
    48   internal sealed class AMD10CPU : AMDCPU {
    49 
    50     private readonly Sensor coreTemperature;
    51     private readonly Sensor[] coreClocks;
    52     private readonly Sensor busClock;
    53       
    54     private const uint PERF_CTL_0 = 0xC0010000;
    55     private const uint PERF_CTR_0 = 0xC0010004;
    56     private const uint P_STATE_0 = 0xC0010064;
    57     private const uint COFVID_STATUS = 0xC0010071;
    58 
    59     private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    60     private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    61     private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;    
    62     private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    63 
    64     private readonly uint miscellaneousControlAddress;
    65     private readonly ushort miscellaneousControlDeviceId;
    66 
    67     private double timeStampCounterMultiplier;
    68 
    69     public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    70       : base(processorIndex, cpuid, settings) 
    71     {            
    72       // AMD family 10h/11h processors support only one temperature sensor
    73       coreTemperature = new Sensor(
    74         "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
    75         SensorType.Temperature, this, new [] {
    76             new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
    77           }, settings);
    78 
    79       switch (family) {
    80         case 0x10: miscellaneousControlDeviceId =
    81           FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    82         case 0x11: miscellaneousControlDeviceId =
    83           FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    84         default: miscellaneousControlDeviceId = 0; break;
    85       }
    86 
    87       // get the pci address for the Miscellaneous Control registers 
    88       miscellaneousControlAddress = GetPciAddress(
    89         MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);        
    90 
    91       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
    92       coreClocks = new Sensor[coreCount];
    93       for (int i = 0; i < coreClocks.Length; i++) {
    94         coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
    95           this, settings);
    96         if (HasTimeStampCounter)
    97           ActivateSensor(coreClocks[i]);
    98       }
    99 
   100       // set affinity to the first thread for all frequency estimations     
   101       ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
   102 
   103       uint ctlEax, ctlEdx;
   104       Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
   105       uint ctrEax, ctrEdx;
   106       Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
   107 
   108       timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
   109 
   110       // restore the performance counter registers
   111       Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
   112       Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
   113 
   114       // restore the thread affinity.
   115       ThreadAffinity.Set(mask);
   116 
   117       Update();                   
   118     }
   119 
   120     private double estimateTimeStampCounterMultiplier() {
   121       // preload the function
   122       estimateTimeStampCounterMultiplier(0);
   123       estimateTimeStampCounterMultiplier(0);
   124 
   125       // estimate the multiplier
   126       List<double> estimate = new List<double>(3);
   127       for (int i = 0; i < 3; i++)
   128         estimate.Add(estimateTimeStampCounterMultiplier(0.025));
   129       estimate.Sort();
   130       return estimate[1];
   131     }
   132 
   133     private double estimateTimeStampCounterMultiplier(double timeWindow) {
   134       uint eax, edx;
   135      
   136       // select event "076h CPU Clocks not Halted" and enable the counter
   137       Ring0.Wrmsr(PERF_CTL_0,
   138         (1 << 22) | // enable performance counter
   139         (1 << 17) | // count events in user mode
   140         (1 << 16) | // count events in operating-system mode
   141         0x76, 0x00000000);
   142 
   143       // set the counter to 0
   144       Ring0.Wrmsr(PERF_CTR_0, 0, 0);
   145 
   146       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   147       uint lsbBegin, msbBegin, lsbEnd, msbEnd;
   148 
   149       long timeBegin = Stopwatch.GetTimestamp() +
   150         (long)Math.Ceiling(0.001 * ticks);
   151       long timeEnd = timeBegin + ticks;
   152       while (Stopwatch.GetTimestamp() < timeBegin) { }
   153       Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   154       while (Stopwatch.GetTimestamp() < timeEnd) { }
   155       Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   156 
   157       Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
   158       uint cpuDid = (eax >> 6) & 7;
   159       uint cpuFid = eax & 0x1F;
   160       double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
   161 
   162       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   163       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   164 
   165       double coreFrequency = 1e-6 * 
   166         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   167         (timeEnd - timeBegin);
   168 
   169       double busFrequency = coreFrequency / coreMultiplier;
   170       return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
   171     }
   172 
   173     protected override uint[] GetMSRs() {
   174       return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
   175     }
   176 
   177     public override string GetReport() {
   178       StringBuilder r = new StringBuilder();
   179       r.Append(base.GetReport());
   180 
   181       r.Append("Miscellaneous Control Address: 0x");
   182       r.AppendLine((miscellaneousControlAddress).ToString("X",
   183         CultureInfo.InvariantCulture));
   184       r.Append("Time Stamp Counter Multiplier: ");
   185       r.AppendLine(timeStampCounterMultiplier.ToString(
   186         CultureInfo.InvariantCulture));
   187       r.AppendLine();
   188 
   189       return r.ToString();
   190     }
   191 
   192     private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
   193       return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
   194     }
   195 
   196     public override void Update() {
   197       base.Update();
   198 
   199       if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
   200         uint value;
   201         if (Ring0.ReadPciConfig(miscellaneousControlAddress,
   202           REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
   203           coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
   204             coreTemperature.Parameters[0].Value;
   205           ActivateSensor(coreTemperature);
   206         } else {
   207           DeactivateSensor(coreTemperature);
   208         }
   209       }
   210 
   211       if (HasTimeStampCounter) {
   212         double newBusClock = 0;
   213 
   214         for (int i = 0; i < coreClocks.Length; i++) {
   215           Thread.Sleep(1);
   216 
   217           uint curEax, curEdx;
   218           if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   219             1UL << cpuid[i][0].Thread)) 
   220           {
   221             // 8:6 CpuDid: current core divisor ID
   222             // 5:0 CpuFid: current core frequency ID
   223             uint cpuDid = (curEax >> 6) & 7;
   224             uint cpuFid = curEax & 0x1F;
   225             double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
   226 
   227             coreClocks[i].Value = 
   228               (float)(multiplier * TimeStampCounterFrequency / 
   229               timeStampCounterMultiplier);
   230             newBusClock = 
   231               (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   232           } else {
   233             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   234           }
   235         }
   236 
   237         if (newBusClock > 0) {
   238           this.busClock.Value = (float)newBusClock;
   239           ActivateSensor(this.busClock);
   240         }
   241       }
   242     }
   243   }
   244 }