Computer Labs 2013/2014 - 1st Semester
Lab 6: The PC's Real Time Clock


1. Objectives

The objectives of this lab are twofold. First that you learn the operation of the PC's Real Time Clock (RTC) and how to use its low level interface. Second, that you learn how to combine C and assembly code.

2. What to Do

Write in C and in assembly language several functions that use the RTC's low level interface. The key functionality to implement is:

  1. To read and display the configuration of the RTC, namely the format used for date and time
  2. To read and display the date and the time
  3. To handle one of the 3 interrupt events

The RTC's interrupt handler should be implemented as an assembly function and must handle one of the interrupt events. Furthermore, the communication between the assembly and C parts should be done via variables declared in assembly.

Like in recent labs you are not given the prototypes of the functions to implement: the specification of these functions is part of your job. In specifying these functions, you should take into account that one of the goals of this lab is that you develop a kind of a function library for the RTC that you will be able to reuse in your project. Therefore, in grading your submission, we will take into account its flexibility/generality.

To make the task of grading your assignment feasible, you are required to implement the following test functions:

  1. int rtc_test_conf()
  2. int rtc_test_date()
  3. int rtc_test_int()

These functions are declared in header file test6.h, and file test6.c contains their implementation stubs. You may find it convenient to add your test code to that file; this way you will avoid mistakes in their definition. Section 5 describes what these functions should do.

IMP. In addition to these files, you should submit the file lab6.c, which should contain only the main() function. This function will be graded based on the flexibility it affords in testing your code. For grading your submission, we will use our own main() function.

2.1 Class Preparation

So that you can accomplish this lab's objectives, you should do some homework. In addition to read, and understand, this script and the class notes, you should:

  1. Create folder lab6 at the top level of your SVN repository;
  2. Write the main() function in lab6.c, that will allow you to test your code in a flexible way
  3. Write a preliminary version of test_conf that reads and displays the RTC configuration without any interpretation, i.e. you need not present the configuration in a human friendly way.

Obviously the files with your code should be added and transferred to the SVN repository, before the beginning of the class.

3. The PC's Real Time Clock (RTC)

The PC's RTC is a component that includes clock circuitry and has either 1) an internal alternative power supply, for example a battery, or 2) pins to connect to such an alternative power supply. This allows the RTC to keep track of the time and of the date, even when the computer is unplugged from power. The RTC also takes advantage of its alternative power supply to provide a set of memory registers whose content is non-volatile, and that is usually used by the BIOS to store configuration parameters.

From the point of view of its programming interface, the RTC offers an array of registers that can be individually accessed for either reading or writing. The first 10 registers are used to access clock-related information, such as date, time or alarm time. The next 4 registers are used for controlling that functionality. The remaining registers can be used as non-volatile memory for arbitrary purposes.

Access to the each register in the array is done via two registers, which are mapped at port addresses 0x70 and 0x71. To access to a a particular register in the array, a program must first write the address of that register to port 0x70. Then, depending on the type of access, is should either write to, or read from, port 0x71, the new contents, or the existing contents, respectively, of the register in the array.

The RTC is able to detect the occurrence of 3 events related to the clock functionality and, when properly configured, to generate interrupts on each of them. In order to enable interrupts, after an event has generated an interrupt, it is necessary to read Register C, i.e. the register in the array at address 12.

Compared to the KBC the RTC is a fairly simple device, although the asynchronous update of the clock-related registers raises some consistency issues when reading from/writing to those registers. This and other issues were presented in class. For details, you can read the class notes or, even better, read the data sheet of an RTC IC.

4. Minix 3 Notes

4.1 Assembly Programming in Minix

Minix 3 can use the GNU build system. Therefore, you can write your assembly code using the GNU assembler (GAS) assembly language (AT&T's syntax). You should also be able to use the so-called Intel's syntax, which is supported since version 2.10. Indeed, the version of GAS in the Minix version we are using is version 2.17, however I have not tried it. If you wish you can give it a try, but be warned that this syntax appears to be poorly documented (so you may find it useful this link to the GAS section of the Linux Assembly HOWTO).

For an example of a simple assembly function in AT&T's syntax, check pg. 18 of the class notes on Mixed C and Assembly Programming.

4.2 Executing I/O Directly (in Assembly)

Minix 3 device drivers, as your code, although privileged execute at user level. In Minix 3, and in almost all OS's, by default, user level programs are not allowed to execute I/O sensitive operations, such as IN, OUT, STI or CLI. Therefore, if your interrupt handler issues any of these instructions, it will abort as a result of a general protection exception.

However, the IA-32 ISA provides a mechanism that allows some processes to execute those operations even when running at user-level. This is based on the use of the IOPL (I/O Privilege Level) field of the Extended Flags (EF) register. This field specifies the minimum current privilege level of a process when it executes those operations. If the current privilege level is lower (meaning that it has more privileges) than that specified in that field of the EF register, then the process will be allowed to perform I/O sensitive operations.

Minix 3 offers a kernel call sys_enable_iop() that allows the IOPL of a process to be set to a value such that it is able to execute I/O sensitive operations, even though it executes at user level. The following code segment illustrates how this can be done.

sef_startup(); /* Enable IO-sensitive operations for ourselves */ sys_iopenable(SELF);

5. Test Code

So that we can grade your work, you are required to implement the following test functions. We will develop the code that will call them, so make sure that your implementation matches their prototypes, defined in test6.h (note that the prototype for rtc_test_int() may vary from section to section, thus you may need to change it in class).

In grading your submission, we will take into account not only whether your code does what it should, but also its quality, including its modularity.

5.1 rtc_test_conf()

The purpose of this function is to test whether your code is able to read the control/status registers of the RTC, and to interpret their content by displaying it in a format that is convenient for humans to read.

As mentioned above, the final version of this function should differ from that of the class preparation in that the latter needs not present the contents of the registers in a human-friendly way.

5.2 rtc_test_date()

The purpose of this function is to test whether your code is able to read the date/time registers of the RTC, and to interpret their content by displaying it in a format that is convenient for humans to read.

To make your job easier, you do not need to support all the formats the RTC can use to store the date and time. It is enough, to support the formats used in the default configuration of the RTC that you should have learned in the previous point.

The main challenge in this part is to ensure that the results shown are consistent. You are be required to use the approach that you will be told at the beginning of the class. (Different sections (turmas), may be required to use different approaches.) Failure to follow the instructions at the beginning of your lab class will be heavily penalized (between 50% and 67%).

5.3 rtc_test_int()

The purpose of this function is to test whether your code is able to handle one of the interrupt events generated by the RTC. Again, we will give the details of this part of the lab, at the beginning of the class. Failure to follow those instructions will be heavily penalized (between 50% and 67%)

Independently of the event to handle, as mentioned in Section 2, the interrupt handler for the RTC should be written in assembly, using the GNU assembler syntax. Furthermore, the variables used to pass information between the interrupt handler and the rest of your program should be defined in the assembly file.

6. Compiling and Running your Program

Follow the procedures described for the previous labs, but do not forget to modify the Makefile. In particular, the name of the file with the assembly code should terminate with .S (upper-case 's'), and be included in the line of the Makefile with the name of the files with C source code.

7. Configuring your Program

Your program invokes functions that are privileged. Thus, before you can run it, you need to add a file named lab6 to the /etc/system.conf.d/ directory to grant your program the necessary permissions.

Unless you use privileged functions that are not really necessary, the following entry is enough:

service lab6 { system DEVIO IRQCTL IOPENABLE ; ipc SYSTEM rs vm pm vfs ; io 40:4 # Timer 60 # KBC 64 # KBC 70:2 # RTC ; irq 0 # TIMER 0 IRQ 1 # KBD IRQ 8 # RTC 12 # AUX/MOUSE IRQ ; uid 0 ; };

8. Submission

As you complete the different milestones of this lab, you should commit your work to the SVN repository in Redmine. Remember that you have a 10 minute tolerance only at the end of the class.

I suggest that you also commit your code even if you have not completed the new functionality you are working on. I.e., I suggest that you use the SVN repository also as a backup. Although this is not the proper way of using an SVN repository, it may save your day. (As the platform we have been using appears not to be robust enough for some of you.) Nevertheless, your final version should compile and run, otherwise you will be severely penalized.

IMP. You should add to the SVN repository all files needed so that compilation of your code can be done by checking out the lab6 directory, and executing:

make

under that directory in the working copy.

References

Acknowledgments

This lab is based on a lab by João Cardoso and Miguel P. Monteiro for DJGPP running on Windows98.