Skip to content
Snippets Groups Projects
Commit 74ea2ef1 authored by aht2nguy's avatar aht2nguy
Browse files

added tc1 doc

parent 38483433
Branches backup727
No related tags found
1 merge request!16Withtime
# CS 452 - TC1
Date submitted: July 26, 2018
Members:
- An Nguyen (SID 20648054)
- Tom (Bingzheng) Feng (SID 20640728)
## I. Operating Details
- Souce Code can be found at https://git.uwaterloo.ca/b26feng/cs452Trainee/tree/withtime
- 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 (modified)
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
- TrackServer at 9
- TrainTasks at 7
- TCSensorCourier at 0
- TSTCCourier at 8
- TCTSCourier at 6
- TCSensorDog at 2
- One-time-use Workers such as Stamper, Worker, ShortDistance at 10
### 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 for K1-K4.
### Sensor Server, Sensor Notifier, Sensor Print Task (modified)
The Sensor "Notifier" mimics the other Notifiers in that it sits in a loop polling for sensor data every 10 ticks, then Sends the raw data in form of a string to the Sensor Server.
The Server can then store the data in its body and 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. For TC1, the TCSensorCourier also queries for the Sensor Update to update the current position of the train in the Train Controller.
For performance, we have implemented a new syscall GetStr(COM1, 10) exclusively used in the Sensor Notifier in place of calling `Getc(COM1)` 10 times to reduce message passing.
### Couriers (new)
We have added 4 Couriers `TCTS`, `TSTC`, `TCSensor`, and `TCDog`.
- `TSTCCourier` waits on the Track Server for any turn-out being switched, then it Sends the TrackNode corresponding to that switch to the Train Controller to notify the train if that newly switched turn-out is going to affect its next sensor's prediction.
- `TSTCCourier`
- `TCSensorCourier` simply sits in a loop, fetches a new poll of sensors and timestamp from the Sensor Server and Sends to the Train Controller to update the current position of the Train.
- `TCDogCourier` is spawned by the Train Controller and kept in a queue in the TC when not in use. Its goal is to first Send to the TC for the amount of the time that we expect the next sensor to be hit, registers its ID onto the Train structure, Delays, then Sends back to the TC after that amount of time to check if that Sensor was indeed triggered i.e. the DogTaskID is not that of itself. If the DogTaskID has not changed, that means the Courier gets back before the Sensor is expected to trigger, so the Train's`bTimedOut` is set to 1. The WatchDog task is always reused, i.e. added back to the buffer.
### Display Task (modified)
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.
For TC1, we have added parsing to allow for some new functionalities below.
- `trk A` or `trk B` initializes the track and its switches to A or B accordingly
-`reg TrainID Sensor Offset` registers the train's initial location in the Train Controller, Offset amount from Sensor
-`stop-after TrainID Sensor` stops the Train after Sensor is hit (for measuring the Stopping Distance) by executing a one-time-use worker called `Worker` spawned by the TC
-`velocity Sensor1 Sensor2` which prints out the Velocity between any two sensors and the timestamps at which the two sensors are hit by executing a one-time-use worker called `Stamper` spawned by the TC
-`shortDistance TrainID Distance` which theoretically stops the train within that distance after using our model to convert the distance to the number of ticks required to Delay between a SetSpeed(max) and a SetSpeed(0). A jump table is required to extrapolate NumTicks given our calibrated data, but it is not working as expected as of now. This operation runs by executing a one-time-use worker called `ShortDistance` spawned by the TC
### struct Train (new)
```
typedef struct{
int TrainID;
int Index;
float Tick14; // calibrated value
float Tick10; // calibrated value
float Dist14; // calibrated value
float Dist10; // calibrated value
float DJerk; // calculated from Tick14, Tick10, Dist14, Dist10
float DTDiff; // Tick14 - Tick10
float Vel; // constant velocity used to estimate stopping distance
float StopDist; // calculated from Tick14, Tick10, Dist14, Dist10
float StopTick; // calculated from Tick14, Tick10, Dist14, Dist10
float DymVel; // current Velocity
float alpha; // alpha value for calculating dynamic velocity
int SDMinTick; //tick to move train for 1 cm
int SDMaxTick;
int Speed; // current Speed of Train
int LocOff; // offset in mm from LocationBase Node
int TimeStamp; //when last Sensor hits
int Dist; // distance to next Sensor
int MyDog; // watchdog's taskID
TrackNode* Last; // the Node before last Sensor
TrackNode* LocBase; // most recent Sensor Node hit
TrackNode* Next; // expected Next Sensor
TrackNode* NNext; // expected Next Next Sensor
char bRegistered; // if Train is registered
char bTimedOut; // set to 1 when NextSensor is not triggered but NextNextSensor is
int RouteLength; // dijkstra's
int NumReserved; // number of Nodes reserved TC2
}Train;
```
### Train Controller (TC) Server (modified)
The Train Controller Server is the main server, in charge of handling SetSpeed, Reverse, QuitProgram, RegisterTrain, SetTrack, UpdateTrain, UpdateWhenSwitchFlipped, StopAfterSensor, VelocityBetweenTwoSensors, WhenNextSensorHits.
To do so, it houses in its body:
- a `Train` array for 5 Trains
- an int array `TCTS` of LocationBase indices for 5 trains that is used to get the corresponding Nodes from the Track Server
- a TrackNode array `TCTSReply` of LocationBase and NextSensorNode of size 10 (first 5 LocationBase, last 5 NextSensorNode, each for each Train) to keep the Nodes gotten from the TrackServer
- an int array `DinnerTime` of size 5 to store the time the NextSensor expected to be hit for each train.
When we get a report from the TSTCCourier that a turn-out is switched, the function `UpdateNextOrNot` is invoked to determine if this switch affects the Train's NextSensor and NNextSensor.
- If the switch's Last Sensor being the same as the Train's LocationBase or the Train's Next Sensor
- If the Train's Offset is greater than the distance between the switch and its last sensor, then this turn-out is behind the train and its status does not affect the train's next expected Sensor or next next.
- Otherwise,
?????????????????????????????????????????????????????????????????????????
If there is a watchdog available in our reservoir buffer, it will be used to "watch" for the next Sensor hit; if there isn't, a new `TCDog` task is spawned.
### Track Server (new)
The Track Server's structure is overlooks the track layout, keeps ahold of all 144 `TrackNodes` in an array and is in charge of
- handling `init_tracka` or `init_trackb` based on a ReceiveMsg from the Display Task (UserInput).
- recording a switch flip in its TrackNode array whenever `SetSwitch` is called, and replying to `TSTCCourier` about this new change so it can be relayed back to the TC
- getting the nodes corresponding to the Location Base indices and their nextSensor Nodes, given by the TCTSCourier for all trains registered (max 5 trains for now).
### Path Finder (new)
We use Min Heap to implement Dijkstra's algorithm to find the shortest path from nodeA to nodeB.
### Self-destroy (new)
is a new syscall implemented by manipulating the TaskID int as follows:
- the first bit is set to 1 to reflect that the Task is available
- the generation bits (the next 15 bits) are incremeneted by 1 to reflect how many times the Task has been destroyed
### Calibration (new)
1. Dynamic Calibration
Whenever a new sensor poll is reported by the `TCSensorCourier`, all the trains are updated using `UpdateTrain`:
- If the next Sensor is hit within the expected time, the LocationBase field is updated to be that SensorNode; the fields Dist (distance to next Sensor), `NextSensor` and `NNext` are also updated accordingly.
- To support the possibility of every other sensor being broken, if the next expected Sensor is not hit, but the Next Next Sensor is hit, the LocationBase field if updated to be the NNext; the fields Dist (distance to next Sensor), NextSensor and NNext are also updated accordingly.
Using the difference in distances and timestamps from last Sensor to this new Sensor, the new `dynamic velocity dymVel` field is re-calculated using the running average formula `v = (1 − α)v + αv'` where alpha = 0.25 for all trains. The DinnerTime array is then updated with new timestamps using the current timestamp, the new `Dist` and `dymVel` values to reflect the expected time the Next Sensor will be hit for all trains.
2. Stopping Distance
is measured manually by issuing the command `stopafter` for each train at speeds 10 and 14. A model is derived from these values to estimate stopping distances at the other speeds.
3. Short Moves
This functionality is implemented but not working as expected due to the absence of a jump table to extrapolate ticks using our current Exponenstial model.
4. Current Location of Trains
is estimated using the LocationBase TrackNode and the dynamic velocity.
# Program Output (new)
![refer-to-tc1-png-in-same-dir-if-photo-not-rendered](tc1.png)
Docs/tc1.png

68.4 KiB

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