CS 452/652 Winter 2023 - Kernel (Part 4)
Due Date: Thu, Mar 2, 2023, 9:00am
In the final part of your development of the kernel, you use the interrupt processing capabilities of your kernel to create tasks that provide interrupt driven input/output. User programs will then be able to call Getc(), Putc() and any other primitives you choose to implement to access input and output streams on either of the two UARTs. It is also expected that during this final stage of development you polish the code of your kernel, integrating tightly the functionality you have implemented and thereby making the code robust.
To demonstrate your serial I/O you are to redo the functionality of Assignment 0, but using interrupt-mediated I/O and task-structured application code. As well as testing your I/O this provides you with a valuable capability that you will certainly use when developing your train application. We explicitly expect that you will have these capabilities available for later demos.
The documentation you provide with this assignment should document your kernel completely, and will be evaluated with this in mind. If you have been providing good documentation as you went along, most of what you need to submit has already been written.
If you have skimped on error-checking in your kernel development, this is a good time to flesh it out. In doing so divide the errors you can detect into two categories:
You will probably find many more in the second category than in the first, because detecting an error is much easier that recovering from it. However, internal error-detection that requires reprogramming is very useful because in most cases it provides precise diagnosis of the error.
- errors from which it is possible to recover at run-time, and
- errors that require reprogramming.
This is the part of the kernel in which you have the most freedom to make your own design decisions.
To accomplish this part of the kernel you must have the following kernel primitives operating:
int UartWriteRegister(int channel, char reg, char data),
int UartReadRegister(int channel, char reg),
int Getc(int tid, int channel), and
int Putc(int tid, int channel, char ch).
See the kernel description for the details of how these primitives should operate. Keep in mind that you might add to this kernel specification sparingly and with good justification.
Aa variety of task structures are possible. You need to create at least one server, but possibly more: do what you think is best. Describe the task structure you chose, and why you chose it, in your documentation.
Regardless of task structure, you must implement Putc() and Getc() with the semantics given in the kernel description. They should be wrapper functions that wrap communicating with the serial server(s). The task ids should be obtained by the client, using a call to the name server.
There is not a unique 'best' task structure, but some task structures are better than others. As mentioned in class, servers should never use Send() or AwaitEvent() and mostly be blocked waiting on Receive(). Notifiers should almost always be in AwaitEvent(). Keep in mind that the two serial devices (UARTs) play very different roles in train applications. One is much more performance-limiting than the other.
There needs to be at least one notifier task. Notifiers may be implemented separately, or there may be a single implementation which is specialized during its initialization. The implementation should take into account the task structure of the task group that works together to provide serial I/O.
To test your serial I/O you are asked to redo the functionality of Assignment 0, this time using interrupt I/O. What collection of tasks you use is implementation-dependent, but you should describe and explain your design decisions.
Once your UART implementation is complete you do not need to do much more than connecting terminal commands to output for the train controller, and connecting train input to the terminal display. In doing so please remember to retain features such as the display of idle time, which you added earlier in your kernel development.
Also, remember that typing a command at the terminal does not test either terminal or train controller I/O very rigorously: human typing is way slower than a machine sending at full bandwidth.
- Until now you have been using busy-wait I/O for debugging.
In class we discussed that it might be possible to mix busy-wait I/O with interrupt-mediated
I/O, potentially using SPI workarounds. Please make sure to
- clearly document your approach to busy-wait debug I/O, if at all, and
- make sure that all workarounds can be removed from compilation via preprocessor switch, and
- submit a version without busy-wait debug I/O.
- The operation of your system depends on the priorities you choose. Starting with all tasks at the same priority and adjusting up and down is a good way of avoiding dead-ends.
Hand in the following, nicely formatted and printed.
- A pointer to your code repository, readable by the TAs and instructor, containing the source code of your assignment, instructions how to make the executable, and documentation (see below). The code and documentation must remain unmodified after submission until the assignments have been marked. Email the commit SHA to the instructor before the deadline.
- A description of how to access, make, and operate your program in a README file, including the full pathname of your executable file, which we might download for testing.
- The names of your group members in the same README file.
- A description of the structure of your kernel so far, highlighting the changes for this assignment. We will judge your kernel primarily on the basis of this description. Describe which algorithms and data structures you used and why you chose them.
- Note that we intend to perform code reviews with students. A TA/instructor and might contact you to set up a (virtual) meeting.