Best divisor for 8253/8254 PIT
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Best divisor for 8253/8254 PIT
When initializing the 8253/8254 PIT we set the frequency by means of setting a counter as divisor for 1193181, which is the frequency of the oscilator. I've seen somwhere that Linux uses 100 ticks per second
Is this a wise number?
Even when 1193181 is a big weird number it is not prime, its integer factors are:
3, 11 ,19 ,33 ,57 ,121 ,173 ,209 ,363 ,519 ,627 ,1903 ,2299 ,3287 ,5709 ,6897 ,9, 861 ,20933 , 36157 ,62799 , 108471 and 397727.
Wouldn't it be better to use 121 or 57 instead of 100 as to have a better approximation to the real ticks per second?
Also why so many examples are using 1193181 when 1193182 is a better approximation to 1193181.6666 Hz ?
Is this a wise number?
Even when 1193181 is a big weird number it is not prime, its integer factors are:
3, 11 ,19 ,33 ,57 ,121 ,173 ,209 ,363 ,519 ,627 ,1903 ,2299 ,3287 ,5709 ,6897 ,9, 861 ,20933 , 36157 ,62799 , 108471 and 397727.
Wouldn't it be better to use 121 or 57 instead of 100 as to have a better approximation to the real ticks per second?
Also why so many examples are using 1193181 when 1193182 is a better approximation to 1193181.6666 Hz ?
NaN - Not a Nerd
Working on: Physical Memory Management with a 5-lod mipmap XD
Working on: Physical Memory Management with a 5-lod mipmap XD
Code: Select all
mov cx, 11931
call reset_pit
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Wether 1193181(2)(.666) is a wise number or not - it does not matter. This number is defined by the PIT and CAN NOT BE CHANGED. Instead we have to adapt to using this number.
Read the PIT article for more information
Read the PIT article for more information
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Don't RTFM me please. RTPA (Read the post again)
I'm not talking about changing the oscillator frequency which theorically ticks 1193181.666 times per second and for historical reasons (one third of the NTSC color sub-carrier needed for the CGA) I'm talking about the divisor used to get the desired TICKS_PER_SEC. If we set the internal PIT counter to a number wich is not an integer factor of 1193181/2 then we loose some precission when counting ticks per second. (That we already loose because of the periodic .6666666)
If we set the PIT counter to 11931 (about 1193181 / 100) as in the egos example then we really tick 100.00684485793311541362836308775 times per second. If we count seconds with integers we have an error of 0.6844%.
If we set the PIT counter to 9861 (exactly 1193181 / 121), we tick 121.00006753878916945543048372376 times per second, with an error of 0.0067%
Am I wrong?
That's what I'm taking about, not about changing the oscillator frequency
I'm not talking about changing the oscillator frequency which theorically ticks 1193181.666 times per second and for historical reasons (one third of the NTSC color sub-carrier needed for the CGA) I'm talking about the divisor used to get the desired TICKS_PER_SEC. If we set the internal PIT counter to a number wich is not an integer factor of 1193181/2 then we loose some precission when counting ticks per second. (That we already loose because of the periodic .6666666)
If we set the PIT counter to 11931 (about 1193181 / 100) as in the egos example then we really tick 100.00684485793311541362836308775 times per second. If we count seconds with integers we have an error of 0.6844%.
If we set the PIT counter to 9861 (exactly 1193181 / 121), we tick 121.00006753878916945543048372376 times per second, with an error of 0.0067%
Am I wrong?
That's what I'm taking about, not about changing the oscillator frequency
Last edited by Dandee Yuyo on Sun Nov 25, 2007 5:13 pm, edited 10 times in total.
NaN - Not a Nerd
Working on: Physical Memory Management with a 5-lod mipmap XD
Working on: Physical Memory Management with a 5-lod mipmap XD
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Since the oscillator frequency isn't an integer, there are no divisors that give the same pulses/sec for every second, hence you will always suffer precision loss this way.
And then there's the variance in the frequency between instances of the oscillator itself. You try to create precision out of nothing. If you are really bothered by precision, google for NTP.
And then you edited your post. And with it, the essence of the question.
There are the consequences of the change that nullify the improvements in the so-called error - when having 100 ticks/s the elapsed time between ticks is close to the integer 10 (in ms), try that with a divisor like 121. And then you'd need to consider the practical aspect of the events - you want not more than x ticks a second and no fewer than y ticks a second due to timing issues related with interrupts and scheduling.
It seems to me that you are looking for perfection in a human-made object. Bad Idea(tm).
And then you edited again, so that the question corresponding to the answer above got removed.
Btw, your factorial reduction is off.
And then there's the variance in the frequency between instances of the oscillator itself. You try to create precision out of nothing. If you are really bothered by precision, google for NTP.
And then you edited your post. And with it, the essence of the question.
The first part of your question made no sense to me. You were contradicting yourself, as noted above. I'll ignore the implied insult. Instead, RTFMDon't RTFM me please. RTPA
There are the consequences of the change that nullify the improvements in the so-called error - when having 100 ticks/s the elapsed time between ticks is close to the integer 10 (in ms), try that with a divisor like 121. And then you'd need to consider the practical aspect of the events - you want not more than x ticks a second and no fewer than y ticks a second due to timing issues related with interrupts and scheduling.
It seems to me that you are looking for perfection in a human-made object. Bad Idea(tm).
And then you edited again, so that the question corresponding to the answer above got removed.
This would be an excuse for me to delete everything helpful mentioned above. Will you please consider your ambiguous proverbs and the consequent chance of misinterpretation before you start blaming people because of the results.That's what I'm taking about, not about changing the oscillator frequency
Btw, your factorial reduction is off.
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Chill out. Why are you flaming? Excuse me but you did misunderstod my post:
Where I said:Wether 1193181(2)(.666) is a wise number or not
DOS used 18.2.. so I wonder myself why Linux and linux-biased examples use 100? Just because is round and nice? I know the oscillator has precission limitations. Then why add even some more error?...100 ticks per second. Is this a wise number?
Of course. I just need the NE2000 driver and the complete TCP/IP stack before.If you are really bothered by precision, google for NTP.
OK. How should I keep the time then? By trusting the CMOS?Yes, of course you loose prission, but, only in a place that doesn't need prission.
It's re-editing a post against some rule? Remove the optionAnd then you edited again
NaN - Not a Nerd
Working on: Physical Memory Management with a 5-lod mipmap XD
Working on: Physical Memory Management with a 5-lod mipmap XD
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
The PIT article that Combuster referred you to has everything you need.
You can use the PIT ports to set the PIT to interrupt at specific times - I set mine to tick once every millisecond. You never actually change the oscillator frequency, instead you choose when it needs to interrupt.
Linux does the same thing I do. If you want to see my code that sets it up to tick once every millisecond, just ask.
You can use the PIT ports to set the PIT to interrupt at specific times - I set mine to tick once every millisecond. You never actually change the oscillator frequency, instead you choose when it needs to interrupt.
Linux does the same thing I do. If you want to see my code that sets it up to tick once every millisecond, just ask.
I keep the time by reading the real-time clock's time. It works well for my purposes (you could even read it once at startup and then use the PIT, but that's a really bad idea).OK. How should I keep the time then? By trusting the CMOS?
You removed a question that Combuster answered, making Combuster's answer compeletely useless to anyone. You're not the only one who could benefit from this post - in the future someone might come across this post in a Google search.It's re-editing a post against some rule? Remove the option
Because it's a lot faster. You don't need to (in your IRQ0 handler) have code that divides a tick count by whatever number (which is so much more imprecise). Again, they reprogrammed the PIT to interrupt 100 times a second.DOS used 18.2.. so I wonder myself why Linux and linux-biased examples use 100? Just because is round and nice? I know the oscillator has precission limitations. Then why add even some more error?
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Hi mattman.
I already have the PIC and PIT reprogrammed, thanks.
And it worked as a breeze...
I was just wondering myself if it would be better to interrupt 121.0000 (again, shorten to four decimals) times a second and sharing my thoughts, but all I got where flames in place.
The PIT article that Combuster referred you to has everything you need.
I already have the PIC and PIT reprogrammed, thanks.
Ok, I'll try in spanish: Jesús, eso ya lo sé desde el primer post!You never actually change the oscillator frequency, instead you choose when it needs to interrupt.
You mean de CMOS? Are you reading it all the time?I keep the time by reading the real-time clock's time.
No you don't need to neither for 18.2 ticks per second case. I was doing (before reprogramming the PIT)You don't need to (in your IRQ0 handler) have code that divides a tick count by whatever number (which is so much more imprecise).
Code: Select all
second_ticks += 10;
if(second_ticks >= 182)
{
second_ticks -= 182;
++datetime.seconds;
And it worked as a breeze...
No, they reprogrammed the PIT to interrupt 100.0068 (shorten to four decimals) times a second.Again, they reprogrammed the PIT to interrupt 100 times a second.
I was just wondering myself if it would be better to interrupt 121.0000 (again, shorten to four decimals) times a second and sharing my thoughts, but all I got where flames in place.
NaN - Not a Nerd
Working on: Physical Memory Management with a 5-lod mipmap XD
Working on: Physical Memory Management with a 5-lod mipmap XD
Hi,
If your code expects 100 IRQs per second (or 121 IRQs per second) then there will be some error in your code. If your code expects 99.99846351547658956 IRQs per second (with divisor set to 11932) then there will still be an extremely small amount of error that can easily be completely ignored.
The main problem is that you're using integers to represent real numbers. Why?
I use fixed point maths instead. The timer IRQ ends up doing something like:
In this case:
Or, if the count is 11932 then "periodHigh = 10 ms" and "periodLow = 0x000A11D5". If you do things like this, then your software might lose one second every million years.
However, there's also error in the hardware itself - the PIT chip's base frequency is effected by the quality of the oscillator, heat, dust, etc. Unless you adjust for this then there's no point being extremely accurate in software. For example, the calculations for "periodHigh" and "periodLow" might just be initial calculations, and the OS might adjust them based on feedback from a time server or some other (more accurate) source.
Of course accuracy isn't the same as precision. Imagine you're measuring how long something takes, and you do "time_taken = end_time - start_time". If an IRQ occurs every 10 ms, then you could set "start_time" just before the IRQ occurs and you could set "end_time" just after an IRQ occurs. In this case you could do "time_taken = 90 - 60 = 30 ms" when in reality it only took 10.1 ms. This is called quantumization. The error you get can be up to double the time between IRQs; or if there's an IRQ every 10.0 ms the error will be up to (but less than) 20.0 ms.
To improve precision you'd want to reduce the time between IRQs (or increase the timer frequency). Of course to improve performance you'd want to increase the time between IRQs (or reduce the timer frequency). The amount of overhead the timer causes depends on the timer frequency and the CPU speed. For example, a 1000 Hz timer might use 1% of CPU time for a 25 MHz 80486 (and you might want to use a slower timer frequency to reduce overhead), but a 1000 Hz timer might use 0.01% of CPU time on a 2.5 Ghz CPU (and you might want to use a faster timer frequency to improve precision).
During boot, you might want to test CPU speed and then dynamically calculate the timer frequency to get the best compromise between precision and overhead for the computer (e.g. less overhead on slow computers and better precision on faster computers).
Of course CPU's don't run at a fixed speed anymore - if you do dynamically calculate the timer frequency during boot, then you might want to recalculate the timer frequency when CPU speeds change (e.g. when a CPU gets hot and starts using thermal throttling).
This can be done accurately if it's done right. To avoid problems caused by the quantumizing effect, you need to adjust the timer frequency when an IRQ occurs. For example:
In this case, if the kernel wants to change the timer frequency it just sets "newCount" and the change will be postponed until it can be done accurately.
Cheers,
Brendan
That depends on your code.Dandee Yuyo wrote:If we set the PIT counter to 11931 (about 1193181 / 100) as in the egos example then we really tick 100.00684485793311541362836308775 times per second. If we count seconds with integers we have an error of 0.6844%.
If we set the PIT counter to 9861 (exactly 1193181 / 121), we tick 121.00006753878916945543048372376 times per second, with an error of 0.0067%
Am I wrong?
If your code expects 100 IRQs per second (or 121 IRQs per second) then there will be some error in your code. If your code expects 99.99846351547658956 IRQs per second (with divisor set to 11932) then there will still be an extremely small amount of error that can easily be completely ignored.
The main problem is that you're using integers to represent real numbers. Why?
I use fixed point maths instead. The timer IRQ ends up doing something like:
Code: Select all
mov eax,[periodLow]
mov ebx,[periodHigh]
add [timerFractions],eax
adc [timerTick],ebx
Code: Select all
periodHigh = int( 1000 * count / (3579545/3) )
periodLow = (2^32 * (1000 * count / (3579545/3) ) & 0xFFFFFFFF
However, there's also error in the hardware itself - the PIT chip's base frequency is effected by the quality of the oscillator, heat, dust, etc. Unless you adjust for this then there's no point being extremely accurate in software. For example, the calculations for "periodHigh" and "periodLow" might just be initial calculations, and the OS might adjust them based on feedback from a time server or some other (more accurate) source.
Of course accuracy isn't the same as precision. Imagine you're measuring how long something takes, and you do "time_taken = end_time - start_time". If an IRQ occurs every 10 ms, then you could set "start_time" just before the IRQ occurs and you could set "end_time" just after an IRQ occurs. In this case you could do "time_taken = 90 - 60 = 30 ms" when in reality it only took 10.1 ms. This is called quantumization. The error you get can be up to double the time between IRQs; or if there's an IRQ every 10.0 ms the error will be up to (but less than) 20.0 ms.
To improve precision you'd want to reduce the time between IRQs (or increase the timer frequency). Of course to improve performance you'd want to increase the time between IRQs (or reduce the timer frequency). The amount of overhead the timer causes depends on the timer frequency and the CPU speed. For example, a 1000 Hz timer might use 1% of CPU time for a 25 MHz 80486 (and you might want to use a slower timer frequency to reduce overhead), but a 1000 Hz timer might use 0.01% of CPU time on a 2.5 Ghz CPU (and you might want to use a faster timer frequency to improve precision).
During boot, you might want to test CPU speed and then dynamically calculate the timer frequency to get the best compromise between precision and overhead for the computer (e.g. less overhead on slow computers and better precision on faster computers).
Of course CPU's don't run at a fixed speed anymore - if you do dynamically calculate the timer frequency during boot, then you might want to recalculate the timer frequency when CPU speeds change (e.g. when a CPU gets hot and starts using thermal throttling).
This can be done accurately if it's done right. To avoid problems caused by the quantumizing effect, you need to adjust the timer frequency when an IRQ occurs. For example:
Code: Select all
mov eax,[periodLow]
mov ebx,[periodHigh]
add [timerFractions],eax
adc [timerTick],ebx
mov ax,0
xchg [newCount],ax
test ax,ax
je .done
call changeTimer ;Change the timer divisor, and recalculate periodLow and periodHigh
.done:
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.
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Wow, that was an insightfull answer, thanks Brendan. In fact the +10 -182 thingie I was doing before programming the PIT is a sort of fixed point math too, fixed to one decimal...
What is the the irq0 "tick" used for then?
That's exactly what I'm doing. If it is a bad idea then what should I do? Ask the time to the CMOS everytime I need a timestamp?pcmattman: (you could even read it once at startup and then use the PIT, but that's a really bad idea).
What is the the irq0 "tick" used for then?
NaN - Not a Nerd
Working on: Physical Memory Management with a 5-lod mipmap XD
Working on: Physical Memory Management with a 5-lod mipmap XD
Hi,
If you are reading the time often (e.g. more often than once every 5 seconds) a better idea is to read the full time during boot (and convert it to whatever the OS uses), and then use the RTC's update interrupt (occurs once per second) or the periodic interrupt (which is programmable - from 2 Hz to 8192 Hz in binary steps, e.g. 2, 4, 8, 16, 32, etc) to keep the OS's time on track.
The other problem with reading the time directly from the RTC is that it is useless for measuring fractions of a second. This makes it inadequate for file system timestamps (unless your file system is severely broken - even FAT uses 100ths of a second for time stamps).
For example, my previous kernel uses the RTC's periodic interrupt for keeping track of real time (and dynamically calculated which frequency to use during boot), and then used either the local APIC timer/s (if present) or the PIT chip for scheduling (using "one-shot" mode to prevent unnecessary IRQs and improve precision).
Cheers,
Brendan
ThanksDandee Yuyo wrote:Wow, that was an insightfull answer, thanks Brendan. In fact the +10 -182 thingie I was doing before programming the PIT is a sort of fixed point math too, fixed to one decimal...
The CMOS is insanely slow (worst case is around 14 ms to wait for the "update in progress" flag and then read the full time), may need to be converted from local time or UTC to whatever the OS uses, and needs some re-entrancy protection so no-one else can change the address I/O port before you've read/written the data I/O port (disabling IRQs is enough for single-CPU systems). Because of these reasons the CMOS time shouldn't be read repeatedly (e.g. every time you create a time-stamp for the file system/s).Dandee Yuyo wrote:That's exactly what I'm doing. If it is a bad idea then what should I do? Ask the time to the CMOS everytime I need a timestamp?pcmattman: (you could even read it once at startup and then use the PIT, but that's a really bad idea).
If you are reading the time often (e.g. more often than once every 5 seconds) a better idea is to read the full time during boot (and convert it to whatever the OS uses), and then use the RTC's update interrupt (occurs once per second) or the periodic interrupt (which is programmable - from 2 Hz to 8192 Hz in binary steps, e.g. 2, 4, 8, 16, 32, etc) to keep the OS's time on track.
The other problem with reading the time directly from the RTC is that it is useless for measuring fractions of a second. This makes it inadequate for file system timestamps (unless your file system is severely broken - even FAT uses 100ths of a second for time stamps).
Some OS's have one timer for keeping track of real time, and seperate timer/s for scheduling.Dandee Yuyo wrote:What is the the irq0 "tick" used for then?
For example, my previous kernel uses the RTC's periodic interrupt for keeping track of real time (and dynamically calculated which frequency to use during boot), and then used either the local APIC timer/s (if present) or the PIT chip for scheduling (using "one-shot" mode to prevent unnecessary IRQs and improve precision).
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.
- Dandee Yuyo
- Member
- Posts: 47
- Joined: Fri Nov 09, 2007 6:46 pm
- Location: Argentina
Hi,
Cheers,
Brendan
The best source of information I've ever seen is a document called PCTIM003.TXT. It's a bit old, but the PIT and RTC are even older..Dandee Yuyo wrote:Periodic time programmable interrupts seems the way to go then... do you have any pointers with more information about it?
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.