apic timer
apic timer
I'm working on some apic code right now and I'm trying to figure out how you can get the apic to fire off an interrupt every lets say...1 seconds. Based on the Intel book, says the timer is derived from the cpu bus clock speed. So how can you effectively set the apic to interrupt every second?
Re:apic timer
Hi,
To measure the CPU's bus speed you'd see how many times the APIC timer's count is decreased over a fixed time delay. After that it's fairly easy. For example:
You'll also want something like:
Depending on the duration of the fixed time value and the local APIC timer frequency you actually want, you may want to adjust the values used for the local APIC timer divider. I'd select timer divider values to get the best accuracy while preventing overflows (e.g. so it works up to 1 GHz bus frequency - nothing wrong with future-proofing). You might also want to play with how long the duration of the fixed time value is - 250 mS is relatively long considering the speed of even the slowest CPU's bus.
BTW for a frequency of 1 Hz it'd probably be better to use the RTC timer instead, but you knew that ...
Cheers,
Brendan
First (assuming the local APIC is enabled and ready), you have to find out how quick the CPU's bus speed is. Then you set the APIC timer divider and APIC timer count to values that are calculated from the measured bus speed.DruG5t0r3 wrote:I'm working on some apic code right now and I'm trying to figure out how you can get the apic to fire off an interrupt every lets say...1 seconds. Based on the Intel book, says the timer is derived from the cpu bus clock speed. So how can you effectively set the apic to interrupt every second?
To measure the CPU's bus speed you'd see how many times the APIC timer's count is decreased over a fixed time delay. After that it's fairly easy. For example:
Code: Select all
%define LAPICapicID 0x0020+LAPIC_BASE_ADDRESS
%define LAPICversion 0x0030+LAPIC_BASE_ADDRESS
%define LAPICtaskPriority 0x0080+LAPIC_BASE_ADDRESS
%define LAPICendOfInterrupt 0x00B0+LAPIC_BASE_ADDRESS
%define LAPIClogicalDestination 0x00D0+LAPIC_BASE_ADDRESS
%define LAPICdestinationFormat 0x00E0+LAPIC_BASE_ADDRESS
%define LAPICspurious 0x00F0+LAPIC_BASE_ADDRESS
%define LAPICinterruptCommandLow 0x0300+LAPIC_BASE_ADDRESS
%define LAPICinterruptCommandHigh 0x0310+LAPIC_BASE_ADDRESS
%define LAPICLVTtimer 0x0320+LAPIC_BASE_ADDRESS
%define LAPICLVTperfCounter 0x0340+LAPIC_BASE_ADDRESS
%define LAPICLVT_LINT0 0x0350+LAPIC_BASE_ADDRESS
%define LAPICLVT_LINT1 0x0360+LAPIC_BASE_ADDRESS
%define LAPICLVTerror 0x0370+LAPIC_BASE_ADDRESS
%define LAPICTIMERinitialCount 0x0380+LAPIC_BASE_ADDRESS
%define LAPICTIMERcurrentCount 0x0390+LAPIC_BASE_ADDRESS
%define LAPICTIMERdivider 0x03E0+LAPIC_BASE_ADDRESS
%define LTIMERdivideBy1 0x0000000B
%define LTIMERdivideBy16 0x00000003
%define LTIMERdivideBy128 0x0000000A
%define LTIMERperiodic 0x00020000
get_bus_speed:
;Install the IRQ handler for the local APIC timer
mov edi,LAPIC_timer_IRQ_handler
mov al,LAPIC_int_vector
call create_IDT_descriptor
;Tell the local APIC to use the LAPIC_int_vector, set it to "one-shot" mode and enable it
mov dword [LAPICLVTtimer],LAPIC_int_vector
;Set the local APIC timer divider to divide by 128
mov dword [LAPICTIMERdivider],LTIMERdivideBy128
;Configure the RTC or PIT timer for an IRQ every 250 mS (4 Hz)
; Note: The handler for this timer's IRQ just does "inc dword [value]"
call configure_RTC_or_PIT
;Wait for the start of the next RTC or PIT IRQ
mov eax,[value]
.wait1:
cmp [value],eax
je .wait1:
;Set the initial count to 0xFFFFFFFF, which starts the local APIC timer
mov dword [LAPICTIMERinitialCount],0xFFFFFFFF
;Wait for the next RTC or PIT IRQ
mov eax,[value]
.wait2:
cmp [value],eax
je .wait2:
;Stop the local APIC timer
mov [LAPICLVTtimer],LVTdisabled
;Read the local APIC timer count
mov ebx,[LAPICTIMERcurrentCount]
;Stop the PIT or RTC timer
call stop_RTC_or_PIT
;Work out how much the local APIC timer's count decreased
mov eax,0xFFFFFFFF ;eax = initial value used
sub eax,ebx ;eax = number of times it changed
;Calculate the CPU's bus frequency (Hz) in EDX:EAX
mov ebx,(128 / 4)
mul ebx
;Calculate the local APIC count for the frequency you want
mov ebx,FREQUENCY
div ebx
;Adjust it for a different local APIC timer divider
xor edx,edx
mov ebx,16
div ebx
;Set the new local APIC timer divider
mov dword [LAPICTIMERdivider],LTIMERdivideBy16
;Set the new local APIC timer count
mov [LAPICTIMERinitialCount],eax
;And then enable the local APIC timer in "periodic" mode and return
mov dword [LAPICLVTtimer], LAPIC_int_vector | LTIMERperiodic
ret
Code: Select all
LAPIC_timer_IRQ_handler:
mov dword [LAPICendOfInterrupt],0 ;Send the EOI
iretd
BTW for a frequency of 1 Hz it'd probably be better to use the RTC timer instead, but you knew that ...
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:apic timer
to me, that seems to be a very "inacurate science". I guess you'd really do have to get the RTC to control time. So what would the apic timer be usefull for? I'm guessing it can fire a lot more interrupts per second than anything else.
Re:apic timer
Hi,
The main use for the local APIC timer is schedulers - mostly because there's one for each CPU. For example, on my OS when the scheduler does a task switch it sets the local APIC timer to interrupt whenever the task should stop running (which depends on the task's priority), using "one shot" mode to minimize unnecessary interrupts. For this purpose accuracy isn't really that important (it doesn't worry me if a task gets to run for a millionth of a second longer or shorter).
For measuring real time I use the real time clock's periodic interrupt, and if the computer doesn't have a local APIC I use the PIT for the scheduler (which means the code to keep track of real time is done the same regardless of what the scheduler does).
Cheers,
Brendan
The local APIC timer is as accurate as the timer & code used to calibrate it, and can provide accurate timing for extremely small time values and very large values - a 100 MHz CPU bus gives delays as small as 10 nano-seconds and as large as (roughly) 5.5 seconds. Because it's built into the CPU there's no IO port delays or bus traffic overheads. It's the fastest and the most versatile timer the 80x86 has ever had.DruG5t0r3 wrote:to me, that seems to be a very "inacurate science". I guess you'd really do have to get the RTC to control time. So what would the apic timer be usefull for? I'm guessing it can fire a lot more interrupts per second than anything else.
The main use for the local APIC timer is schedulers - mostly because there's one for each CPU. For example, on my OS when the scheduler does a task switch it sets the local APIC timer to interrupt whenever the task should stop running (which depends on the task's priority), using "one shot" mode to minimize unnecessary interrupts. For this purpose accuracy isn't really that important (it doesn't worry me if a task gets to run for a millionth of a second longer or shorter).
For measuring real time I use the real time clock's periodic interrupt, and if the computer doesn't have a local APIC I use the PIT for the scheduler (which means the code to keep track of real time is done the same regardless of what the scheduler does).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.