From bfc65c7e9ad3d9085cad28e11c5390d974df3f53 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Sun, 28 Jun 2009 22:44:44 +0200 Subject: [PATCH] update making-of --- report/making-of.tex | 102 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 6 deletions(-) diff --git a/report/making-of.tex b/report/making-of.tex index ab71962..c5cc328 100644 --- a/report/making-of.tex +++ b/report/making-of.tex @@ -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}