fix refrences and add more code

This commit is contained in:
2025-09-30 15:13:49 +02:00
parent 43e1596e41
commit e78db0cdeb
10 changed files with 947 additions and 13 deletions

View File

@@ -19,7 +19,7 @@ $$
T = \frac{AHB/8}{f_{out}} = \frac{16\cdot 10^6/8}{2} = 10^6
$$
My resulting code^[also available at [/report-1/week_1.3/assignment_3.1/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.1/main.c)]:
My resulting code^[also available at [/report-2/week_1.3/assignment_3.1/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.1/main.c)]:
```c {.numberLines}
#include <stdint.h>
@@ -56,15 +56,15 @@ int main(void)
}
```
I measured the resulting frequency with an logic analyser ([[#logic-analiser-view-of-LEDs-for-assignment-31]]; channels are coloured to the led colour). It measured a period time of $499.568ms$, I call this error could be my cheap logic analyser or en error in the internal oscillator.
I measured the resulting frequency with an logic analyser (figure \ref{31_logic}; channels are coloured to the led colour). It measured a period time of $499.568ms$, I call this error could be my cheap logic analyser or en error in the internal oscillator.
![Logic analiser view of LEDs for assignment 3.1](https://live.kladjes.nl/uploads/84591270-95b0-4601-aea2-ca44b51adbf9.png)
![Logic analiser view of LEDs for assignment 3.1\label{31_logic}](https://live.kladjes.nl/uploads/84591270-95b0-4601-aea2-ca44b51adbf9.png)
## assignment 3.2
> Configure the SysTick timer to set the `COUNTFLAG` in the `STK_CTRL` register every $0.5s$ using the CMSIS API. Replace the for-loop with the following C code: `while ((SysTick->CTRL & (1 << 16)) == 0);` The symbol SysTick is defined in the CMSIS API. Build and debug the project. If all is well, the user LEDs will blink with a frequency of $1 Hz$.
code also available at [/report-1/week_1.3/assignment_3.2/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.2/main.c)
Code also available at [/report-2/week_1.3/assignment_3.2/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.2/main.c)
```c {.numberLines}
#include <stdint.h>
@@ -106,7 +106,7 @@ This time I measured $499.586ms$ with the logic analyser.
> B) Configure the SysTick timer to generate an interrupt (also called an exception) every `0.5s`.
code also available at [/report-1/week_1.3/assignment_3.3/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.3/main.c)
code also available at [/report-2/week_1.3/assignment_3.3/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.3/main.c)
```c {.numberLines}
#include <stdint.h>
@@ -162,7 +162,7 @@ because the compiler doesn't know when `flag` changes. Without `volatile` optimi
> Configure the SysTick timer to generate an interrupt every `0.5s`. This can be done by using the function `SysTick_Config` from the CMSIS API.
code also available at [/report-1/week_1.3/assignment_3.4/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.4/main.c)
code also available at [/report-2/week_1.3/assignment_3.4/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.4/main.c)
```c {.numberLines}
#include <stdint.h>
@@ -214,7 +214,7 @@ int main(void)
> Now based on project opdr_3_4 create a rotation loop which simulates a simple traffic light: green (5 seconds), orange (1 second), red (4 seconds). The time each light is on must be easily adjustable with a granularity of $0.5s$. The processor must be put to sleep in between interrupts. Make use of an enumeration construct (`enum`) for the colors and a `switch`-`case`-statement for the rotation.
code also available at [/report-1/week_1.3/assignment_3.5/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.5/main.c)
code also available at [/report-2/week_1.3/assignment_3.5/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.5/main.c)
```c {.numberLines}
#include <stdint.h>
@@ -314,7 +314,7 @@ Again I validated the timings with the logic-analyser.
> - Create a function runReadyTasks() that will walk through the task list and execute any task in the READY state. Replace your switch-case rotation in the function main with a call to this function.
> - Make use of a logic analyzer to verify the timing of the tasks
code also available at [/report-1/week_1.3/assignment_3.6/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.6/main.c)
code also available at [/report-2/week_1.3/assignment_3.6/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.6/main.c)
```c {.numberLines}
#include <stdint.h>
@@ -460,7 +460,7 @@ Here the `counter` and `counter_rst` members are set to the same value. `counter
Setting `counter` to the initial delay already solves this assignment. The following code implements this change.
code also available at [/report-1/week_1.3/assignment_3.7/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-1/week_1.3/assignment_3.7/main.c)
code also available at [/report-2/week_1.3/assignment_3.7/main.c](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.3/assignment_3.7/main.c)
```c {.numberLines}
#include <stdint.h>

View File

@@ -75,9 +75,11 @@ Now all the line `extern void delay(uint32_t ticks);` can be added to the begin
### Result
![logic-analyser measurement of the preemptive scheduler](https://live.kladjes.nl/uploads/c65810e2-a5fc-48f3-be6b-2f30acc5747a.png)
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).
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 [[#logic-analyser measurement of the preemptive scheduler]] 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.
![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)
@@ -118,7 +120,7 @@ With Ctrl+F in some books I only found the _nPRIV_ bit in the control register (
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 toPrivileged
.global toUnprivileged
toUnprivileged:
mrs R0, CONTROL // copy control register to R0
@@ -130,7 +132,7 @@ toUnprivileged:
And called this function in the `PendSV_Handeler`
```c
void toPrivileged(void);
void toUnprivileged(void);
__attribute__((naked)) // No function entry and exit code
void PendSV_Handler(void)
@@ -154,6 +156,8 @@ void PendSV_Handler(void)
}
```
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.
@@ -161,6 +165,7 @@ 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.

View File

@@ -0,0 +1,270 @@
/*
* Copyright 2015 Daniel Versluis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* schedule.h
*
* Created on: 12 nov. 2013
* Author: Daniel Versluis
* Email: VersD @ hr.nl
*
* Description:
* Contains the scheduler and the systick ISR.
* Simple and incomplete implementation.
*
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
//The (external) assembly functions
void pushRegistersToCurrentPSP(void);
void popRegistersFromCurrentPSP(void);
void * readPSP(void);
void writePSP(void * ptr);
void returnToPSP(void);
void returnToMSP(void);
void toUnprivileged(void);
#define TASK_MASK MAX_TASKS-1
#define IDLE_TASK MAX_TASKS
//What states can our task have?
enum taskState {UNUSED = 0, RUNNING, READY, SLEEPING_DELAY};
//The task itself
typedef struct _task{
int *stack; // pointer to the stack on the heap
uint32_t counter; // a counter for delays
void (*function)(void); // function to execute
enum taskState state; // state
int8_t priority; // priority
} task;
// List of tasks
//add one space for idle task
task taskList[MAX_TASKS+1];
task * currentTask;
task * taskToExecute;
// Idle task
void idleTask(void)
{
while (1)
{
__asm(" wfi"); // Sleep until next SysTick
}
}
void addTaskToListAtIndex(void (*function)(void), uint32_t stackSize, int8_t priority, size_t pos)
{
task *taskToAdd = &taskList[pos];
taskToAdd->function = function;
// Allocate memory... do we wanna use malloc or our own implementation ;-) ?
taskToAdd->stack = (int *)malloc(stackSize)+stackSize;
/*
* For debugging purposes we initialize the stack with
* values that we can recognize.
*/
*(--(taskToAdd->stack)) = 0x01000000; //XSPR Thumb bit set
*(--(taskToAdd->stack)) = (int)taskToAdd->function; //set PC to function pointer, cast as int to silence the compiler
*(--(taskToAdd->stack)) = 0xFFFFFFFD; //LR, return with process stack (PSP)
*(--(taskToAdd->stack)) = 0x0000000C; //R12 Initial values used for debugging purposes
*(--(taskToAdd->stack)) = 0x00000003; //R3
*(--(taskToAdd->stack)) = 0x00000002; //R2
*(--(taskToAdd->stack)) = 0x00000001; //R1
*(--(taskToAdd->stack)) = 0x00000000; //R0
if(pos!=IDLE_TASK)
{
*(--(taskToAdd->stack)) = 0x0000000B; //R11
*(--(taskToAdd->stack)) = 0x0000000A; //R10
*(--(taskToAdd->stack)) = 0x00000009; //R9
*(--(taskToAdd->stack)) = 0x00000008; //R8
*(--(taskToAdd->stack)) = 0x00000007; //R7
*(--(taskToAdd->stack)) = 0x00000006; //R6
*(--(taskToAdd->stack)) = 0x00000005; //R5
*(--(taskToAdd->stack)) = 0x00000004; //R4
// Initialize the task properties
taskToAdd->state = READY;
}else{
taskToAdd->state = RUNNING;
currentTask = taskToAdd;
// Update the CPU PSP with our new stack pointer
writePSP(taskToAdd->stack);
}
taskToAdd->priority = priority;
}
/*
* Function to add a new task to the taskList
* Not sorted, prioritized or any of that kind.
*
* It simply allocates memory for the new task stack,
* fills the stack up so that the context switch will
* successfully pop these registers and start running
* at the correct address when returning from the SysTick ISR
*/
void addTaskToList(void (*function)(void), uint32_t stackSize, int8_t priority)
{
size_t i = 0;
// Simply find the next empty slot
// Loops when no more slots are available
while(taskList[i].state != UNUSED)
{
//increment i and roll back at the limit
i++;
i &= TASK_MASK;
}
addTaskToListAtIndex(function, stackSize, priority, i);
}
void startVersdOS(uint16_t sysTickPeriodIn_ms) {
// Configure SysTick of 1 ms
SysTick->LOAD = sysTickPeriodIn_ms * CLOCK_FREQ_IN_KHz - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk //Clock source selection = Processor clock (AHB)
| SysTick_CTRL_TICKINT_Msk //Counting down to zero to asserts the SysTick exception request
| SysTick_CTRL_ENABLE_Msk; //Counter enable
//set systick and pendsv interrupt priority to lowest.
//svc will be highest.
SCB->SHP[2] |= 0xFF<<24;
SCB->SHP[2] |= 0xFF<<16;
// Create Idle task
addTaskToListAtIndex(idleTask, 128, -1, IDLE_TASK);
__asm(" wfi"); // Sleep until next SysTick
}
// currentTask is running now, return next task to run
task * schedule()
{
task* tempTaskPtr = currentTask;
task *idleTaskPtr = &taskList[IDLE_TASK];
// tempTaskPtr->state = READY;
int teller=0;
//Find next ready, non idle task.
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];
}
}while(tempTaskPtr->state != READY && teller++ <= MAX_TASKS);
//if no task was found
if(tempTaskPtr->state != READY)
{
//idle task
tempTaskPtr = idleTaskPtr;
}
return tempTaskPtr;
}
// flag used in blocking_delay function
bool SysTick_flag;
void decrement_sleeping_delay()
{
for (size_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--;
}
}
}
}
/*
* Interrupt routine for the Systick timer
* simply calls the scheduler
* */
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);
}
__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 most simple SVC implementation
*/
void SVC_Handler(void)
{
taskToExecute = schedule();
SCB->ICSR |= (1<<28);
}
//Call Super Visor
void taskYield(void)
{
asm(" svc #1");
}
void delay(uint32_t ticks)
{
currentTask->state = SLEEPING_DELAY;
currentTask->counter = ticks;
taskYield();
}

View File

@@ -0,0 +1,27 @@
/*
* VersOS.h
*
* Created on: 12 nov. 2013
* Author: VersD @ hr.nl
*
*/
#ifndef VERSDOS_H_
#define VERSDOS_H_
#include <stdint.h>
// define the clock frequency you use. You have to configure it YOURSELF!
#define CLOCK_FREQ_IN_KHz 100000
// define the max number of task you can use
#define MAX_TASKS 4 // MUST be a power of 2
// Add new task to the taskList
// priority can be between 0 and 127, higher number means higher priority
void addTaskToList(void(*function)(void), uint32_t stackSize, int8_t priority);
// Start OS
void startVersdOS(uint16_t sysTickPeriodIn_ms);
void delay(uint32_t ticks);
#endif /* VERSDOS_H_ */

View File

@@ -0,0 +1,53 @@
// Author: Daniel Versluis
// Email: VersD@hr.nl
// Description: Contains all neccesary low-level functions
// for pushing and popping CPU/Stack
.thumb
.syntax unified
.global pushRegistersToCurrentPSP
.global popRegistersFromCurrentPSP
.global readPSP
.global writePSP
.global returnToPSP
.global returnToMSP
.global toUnprivileged
// .global SVC_Handler
pushRegistersToCurrentPSP:
MRS r0 , psp //Move PSP to register R0 (the function argument)
STMDB r0! , {r4-r11} //Store multiple registers decrement address before each access,
//when done write back last used address to %0 (new SP location)
MSR psp , r0 //Move register %0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
popRegistersFromCurrentPSP:
MRS R0 , psp //Move PSP to register R0
LDMIA R0! , {r4-r11} //Load multiple registers increment address after each access,
//when done write back last used address to R0 (new SP location)
MSR psp , R0 //Move register R0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
readPSP:
MRS R0 , psp //move psp to r0
MOV PC , LR //return by moving link-register to program counter
writePSP:
MSR psp , R0 //Move r0 to psp.
ISB //flush pipeline
MOV PC , LR //return by moving link-register to program counter
returnToPSP:
ldr lr, =0xFFFFFFFD //terug met process stack
bx lr
returnToMSP:
ldr lr, =0xFFFFFFF9 //terug met process stack
bx lr
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

View File

@@ -0,0 +1,115 @@
#include <stdint.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
/*-------------------------------pin numbers for leds ---------------------------*/
#define GREEN 12
#define ORANGE 13
#define RED 14
#define BLUE 15
/*------------------------------------DEMO---------------------------------------*/
// blocking delay function
// keep the CPU busy for at least ticks SysTicks
void blocking_delay(unsigned int ticks)
{
while (ticks != 0) {
extern bool SysTick_flag;
while (SysTick_flag == false); // busy wait
SysTick_flag = false;
ticks--;
}
}
void toggleGreen(void)
{
while(1)
{
GPIOD->ODR ^= 1 << GREEN;
delay(100-1);
}
}
void toggleOrange(void)
{
while(1)
{
GPIOD->ODR ^= 1 << ORANGE;
delay(200-1);
}
}
void toggleRed(void)
{
while(1)
{
GPIOD->ODR ^= 1 << RED;
delay(400-1);
}
}
void toggleBlue(void)
{
while(1)
{
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
SysTick->LOAD = 2 * CLOCK_FREQ_IN_KHz - 1;
}
}
int main(void)
{
// Use HSE (8 MHz)
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait until HSE is stable
while ((RCC->CR & RCC_CR_HSERDY) == 0);
// Power interface clock enable
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Regulator voltage scaling output selection Scale 1 mode <= 100 MHz
PWR->CR |= PWR_CR_VOS;
// Use 3 wait states when reading Flash at 100 MHz.
FLASH->ACR = FLASH_ACR_LATENCY_3WS;
// Wait until 3 wait states are used
while ((FLASH->ACR & FLASH_ACR_LATENCY_3WS) == 0);
// Enable flash prefetch buffer, instruction and data cache
FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
// Use PLL to generate 100 MHz clock
// PLL output = 8 / M * N / P: M = 4, N = 100, P = 2 to generate 100 MHz
RCC->PLLCFGR = 0x20000000
| RCC_PLLCFGR_PLLSRC_HSE
| (0 << RCC_PLLCFGR_PLLP_Pos)
| (100 << RCC_PLLCFGR_PLLN_Pos)
| (4 << RCC_PLLCFGR_PLLM_Pos);
// Enable PLL
RCC->CR |= RCC_CR_PLLON;
// Wait until PLL is locked
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// Select PLL as the system clock. AHB clock divided by 2.
RCC->CFGR |= RCC_CFGR_SW_PLL | RCC_CFGR_PPRE1_DIV2;
// Wait until PLL used as the system clock
while ((RCC->CFGR & RCC_CFGR_SWS_PLL) == 0);
// Disable HSI
RCC->CR &= ~RCC_CR_HSION;
// GPIO Port D Clock Enable
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
// GPIO Port D led pins Push/Pull Output
GPIOD->MODER |= 1 << 2 * GREEN | 1 << 2 * ORANGE | 1 << 2 * RED | 1 << 2 * BLUE;
// Create tasks. Provide fpointer, stacksize, and priority:
addTaskToList(toggleGreen, 128, 2);
addTaskToList(toggleOrange, 128, 2);
addTaskToList(toggleRed, 128, 2);
addTaskToList(toggleBlue, 128, 2);
// Start VersdOS with 1 ms sysTick
startVersdOS(1);
while(1);
}

View File

@@ -0,0 +1,270 @@
/*
* Copyright 2015 Daniel Versluis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* schedule.h
*
* Created on: 12 nov. 2013
* Author: Daniel Versluis
* Email: VersD @ hr.nl
*
* Description:
* Contains the scheduler and the systick ISR.
* Simple and incomplete implementation.
*
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
//The (external) assembly functions
void pushRegistersToCurrentPSP(void);
void popRegistersFromCurrentPSP(void);
void * readPSP(void);
void writePSP(void * ptr);
void returnToPSP(void);
void returnToMSP(void);
void toUnprivileged(void);
#define TASK_MASK MAX_TASKS-1
#define IDLE_TASK MAX_TASKS
//What states can our task have?
enum taskState {UNUSED = 0, RUNNING, READY, SLEEPING_DELAY};
//The task itself
typedef struct _task{
int *stack; // pointer to the stack on the heap
uint32_t counter; // a counter for delays
void (*function)(void); // function to execute
enum taskState state; // state
int8_t priority; // priority
} task;
// List of tasks
//add one space for idle task
task taskList[MAX_TASKS+1];
task * currentTask;
task * taskToExecute;
// Idle task
void idleTask(void)
{
while (1)
{
__asm(" wfi"); // Sleep until next SysTick
}
}
void addTaskToListAtIndex(void (*function)(void), uint32_t stackSize, int8_t priority, size_t pos)
{
task *taskToAdd = &taskList[pos];
taskToAdd->function = function;
// Allocate memory... do we wanna use malloc or our own implementation ;-) ?
taskToAdd->stack = (int *)malloc(stackSize)+stackSize;
/*
* For debugging purposes we initialize the stack with
* values that we can recognize.
*/
*(--(taskToAdd->stack)) = 0x01000000; //XSPR Thumb bit set
*(--(taskToAdd->stack)) = (int)taskToAdd->function; //set PC to function pointer, cast as int to silence the compiler
*(--(taskToAdd->stack)) = 0xFFFFFFFD; //LR, return with process stack (PSP)
*(--(taskToAdd->stack)) = 0x0000000C; //R12 Initial values used for debugging purposes
*(--(taskToAdd->stack)) = 0x00000003; //R3
*(--(taskToAdd->stack)) = 0x00000002; //R2
*(--(taskToAdd->stack)) = 0x00000001; //R1
*(--(taskToAdd->stack)) = 0x00000000; //R0
if(pos!=IDLE_TASK)
{
*(--(taskToAdd->stack)) = 0x0000000B; //R11
*(--(taskToAdd->stack)) = 0x0000000A; //R10
*(--(taskToAdd->stack)) = 0x00000009; //R9
*(--(taskToAdd->stack)) = 0x00000008; //R8
*(--(taskToAdd->stack)) = 0x00000007; //R7
*(--(taskToAdd->stack)) = 0x00000006; //R6
*(--(taskToAdd->stack)) = 0x00000005; //R5
*(--(taskToAdd->stack)) = 0x00000004; //R4
// Initialize the task properties
taskToAdd->state = READY;
}else{
taskToAdd->state = RUNNING;
currentTask = taskToAdd;
// Update the CPU PSP with our new stack pointer
writePSP(taskToAdd->stack);
}
taskToAdd->priority = priority;
}
/*
* Function to add a new task to the taskList
* Not sorted, prioritized or any of that kind.
*
* It simply allocates memory for the new task stack,
* fills the stack up so that the context switch will
* successfully pop these registers and start running
* at the correct address when returning from the SysTick ISR
*/
void addTaskToList(void (*function)(void), uint32_t stackSize, int8_t priority)
{
size_t i = 0;
// Simply find the next empty slot
// Loops when no more slots are available
while(taskList[i].state != UNUSED)
{
//increment i and roll back at the limit
i++;
i &= TASK_MASK;
}
addTaskToListAtIndex(function, stackSize, priority, i);
}
void startVersdOS(uint16_t sysTickPeriodIn_ms) {
// Configure SysTick of 1 ms
SysTick->LOAD = sysTickPeriodIn_ms * CLOCK_FREQ_IN_KHz - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk //Clock source selection = Processor clock (AHB)
| SysTick_CTRL_TICKINT_Msk //Counting down to zero to asserts the SysTick exception request
| SysTick_CTRL_ENABLE_Msk; //Counter enable
//set systick and pendsv interrupt priority to lowest.
//svc will be highest.
SCB->SHP[2] |= 0xFF<<24;
SCB->SHP[2] |= 0xFF<<16;
// Create Idle task
addTaskToListAtIndex(idleTask, 128, -1, IDLE_TASK);
__asm(" wfi"); // Sleep until next SysTick
}
// currentTask is running now, return next task to run
task * schedule()
{
task* tempTaskPtr = currentTask;
task *idleTaskPtr = &taskList[IDLE_TASK];
// tempTaskPtr->state = READY;
int teller=0;
//Find next ready, non idle task.
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];
}
}while(tempTaskPtr->state != READY && teller++ <= MAX_TASKS);
//if no task was found
if(tempTaskPtr->state != READY)
{
//idle task
tempTaskPtr = idleTaskPtr;
}
return tempTaskPtr;
}
// flag used in blocking_delay function
bool SysTick_flag;
void decrement_sleeping_delay()
{
for (size_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--;
}
}
}
}
/*
* Interrupt routine for the Systick timer
* simply calls the scheduler
* */
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);
}
__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 most simple SVC implementation
*/
void SVC_Handler(void)
{
taskToExecute = schedule();
SCB->ICSR |= (1<<28);
}
//Call Super Visor
void taskYield(void)
{
asm(" svc #1");
}
void delay(uint32_t ticks)
{
currentTask->state = SLEEPING_DELAY;
currentTask->counter = ticks;
taskYield();
}

View File

@@ -0,0 +1,27 @@
/*
* VersOS.h
*
* Created on: 12 nov. 2013
* Author: VersD @ hr.nl
*
*/
#ifndef VERSDOS_H_
#define VERSDOS_H_
#include <stdint.h>
// define the clock frequency you use. You have to configure it YOURSELF!
#define CLOCK_FREQ_IN_KHz 100000
// define the max number of task you can use
#define MAX_TASKS 4 // MUST be a power of 2
// Add new task to the taskList
// priority can be between 0 and 127, higher number means higher priority
void addTaskToList(void(*function)(void), uint32_t stackSize, int8_t priority);
// Start OS
void startVersdOS(uint16_t sysTickPeriodIn_ms);
void delay(uint32_t ticks);
#endif /* VERSDOS_H_ */

View File

@@ -0,0 +1,53 @@
// Author: Daniel Versluis
// Email: VersD@hr.nl
// Description: Contains all neccesary low-level functions
// for pushing and popping CPU/Stack
.thumb
.syntax unified
.global pushRegistersToCurrentPSP
.global popRegistersFromCurrentPSP
.global readPSP
.global writePSP
.global returnToPSP
.global returnToMSP
.global toUnprivileged
// .global SVC_Handler
pushRegistersToCurrentPSP:
MRS r0 , psp //Move PSP to register R0 (the function argument)
STMDB r0! , {r4-r11} //Store multiple registers decrement address before each access,
//when done write back last used address to %0 (new SP location)
MSR psp , r0 //Move register %0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
popRegistersFromCurrentPSP:
MRS R0 , psp //Move PSP to register R0
LDMIA R0! , {r4-r11} //Load multiple registers increment address after each access,
//when done write back last used address to R0 (new SP location)
MSR psp , R0 //Move register R0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
readPSP:
MRS R0 , psp //move psp to r0
MOV PC , LR //return by moving link-register to program counter
writePSP:
MSR psp , R0 //Move r0 to psp.
ISB //flush pipeline
MOV PC , LR //return by moving link-register to program counter
returnToPSP:
ldr lr, =0xFFFFFFFD //terug met process stack
bx lr
returnToMSP:
ldr lr, =0xFFFFFFF9 //terug met process stack
bx lr
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

View File

@@ -0,0 +1,114 @@
#include <stdint.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
/*-------------------------------pin numbers for leds ---------------------------*/
#define GREEN 12
#define ORANGE 13
#define RED 14
#define BLUE 15
/*------------------------------------DEMO---------------------------------------*/
// blocking delay function
// keep the CPU busy for at least ticks SysTicks
void blocking_delay(unsigned int ticks)
{
while (ticks != 0) {
extern bool SysTick_flag;
while (SysTick_flag == false); // busy wait
SysTick_flag = false;
ticks--;
}
}
void toggleGreen(void)
{
while(1)
{
GPIOD->ODR ^= 1 << GREEN;
delay(100-1);
}
}
void toggleOrange(void)
{
while(1)
{
GPIOD->ODR ^= 1 << ORANGE;
delay(200-1);
}
}
void toggleRed(void)
{
while(1)
{
GPIOD->ODR ^= 1 << RED;
delay(400-1);
}
}
void toggleBlue(void)
{
while(1)
{
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
}
}
int main(void)
{
// Use HSE (8 MHz)
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait until HSE is stable
while ((RCC->CR & RCC_CR_HSERDY) == 0);
// Power interface clock enable
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Regulator voltage scaling output selection Scale 1 mode <= 100 MHz
PWR->CR |= PWR_CR_VOS;
// Use 3 wait states when reading Flash at 100 MHz.
FLASH->ACR = FLASH_ACR_LATENCY_3WS;
// Wait until 3 wait states are used
while ((FLASH->ACR & FLASH_ACR_LATENCY_3WS) == 0);
// Enable flash prefetch buffer, instruction and data cache
FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
// Use PLL to generate 100 MHz clock
// PLL output = 8 / M * N / P: M = 4, N = 100, P = 2 to generate 100 MHz
RCC->PLLCFGR = 0x20000000
| RCC_PLLCFGR_PLLSRC_HSE
| (0 << RCC_PLLCFGR_PLLP_Pos)
| (100 << RCC_PLLCFGR_PLLN_Pos)
| (4 << RCC_PLLCFGR_PLLM_Pos);
// Enable PLL
RCC->CR |= RCC_CR_PLLON;
// Wait until PLL is locked
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// Select PLL as the system clock. AHB clock divided by 2.
RCC->CFGR |= RCC_CFGR_SW_PLL | RCC_CFGR_PPRE1_DIV2;
// Wait until PLL used as the system clock
while ((RCC->CFGR & RCC_CFGR_SWS_PLL) == 0);
// Disable HSI
RCC->CR &= ~RCC_CR_HSION;
// GPIO Port D Clock Enable
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
// GPIO Port D led pins Push/Pull Output
GPIOD->MODER |= 1 << 2 * GREEN | 1 << 2 * ORANGE | 1 << 2 * RED | 1 << 2 * BLUE;
// Create tasks. Provide fpointer, stacksize, and priority:
addTaskToList(toggleGreen, 128, 2);
addTaskToList(toggleOrange, 128, 2);
addTaskToList(toggleRed, 128, 2);
addTaskToList(toggleBlue, 128, 2);
// Start VersdOS with 1 ms sysTick
startVersdOS(1);
while(1);
}