
# Week 1.4

## Assinment 4.1

> The teacher has built a simple preemptive OS that is still missing some important features. In these assignments you’ll implement some extra features and gain a bigger understanding in how an OS operates. Next week we’ll start using a fully developed RTOS with all necessary features for a production environment.  
> A) Download the project VersdOS.zip.  
> B) Import this project, Open and Finish. Build and debug the project. The LEDs should blink.  
> C) Browse through the code and especially make sure you understand the scheduling process and the context switch.  
> D) Measure the periods at which the LEDs toggle using a logic analyzer and explain why this is not 2× but 8× the `blocking_delay` time.

![](https://live.kladjes.nl/uploads/1cdce708-a2e7-4a3d-b727-d5423b892687.png)

In the Code it the values for blocking delay are 100, 200, 400, 800 for green, orange, red, blue. The `blocking_delay` function wait for this number of SysTicks, witch is set to $1ms$. I measure delays of $0.4s$, $0.8s$, $1.6s$ and $3.2s$. This is indeed 4x slower of what I would expect on first glance. This delay is because this is a preemptive scheduler, the other tasks run in between. There are 4 task all with the same priority, so each task takes 4 time longer.

## Assignment 4.2

> A) implement a non-blocking delay so that a task can request the OS to be kept out of the scheduling loop for a certain number of system ticks. The scheduler should remain preemptive and perform round-robin on all the available(not delayed) tasks. Once the requested number of system ticks have passed, the scheduler should include the task in the selection process. You may use the `taskYield()` function to let the OS know a task is ready to be switched out.

To allow for a non blocking delay the OS should keep track of the time the to wake it up on the correct time. I did this by adding the new task state `SLEEPING_DELAY` to the `enum`. 

There already is an counter in the task struct. but no function that decrements this counter. So I added the following function the the OS.

```c
void decrement_sleeping_delay()
{
    for (usize_t i=0; i < MAX_TASKS; i++)
    {
        if (taskList[i].state == SLEEPING_DELAY)
        {
            if (taskList[i].counter == 0)
            {
                taskList[i].state = READY;
            }
            else
            {
                taskList[i].counter--;
            }
        }
    }
}
```

This function is added to the `SysTick_Handeler` before the scheduler is run. So that if the counter is 0 the `decrement_sleeping_delay` makes the task ready again, and is directly available for to be scheduled. This makes a delay of 0, wait for the next SysTick.

The `SysTick_Handeler` now look like the following

```c
void SysTick_Handler(void)
{
    SysTick_flag = true;
    //decrement counter for task in SLEEPING_DELAY
    decrement_sleeping_delay();
    //select the next task
    taskToExecute = schedule();
    //request context switch
    SCB->ICSR |= (1<<28);
}
```

Before stating to write the delay function for the tasks, in the `schedule` funciton the line that sets the current task to ready sould be removed (line 163 in `VersdOS.c`). If the state is chanced during execution of the task, this line reset is at the next try to reschedule this state back to `READY` so the state will never actually be changed.

As last the delay function itself.

```c
void delay(uint32_t ticks)
{
    currentTask->state = SLEEPING_DELAY;
    currentTask->counter = ticks;
    taskYield();
}
```

Now all the line `extern void delay(uint32_t ticks);` can be added to the begin the `main.c` and all the `blocking_delay` calls can be replace by `delay`.

### Result

The compete code can be found at [/report-2/week_1.4/assignment_4.2/](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.4/assignment_4.2).

![logic-analyser measurement of the preemptive scheduler\label{42_logic}](https://live.kladjes.nl/uploads/c65810e2-a5fc-48f3-be6b-2f30acc5747a.png)

This implementation works, only the delays are a bit off. To make it a nice counter again all delays should be decremented by one (figure \ref{42_logic} shows the output with the original timings, not the decremented ones). This is because with my implementation there is a delay added until the next SysTick after `delay` is called. `blocking_delay(0)` instantly returns without waiting for an SysTick (code below). It check if the counter is zero before waiting. For my `delay(0)`, it does wait for a SysTick and checks the counter after. To me the added delay make more sens. My delay is between number of the arguments in SysTick up to one extra, depending how long until the first SysTick is.

```c
void blocking_delay(unsigned int ticks)
{
    while (ticks != 0) {
        extern bool SysTick_flag;
        while (SysTick_flag == false); // busy wait
        SysTick_flag = false;
        ticks--;
    }
}
```

## Assignment 4.3

> A) Try changing the SysTick period to 2 ms within a task. A task can change the SysTick period to any value it wants, but you can imagine this isn’t desired behavior.

I changed the `toggleBlue` task to the following

```c
void toggleBlue(void)
{
    while(1)
    {
        GPIOD->ODR ^= 1 << BLUE;
        delay(800-1);
        SysTick->LOAD = 2 * CLOCK_FREQ_IN_KHz - 1;
    }
}
```

![](https://live.kladjes.nl/uploads/06bc9958-cc48-4f9c-8b5f-300ea7efb231.png)

> B) Implement unprivileged mode for the tasks. When succeeded the previous test should fail and be caught in an infinite ISR fault-handler.

With Ctrl+F in some books I only found the _nPRIV_ bit in the control register (PM0214 - STM32 Cortex® -M4 MCUs and MPUs programming manual) as information how to switch privilege. I assume the interrupts used by the OS automatically go to privileged mode, and I only need to go to unprivileged mode before starting a task.

The control register is a special register to it need to be set and read with special instructions. With `MSR` (page 187 of PM0214) a special register, like control, can be set and `MRS` (page 186 of PM0214) it can be read. With this information I added the following function to `VersdOS_ll.s` to set the `nPRIV` bit.

```asm
        .global toUnprivileged

toUnprivileged:
        mrs     R0, CONTROL    // copy control register to R0
        ORR     R0, R0, #1     // clear bit 0 (nPRIV)
        msr     CONTROL, R0    // copy R0 to control register
        bx      lr
```

And called this function in the `PendSV_Handeler` 

```c
void toUnprivileged(void);

__attribute__((naked)) // No function entry and exit code
void PendSV_Handler(void)
{
    //Push {R4-R11} context to PSP
    pushRegistersToCurrentPSP();
    //Save the new stack pointer after the push
    currentTask->stack = readPSP();

    currentTask = taskToExecute;

    //Load the new stack pointer from (new) currentTask
    writePSP(currentTask->stack);

    //Pop {R4-R11} context from PSP
    popRegistersFromCurrentPSP();

    toUnprivileged(); // <--- this line was added

    returnToPSP();
}
```

The resulting code can be found at [/report-2/week_1.4/assignment_4.3/](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.4/assignment_4.3)

And tested it.

One step after the 'malicious' code, it jumps to this infinite loop.

![](https://live.kladjes.nl/uploads/e3f8273a-2a70-44d2-a6db-28f2a22fe5f2.png)

and when the 'malicious' code is removed, it functions as normal.

## Assignment 4.4

> Tasks are no longer able to access the SysTick peripheral. It is time to implement a system call using the SVC interrupt mechanism. The `taskYield()` function would also classify as a system call. Write a system call function that will allow a task to change the SysTick period to a value between 1 and 10 ms . The system call should utilize the SVC mechanism by passing a different number than the `taskYield()` call. Make sure both calls keep working! Within the SVC interrupt service routine you will have to find the used SVC instruction using the PC value and look at the LSB to determine the system call number. You can also utilize the tasks’ stack to find the passed SysTick period parameter. When this parameter is invalid the system call should simply return.

To find the instruction I first added the function to that contains the different `SVC` insurrections. I did this by adding the following code to `VersOS.c` and add the corresponding lines to `VersOS.h`.

```c
void setSysTick_10ms(void)
{
    asm(" svc #2");
}

void setSysTick_1ms(void)
{
    asm(" svc #3");
}
```

And made use of them in the `toggelBlue` task by updating it to the following.

```c
void toggleBlue(void)
{
    while(1)
    {
        GPIOD->ODR ^= 1 << BLUE;
        delay(800-1);
        setSysTick_10ms();
        GPIOD->ODR ^= 1 << BLUE;
        delay(800-1);
        setSysTick_1ms();
    }
}
```

Then added an break-point at each `SVC` instruction and noted `PS` and `PC`. Then stepped to the `SVC_Handler`, and looked at the memory at the address stored in the `PSP` register.

```
task:
  PC: 0x08000660
  SP: 0x200005ac
SVC_Handler:
  PSP: 0x20000588

0x20000588 : 0x20000588 <Hex>
  Address   0 - 3     4 - 7     8 - B     C - F               
  20000580  1F030000  9C000020  1F030000  01000000          
  20000590  1F030000  9C000020  0C000000  F9060008          
  200005A0  62060008  00020001  B0050020  B0050020          
  200005B0  07000000  FDFFFFFF  0030012B  11D10260          
  200005C0  08230C68  E40348BF  0123F2D4  0C68E406          
  200005D0  48BF0523  EDD40C68  14F0EF0F  E5D00623          
  200005E0  E7E79DF8  00004A68  52085200  4A6032BD          
  200005F0  78B40822  784B8DF8  14020020  00000000          
  20000600  00000000  3C060020  00000000  00000000          
  20000610  00000000  00000000  00000000  01000000          
  20000620  02000000  03000000  0C000000  FDFFFFFF          
  20000630  28020008  00020001  23020008  E8FF0120          
  20000640  11D11460  08251E68  F60348BF  0125F2D4          
  20000650  1E68F606  48BF0525  EDD41E68  16F0EF0F          
  20000660  E5D00625  E7E79DF8  0020082A  47D15A68          
  20000670  22F44072  5A605A68  5A605A68  42F00102  
```

In the memory map I found a littleendien encoded address witch was very close to the tasks `PC` at `0x200005A0`. This could very well be the place the `PC` is stored. To test this I updated the `SVC_Handler` to the following.

```c
void SVC_Handler(void)
{
    uint32_t* sp = readPSP();
    // get PC location
    sp = sp + 6;
    // get PC
    uint16_t* pc = (uint16_t*)*sp;
    // get SVC intruction
    uint16_t instruction = *(pc - 1);
    if ((instruction & 0xFF00) != 0xDF00) {
        return;
    }

    uint8_t svc_id = instruction & 0x00FF;

    switch (svc_id)
    {
        case 1: // taskYield
            taskToExecute = schedule();
            SCB->ICSR |= (1<<28);
            break;

        case 2: // setSysTick_10ms
            SysTick->LOAD = 10 * CLOCK_FREQ_IN_KHz - 1;
            break;

        case 3: // setSysTick_1ms
            SysTick->LOAD = 1 * CLOCK_FREQ_IN_KHz - 1;
            break;
    }
}
```

This code worked. The resulting code can be found at [/report-2/week_1.4/assignment_4.4/](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.4/assignment_4.4).

![](https://live.kladjes.nl/uploads/fb408589-67d1-4b37-8eb2-f852c65575f8.png)


## assignment 4.5

> The current scheduler implements a round-robin algorithm. In a real-time situation it would be necessary to give tasks different priorities in order to make important deadlines. 
> A) In reality tasks have more important jobs to do than just toggling a LED. This means tasks will take time. Use the original blocking delay function to give tasks a realistic waiting time in a random amount of milliseconds. In order to verify timing requirements with a logic analyzer, you can toggle the LED at the end of the busy waiting period.  

The LED is turn on while the task is await, and of when it is sleeping.

|                | green  | orange | red    | blue   |
| -------------- | ------ | ------ | ------ | ------ |
| blocking_delay | 100 ms | 200 ms | 300 ms | 400 ms |
| delay (sleep)  | 100 ms | 200 ms | 300 ms | 400 ms |

```c
void toggleGreen(void)
{
    while(1)
    {
        GPIOD->ODR |= 1 << GREEN;
        blocking_delay(100);
        GPIOD->ODR &= ~(1 << GREEN);
        delay(100-1);
    }
}

void toggleOrange(void)
{
    while(1)
    {
        GPIOD->ODR |= 1 << ORANGE;
        blocking_delay(200);
        GPIOD->ODR &= ~(1 << ORANGE);
        delay(200-1);
    }
}

void toggleRed(void)
{
    while(1)
    {
        GPIOD->ODR |= 1 << RED;
        blocking_delay(300);
        GPIOD->ODR &= ~(1 << RED);
        delay(300-1);
    }
}

void toggleBlue(void)
{
    while(1)
    {
        GPIOD->ODR |= 1 << BLUE;
        blocking_delay(400);
        GPIOD->ODR &= ~(1 << BLUE);
        delay(400-1);
    }
}
```

> B) Implement a priority based scheduling algorithm. When adding tasks you should be able to pass the priority. When there are multiple tasks available with the same priority a round-robin algorithm should be applied. Test this functionality for multiple tasks with and without non-blocking delays.  

The priority is already passed with the `addTaskToList` function and this is saved in the task struct. I did update the priorities to have green as the highest priority, then orange, red and blue has the lowest priority.

```c
    addTaskToList(toggleGreen, 128, 2);
    addTaskToList(toggleOrange, 128, 3);
    addTaskToList(toggleRed, 128, 4);
    addTaskToList(toggleBlue, 128, 5);
```

> C) The round-robin period thus far equaled the SysTick period. Implement the ability to have a task-dependent round-robin period. E.g. if taskA and taskB have equal priorities, TaskA will run 2 SysTicks, taskB will need 5 SysTicks. This period should also be something passed at task creation. Verify this functionality with a logic analyzer.

I added a new pointer `nextTaskPtr` to store the highest priority task that is ready. The do while loop is updated to first filter all task tasks that aren't ready than if there is no task yet in `nextTaskPtr` it should not compare priorities. Otherwise check if it has a higher priority than the current `nextTaskPtr`, if so replace it.

```c
task * schedule()
{
    task* nextTaskPtr = NULL;
    task* tempTaskPtr = currentTask;
    task* idleTaskPtr = &taskList[IDLE_TASK];

    int teller=0;

    do
    {
        tempTaskPtr++;

        if( (tempTaskPtr-1) == idleTaskPtr || tempTaskPtr == idleTaskPtr)
        {
            //since idle task is the last in the list, we've reached the end
            //and need to continue at the beginning
            tempTaskPtr = &taskList[0];
        }

        if (tempTaskPtr->state != READY)
        {
            continue;
        }

        if (nextTaskPtr == NULL)
        {
            nextTaskPtr = tempTaskPtr;
            continue;
        }

        if (tempTaskPtr->priority < nextTaskPtr->priority)
        {
            nextTaskPtr = tempTaskPtr;
            continue;
        }
    } while (teller++ < MAX_TASKS);

    //if no task was found
    if(nextTaskPtr == NULL)
    {
        //idle task
        nextTaskPtr = idleTaskPtr;
    }

    return nextTaskPtr;
}
```

The full code is available at [/report-2/week_1.4/assignment_4.5/](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.4/assignment_4.5).

### Testing

![](https://live.kladjes.nl/uploads/cbbdbcdb-0484-4074-8112-4a48b80f424f.png)

Green (`D0`) has the highest priority, so it starts. Orange (`D2`) follows after the $100ms$ green is taking, orange should take $200ms$ but green is correctly halting orange. 

![](https://live.kladjes.nl/uploads/a3e2237d-2099-4695-a943-b447098f3ef5.png)

It is also clearly viable that red (`D1`) and blue (`D3`) have lower priorities. It seems to work correctly.