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