1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-06-28 15:32:56 +03:00

update making-of

This commit is contained in:
Bas Wijnen 2009-06-28 22:44:44 +02:00
parent c2dbe6e1e9
commit bfc65c7e9a

View File

@ -349,11 +349,13 @@ This worked well. Now I expected to get a timer interrupt soon after jumping
to the idle task. After all, I have set up the compare register, the timer
should be running and I enabled the interrupts. However, nothing happened. I
looked at the contents of the count register, and found that it was 0. This
means that it is not actually counting at all. Looking at the Linux sources,
they don't use this timer either, but instead use the cpu-external (but
integrated in the chip) timer. The documentation says that they have a
different reason for this than a non-functional cpu timer. Still, it means it
can be used as an alternative.
means that it is not actually counting at all.\footnote{I also checked the
random register, which didn't seem to change either. This is a huge
performance problem, but it is easily solved by changing the random register
manually.} Looking at the Linux sources, they don't use this timer either, but
instead use the cpu-external (but integrated in the chip) timer. The
documentation says that they have a different reason for this than a
non-functional cpu timer. Still, it means it can be used as an alternative.
Having a timer is important for preemptive multitasking: a process needs to be
interrupted in order to be preempted, so there needs to be a periodic interrupt
@ -476,7 +478,13 @@ enough to spend kernel space on. But I can change my mind on that later.
The last thing to do for now is allowing a memory to be listed. That is,
having a suitably priviledged capability to a Memory should allow a program to
see what's in it. In particular, what objects it holds, and where pages are
mapped. Probably also what messages are in a receiver's queue.
mapped. Probably also what messages are in a receiver's queue. For now, I
postponsed the actual implementation of this, but I have reserved the code.
This is possibly the hardest kernel operation to implement, because a list of
items does not have a hard limit on its size. For other operations, it is
possible to return a value in a register, or in a page (which needs to be
provided by the caller). But in this case, that is not guaranteed to be
possible. So I need to think about how to do this.
\section{A name for the kernel}
However, at this point I am publishing the existence of the kernel, and so I
@ -488,4 +496,86 @@ logo which is furrier than a winged boot or staff. So I chose Iris, who is
also a messenger of gods, but she has a rainbow symbol. This is much nicer for
creating a logo.
\section{Device drivers}
It's time to do some real testing of the kernel. So I've read the Linux
keyboard driver source, and implemented the same functionality in a boot
thread. During kernel load, several boot threads are started. At first, it is
just this one.
The keyboard of the device is like any other keyboard, except that it doesn't
have a keyboard controller. So the cpu must do this task itself. A keyboard
is built as a matrix of copper wires, organised in rows and columns. Every
intersection is a key. Pressing the key makes a connection between the row and
the column wire. In the Trendtac, there are 8 rows and 17 columns. All of
these lines go to a general purpose input/output pin on the cpu. The keyboard
driver sets 0 volt on each column in turn, and reads the rows, which are set as
pull-up inputs. If they are not connected, the pull-up makes them return 1.
But if the key of the column which is scanned is pressed, the 0 is connected to
the row line and 0 is read out. Thus the entire keyboard can be read.
Linux does all this in kernel space. That means it can access the GPIO ports
in kseg2 (unmapped and uncached physical memory). In user space, this is not
possible. User space programs can only use mapped memory. So the page with
the GPIO ports needs to be mapped to the device driver's address space. For
this, I added an operation to the thread capability. Not because it has
something to do with a thread, but because every process has its own thread
capability, so no special other capability is needed. I'm adding some more
priviledged operations while I'm at it: allocate physical memory to a Page
object is what I need here. Make a thread ``priviledged'', which means it can
use coprocessor 0, and perform these operations. Get a capability for the top
memory. Register an interrupt handler. I think these should be enough, but I
can always add more, because threads don't need so many operations. I also
added a debug operation, which blinks the lock leds. This operation will be
removed once the display is working.
Writing the keyboard driver was as easy as could be expected: I had some
problems with the meaning of the bits in the registers (does 1 mean input or
output?), and for some reason the above scheme was needed and doing the other
way (scanning the rows and reading the columns) didn't work. But for the rest,
it wasn't very troublesome. And I was happy to see that it is indeed possible
to address device memory through the tlb (so using a mapping). Had that not
been possible, then the device drivers would have been forced into the kernel.
The resulting keyboard driver uses maximum cpu time, because I don't have a
timer interrupt yet, and it flashes the leds when a key is pressed or released,
without telling which key it was. The final driver will be much better. Of
course it will send messages for key events instead of flashing the leds. It
will also be interrupt-driven: when no keys are pressed, all columns will be
set to 0, and all rows will be set to input with pull-up enabled and interrupt
on falling edge. Then no scanning is required. When a key is pressed, the
keyboard will be scanned periodically (on the timer interrupt) until no key is
pressed anymore. It is not possible to use an interrupt-driven approach while
a key is pressed, because there is no way to set up the lines such that there
will always be a change when a key is pressed or released. That's not a
problem: scanning doesn't take much time, and when the keyboard is being used,
the machine is active anyway. While no keys are pressed it makes sense to
minimize power consumption, so then the interrupt-driven approach is more
important.
\section{Display driver}
The next thing to write is a display driver. With a keyboard and a display, it
starts to look like a real computer. However, this proved to be a lot harder
than I expected.
First of all, it wasn't entirely clear which part of the Linux driver I needed
to copy. It has support for many displays on all kinds of mips devices, and I
only want support for the hardware in the machine. After some searching, it
seems that the Trendtac uses the ``pmpv1'' settings.
Now the display consists of two parts: the pixels and the backlight. I started
with the easy part, the backlight. It is connected to a pulse-width-modulator
(pwm) of the cpu. This means that the cpu has some logic to make very fast
pulses of well-defined width. Connecting this to a light allows software to
set the intensity of the output. This means the backlight can be dimmed.
That's nice. Or well, it should be. When copying the Linux code, I can switch
the backlight on and off, but the pwm doesn't seem to work. That's the third
counter\footnote{a pwm is implemented using a counter to determine the pulse
width} that doesn't count: the count register, the random register and the pwm.
\section{Clocks}
Because this didn't feel good, I decided to implement the timer interrupt
first. I copied some code for it from Linux and found, as I feared, that it
didn't give any interrupts. I suppose the os timer isn't running either.
\end{document}