In this milestone you will implement a clock driver, and the two timer-related system calls. The timer-related system calls must be implemented using your driver.
Although the kernel provides a SystemClock
system
call, for many purposes, e.g., benchmarking, a higher clock
resolution is desired. The U4600 machines feature a chip which
contains several high-frequency counters which can be used for a
timer.
Your driver needs to export the interface specified in src/clock.h. There are the following functions:
The interface is just an internal funciton call interface. You
do not need to export this interface to the users. User programs
will indirectly access the clock driver through the
time
and sleep
syscalls.
The IPC message sent by the timer contains in MR0 a return code (TIMER_R_OK, TIMER_R_UINT, TIMER_R_CNCL) and in MR1 the present value of the real-time clock.
This is a simplified block diagram of the U4600 boards. The GT chip is a PCI bridge. It acts to interface the CPU with the PCI bus as well as to memory. Full details of the GT chip can be found its reference manual [1].
The R4600 has 8 IRQ exceptions consisting of 2 "software IRQs", 5 external hardware IRQs, and 1 IRQ that can be configured via external logic to either the internal timer (which it is on the U4600) or another external IRQ.
Your main job is to learn how to program the GT chip to generate timer interrupts and how to write an L4 driver to handle these interrupts.
The GT chip contains four 24 bit timer/counters. For our purposes, we are only interested in the GT chip as our source of timer interrupts. You will only be using one of these timers. The GT's interrupt line is connected to CPU interrupt pin 3 (which corresponds to interrupt 5 when you take into account the two software interrupts).
There is an abundance of different devices and writing device drivers is usually seen as a very difficult task. This is true in a sense. However, programming a device is really just a matter of learning about its registers, what values to read and write to those registers and when to do it.
The minimal subset of the GT chip's functionality that you must understand and use is listed below (the numbers in parentheses are the offsets of the register addresses from the chip's base address). Refer to the reference manual for full(er) descriptions [1].
Again, this is only the minimal understanding that you need. For deeper understanding, you are encouraged to learn about the other registers and can even play with those as well.
NOTE: This section is deliberately kept short (e.g., we do not dictate which timer to use or in what mode to use it in). The idea is that you work things out for yourself and make your own design and implementation decisions. There are only two conditions that must be satisfied:
The Pistachio/MIPS kernel exports specific interrupts to a user level
interrupt handler via IPC. User level threads are associated with
interrupts by the privileged task using the
ThreadControl
call. After that, any interrupts of
the registered number will get sent as an IPC to the interrupt
handler thread.
Refer to the L4Ka reference manual for further information on the interrupt registration and delivery protocol.
You are provided with a libdevice which simplifies the
process of registering an interrupt handler. You can are free to
either use libdevice
or manually register for
interrupts yourself.
In Pistachio/MIPS, device registers are memory mapped. That is, hardware registers can be accessed via normal load/store operations to special addresses. To access device registers, you must first request the correct device mapping from .
Note that all accesses device registers must bypass the cache. When requesting device mappings from you must specify that you want unchached mappings. Again using libdevice should simplify the task of obtaining device mappings.
Device frequently use a different byte order than the CPU, so device drivers must generally deal with endianess, and your clock driver is no exception. Endianess refers to how indivdual bytes within a multi-byte word are addressed. The following two figures should illustrate how big and little endian works. In the figure the numbers refer to the relative addresses of the individual bytes.
The R4600 is big endian while all GT chip registers are little endian. Therefore, you will need to do some byte swapping when reading from and writing to the GT registers.
You may need to resolve some or all of these issues:
The file clock.h, as well as some other header files you might find useful, can be found here.
Should be able to show some test code that uses all the function specified in the driver interface.
You should also show some user level test code that uses the
time_stamp
and sleep
system calls. You
may find it useful to extend sosh
to have a
time
and sleep
command. The
sleep
implemention must use your
clock driver.