APIC Timer: Difference between revisions

1,029 bytes added ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(7 intermediate revisions by 4 users not shown)
Line 1:
The great benefit of the Local [[APIC]] timer is that it is hardwired to each CPU core, unlike the [[PIT|Programmable Interval Timer]] which is a separate circuit. Because of this, there is no need for any resource management, which makes things easier. The downside is that it's oscillating at (one of) the CPU's frequencies, which varies from machine to machine, while the PIT uses a standard frequency (11931821,193,182 Hz). To make use of it, you have to know how many interrupts/sec it's capable of.
 
== APIC Timer Modes ==
Line 42:
 
== Initializing ==
''Please note that this is the recommended way of determining the frequency of the APIC timer.''
There are several ways to do this, but all of them use a different, CPU bus frequency independent clock source to do that. Examples: [[RTC|Real Time Clock]], [[TSC|TimeStamp Counter]], PIT or even polling [[CMOS#Getting_Current_Date_and_Time_from_RTC|CMOS registers]]. In this tutorial we will use the good old PIT, as it's the easiest. Steps need to be done:
 
'''NOTE:''' According to Intel's documentation for IA-32 (x86) and Intel 64 (x86_64), APIC timer's frequency is equal to the bus' frequency '''OR''' the core crystal's frequency divided by the chosen frequency divider. The bus' and the core crystal's frequency can be found in the [[CPUID|CPUID]] functions [https://sandpile.org/x86/cpuid.htm#level_0000_0015h 0x15] and [https://sandpile.org/x86/cpuid.htm#level_0000_0016h 0x16], respectively. Through CPUID.0x15 can also be determined the TSC frequency. The frequency of the APIC timer depends on whether the system is using local APIC or ''discrete'' APIC ([https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller 82489DX]). When the local APIC is built in core's crystal, the APIC timer is using core's frequency. Otherwise, it is using the bus frequency.
 
 
There are several ways to do this, but all of them use a different, CPU bus frequency independent clock source to do that. Examples: [[RTC|Real Time Clock]], [[TSC|TimeStamp Counter]], PIT or even polling [[CMOS#Getting_Current_Date_and_Time_from_RTC|CMOS registers]]. In this tutorial we will use the good old PIT, as it's the easiest. Steps that need to be done:
* Reset APIC to a well known state
* Enable APIC timer
Line 52 ⟶ 57:
* Make the APIC timer fire an interrupt at every X ticks
 
The APIC timer can be set to make a tick (decrease counter) at a given frequency, which is called "divide value". This means you have to multiply APIC timer counter ticks by this divide value to get the true CPU bus frequency. You could use a value of 1 (ticks on every bus cycle) up to 128 (ticks on every 128th cycle). See Intel manual vol3A Chapter 9.5.4 onfor details. Note that according to my tests, Bochs seems not to handle divide value of 1 properly, so I will use 16.
 
=== Prerequisites ===
Before we start, let's define some constant and functions.
<sourcesyntaxhighlight lang="asm">
apic = the linear address where you have mapped the APIC registers
 
Line 93 ⟶ 99:
writegate: ...
ret
</syntaxhighlight>
</source>
I will also assume that you have a working [[IDT]], and you have a function to write a gate for a specific interrupt: writegate(intnumber,israddress).
Furthermore, to make things simple, I'll assume that you did not changedchange the default interrupt mapping found in almost every tutorial:
* interrupt 0-31: exceptions
* interrupt 32: timer, IRQ0
Line 104 ⟶ 110:
=== Example code in ASM ===
Here's a possible way to initialize APIC timer in fasm syntax assembly:
<sourcesyntaxhighlight lang="asm">
;you should read MSR, get APIC base and map to "apic"
;you should have used lidt properly
Line 208 ⟶ 214:
;although I have found buggy hardware that required it
mov dword [apic+APIC_TMRDIV], 03h
</syntaxhighlight>
</source>
 
=== Example code in C ===
This code is an example of how to initialize the APIC timer so that it ticks every 10 milliseconds. This is done by letting the APIC timer run, waiting for 10ms using the PIT and then getting the number of ticks that were done from the APIC timer. It assumes that you have functions to "write"/"read" the APIC's registers and "pit_prepare_sleep"/"pit_perform_sleep" to perform an as accurate as possible measuringmeasurement of the timerstimer's frequency.
<sourcesyntaxhighlight lang="c">
void apic_start_timer() {
// Tell APIC timer to use divider 16
Line 237 ⟶ 243:
write(APIC_REGISTER_TIMER_INITCNT, ticksIn10ms);
}
</syntaxhighlight>
</source>
 
== See also ==
Line 247 ⟶ 253:
=== External Links ===
* [http://www.intel.com/products/processor/manuals/ Volume 3A:System Programming Guide, Part 1,manuals has a chapter on the APIC]
* [http://www.osdever.net/tutorials/pdfview/apic.pdfadvanced-programming-interrupt-controller Advanced Programmable Interrupt Controller by Mike Rieker]
 
[[Category:Interrupts]]
[[Category:TimeTimers]]