Any alternitive timing solution?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Ryu

Any alternitive timing solution?

Post by Ryu »

Greetings, I'm just wondering is there any alternitive timing chips on the standard 0x86 that is not software driven? The PIT IRQ-0 and CMOS RTC IRQ-8 is accurate in its sense, however the problem I'm facing is if I require accurcy then I should never disable interrupts, if doing so can miss some ticks.. but I do require to disable interrupts in some occasions.

The only solution I have with these two timers is using rdtsc instruction. When one timing service at x frequency is entered which will take the number of clocks via rdtsc and subtracts it with the previous nclocked and then updates the time based on processor frequency. The thing about this is I think it would run into problems when a computer goes into low power mode in laptops.

This isn't really a big issue to keep accurate times but I sure would like too. :)
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Any alternitive timing solution?

Post by Candy »

You could let the RTC keep your time and read it out regularly, and you can make all your interrupt-disabled code run very shortly with short periods of interrupt-enabled in between, so that you don't miss any interrupts (except for #STPCLK, but that would be equivalent to cheating on your OS).
nick8325
Member
Member
Posts: 200
Joined: Wed Oct 18, 2006 5:49 am

Re:Any alternitive timing solution?

Post by nick8325 »

Ryu wrote: Greetings, I'm just wondering is there any alternitive timing chips on the standard 0x86 that is not software driven? The PIT IRQ-0 and CMOS RTC IRQ-8 is accurate in its sense, however the problem I'm facing is if I require accurcy then I should never disable interrupts, if doing so can miss some ticks.. but I do require to disable interrupts in some occasions.
You shouldn't lose ticks unless you disable interrupts for a long time.

Whenever a tick comes around, the PIT asks the PIC to signal an interrupt to the processor. If the PIC is disabled (e.g. you haven't sent EOI to a previous interrupt) then the PIC will wait until it's enabled. Then it'll signal the processor.

If the processor has interrupts disabled, it won't forget about the IRQ - it will call the interrupt handler as soon as you enable interrupt handling again.

So you'll get a timer tick as soon as the processor and PIC have interrupts enabled, even if they were disabled when the timer signalled the tick. You'll only lose a tick if you disable interrupts for so long that the timer ticks *again* before you handle the first tick - the PIC will just remember that the timer has fired, and not that it has fired twice. So if interrupts are disabled (and unmasked at the PIC) for less than the tick length you should be fine :)
mystran

Re:Any alternitive timing solution?

Post by mystran »

Like said, you'll never lose a tick because your interrupts were disabled when it occured. You only lose if you keep them disabled so long that you should have got two ticks.

The cure to this is avoid doing very long computations with interrupts disabled. It's good idea for other reasons as well (like avoiding audio clicking, mouse cursor jamming, network packet loss, and so on).

If you still miss a lot of ticks, then you probably have too high frequency with your timer. Some systems set timer frequency depending on processor speed. High resolution might not be too important on slow hardware anyway.

You can also use the PIT, but periodically synchronize your time with RTC. Missing ticks will then affect your timing only temporarily, until the next sync. Then you'll be as accurate as the RTC, but still have most of the simplicity of using a PIT.

Finally, if you really want to reliably track realtime, there's just one solution: use ntp (or something similar) to keep it synchronized with the official time. It's not like your PIT is likely to pulse at exactly the nominal frequency anyway, and RTC does drift as well.

Ofcourse you can cascade these, using ntp to sync the RTC to official time, then syncing your tick-time to that every few minutes, and calculate accurate average frequency for your ticktimer by keeping running average of drift amounts; you can then compensate this on the fly. If you want to go really high-tech, you can estimate sub-tick time with RDTSC and keep running average for clock-cycles-per-tick to react to processor frequency changes.

But before you go implementing the above, you should first decide how much inaccuracy your system can tolerate. Then it's easier to decide what's worth the trouble.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Any alternitive timing solution?

Post by Brendan »

Hi,
mystran wrote:But before you go implementing the above, you should first decide how much inaccuracy your system can tolerate. Then it's easier to decide what's worth the trouble.
Also work out how well your code (and applications) can handle "time warps" - if a device driver needs a 300 ms delay and you resync the clock back 10 seconds it might cause problems. Timestamps on files (combined with "make" and backup utilities for e.g.) can be another problem.

One way around this is to sync slowly. For example, make the clock tick once every 0.9 seconds or every 1.1 seconds rather than every second until it's correct.

It'd also be good to work out how much drift there was each time you sync and expect the same amount of drift for the next sync (and account for it). Of course here you'd have problems with "jitter", so you'd want to keep track of average drift for the last N syncs.


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.
Ryu

Re:Any alternitive timing solution?

Post by Ryu »

Hellos, and thanks for your replies.

Nick: Yes I'm aware that if you don't disable interrupts for more then the time that is dued then you shouldn't miss a tick. This is what I thought it does first, once timer counter goes to zero it stays zero and interrupts, once EOI is sent it then resets timer and recounts. However, with some test this isn't the case. My test shows on Bochs and my real hardware that these counters continue to count even if interrupts are disabled (in Bochs, even on mode 1). So, whether or not interrupts are disabled you are going to be inaccurate and have lost a tick every so often. It also makes each tick entered at irregular frequency (I'm quite sure the chip is very very accurate, its just how you are going to exploit its behavior and somehow syncronize).

mystran: My operating system isn't even close to be network or internet ready. But for sure I will take this in account in another time. I had in mind for these timers was towards more for the task scheduler and video syncronizing though. RDTSC is out of the question.

Brendan: The time warps is a big consideration, after you mentioned it I've tested RTC and noticed it does this jitter once every 60 seconds. It goes ahead 1 second and then resyncs somehow and goes back a second. I don't know what the cause for it but I can't have that in my system counter. For drifts and such, it really doesn't matter to be syncronized with real time, but syncronized in a regular frequency (if that makes any sense).


I think what candy said is the right way on doing this, But I will avoid RTC and leave that for real time. For my system counter that will handle scheduling and video syncronizing it will be based on two PIT counters, using counter 0 as the higher output frequency and counter 2 and the lower output frequency. Basically the higher frequency reads the lower frequency's counter and updates the system counter. This way it is syncronized and only loose a tick once its out of the tolerance of lower and higher fequency.

The only problem now is Bochs seem to react differnt from my real hardware. I am using counter 2 on the PIT, disabled the GATE via port 61h before counter 2 is set up. Then after, reading counter 2 is the same as counter 0 for some funny reason. Any one have any clues?

edit: Oops that is my error, I was subracting the counter value with the wrong value. :P
nick8325
Member
Member
Posts: 200
Joined: Wed Oct 18, 2006 5:49 am

Re:Any alternitive timing solution?

Post by nick8325 »

Ryu wrote: My test shows on Bochs and my real hardware that these counters continue to count even if interrupts are disabled (in Bochs, even on mode 1).
Isn't that what should happen? If your timer ticks every 10ms (say), and to handle a timer tick you disable interrupts for 1ms, then if the timer stops until you enable interrupts:
1. The timer will tick, and set itself to 0 and stop.
2. The kernel will do stuff for 1ms and then enable interrupts.
3. The timer will start again with 10ms left to go.
4. The timer will tick 10ms later, 11ms after the last tick. So the timer will be 1ms slow on each 10ms.

But if the timer carries on ticking while interrupts are disabled:
1. The timer will tick, and start again with 10ms left to go.
2. The kernel will do stuff for 1ms and then enable interrupts.
3. The timer will carry on completely obliviously. By now it will have 9ms left.
4. The timer will tick 9ms later, 10ms after the last tick. So it will tick correctly.

Am I just confused? ???
mystran

Re:Any alternitive timing solution?

Post by mystran »

Lets clear this now.

There is a processor, and there is a PIT (the timer).

The processor and PIT normally work independently. The processor will tell PIT if it wants to change PIT settings, but other than that, the PIT will do it's countdown on it's own internal clock frequency independent of what the processor is doing.

When the PIT will done, it sends a signal to PIC, which will set a bit "IRQ0 has been sent at least once" and tries to interrupt the processor. Once the processor has been interrupted, the processor will clear (with EOI) the "IRQ0 has been sent.." bit.

PIT itself doesn't know if it's interrupt gets processed, and it need not care either. That's PIC's problem.

If you use oneshot PIT, it'll stop once it reaches 0. You have to reset the counter to get a new timer. You will never miss any interrupts, because you will manually have to set it again to get another, but your time will lag depending on how long it takes you to set the PIT again.

Alternatively, you use periodic PIT. Now you never lag any, because as soon as the PIT has notified the PIC, it'll start counting again. Only if additional periods expire before the first interrupt is processed, we will lose any interrupts, and then we'll lag a full period every time this happens.

Most systems would want to use periodic timer, and simply set the frequency such, that they can always process the previous timer within the period. The timer itself will never lag whatever you do (it doesn't even know if it's interrupts ever get delivered), but you simply might not get notification about every tick if you're too slow -> hence "missed ticks".
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Any alternitive timing solution?

Post by Brendan »

Hi,
Ryu wrote:Brendan: The time warps is a big consideration, after you mentioned it I've tested RTC and noticed it does this jitter once every 60 seconds. It goes ahead 1 second and then resyncs somehow and goes back a second. I don't know what the cause for it but I can't have that in my system counter. For drifts and such, it really doesn't matter to be syncronized with real time, but syncronized in a regular frequency (if that makes any sense).
For the RTC, the electronics in the RTC are slow and it can take a little while for it to do everything it has to. If you read it at the wrong spot you can get very dodgy results - for example, this is mostly how the RTC updates it's time & date values (assuming that the starting time = 31/12/1999 at 23:59:59):

[tt]- set "update in progress" flag
- wait for about 244 us
- increment seconds counter, time = 31/12/1999 at 23:59:00
- if seconds didn't roll over then clear "update in progress" flag and return
- increment minutes counter, time = 31/12/1999 at 23:00:00
- if minutes didn't roll over then clear "update in progress" flag and return
- increment hour counter, time = 31/12/1999 at 00:00:00
- if hour didn't roll over then clear "update in progress" flag and return
- increment day counter, time = 1/12/1999 at 00:00:00
- if day didn't roll over then clear "update in progress" flag and return
- increment month counter, time = 1/1/1999 at 00:00:00
- if month didn't roll over then clear "update in progress" flag and return
- increment year counter, time = 1/1/1900 at 00:00:00
- if year didn't roll over then clear "update in progress" flag and return
- increment century counter, time = 1/1/2000 at 00:00:00
- clear "update in progress" flag and return[/tt]

As you can see, if you don't check the "update in progress" flag the value you read could be as much as a century wrong. :)

The update in progress flag is bit 7 of RTC register A. To avoid getting the wrong time you need to check if the update in progress flag is set and wait for it to be clear if it is set. You also need to read the time and date quickly - if it takes you more than about 240 us then you could start reading it just before the update in progress flag is set and still get the wrong result. This means you need to disable IRQs while reading the time and date. You also need to make sure that nothing changes the RTC address before you read the data from that address (i.e. an IRQ handler and other CPUs).

Also, my reference material says that an RTC update can take up to 2 milliseconds, so you probably won't want interrupts to be disabled while you wait for the update in progress flag to become clear.

Adding it all up, the general idea is something like this:

Code: Select all

.retry:
    call acquire_CMOS_reentrancy_lock

    mov al,0x0A
    out 0x70,al         ;CMOS address = register A
    IODELAY
    in al,0x70          ;al = register A
    test al,0x80        ;Is update in progress?
    je .getTime         ; no, safe to get time & date

    call free_CMOS_reentrancy_lock
    call do_small_delay
    jmp .retry

.getTime:
    ** Read the time and date here **

    call free_CMOS_reentrancy_lock
For "single CPU only" you can just use CLI and STI instead of using the "CMOS_reentrancy_lock" calls.

As you can see, this is ugly and slow and would suck completely if your OS reads the time and date often. It's usually much better to read the RTC time and date once during boot and then configure the RTC to generate an IRQ each second. Your IRQ handler would update the OS's internal time and date values so that the RTC itself doesn't need to be read.

This is also a good idea for other reasons - mostly you won't know if the RTC is set to "wall clock time" or if it's set to Universal Co-ordinated Time (UTC) and may need to convert from one to the other. There's also a huge "daylight saving mode" mess that you'd need to handle...


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.
earlz

Re:Any alternitive timing solution?

Post by earlz »

this is a pretty wild idea but what if when you needed to disable ints you set the timer freq to say 1sec or even longer if needed and then when you enable ints again if your timer has already went off then wait for another tick so your accurate and then convert 1sec or whatever to what your normal time measurment is and change the timer freq back; and if the timer did not go off then wait until it does go off and convert it, and then reset the freq.

well theres my wild post for the day
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Any alternitive timing solution?

Post by Brendan »

Hi,
Jordan3 wrote:this is a pretty wild idea but what if when you needed to disable ints you set the timer freq to say 1sec or even longer if needed and then when you enable ints again if your timer has already went off then wait for another tick so your accurate and then convert 1sec or whatever to what your normal time measurment is and change the timer freq back; and if the timer did not go off then wait until it does go off and convert it, and then reset the freq.
Apart from within the task switch code you should never need to disable IRQs for more than around 10 instructions. If you do need to disable IRQs for longer than this then there's a better way of doing things that you've overlooked. :)


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.
blip

Re:Any alternitive timing solution?

Post by blip »

Slight correction, 0x71 is the data port IIRC:

Code: Select all

    mov al,0x0A
    out 0x70,al        ;CMOS address = register A
    IODELAY
    in al,0x71          ;al = register A
    test al,0x80        ;Is update in progress?
    je .getTime        ; no, safe to get time & date
Ryu

Re:Any alternitive timing solution?

Post by Ryu »

Brendan: Thanks for that info, let me add.. what a pain in the neck to check that bit each time. The PIT has a simular flag before you should read its counter and I had to fix up my code for the PIT as well.

I have some things to clear up too, I really mean the register counter. Which is part of the PIT internal bus, and is independant from the processor. Reading these counters can be converted as 18.2Hz (and a bunch of other decimals) for each time the PIT has decremented it. Therefore if you set the counter at 10000h which is setting the LSB and MSB to zero, then next zero occurance in the counter register, the output frequency is 1.19MHz (1193180Hz to be precise).

Nick: What your wondering, I'm also wondering actually. Fact is.. they do still count within the register, it would then depend on what mode your using and so and so. I can't really test it because they count so fast I'm not sure if anything is reset.

I haven't had enough time to fine tune my double counter method it is a bit off due to the time it spent disabling GATE and reading the counter and re-enabling GATE. However its quite close to RTC. :)
Post Reply