Hardware/CPU/GenericCPU.cs
author moel.mich
Sun, 08 May 2011 22:10:13 +0000
changeset 279 6bce967ba1b5
parent 275 35788ddd1825
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@191
     1
/*
moel@191
     2
  
moel@191
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@191
     4
moel@191
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@191
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@191
     7
  the License. You may obtain a copy of the License at
moel@191
     8
 
moel@191
     9
  http://www.mozilla.org/MPL/
moel@191
    10
moel@191
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@191
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@191
    13
  for the specific language governing rights and limitations under the License.
moel@191
    14
moel@191
    15
  The Original Code is the Open Hardware Monitor code.
moel@191
    16
moel@191
    17
  The Initial Developer of the Original Code is 
moel@191
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@266
    19
  Portions created by the Initial Developer are Copyright (C) 2010-2011
moel@191
    20
  the Initial Developer. All Rights Reserved.
moel@191
    21
moel@191
    22
  Contributor(s):
moel@191
    23
moel@191
    24
  Alternatively, the contents of this file may be used under the terms of
moel@191
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@191
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@191
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@191
    28
  of those above. If you wish to allow use of your version of this file only
moel@191
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@191
    30
  use your version of this file under the terms of the MPL, indicate your
moel@191
    31
  decision by deleting the provisions above and replace them with the notice
moel@191
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@191
    33
  the provisions above, a recipient may use your version of this file under
moel@191
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@191
    35
 
moel@191
    36
*/
moel@191
    37
moel@191
    38
using System;
moel@191
    39
using System.Collections.Generic;
moel@191
    40
using System.Diagnostics;
moel@191
    41
using System.Globalization;
moel@236
    42
using System.Runtime.InteropServices;
moel@191
    43
using System.Text;
moel@191
    44
using System.Threading;
moel@191
    45
moel@191
    46
namespace OpenHardwareMonitor.Hardware.CPU {
moel@195
    47
  internal class GenericCPU : Hardware {
moel@191
    48
moel@191
    49
    protected readonly CPUID[][] cpuid;
moel@191
    50
   
moel@191
    51
    protected readonly uint family;
moel@191
    52
    protected readonly uint model;
moel@191
    53
    protected readonly uint stepping;
moel@191
    54
moel@191
    55
    protected readonly int processorIndex;
moel@191
    56
    protected readonly int coreCount;
moel@191
    57
moel@236
    58
    private readonly bool hasModelSpecificRegisters;
moel@236
    59
moel@201
    60
    private readonly bool hasTimeStampCounter;
moel@201
    61
    private readonly bool isInvariantTimeStampCounter;
moel@201
    62
    private readonly double estimatedTimeStampCounterFrequency;
moel@279
    63
    private readonly double estimatedTimeStampCounterFrequencyError;
moel@191
    64
moel@191
    65
    private ulong lastTimeStampCount;
moel@191
    66
    private long lastTime;
moel@279
    67
    private double timeStampCounterFrequency;
moel@279
    68
    
moel@191
    69
moel@195
    70
    private readonly Vendor vendor;
moel@191
    71
moel@191
    72
    private readonly CPULoad cpuLoad;
moel@191
    73
    private readonly Sensor totalLoad;
moel@191
    74
    private readonly Sensor[] coreLoads;
moel@191
    75
moel@191
    76
    protected string CoreString(int i) {
moel@191
    77
      if (coreCount == 1)
moel@191
    78
        return "CPU Core";
moel@191
    79
      else
moel@191
    80
        return "CPU Core #" + (i + 1);
moel@191
    81
    }
moel@191
    82
moel@275
    83
    public GenericCPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
moel@275
    84
      : base(cpuid[0][0].Name, CreateIdentifier(cpuid[0][0].Vendor, 
moel@275
    85
      processorIndex), settings)
moel@275
    86
    {
moel@191
    87
      this.cpuid = cpuid;
moel@191
    88
moel@191
    89
      this.vendor = cpuid[0][0].Vendor;
moel@191
    90
moel@191
    91
      this.family = cpuid[0][0].Family;
moel@191
    92
      this.model = cpuid[0][0].Model;
moel@191
    93
      this.stepping = cpuid[0][0].Stepping;
moel@191
    94
moel@191
    95
      this.processorIndex = processorIndex;
moel@275
    96
      this.coreCount = cpuid.Length;  
moel@236
    97
  
moel@236
    98
      // check if processor has MSRs
moel@236
    99
      if (cpuid[0][0].Data.GetLength(0) > 1
moel@236
   100
        && (cpuid[0][0].Data[1, 3] & 0x20) != 0)
moel@236
   101
        hasModelSpecificRegisters = true;
moel@236
   102
      else
moel@236
   103
        hasModelSpecificRegisters = false;
moel@191
   104
moel@201
   105
      // check if processor has a TSC
moel@191
   106
      if (cpuid[0][0].Data.GetLength(0) > 1
moel@191
   107
        && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
moel@201
   108
        hasTimeStampCounter = true;
moel@191
   109
      else
moel@201
   110
        hasTimeStampCounter = false;
moel@191
   111
moel@201
   112
      // check if processor supports an invariant TSC 
moel@191
   113
      if (cpuid[0][0].ExtData.GetLength(0) > 7
moel@191
   114
        && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
moel@201
   115
        isInvariantTimeStampCounter = true;
moel@191
   116
      else
moel@201
   117
        isInvariantTimeStampCounter = false;
moel@191
   118
moel@191
   119
      if (coreCount > 1)
moel@191
   120
        totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
moel@191
   121
      else
moel@191
   122
        totalLoad = null;
moel@191
   123
      coreLoads = new Sensor[coreCount];
moel@191
   124
      for (int i = 0; i < coreLoads.Length; i++)
moel@191
   125
        coreLoads[i] = new Sensor(CoreString(i), i + 1,
moel@191
   126
          SensorType.Load, this, settings);
moel@191
   127
      cpuLoad = new CPULoad(cpuid);
moel@191
   128
      if (cpuLoad.IsAvailable) {
moel@191
   129
        foreach (Sensor sensor in coreLoads)
moel@191
   130
          ActivateSensor(sensor);
moel@191
   131
        if (totalLoad != null)
moel@191
   132
          ActivateSensor(totalLoad);
moel@191
   133
      }
moel@191
   134
moel@203
   135
      if (hasTimeStampCounter) {
moel@238
   136
        ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
moel@279
   137
moel@279
   138
        EstimateTimeStampCounterFrequency(
moel@279
   139
          out estimatedTimeStampCounterFrequency, 
moel@279
   140
          out estimatedTimeStampCounterFrequencyError);  
moel@238
   141
        
moel@238
   142
        ThreadAffinity.Set(mask);
moel@203
   143
      } else {
moel@201
   144
        estimatedTimeStampCounterFrequency = 0;
moel@203
   145
      }
moel@203
   146
moel@203
   147
      timeStampCounterFrequency = estimatedTimeStampCounterFrequency;                  
moel@191
   148
    }
moel@191
   149
moel@275
   150
    private static Identifier CreateIdentifier(Vendor vendor,
moel@275
   151
      int processorIndex) 
moel@275
   152
    {
moel@275
   153
      string s;
moel@275
   154
      switch (vendor) {
moel@275
   155
        case Vendor.AMD: s = "amdcpu"; break;
moel@275
   156
        case Vendor.Intel: s = "intelcpu"; break;
moel@275
   157
        default: s = "genericcpu"; break;
moel@275
   158
      }
moel@275
   159
      return new Identifier(s,
moel@275
   160
        processorIndex.ToString(CultureInfo.InvariantCulture));
moel@275
   161
    }
moel@275
   162
moel@279
   163
    private void EstimateTimeStampCounterFrequency(out double frequency, 
moel@279
   164
      out double error) 
moel@279
   165
  {     
moel@279
   166
      double f, e;
moel@279
   167
      
moel@191
   168
      // preload the function
moel@279
   169
      EstimateTimeStampCounterFrequency(0, out f, out e);
moel@279
   170
      EstimateTimeStampCounterFrequency(0, out f, out e);
moel@191
   171
moel@279
   172
      // estimate the frequency
moel@279
   173
      error = double.MaxValue;
moel@279
   174
      frequency = 0;
moel@279
   175
      for (int i = 0; i < 5; i++) {
moel@279
   176
        EstimateTimeStampCounterFrequency(0.025, out f, out e);
moel@279
   177
        if (e < error) {
moel@279
   178
          error = e;
moel@279
   179
          frequency = f;
moel@279
   180
        }
moel@279
   181
moel@279
   182
        if (error < 1e-4)
moel@279
   183
          break;
moel@279
   184
      }                
moel@191
   185
    }
moel@191
   186
moel@279
   187
    private void EstimateTimeStampCounterFrequency(double timeWindow, 
moel@279
   188
      out double frequency, out double error) 
moel@279
   189
    {
moel@191
   190
      long ticks = (long)(timeWindow * Stopwatch.Frequency);
moel@236
   191
      ulong countBegin, countEnd;
moel@191
   192
moel@191
   193
      long timeBegin = Stopwatch.GetTimestamp() +
moel@191
   194
        (long)Math.Ceiling(0.001 * ticks);
moel@191
   195
      long timeEnd = timeBegin + ticks;
moel@279
   196
moel@191
   197
      while (Stopwatch.GetTimestamp() < timeBegin) { }
moel@236
   198
      countBegin = Opcode.Rdtsc();
moel@279
   199
      long afterBegin = Stopwatch.GetTimestamp();
moel@279
   200
moel@191
   201
      while (Stopwatch.GetTimestamp() < timeEnd) { }
moel@236
   202
      countEnd = Opcode.Rdtsc();
moel@279
   203
      long afterEnd = Stopwatch.GetTimestamp();
moel@191
   204
moel@279
   205
      double delta = (timeEnd - timeBegin);
moel@279
   206
      frequency = 1e-6 * 
moel@279
   207
        (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / delta;
moel@279
   208
moel@279
   209
      double beginError = (afterBegin - timeBegin) / delta;
moel@279
   210
      double endError = (afterEnd - timeEnd) / delta;
moel@279
   211
      error = beginError + endError;
moel@191
   212
    }
moel@191
   213
moel@236
   214
moel@191
   215
    private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
moel@191
   216
      uint eax, edx;
moel@238
   217
      if (Ring0.RdmsrTx(msr, out eax, out edx, 1UL << thread)) {
moel@191
   218
        r.Append(" ");
moel@191
   219
        r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
moel@191
   220
        r.Append("  ");
moel@191
   221
        r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
moel@191
   222
        r.Append("  ");
moel@191
   223
        r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
moel@191
   224
        r.AppendLine();
moel@191
   225
      }
moel@191
   226
    }
moel@191
   227
moel@191
   228
    protected virtual uint[] GetMSRs() {
moel@191
   229
      return null;
moel@191
   230
    }
moel@191
   231
moel@191
   232
    public override string GetReport() {
moel@191
   233
      StringBuilder r = new StringBuilder();
moel@191
   234
moel@191
   235
      switch (vendor) {
moel@195
   236
        case Vendor.AMD: r.AppendLine("AMD CPU"); break;
moel@191
   237
        case Vendor.Intel: r.AppendLine("Intel CPU"); break;
moel@191
   238
        default: r.AppendLine("Generic CPU"); break;
moel@191
   239
      }
moel@191
   240
moel@191
   241
      r.AppendLine();
moel@191
   242
      r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
moel@191
   243
      r.AppendFormat("Number of Cores: {0}{1}", coreCount,
moel@191
   244
        Environment.NewLine);
moel@191
   245
      r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
moel@191
   246
        Environment.NewLine);
moel@191
   247
      r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@191
   248
        "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
moel@201
   249
      r.AppendLine("Time Stamp Counter: " + (hasTimeStampCounter ? (
moel@201
   250
        isInvariantTimeStampCounter ? "Invariant" : "Not Invariant") : "None"));
moel@191
   251
      r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@279
   252
        "Estimated Time Stamp Counter Frequency: {0} MHz",
moel@279
   253
        Math.Round(estimatedTimeStampCounterFrequency * 100) * 0.01));
moel@279
   254
      r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@279
   255
        "Estimated Time Stamp Counter Frequency Error: {0} Mhz",
moel@279
   256
        Math.Round(estimatedTimeStampCounterFrequency *
moel@279
   257
        estimatedTimeStampCounterFrequencyError * 1e5) * 1e-5));
moel@279
   258
      r.AppendLine(string.Format(CultureInfo.InvariantCulture,
moel@201
   259
        "Time Stamp Counter Frequency: {0} MHz",
moel@201
   260
        Math.Round(timeStampCounterFrequency * 100) * 0.01));   
moel@191
   261
      r.AppendLine();
moel@191
   262
moel@191
   263
      uint[] msrArray = GetMSRs();
moel@191
   264
      if (msrArray != null && msrArray.Length > 0) {
moel@191
   265
        for (int i = 0; i < cpuid.Length; i++) {
moel@191
   266
          r.AppendLine("MSR Core #" + (i + 1));
moel@191
   267
          r.AppendLine();
moel@191
   268
          r.AppendLine(" MSR       EDX       EAX");
moel@191
   269
          foreach (uint msr in msrArray)
moel@191
   270
            AppendMSRData(r, msr, cpuid[i][0].Thread);
moel@191
   271
          r.AppendLine();
moel@191
   272
        }
moel@191
   273
      }
moel@191
   274
moel@191
   275
      return r.ToString();
moel@191
   276
    }
moel@191
   277
moel@191
   278
    public override HardwareType HardwareType {
moel@191
   279
      get { return HardwareType.CPU; }
moel@191
   280
    }
moel@191
   281
moel@236
   282
    public bool HasModelSpecificRegisters {
moel@236
   283
      get { return hasModelSpecificRegisters; }
moel@236
   284
    }
moel@236
   285
moel@201
   286
    public bool HasTimeStampCounter {
moel@201
   287
      get { return hasTimeStampCounter; }
moel@201
   288
    }
moel@201
   289
moel@201
   290
    public double TimeStampCounterFrequency {
moel@201
   291
      get { return timeStampCounterFrequency; }
moel@191
   292
    }
moel@191
   293
moel@191
   294
    public override void Update() {
moel@222
   295
      if (hasTimeStampCounter && isInvariantTimeStampCounter) {
moel@236
   296
moel@236
   297
        // make sure always the same thread is used
moel@238
   298
        ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
moel@222
   299
moel@222
   300
        // read time before and after getting the TSC to estimate the error
moel@222
   301
        long firstTime = Stopwatch.GetTimestamp();
moel@236
   302
        ulong timeStampCount = Opcode.Rdtsc();
moel@191
   303
        long time = Stopwatch.GetTimestamp();
moel@222
   304
moel@236
   305
        // restore the thread affinity mask
moel@238
   306
        ThreadAffinity.Set(mask);
moel@236
   307
moel@191
   308
        double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
moel@222
   309
        double error = ((double)(time - firstTime)) / Stopwatch.Frequency;
moel@222
   310
moel@222
   311
        // only use data if they are measured accuarte enough (max 0.1ms delay)
moel@222
   312
        if (error < 0.0001) {
moel@222
   313
moel@222
   314
          // ignore the first reading because there are no initial values 
moel@222
   315
          // ignore readings with too large or too small time window
moel@222
   316
          if (lastTime != 0 && delta > 0.5 && delta < 2) {
moel@222
   317
moel@222
   318
            // update the TSC frequency with the new value
moel@222
   319
            timeStampCounterFrequency =
moel@201
   320
              (timeStampCount - lastTimeStampCount) / (1e6 * delta);
moel@222
   321
          }
moel@191
   322
moel@191
   323
          lastTimeStampCount = timeStampCount;
moel@191
   324
          lastTime = time;
moel@191
   325
        }        
moel@191
   326
      }
moel@191
   327
moel@191
   328
      if (cpuLoad.IsAvailable) {
moel@191
   329
        cpuLoad.Update();
moel@191
   330
        for (int i = 0; i < coreLoads.Length; i++)
moel@191
   331
          coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
moel@191
   332
        if (totalLoad != null)
moel@191
   333
          totalLoad.Value = cpuLoad.GetTotalLoad();
moel@191
   334
      }
moel@191
   335
    }
moel@266
   336
moel@266
   337
    public virtual void Close() {
moel@266
   338
moel@266
   339
    }
moel@191
   340
  }
moel@191
   341
}