Hardware/CPU/AMD10CPU.cs
author moel.mich
Tue, 14 Feb 2012 23:07:55 +0000
changeset 340 600962f8a298
parent 301 d14ce71cef44
child 344 3145aadca3d2
permissions -rw-r--r--
Added a new sensor type "Factor" for dimensionless values (and similar) that are not to be shown as percent ("Level" type). Changed the write amplification sensor to use the new "Factor" sensor type. Added the temperature SMART attribute for Sandforce SSDs as hidden sensor (as it may show fake results on some hardware).
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@301
    64
    private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
moel@301
    65
    private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
moel@301
    66
    private const ushort FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1603; 
moel@197
    67
    private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
moel@273
    68
    private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
moel@201
    69
moel@196
    70
    private readonly uint miscellaneousControlAddress;
moel@251
    71
    private readonly ushort miscellaneousControlDeviceId;
moel@1
    72
moel@268
    73
    private readonly FileStream temperatureStream;
moel@266
    74
moel@279
    75
    private readonly double timeStampCounterMultiplier;
moel@279
    76
    private readonly bool corePerformanceBoostSupport;
moel@201
    77
moel@191
    78
    public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
moel@191
    79
      : base(processorIndex, cpuid, settings) 
moel@196
    80
    {            
moel@301
    81
      // AMD family 1Xh processors support only one temperature sensor
moel@22
    82
      coreTemperature = new Sensor(
moel@134
    83
        "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
moel@195
    84
        SensorType.Temperature, this, new [] {
moel@122
    85
            new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
moel@165
    86
          }, settings);
moel@1
    87
moel@251
    88
      switch (family) {
moel@251
    89
        case 0x10: miscellaneousControlDeviceId =
moel@251
    90
          FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
moel@251
    91
        case 0x11: miscellaneousControlDeviceId =
moel@251
    92
          FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
moel@301
    93
        case 0x12: miscellaneousControlDeviceId =
moel@301
    94
          FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
moel@271
    95
        case 0x14: miscellaneousControlDeviceId = 
moel@273
    96
          FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
moel@301
    97
        case 0x15: miscellaneousControlDeviceId =
moel@301
    98
          FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
moel@251
    99
        default: miscellaneousControlDeviceId = 0; break;
moel@251
   100
      }
moel@251
   101
moel@196
   102
      // get the pci address for the Miscellaneous Control registers 
moel@196
   103
      miscellaneousControlAddress = GetPciAddress(
moel@251
   104
        MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);        
moel@42
   105
moel@197
   106
      busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
moel@197
   107
      coreClocks = new Sensor[coreCount];
moel@197
   108
      for (int i = 0; i < coreClocks.Length; i++) {
moel@197
   109
        coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
moel@197
   110
          this, settings);
moel@201
   111
        if (HasTimeStampCounter)
moel@197
   112
          ActivateSensor(coreClocks[i]);
moel@197
   113
      }
moel@197
   114
moel@279
   115
      corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
moel@279
   116
moel@238
   117
      // set affinity to the first thread for all frequency estimations     
moel@238
   118
      ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
moel@201
   119
moel@279
   120
      // disable core performance boost  
moel@279
   121
      uint hwcrEax, hwcrEdx;
moel@279
   122
      Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
moel@279
   123
      if (corePerformanceBoostSupport) 
moel@279
   124
        Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
moel@279
   125
moel@201
   126
      uint ctlEax, ctlEdx;
moel@236
   127
      Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
moel@201
   128
      uint ctrEax, ctrEdx;
moel@236
   129
      Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
moel@201
   130
moel@201
   131
      timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
moel@201
   132
moel@201
   133
      // restore the performance counter registers
moel@236
   134
      Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
moel@236
   135
      Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
moel@201
   136
moel@279
   137
      // restore core performance boost
moel@279
   138
      if (corePerformanceBoostSupport)     
moel@279
   139
        Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
moel@279
   140
moel@201
   141
      // restore the thread affinity.
moel@238
   142
      ThreadAffinity.Set(mask);
moel@201
   143
moel@266
   144
      // the file reader for lm-sensors support on Linux
moel@268
   145
      temperatureStream = null;
moel@266
   146
      int p = (int)Environment.OSVersion.Platform;
moel@266
   147
      if ((p == 4) || (p == 128)) {
moel@266
   148
        string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
moel@266
   149
        foreach (string path in devicePaths) {
moel@266
   150
          string name = null;
moel@266
   151
          try {
moel@266
   152
            using (StreamReader reader = new StreamReader(path + "/device/name"))
moel@266
   153
              name = reader.ReadLine();
moel@266
   154
          } catch (IOException) { }
moel@266
   155
          switch (name) {
moel@266
   156
            case "k10temp":
moel@268
   157
              temperatureStream = new FileStream(path + "/device/temp1_input", 
moel@268
   158
                FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
moel@266
   159
              break;
moel@266
   160
          }
moel@266
   161
        }
moel@266
   162
      }
moel@266
   163
moel@1
   164
      Update();                   
moel@1
   165
    }
moel@1
   166
moel@201
   167
    private double estimateTimeStampCounterMultiplier() {
moel@201
   168
      // preload the function
moel@201
   169
      estimateTimeStampCounterMultiplier(0);
moel@201
   170
      estimateTimeStampCounterMultiplier(0);
moel@201
   171
moel@201
   172
      // estimate the multiplier
moel@201
   173
      List<double> estimate = new List<double>(3);
moel@201
   174
      for (int i = 0; i < 3; i++)
moel@201
   175
        estimate.Add(estimateTimeStampCounterMultiplier(0.025));
moel@201
   176
      estimate.Sort();
moel@201
   177
      return estimate[1];
moel@201
   178
    }
moel@201
   179
moel@201
   180
    private double estimateTimeStampCounterMultiplier(double timeWindow) {
moel@201
   181
      uint eax, edx;
moel@201
   182
     
moel@201
   183
      // select event "076h CPU Clocks not Halted" and enable the counter
moel@236
   184
      Ring0.Wrmsr(PERF_CTL_0,
moel@201
   185
        (1 << 22) | // enable performance counter
moel@201
   186
        (1 << 17) | // count events in user mode
moel@201
   187
        (1 << 16) | // count events in operating-system mode
moel@201
   188
        0x76, 0x00000000);
moel@201
   189
moel@201
   190
      // set the counter to 0
moel@236
   191
      Ring0.Wrmsr(PERF_CTR_0, 0, 0);
moel@201
   192
moel@201
   193
      long ticks = (long)(timeWindow * Stopwatch.Frequency);
moel@273
   194
      uint lsbBegin, msbBegin, lsbEnd, msbEnd;      
moel@201
   195
moel@201
   196
      long timeBegin = Stopwatch.GetTimestamp() +
moel@201
   197
        (long)Math.Ceiling(0.001 * ticks);
moel@201
   198
      long timeEnd = timeBegin + ticks;
moel@201
   199
      while (Stopwatch.GetTimestamp() < timeBegin) { }
moel@236
   200
      Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
moel@279
   201
moel@201
   202
      while (Stopwatch.GetTimestamp() < timeEnd) { }
moel@236
   203
      Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
moel@279
   204
      Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
moel@301
   205
      double coreMultiplier = GetCoreMultiplier(eax);
moel@201
   206
moel@201
   207
      ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
moel@201
   208
      ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
moel@201
   209
moel@201
   210
      double coreFrequency = 1e-6 * 
moel@201
   211
        (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
moel@201
   212
        (timeEnd - timeBegin);
moel@201
   213
moel@201
   214
      double busFrequency = coreFrequency / coreMultiplier;
moel@279
   215
moel@273
   216
      return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
moel@201
   217
    }
moel@201
   218
moel@191
   219
    protected override uint[] GetMSRs() {
moel@279
   220
      return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0, 
moel@279
   221
        COFVID_STATUS };
moel@1
   222
    }
moel@1
   223
moel@196
   224
    public override string GetReport() {
moel@196
   225
      StringBuilder r = new StringBuilder();
moel@196
   226
      r.Append(base.GetReport());
moel@196
   227
moel@197
   228
      r.Append("Miscellaneous Control Address: 0x");
moel@196
   229
      r.AppendLine((miscellaneousControlAddress).ToString("X",
moel@196
   230
        CultureInfo.InvariantCulture));
moel@201
   231
      r.Append("Time Stamp Counter Multiplier: ");
moel@201
   232
      r.AppendLine(timeStampCounterMultiplier.ToString(
moel@201
   233
        CultureInfo.InvariantCulture));
moel@273
   234
      if (family == 0x14) {
moel@273
   235
        uint value = 0;
moel@273
   236
        Ring0.ReadPciConfig(miscellaneousControlAddress,
moel@273
   237
          CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
moel@273
   238
        r.Append("PCI Register D18F3xD4: ");
moel@273
   239
        r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
moel@273
   240
      }
moel@201
   241
      r.AppendLine();
moel@201
   242
moel@196
   243
      return r.ToString();
moel@196
   244
    }
moel@196
   245
moel@301
   246
    private double GetCoreMultiplier(uint cofvidEax) {
moel@301
   247
      switch (family) {
moel@301
   248
        case 0x10:
moel@329
   249
        case 0x11: 
moel@329
   250
        case 0x15: {
moel@329
   251
            // 8:6 CpuDid: current core divisor ID
moel@329
   252
            // 5:0 CpuFid: current core frequency ID
moel@329
   253
            uint cpuDid = (cofvidEax >> 6) & 7;
moel@329
   254
            uint cpuFid = cofvidEax & 0x1F;
moel@329
   255
            return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
moel@301
   256
          }
moel@329
   257
        case 0x12: {
moel@329
   258
            // 8:4 CpuFid: current CPU core frequency ID
moel@329
   259
            // 3:0 CpuDid: current CPU core divisor ID
moel@329
   260
            uint cpuFid = (cofvidEax >> 4) & 0x1F;
moel@329
   261
            uint cpuDid = cofvidEax & 0xF;
moel@329
   262
            double divisor;
moel@329
   263
            switch (cpuDid) {
moel@329
   264
              case 0: divisor = 1; break;
moel@329
   265
              case 1: divisor = 1.5; break;
moel@329
   266
              case 2: divisor = 2; break;
moel@329
   267
              case 3: divisor = 3; break;
moel@329
   268
              case 4: divisor = 4; break;
moel@329
   269
              case 5: divisor = 6; break;
moel@329
   270
              case 6: divisor = 8; break;
moel@329
   271
              case 7: divisor = 12; break;
moel@329
   272
              case 8: divisor = 16; break;
moel@329
   273
              default: divisor = 1; break;
moel@329
   274
            }
moel@329
   275
            return (cpuFid + 0x10) / divisor;
moel@329
   276
          }
moel@329
   277
        case 0x14: {
moel@329
   278
            // 8:4: current CPU core divisor ID most significant digit
moel@329
   279
            // 3:0: current CPU core divisor ID least significant digit
moel@329
   280
            uint divisorIdMSD = (cofvidEax >> 4) & 0x1F;
moel@329
   281
            uint divisorIdLSD = cofvidEax & 0xF;
moel@329
   282
            uint value = 0;
moel@329
   283
            Ring0.ReadPciConfig(miscellaneousControlAddress,
moel@329
   284
              CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
moel@329
   285
            uint frequencyId = value & 0x1F;
moel@329
   286
            return (frequencyId + 0x10) /
moel@329
   287
              (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
moel@329
   288
          }
moel@301
   289
        default:
moel@301
   290
          return 1;
moel@301
   291
      }
moel@273
   292
    }
moel@273
   293
moel@268
   294
    private string ReadFirstLine(Stream stream) {
moel@268
   295
      StringBuilder sb = new StringBuilder();
moel@268
   296
      try {
moel@268
   297
        stream.Seek(0, SeekOrigin.Begin);
moel@268
   298
        int b = stream.ReadByte();
moel@268
   299
        while (b != -1 && b != 10) {
moel@268
   300
          sb.Append((char)b);
moel@268
   301
          b = stream.ReadByte();
moel@268
   302
        }
moel@268
   303
      } catch { }
moel@268
   304
      return sb.ToString();
moel@268
   305
    }
moel@268
   306
moel@110
   307
    public override void Update() {
moel@191
   308
      base.Update();
moel@191
   309
moel@268
   310
      if (temperatureStream == null) {
moel@266
   311
        if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
moel@266
   312
          uint value;
moel@266
   313
          if (Ring0.ReadPciConfig(miscellaneousControlAddress,
moel@266
   314
            REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
moel@329
   315
            if (family == 0x15 && (value & 0x30000) == 0x30000) {
moel@329
   316
              coreTemperature.Value = ((value >> 21) & 0x7FC) / 8.0f +
moel@329
   317
                coreTemperature.Parameters[0].Value - 49;
moel@329
   318
            } else {
moel@329
   319
              coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
moel@329
   320
                coreTemperature.Parameters[0].Value;
moel@329
   321
            }
moel@266
   322
            ActivateSensor(coreTemperature);
moel@266
   323
          } else {
moel@266
   324
            DeactivateSensor(coreTemperature);
moel@266
   325
          }
moel@266
   326
        }
moel@266
   327
      } else {
moel@268
   328
        string s = ReadFirstLine(temperatureStream);
moel@266
   329
        try {
moel@266
   330
          coreTemperature.Value = 0.001f *
moel@266
   331
            long.Parse(s, CultureInfo.InvariantCulture);
moel@42
   332
          ActivateSensor(coreTemperature);
moel@266
   333
        } catch {
moel@42
   334
          DeactivateSensor(coreTemperature);
moel@266
   335
        }        
moel@24
   336
      }
moel@197
   337
moel@201
   338
      if (HasTimeStampCounter) {
moel@197
   339
        double newBusClock = 0;
moel@197
   340
moel@197
   341
        for (int i = 0; i < coreClocks.Length; i++) {
moel@197
   342
          Thread.Sleep(1);
moel@197
   343
moel@197
   344
          uint curEax, curEdx;
moel@236
   345
          if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
moel@238
   346
            1UL << cpuid[i][0].Thread)) 
moel@197
   347
          {
moel@273
   348
            double multiplier;
moel@301
   349
            multiplier = GetCoreMultiplier(curEax);
moel@197
   350
moel@201
   351
            coreClocks[i].Value = 
moel@201
   352
              (float)(multiplier * TimeStampCounterFrequency / 
moel@201
   353
              timeStampCounterMultiplier);
moel@201
   354
            newBusClock = 
moel@201
   355
              (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
moel@197
   356
          } else {
moel@201
   357
            coreClocks[i].Value = (float)TimeStampCounterFrequency;
moel@197
   358
          }
moel@197
   359
        }
moel@197
   360
moel@197
   361
        if (newBusClock > 0) {
moel@197
   362
          this.busClock.Value = (float)newBusClock;
moel@197
   363
          ActivateSensor(this.busClock);
moel@197
   364
        }
moel@197
   365
      }
moel@201
   366
    }
moel@266
   367
moel@298
   368
    public override void Close() {      
moel@268
   369
      if (temperatureStream != null) {
moel@268
   370
        temperatureStream.Close();
moel@266
   371
      }
moel@298
   372
      base.Close();
moel@266
   373
    }
moel@1
   374
  }
moel@1
   375
}