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