singrdk/base/Kernel/Singularity.Hal.ApicPC/ApicTimer.cs

242 lines
7.4 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: ApicTimer.cs
//
2008-11-17 18:29:00 -05:00
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
2008-03-05 09:52:00 -05:00
namespace Microsoft.Singularity.Hal
{
2008-11-17 18:29:00 -05:00
public class ApicTimer : HalTimer
2008-03-05 09:52:00 -05:00
{
//
// Constants
//
private readonly byte [] divisors = new byte [] {
11 /* 1 */, 0 /* 2 */, 1 /* 4 */, 2 /* 8 */,
2008-11-17 18:29:00 -05:00
3 /* 16 */, 8 /* 32 */, 9 /* 64 */, 10 // 128
2008-03-05 09:52:00 -05:00
};
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;
2008-11-17 18:29:00 -05:00
private readonly TimeSpan maxInterruptInterval = new TimeSpan(TimeSpanHz / 10); // 100ms
private readonly TimeSpan minInterruptInterval = new TimeSpan(TimeSpanHz / 2000); // 500us
private readonly TimeSpan interruptGranularity = new TimeSpan(TimeSpanHz / 100000); // 10us
2008-03-05 09:52:00 -05:00
//
// 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);
}
2008-11-17 18:29:00 -05:00
internal void ReleaseResources()
2008-03-05 09:52:00 -05:00
{
}
internal byte Initialize()
{
SetInterruptVector(interrupt);
SetDivisor(1);
SetOneShot();
SetBusFrequency(busFrequency);
2008-11-17 18:29:00 -05:00
DebugStub.Print("ApicTimer interrupt is {0}\n", __arglist(interrupt));
2008-03-05 09:52:00 -05:00
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);
}
/// <summary>
/// Set processor clock bus divider.
/// </summary>
///
/// <param name ="amount">
/// Amount to divide by. Must be a power of 2 between 1 and 128.
/// </param>
[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;
}
}
}
/// <summary>
/// Get processor clock bus divider.
/// </summary>
[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]
2008-11-17 18:29:00 -05:00
public override void ClearInterrupt()
2008-03-05 09:52:00 -05:00
{
2008-11-17 18:29:00 -05:00
Microsoft.Singularity.Hal.Platform p = Microsoft.Singularity.Hal.Platform.ThePlatform;
apic.AckIrq(Apic.TimerIrq);
SetNextInterrupt(maxInterruptInterval);
2008-03-05 09:52:00 -05:00
}
/// <summary>
/// Set relative time of next interrupt.
///
2008-11-17 18:29:00 -05:00
/// <param name="delta">
/// Relative time of next interrupt.
/// The time should be with the range between from <c>SetNextInterruptMinDelta</c> to
2008-03-05 09:52:00 -05:00
/// <c>SetNextInterruptMaxDelta</c></param>.
/// </summary>
[NoHeapAllocation]
2008-11-17 18:29:00 -05:00
public override void SetNextInterrupt(TimeSpan delta)
2008-03-05 09:52:00 -05:00
{
2008-11-17 18:29:00 -05:00
DebugStub.Assert(Processor.InterruptsDisabled());
DebugStub.Assert(delta >= minInterruptInterval);
2008-03-05 09:52:00 -05:00
DebugStub.Assert(delta <= maxInterruptInterval);
SetInitialCount(TimeSpanToTimerTicks(delta));
}
/// <value>
/// Maximum value accepted by SetNextInterrupt (in units of 100ns).
/// </value>
2008-11-17 18:29:00 -05:00
public override TimeSpan MaxInterruptInterval
2008-03-05 09:52:00 -05:00
{
[NoHeapAllocation]
get { return maxInterruptInterval; }
}
/// <value>
/// Minimum value accepted by SetNextInterrupt (in units of 100ns).
/// </value>
2008-11-17 18:29:00 -05:00
public override TimeSpan MinInterruptInterval
2008-03-05 09:52:00 -05:00
{
[NoHeapAllocation]
get { return minInterruptInterval; }
}
[NoHeapAllocation]
2008-11-17 18:29:00 -05:00
public TimeSpan TimerToTimeSpanTicks(uint timerTicks)
2008-03-05 09:52:00 -05:00
{
2008-11-17 18:29:00 -05:00
return new TimeSpan(((long) TimeSpanHz * (long) timerTicks) / frequency);
2008-03-05 09:52:00 -05:00
}
[NoHeapAllocation]
2008-11-17 18:29:00 -05:00
public uint TimeSpanToTimerTicks(TimeSpan timeSpan)
2008-03-05 09:52:00 -05:00
{
2008-11-17 18:29:00 -05:00
return (uint) (((long) frequency) * timeSpan.Ticks / TimeSpanHz);
2008-03-05 09:52:00 -05:00
}
}
}