StuBS
Loading...
Searching...
No Matches
APIC timer

The APIC architecture contains a high-resolution timer on each of the CPU-local APICs, which can be used to generate interrupts: The APIC timer.

Functionality of the APIC timer

A timer is implemented by a clock generator that emits rhythmic current pulses. For example, a quartz can be used for this purpose, which vibrates when a voltage is applied. The signal from the quartz is converted into a smooth square-wave signal by a specific circuit and amplified so that the logic gates can handle it.

Structure of the timer in the Local APIC

The signal is then divided by a prescaler (or divider) so that, for example, the frequency f can be used to generate f/2. The division into powers of two is particularly easy to realize using T-flip-flips connected in series. The APIC timer offers adjustable prescaler stages that advance the signal by powers of two.

The divided signal is then used to periodically count down a 32-bit counter (CCNT). This counter is pre-initialized with the value of the initial count register (ICNT) and then decremented by one with each clock pulse (after the prescaling). The CCNT thus indicates the current status of the timer. The value of the counter is compared against zero with each clock pulse; if it is zero, a timer interrupt is sent to the processor. In addition, the current count register is reset to the value of the initial count register (if the timer is running in periodic mode). The value of the ICNT is freely adjustable.

Frequency and ticks

The timer shall be used to send periodic interrupts, e.g. every 5 ms. To ensure that the timer sends interrupts at exactly this time interval, it is necessary to set the prescaler and the ICNT appropriately. This in turn depends on the frequency of the timer.

With the LAPIC timer, the frequency is not fixed but depends on the hardware. It must therefore be determined by calibration with another time source (where the frequency is known). We use the PIT (Programmable Interval Timer) for this; the code for using the PIT is already included in the handout code. This timer must be used to determine the frequency of the APIC timer in LAPIC::Timer::ticks(). This function returns the clock ticks per millisecond.

The frequency can be used to calculate the number of ticks required per interrupt (without divider) as follows:

\[ \mathrm{ticks} = \frac{\mu s \times \text{LAPIC::Timer::ticks()}}{ 1000 } \]

Please note that CCNT and ICNT are 32-bit registers and must not overflow.

When multiplying \(\mu\) with the value returned by LAPIC::Timer::ticks(), for example, the 32-bit value range may already overflow. In this case, the operation should therefore be performed in the 64-bit value space (uint64_t).

The correct divider and ICNT value must then be calculated from the ticks. The resulting values should set the timer as precisely as possible.

Programming the timer

Four 32-bit registers (memory-mapped) are responsible for programming the APIC timer:

AddressRegister name
0xfee00320LVT Timer Register (aka Timer Control Register)
0xfee00380Initial Count Register (ICNT)
0xfee00390Current Count Register (CCNT)
0xfee003e0Divide Configuration Register (TimeDIV)

The two count registers (ICNT and CCNT) are used to set the start value of the timer and to read out the current value. In addition, the timer can be stopped completely with an initial count value of 0. Subsequent writing of the ICNT with a value not equal to 0 starts the timer.

The behavior of the timer can be influenced via the control register and the divisor. The structure of the control register is:

Bit(s)ValueMeaning
31-19(reserved)
18-17Operating mode
0 one-shot (stops at 0)
1 periodic (starts again with ICR value)
2 TSC-Deadline (compare absolute time via a special register IA32_TSC_DEADLINE, ignore ICR and CCR)
3 reserved
16Interrupt mask
0 Interrupts are delivered
1 Interrupts are disabled
15-13(reserved)
12Interrupt status (read-only)
0 idle
1 Interrupt pending
11-8(reserved)
7-0Vector number
of the interrupt to be triggered

The assignment of the divisor to the corresponding register values can be found in the Intel manual, but is also already implemented by the auxiliary function LAPIC::Timer::getClockDiv() as part of the handout.