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