Hardware/CPU/AMD10CPU.cs
author moel.mich
Sat, 02 Oct 2010 18:15:46 +0000
changeset 206 1fa8eddc24a7
parent 201 958e9fe8afdf
child 236 763675f19ff4
permissions -rw-r--r--
Replaced HttpUtility.UrlEncode with Uri.EscapeDataString and deleted the reference to the System.Web assembly. The System.Web assembly seems to be missing on some .NET 4.0 installations (and the overhead of using it is a bit large, just for the UrlEncode method).
moel@1
     1
/*
moel@1
     2
  
moel@1
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@1
     4
moel@1
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@1
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@1
     7
  the License. You may obtain a copy of the License at
moel@1
     8
 
moel@1
     9
  http://www.mozilla.org/MPL/
moel@1
    10
moel@1
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@1
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@1
    13
  for the specific language governing rights and limitations under the License.
moel@1
    14
moel@1
    15
  The Original Code is the Open Hardware Monitor code.
moel@1
    16
moel@1
    17
  The Initial Developer of the Original Code is 
moel@1
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@1
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2010
moel@1
    20
  the Initial Developer. All Rights Reserved.
moel@1
    21
moel@1
    22
  Contributor(s):
moel@1
    23
moel@1
    24
  Alternatively, the contents of this file may be used under the terms of
moel@1
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@1
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@1
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@1
    28
  of those above. If you wish to allow use of your version of this file only
moel@1
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@1
    30
  use your version of this file under the terms of the MPL, indicate your
moel@1
    31
  decision by deleting the provisions above and replace them with the notice
moel@1
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@1
    33
  the provisions above, a recipient may use your version of this file under
moel@1
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@1
    35
 
moel@1
    36
*/
moel@1
    37
moel@196
    38
using System;
moel@201
    39
using System.Collections.Generic;
moel@201
    40
using System.Diagnostics;
moel@196
    41
using System.Globalization;
moel@201
    42
using System.Runtime.InteropServices;
moel@196
    43
using System.Text;
moel@197
    44
using System.Threading;
moel@196
    45
moel@1
    46
namespace OpenHardwareMonitor.Hardware.CPU {
moel@165
    47
moel@196
    48
  internal sealed class AMD10CPU : AMDCPU {
moel@1
    49
moel@195
    50
    private readonly Sensor coreTemperature;
moel@197
    51
    private readonly Sensor[] coreClocks;
moel@197
    52
    private readonly Sensor busClock;
moel@201
    53
      
moel@201
    54
    private const uint PERF_CTL_0 = 0xC0010000;
moel@201
    55
    private const uint PERF_CTR_0 = 0xC0010004;
moel@201
    56
    private const uint P_STATE_0 = 0xC0010064;
moel@197
    57
    private const uint COFVID_STATUS = 0xC0010071;
moel@1
    58
moel@196
    59
    private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
moel@196
    60
    private const ushort MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
moel@197
    61
    private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
moel@201
    62
moel@196
    63
    private readonly uint miscellaneousControlAddress;
moel@1
    64
moel@201
    65
    private double timeStampCounterMultiplier;
moel@201
    66
moel@191
    67
    public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
moel@191
    68
      : base(processorIndex, cpuid, settings) 
moel@196
    69
    {            
moel@1
    70
      // AMD family 10h processors support only one temperature sensor
moel@22
    71
      coreTemperature = new Sensor(
moel@134
    72
        "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
moel@195
    73
        SensorType.Temperature, this, new [] {
moel@122
    74
            new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
moel@165
    75
          }, settings);
moel@1
    76
moel@196
    77
      // get the pci address for the Miscellaneous Control registers 
moel@196
    78
      miscellaneousControlAddress = GetPciAddress(
moel@196
    79
        MISCELLANEOUS_CONTROL_FUNCTION, MISCELLANEOUS_CONTROL_DEVICE_ID);
moel@42
    80
moel@197
    81
      busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
moel@197
    82
      coreClocks = new Sensor[coreCount];
moel@197
    83
      for (int i = 0; i < coreClocks.Length; i++) {
moel@197
    84
        coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
moel@197
    85
          this, settings);
moel@201
    86
        if (HasTimeStampCounter)
moel@197
    87
          ActivateSensor(coreClocks[i]);
moel@197
    88
      }
moel@197
    89
moel@201
    90
      // set affinity to the first thread for all frequency estimations
moel@201
    91
      IntPtr thread = NativeMethods.GetCurrentThread();
moel@201
    92
      UIntPtr mask = NativeMethods.SetThreadAffinityMask(thread,
moel@201
    93
        (UIntPtr)(1L << cpuid[0][0].Thread));
moel@201
    94
moel@201
    95
      uint ctlEax, ctlEdx;
moel@201
    96
      WinRing0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
moel@201
    97
      uint ctrEax, ctrEdx;
moel@201
    98
      WinRing0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
moel@201
    99
moel@201
   100
      timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
moel@201
   101
moel@201
   102
      // restore the performance counter registers
moel@201
   103
      WinRing0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
moel@201
   104
      WinRing0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
moel@201
   105
moel@201
   106
      // restore the thread affinity.
moel@201
   107
      NativeMethods.SetThreadAffinityMask(thread, mask);
moel@201
   108
moel@1
   109
      Update();                   
moel@1
   110
    }
moel@1
   111
moel@201
   112
    private double estimateTimeStampCounterMultiplier() {
moel@201
   113
      // preload the function
moel@201
   114
      estimateTimeStampCounterMultiplier(0);
moel@201
   115
      estimateTimeStampCounterMultiplier(0);
moel@201
   116
moel@201
   117
      // estimate the multiplier
moel@201
   118
      List<double> estimate = new List<double>(3);
moel@201
   119
      for (int i = 0; i < 3; i++)
moel@201
   120
        estimate.Add(estimateTimeStampCounterMultiplier(0.025));
moel@201
   121
      estimate.Sort();
moel@201
   122
      return estimate[1];
moel@201
   123
    }
moel@201
   124
moel@201
   125
    private double estimateTimeStampCounterMultiplier(double timeWindow) {
moel@201
   126
      uint eax, edx;
moel@201
   127
     
moel@201
   128
      // select event "076h CPU Clocks not Halted" and enable the counter
moel@201
   129
      WinRing0.Wrmsr(PERF_CTL_0,
moel@201
   130
        (1 << 22) | // enable performance counter
moel@201
   131
        (1 << 17) | // count events in user mode
moel@201
   132
        (1 << 16) | // count events in operating-system mode
moel@201
   133
        0x76, 0x00000000);
moel@201
   134
moel@201
   135
      // set the counter to 0
moel@201
   136
      WinRing0.Wrmsr(PERF_CTR_0, 0, 0);
moel@201
   137
moel@201
   138
      long ticks = (long)(timeWindow * Stopwatch.Frequency);
moel@201
   139
      uint lsbBegin, msbBegin, lsbEnd, msbEnd;
moel@201
   140
moel@201
   141
      long timeBegin = Stopwatch.GetTimestamp() +
moel@201
   142
        (long)Math.Ceiling(0.001 * ticks);
moel@201
   143
      long timeEnd = timeBegin + ticks;
moel@201
   144
      while (Stopwatch.GetTimestamp() < timeBegin) { }
moel@201
   145
      WinRing0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
moel@201
   146
      while (Stopwatch.GetTimestamp() < timeEnd) { }
moel@201
   147
      WinRing0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
moel@201
   148
moel@201
   149
      WinRing0.Rdmsr(COFVID_STATUS, out eax, out edx);
moel@201
   150
      uint cpuDid = (eax >> 6) & 7;
moel@201
   151
      uint cpuFid = eax & 0x1F;
moel@201
   152
      double coreMultiplier = MultiplierFromIDs(cpuDid, cpuFid);
moel@201
   153
moel@201
   154
      ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
moel@201
   155
      ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
moel@201
   156
moel@201
   157
      double coreFrequency = 1e-6 * 
moel@201
   158
        (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
moel@201
   159
        (timeEnd - timeBegin);
moel@201
   160
moel@201
   161
      double busFrequency = coreFrequency / coreMultiplier;
moel@201
   162
      return 0.5 * Math.Round(2 * TimeStampCounterFrequency / busFrequency);
moel@201
   163
    }
moel@201
   164
moel@191
   165
    protected override uint[] GetMSRs() {
moel@201
   166
      return new uint[] { PERF_CTL_0, PERF_CTR_0, P_STATE_0, COFVID_STATUS };
moel@1
   167
    }
moel@1
   168
moel@196
   169
    public override string GetReport() {
moel@196
   170
      StringBuilder r = new StringBuilder();
moel@196
   171
      r.Append(base.GetReport());
moel@196
   172
moel@197
   173
      r.Append("Miscellaneous Control Address: 0x");
moel@196
   174
      r.AppendLine((miscellaneousControlAddress).ToString("X",
moel@196
   175
        CultureInfo.InvariantCulture));
moel@201
   176
      r.Append("Time Stamp Counter Multiplier: ");
moel@201
   177
      r.AppendLine(timeStampCounterMultiplier.ToString(
moel@201
   178
        CultureInfo.InvariantCulture));
moel@201
   179
      r.AppendLine();
moel@201
   180
moel@196
   181
      return r.ToString();
moel@196
   182
    }
moel@196
   183
moel@201
   184
    private static double MultiplierFromIDs(uint divisorID, uint frequencyID) {
moel@197
   185
      return 0.5 * (frequencyID + 0x10) / (1 << (int)divisorID);
moel@197
   186
    }
moel@197
   187
moel@110
   188
    public override void Update() {
moel@191
   189
      base.Update();
moel@191
   190
moel@196
   191
      if (miscellaneousControlAddress != WinRing0.InvalidPciAddress) {
moel@42
   192
        uint value;
moel@196
   193
        if (WinRing0.ReadPciConfigDwordEx(miscellaneousControlAddress,
moel@42
   194
          REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
moel@63
   195
          coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
moel@63
   196
            coreTemperature.Parameters[0].Value;
moel@42
   197
          ActivateSensor(coreTemperature);
moel@42
   198
        } else {
moel@42
   199
          DeactivateSensor(coreTemperature);
moel@42
   200
        }
moel@24
   201
      }
moel@197
   202
moel@201
   203
      if (HasTimeStampCounter) {
moel@197
   204
        double newBusClock = 0;
moel@197
   205
moel@197
   206
        for (int i = 0; i < coreClocks.Length; i++) {
moel@197
   207
          Thread.Sleep(1);
moel@197
   208
moel@197
   209
          uint curEax, curEdx;
moel@197
   210
          if (WinRing0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
moel@197
   211
            (UIntPtr)(1L << cpuid[i][0].Thread))) 
moel@197
   212
          {
moel@197
   213
            // 8:6 CpuDid: current core divisor ID
moel@197
   214
            // 5:0 CpuFid: current core frequency ID
moel@201
   215
            uint cpuDid = (curEax >> 6) & 7;
moel@201
   216
            uint cpuFid = curEax & 0x1F;
moel@201
   217
            double multiplier = MultiplierFromIDs(cpuDid, cpuFid);
moel@197
   218
moel@201
   219
            coreClocks[i].Value = 
moel@201
   220
              (float)(multiplier * TimeStampCounterFrequency / 
moel@201
   221
              timeStampCounterMultiplier);
moel@201
   222
            newBusClock = 
moel@201
   223
              (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
moel@197
   224
          } else {
moel@201
   225
            coreClocks[i].Value = (float)TimeStampCounterFrequency;
moel@197
   226
          }
moel@197
   227
        }
moel@197
   228
moel@197
   229
        if (newBusClock > 0) {
moel@197
   230
          this.busClock.Value = (float)newBusClock;
moel@197
   231
          ActivateSensor(this.busClock);
moel@197
   232
        }
moel@197
   233
      }
moel@201
   234
    }
moel@201
   235
moel@201
   236
    private static class NativeMethods {
moel@201
   237
      private const string KERNEL = "kernel32.dll";
moel@201
   238
moel@201
   239
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@201
   240
      public static extern UIntPtr
moel@201
   241
        SetThreadAffinityMask(IntPtr handle, UIntPtr mask);
moel@201
   242
moel@201
   243
      [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
moel@201
   244
      public static extern IntPtr GetCurrentThread();
moel@201
   245
    }
moel@1
   246
  }
moel@1
   247
}