singrdk/base/Kernel/Singularity.Hal.Omap3430/ClockLogger.cs

232 lines
8.2 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: ClockLogger.cs
//
// Useful reference URLs:
// http://developer.intel.com/design/archives/periphrl/index.htm
// http://developer.intel.com/design/archives/periphrl/docs/7203.htm
// http://developer.intel.com/design/archives/periphrl/docs/23124406.htm
//
// The basic ideas for this driver come from the MMOSA code,
// though the implementation differs. This is partly because
// the code needs to run on Virtual PC and it isn't able to do
// a very accurate emulation of the i8254.
//
// There are two source available for timing - the Real-Time
// Clock (RTC) and the programmable interval timer (PIT). The
// standard PC RTC is based on derivatives of the Motorola
// MC146818A. It's able to provide the time with a resolution
// of 1 second and also has a programmable periodic interrupt.
//
// The programmable interrupt timer is based on the i8254. It
// can be programmed in a variety of modes - we use it to
// generate an interrupt at a configurable time in the future
// and then reprogram it each interrupt. The maximum interrupt
// period is 65535 ticks of a 1.193MHz clock.
//
// We use both of the RTC and the programmable interrupt timer to
// maintain our estimate of the current time. The RTC provides granularity
// to with 1/64 seconds and the time is used to get an estimate to within
// 1/1.193 * 10e-6 seconds within each RTC interval.
//
// The key variables are:
//
// upTime - the time the system has been up. This variable gets
// updated during the periodic RTC interrupt handling
// (delta = 1/64Hz).
//
// pitLast - the last value programmed into the PIT. The PIT counts down
// and generates an interrupt at (or shortly after) the instant
// the current PIT value reaches zero.
//
// pitAccum - the accumulated time measured by the PIT since upTime
// was updated.
//
// The current kernel time is always:
// upTime + pitAccum + (pitLast - pitCurrent)
//
// The PIT is always programmed to run, either by the consumer of the timer
// interface or by the timer itself.
//
// Timer::SetNextInterrupt(t)
// pitAccum += (pitLast - pitCurrent)
// // program PIT (not shown)
// pitLast = t
//
// Timer::Interrupt()
// pitAccum += pitLast;
// // But PIT time may accumulate between interrupt dispatch and crossing
// // Zero so.
// if (pitCurrent != 0)
// pitAccum += (MaxPitValue - pitCurrent)
// // Inform user of interrupt
// if (userNotScheduledInterrupt)
// SetNextInterrupt(MaxInterruptInterval)
//
// RTC::Interrupt()
// pitLast = pitNow
// pitAccum = 0
// upTime += RTCInterruptPeriod
//
// All of these methods are atomic interrupt-wise.
//
// Note: if we want to test the accuracy of the timer over a
// period we can set RTC::Interrupt to just return without
// touching any variables. All of the time accumulated will end
// up in pitAccum.
//
// Conditionals
//
// TIMER_NO_GO - disables timer and scheduling of timer interrupts.
//
// RTC_NO_GO - disable RTC.
//
// DEBUG_TIMER - enable timer debug messages and boot-time diagnostics.
//
// DEBUG_CLOCK - enable clock debug messages
//
// LOG_CLOCK - log adjustments to clock time and dump out later.
//
// LOG_SNI - log calls to SetNextInterrupt to see what's being thrown in.
//
// Tip: When this code does not behave useful things to check
// are the interrupt rate and the rate of calls to
// SetNextInterrupt.
//
// #define VERBOSE
using Microsoft.Singularity.Io;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Diagnostics;
namespace Microsoft.Singularity.Hal
{
[ CLSCompliant(false) ]
internal sealed class ClockLogger
{
public struct Record
{
public int who;
public ulong when;
public ulong currentTime;
public long upTime;
public int pitLastClock;
public int pitNow;
public long cookie;
};
private static int currentRecord = 0;
private static Record [] entries = null;
private static int ignoreCount = 20000;
static ClockLogger()
{
InitializeEntries();
#if LOG_CLOCK
DebugStub.Print("Initializing ClockLogger.\n");
#endif // LOG_CLOCK
}
[System.Diagnostics.Conditional("LOG_CLOCK")]
internal static void InitializeEntries()
{
entries = new Record[10000];
}
[System.Diagnostics.Conditional("LOG_CLOCK")]
[NoHeapAllocation]
internal static void AddEntry(int who, RtcPitState rps, TimerOmap3430 timer, long cookie)
{
if (ignoreCount != 0) {
ignoreCount--;
return;
}
if (currentRecord == entries.Length)
return;
entries[currentRecord].who = who;
entries[currentRecord].cookie = cookie;
entries[currentRecord].when = Processor.CycleCount;
// int pitNow = timer.Timer2Read();
int pitNow = 0;
entries[currentRecord].currentTime =
(ulong)rps.GetKernelTicks(pitNow);
entries[currentRecord].upTime = rps.upTime;
entries[currentRecord].pitLastClock = rps.pitLastClock;
entries[currentRecord].pitNow = pitNow;
currentRecord = currentRecord + 1;
if (currentRecord == entries.Length) {
bool iflag = Processor.DisableInterrupts();
try {
DumpEntries();
DebugStub.Assert(false);
}
finally {
Processor.RestoreInterrupts(iflag);
}
}
}
[System.Diagnostics.Conditional("LOG_CLOCK")]
[NoHeapAllocation]
internal static void AddEntry(int who, RtcPitState rps, TimerOmap3430 timer)
{
AddEntry(who, rps, timer, 0);
}
[System.Diagnostics.Conditional("LOG_CLOCK")]
[NoHeapAllocation]
internal static void SetCookie(long cookie)
{
if (currentRecord < entries.Length)
entries[currentRecord].cookie = cookie;
}
[NoHeapAllocation]
static ulong ToMicros(ulong t, ulong t0, ulong tps)
{
return ((t - t0) * 10000000) / tps;
}
[System.Diagnostics.Conditional("LOG_CLOCK")]
[NoHeapAllocation]
static void DumpEntries()
{
DebugStub.Print("# Clock column 1 who\n");
DebugStub.Print("# Clock column 2 time in cpu cycles\n");
DebugStub.Print("# Clock column 3 time from clock\n");
DebugStub.Print("# Clock column 4 pitLastClock\n");
DebugStub.Print("# Clock column 5 pitNow\n");
DebugStub.Print("# Clock column 6 upTime\n");
DebugStub.Print("# Clock column 7 currentTime\n");
DebugStub.Print("# Clock column 8 cookie\n");
DebugStub.Print("# Clock column 9 record number\n");
for (int i = 0; i != currentRecord; i++) {
DebugStub.Print("Clock {0:d6} {1:d6} {2} {3} {4} {5} {6} {7}\n",
__arglist(
ToMicros(entries[i].when,
entries[0].when,
Processor.CyclesPerSecond),
ToMicros(entries[i].currentTime, 0, 10000000),
entries[i].pitLastClock,
entries[i].pitNow,
entries[i].upTime,
entries[i].currentTime,
entries[i].cookie,
i));
}
}
}
}