A timer is essential to the working of many electronics circuits. For modern consumer devices, the software won’t work properly if the timer does not behave as expected. This blog post presents a quick look at something which is quite ubiquitous: real-time clock.
Back in the days, an IBM PC machine (or compatible one) sets the date/time to a predefined value everything it starts, Tuesday, January 1, 1980. It was quite common to place the DATE
in the AUTOEXEC.BAT
so that the user can supply the correct date/time. This information is however lost when the power is down.
To solve this annoyance, later models employed a real-time clock, a simple machinery to keep track of the time, even when the system is switched off. This can be an external or a built-in circuitry. IBM PC/AT started to have this feature by using MC146818 chip from Motorola, now Freescale Semiconductor (see also the 1984 version of the datasheet). There are many other compatible drop-in substitutes, e.g DS12885 from Dallas Semiconductor (now part of Maxim) and M48T86 from STMicroelectronics. If you have played with all kind of motherboards, you know that it has this CMOS battery which ensures, among others, that the date and time are always correct because it keeps powering the circuit even if the main supply is cut off.
The functional diagram (from the datasheet) of MC146818 is shown below. Typically a crystal is connected to the chip at OSC1
and OSC2
pins.
How to get and set the date/time? This is done via register read and write. Register 7, 8, and 9 are for day of the month, month, and year, respectively. There is also register 6 to know the day of the week, in case there is no room to implement the calendar algorithm to find it the harder way. The current time is obtained or altered via register 0, 2, 4, each for the seconds, minutes, and hours.
A series of divider gives the ability to prescale the frequency. A crystal with a frequency of 32.768 KHz is particularly useful since it can be combined with a 15-bit prescaler to give a good 1 Hz signal for the clock.Note that the prescaler can be programmed as well, specially to trigger IRQ
(interrupt request) at a predefined frequency. In the datasheet, Table 5 gives possible different values for Register A, essentially setting up the frequency divider to invoke IRQ at any rate (depending on the crystal frequency) from 8 KHz to 2 Hz. If you use a microcontroller, MC146818 does not only help keep tracking of date and time, this extra IRQ can be used for triggering any periodic tasks.
Now the fun part is trying to figure out what modern operating systems do with the real-time clock? Fortunately, it’s rather easy to answer that question with a little bit of experiment using a virtualization approach. We will take a look at QEMU, a very capable open-source emulator. If you want to play with QEMU yourself, read my previous blog post on building QEMU.
A quick glance at the code proves that it does already support real-time clock by emulating MC146818 (surprise!) , hw/mc146818rtc.c in its source tree. The handling of setting and getting the time has been there since the early days of QEMU, the custom time interval by Register A programming was implemented as early as 2004. Note that it is also possible to use VirtualBox. If you look at VirtualBox source code, its implementation of real-time clock, VBox/Devices/PC/DevRTC.cpp, is pretty much derived from the QEMU version.
To monitor what the virtualized operating system does with respect to MC146818 we can just active the logging for two important functions: cmos_ioport_read
and cmos_ioport_write
. Launching Windows and checking the log gives something like:
cmos: read index=0x00 val=0x14 cmos: read index=0x02 val=0x11 cmos: read index=0x04 val=0x05 cmos: read index=0x06 val=0x04 cmos: read index=0x07 val=0x29 cmos: read index=0x08 val=0x08 cmos: read index=0x09 val=0x12 cmos: write index=0x00 val=0x14 cmos: write index=0x02 val=0x11 cmos: write index=0x04 val=0x05 cmos: write index=0x06 val=0x03 cmos: write index=0x07 val=0x29 cmos: write index=0x08 val=0x08 cmos: write index=0x09 val=0x12
which clearly proves that Windows reads the date/time during its system initialization and possibly adjusts it as well. Running QEMU with Linux demonstrates the same behavior.
What about the IRQ
? Monitoring what happens to register A shows that either Linux or Windows do not really do something useful with it. The guest OS leaves the value at its default (0x26
) which corresponds to a divider of 32 (see Table 5 and 6 in the datasheet), giving the output signal at a frequency of 1024 Hz (assuming 32 KHz crystal is being used).
As a final note, a software implementation of MC146818 can be found also in the source code of MAME, Multiple Arcade Machine Emulator. This is interesting because it means some arcade machines out there are using it. If you are a fan of classic games, real-time clock investigation for such arcades is left as an exercise for you.
That’s all about real-time clock! Don’t hesitate to dig the datasheet to find more useful info and sprinkle QEMU/MAME with more logging to investigate how an operating system is using the RTC chip. In some weeks, let’s have a look at programmable interval timer.