Skip to content
Snippets Groups Projects
Commit 1c1f7115 authored by aht2nguy's avatar aht2nguy
Browse files

added doc for k4

parent 2c03fbd8
Branches backup727
No related tags found
2 merge requests!16Withtime,!15Samplek4
# CS 452 - Kernel 4
Date submitted: Jul 9, 2018
Members:
- Tom (Bingzheng) Feng (SID 20640728)
- An Nguyen (SID 20648054)
## I. Operating Details
- Souce Code can be found at https://git.uwaterloo.ca/b26feng/cs452Trainee/tree/samplek4
- Full pathname of the executable file:
```
load -b 0x218000 -h 10.15.167.5 "ARM/b26feng/main.elf"
go
```
- To compile the code,
```
cd lib
make clean; make; make install
cd ../src
make clean; make
// download the executable main.elf onto the ARM box and run as usual
```
## II. Kernel
### Context Switch
kerent (from userspace to the kernel):
- save spsr to r1
- switch to system mode
- push user's registers onto user's stack
- save sp_user to r2
- switch to svc mode
- store lr_svc onto user's stack
- move address of Args struct to r1
- get swi number into r0 by masking off the high 8 bits
- restore kernel's stack
kerxit (from the kernel to userspace) with arguments int* sp and int retval:
- push kernel's registers onto kernel's stack
- switch to system mode
- get task->sp_usr from arg0
- load spsr to r0, lr_svc to r2
- switch to svc mode
- set spsr to r0
- set lr_svc to r2
- switch to system mode
- restore user's registers
- switch to svc mode
- load return value into r0
- go to user program by setting pc to lr and spsr_svc to cpsr
### Hash Table
For performance, a slight modification is made to the hash table in the way two hash numbers h1 and h2 are calculated and picking a better prime number i.e. 251.
### Min Heap
implemented as an array, is used to store HeapNodes, each of which contains the TaskID and Tick of the Tasks whose Tick has not reached the SysTick in the ClockServer when the Receive Message is Delay or DelayUntil. Since the DelayedTicks are ordered chronologically, the min HeapNode has the smallest DelayedTick. Inserting n HeapNodes takes O(nlogn) time, removing the min HeapNode takes O(logn) time while getMin, however, only takes constant time. We find our
### Pass()
A Task calling Pass() will now still stay Ready but get added to *second* lowest priority queue as opposed to the lowest as in K2 so it would not have to "compete" with the Idle Task in the lowest Priority Queue.
### Name Server and Clock Server
In k2, our design was to let a Server Task call Pass if its SendQ is empty. In k3, in order for the Idle Task with the lowest Priority to have its time to run, instead of calling Pass when the SendQs are empty, we decided:
- For the NameServer to call Receive on the ClockServer for its state to be changed to SendBlocked, thus skipped over by the Scheduler.
- For the ClockServer to call Receive on the Notifier for its state to be changed to SendBlocked, thus skipped over by the Scheduler.
### AwaitEvent
Since an Event should only have one Task waiting on it, we decided to have a *static* array of EventAwaitTask called `EventAwaitTask WaitEventTable[NUM_EVENTS]` to keep track of which Task is waiting on which Event. Since TIMER3 is the only source of interrupt in K3 among the 61 available, we check the IRQ_STATUS corresponding to the device to see if it's set, and set the Task waiting to Ready and back to its priority queue. For K4, we use EventID = 25 for UART2 Receive Interrupt, 26 for UART2 Transmit Interrupt and 52 for the combined interrupts. Due to the nature of the UART1 Transmit Interrupt making up of
### Priorities
We find that the program works best with the priorities set as follows. Note that the notifiers are always higher in priority than their servers so they can go back to AwaitEvent as quickly as possible.
- FirstUserTask at 1
- NameServer at 13
- ClockServer at 1
- Notifier at 0
- COM2 Servers at 2
- COM2 Notifiers at 1
- IdleTask 15 (lowest)
- COM1 Servers at 1
- COM1 Notifiers at 0
- Sensor Server at 9
- SensorPrintTask at 10
- Sensor Notifier at 8
- TCServer at 5
- Display at 12
- TimeDisplay at 1
### Printing the Idle Time and Time
In order to display the Time, we spawn a task called TimeDisplay at priority 1 that sits in a loop and keeps incrementing the second and transforming it into Hour:Min:Sec and prints to the Display.
To display the idle time, we involve a more complicated scheme by using a shared address that is implemented using a system call called GetShared. The ClockServer's SysTem Tick is stored at this address, that can be fetched by the IdleTask when it is running to compute the time that it has run and display the idle percentage onto the Display.
### Scheduler
The scheduler has been re-implemented using a min heap and we saw a great improvement in performance. Before the scheduler was just looping through all the queues to get to the task with the highest (smallest in number) priority. Since the idle task runs roughly 90% of the time, that is a lot of time wasted to get to it. Popping from the MinHeap is now constant time to get the idle task out despite having 16 priority levels.
### Interrupt-driven IO
We have in total 4 servers (TrainIn, TrainOut, TerminalIn, TerminalOut) and 4 corresponding notifiers.
This is the minimum amount of tasks that we think is vital for handling the different interrupts.
### I/O Servers
We decided to leave FIFOs off for all the 4 servers due to time constraints in testing and getting the interrupts right.
We spawn all 4 tasks in the firstUserTasks together with their notifiers. However, the notifiers have higher priority than their servers as we want them to return to `AwaitEvent` on their corresponding interrupt as fast as possible. To minimize the amount of code written and allow for easier maintenance in the future, we decided to have the 4 servers share the same structure body in which instead of calling GetC/NotifierInput or Putc/NotifierOutput, we collectively call the former Consumer and the latter Producer. To allow for sending/receiving multiple bytes atomically, we employ three buffers to keep track of the Consumer ID's and the address to send the bytes to or receive from and the number of bytes requested, and add/remove from the buffers synchronously.
### Notifiers
The notifiers all `AwaitEvent` on their corresponding interrupt similar to the TIMER3_Interrupt in kernel3, except for the TrainOut Notifier also having to deal with the CTS interrupt. To mitigate the effects of the CTS interrupt having not been disabled and re-asserted before the next byte is written which overwrites the previous byte, we decided to go with the Marklin's suggestion to use a delay of 10 ticks before writing the next byte. In order to minimize the number of times that the Notifiers have to go back to the Servers to fetch a byte on each interrupt thus improving performance, we also employ a 3-byte buffer for Transmit COM1 and a 64-byte buffer for Transmit COM2.
In addition to Putc(), we also implement PutStr() by making them both into system calls to reduce message passing by just simply calling Send() otherwise.
Printf() is achieved by making a copy of `bwio.c` and replacing the bw functions with the corresponding interrupt-driven functions. We name the file `interrupt-io.c`. This is needed to allow for transmitting bytes atomically thus rendering the escape sequences properply.
Only Getc() is supported for receiving data.
### Train Controller Server
Train Controller Server is not much different from a run-of-the-mill server. It is in charge of handling the Send calls to set speed for a train and reverse a train by calling Putstr to the IO Servers with the appropriate Delays in between to ensure the Train come to a full stop before starting up again in the opposite direction.
### Sensor Server, Sensor Notifier, Sensor Print Task
The Sensor "Notifier" mimics the other Notifiers in that it sits in a loop polling for sensor data, Sends the raw data in form of a string to the Server.
The Server can then store the data in its bodies and wakes up/replies to any task that queries for it. For kernel 4, there is only one task querying for it that is the SensorPrintTask. It sits in a loop Sending for the data from the Server, process it and display onto the screen the 5 most recently triggered sensors.
# Display Task
After initializing the startup layout, and the switches, the display task sits in a loop calling Getc(COM2) and parses the input into commands and call the appropriate functions to set speed for the train, reverse the train and set state for the switches. Sadly, `q` is now not a clean exit and only breaks out of the main loop back to RedBoot.
### Output of the Program:
All functions in A0 `tr`, `rv`, `sw` and `q` are implemented.
![display-photo-in-same-folder][display.png]
### Ideas to improve the kernel
- Make `quit` into a clean exit by properly calling Exit() on all the tasks
- Turning on FIFOs
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment