/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: ApicTimer.cs // namespace Microsoft.Singularity.Hal { using System; using System.Diagnostics; using System.Runtime.CompilerServices; public class ApicTimer { // // Constants // private readonly byte [] divisors = new byte [] { 11 /* 1 */, 0 /* 2 */, 1 /* 4 */, 2 /* 8 */, 3 /* 16 */, 8 /* 32 */, 9 /* 64 */, 10 /* 128 */ }; private const uint TimerPending = 1u << 12; private const uint TimerMasked = 1u << 16; private const uint TimerPeriodic = 1u << 17; private const uint TimeSpanHz = 10 * 1000 * 1000; private const long maxInterruptInterval = TimeSpanHz / 10; // 100ms private const long minInterruptInterval = TimeSpanHz / 2000; // 500us private const long interruptGranularity = TimeSpanHz / 100000; // 10us // // Members // private Apic apic; private uint divisor = 1; private uint busFrequency = 100000000; private uint frequency; // == busFrequency / divisor private byte interrupt; // // Methods // internal ApicTimer(Apic apic) { this.apic = apic; SetDivisor(1); SetOneShot(); SetInterruptEnabled(false); interrupt = apic.IrqToInterrupt(Apic.TimerIrq); } internal void Finalize() { } internal byte Initialize() { SetInterruptVector(interrupt); SetDivisor(1); SetOneShot(); SetBusFrequency(busFrequency); return interrupt; } internal void Start() { SetInterruptEnabled(true); SetNextInterrupt(maxInterruptInterval); } [NoHeapAllocation] internal void SetBusFrequency(uint measuredFrequency) { busFrequency = measuredFrequency; for (divisor = 1; divisor <= 128; divisor *= 2) { frequency = busFrequency / divisor; if (frequency < 100 * 1000 * 1000) { break; } } SetDivisor(divisor); } /// /// Set processor clock bus divider. /// /// /// /// Amount to divide by. Must be a power of 2 between 1 and 128. /// [NoHeapAllocation] internal void SetDivisor(uint amount) { for (int i = 0; i < divisors.Length; i++) { if (amount <= (1u << i)) { uint v = apic.Read(ApicOffset.TimerDivideConf) & ~0xfu; v |= (uint)divisors[i]; apic.Write(ApicOffset.TimerDivideConf, v); divisor = 1u << i; return; } } } /// /// Get processor clock bus divider. /// [NoHeapAllocation] internal uint GetDivisor() { uint v = apic.Read(ApicOffset.TimerDivideConf) & 0xbu; for (int i = 0; i < divisors.Length; i++) { if ((uint)divisors[i] == v) { return 1u << i; } } return ~0u; } [NoHeapAllocation] internal uint GetCurrentCount() { return apic.Read(ApicOffset.TimerCurrentCount); } internal uint Value { [NoHeapAllocation] get { return GetCurrentCount(); } } [NoHeapAllocation] internal void SetInitialCount(uint value) { apic.Write(ApicOffset.TimerInitialCount, value); } [NoHeapAllocation] internal void SetInterruptEnabled(bool enabled) { uint r = apic.Read(ApicOffset.LvtTimer) & ~TimerMasked; if (enabled == false) { r |= TimerMasked; } apic.Write(ApicOffset.LvtTimer, r); } [NoHeapAllocation] internal bool InterruptEnabled() { return (apic.Read(ApicOffset.LvtTimer) & TimerMasked) == 0; } [NoHeapAllocation] private void SetInterruptVector(byte interrupt) { uint r = (apic.Read(ApicOffset.LvtTimer) & ~0xffu) | interrupt; apic.Write(ApicOffset.LvtTimer, r); } [NoHeapAllocation] internal void SetPeriodic() { uint r = apic.Read(ApicOffset.LvtTimer) & ~TimerPeriodic; apic.Write(ApicOffset.LvtTimer, r | TimerPeriodic); } [NoHeapAllocation] internal void SetOneShot() { uint r = apic.Read(ApicOffset.LvtTimer) & ~TimerPeriodic; apic.Write(ApicOffset.LvtTimer, r); } internal byte Interrupt { [NoHeapAllocation] get { return interrupt; } } [NoHeapAllocation] public void ClearInterrupt() { apic.Write(ApicOffset.EoiRegister, 0); SetNextInterrupt(maxInterruptInterval); } /// /// Set relative time of next interrupt. /// /// Relative time of next interrupt in units /// of 100ns. The time should be with the range between /// from SetNextInterruptMinDelta to /// SetNextInterruptMaxDelta. /// true on success. /// [NoHeapAllocation] public bool SetNextInterrupt(long delta) { DebugStub.Assert(delta <= maxInterruptInterval); SetInitialCount(TimeSpanToTimerTicks(delta)); return true; } /// /// Maximum value accepted by SetNextInterrupt (in units of 100ns). /// public long MaxInterruptInterval { [NoHeapAllocation] get { return maxInterruptInterval; } } /// /// Minimum value accepted by SetNextInterrupt (in units of 100ns). /// public long MinInterruptInterval { [NoHeapAllocation] get { return minInterruptInterval; } } /// /// Granularity of interrupt timeout (in units of 100ns). /// public long InterruptIntervalGranularity { [NoHeapAllocation] get { return interruptGranularity; } } [NoHeapAllocation] public long TimerToTimeSpanTicks(uint timerTicks) { long r = ((long) TimeSpanHz * (long) timerTicks) / frequency; return r; } [NoHeapAllocation] public uint TimeSpanToTimerTicks(long timeSpanTicks) { return (uint) (((long) frequency) * timeSpanTicks / TimeSpanHz); } } }