From e78db0cdeb2d3d16c14c2385a2af2c72b67fc193 Mon Sep 17 00:00:00 2001 From: LailaTheElf Date: Tue, 30 Sep 2025 15:13:49 +0200 Subject: [PATCH] fix refrences and add more code --- report-2/week_1.3.md | 18 +- report-2/week_1.4.md | 13 +- report-2/week_1.4/assignment_4.3/VersdOS.c | 270 ++++++++++++++++++ report-2/week_1.4/assignment_4.3/VersdOS.h | 27 ++ report-2/week_1.4/assignment_4.3/VersdOS_ll.s | 53 ++++ report-2/week_1.4/assignment_4.3/main.c | 115 ++++++++ report-2/week_1.4/assignment_4.4/VersdOS.c | 270 ++++++++++++++++++ report-2/week_1.4/assignment_4.4/VersdOS.h | 27 ++ report-2/week_1.4/assignment_4.4/VersdOS_ll.s | 53 ++++ report-2/week_1.4/assignment_4.4/main.c | 114 ++++++++ 10 files changed, 947 insertions(+), 13 deletions(-) create mode 100644 report-2/week_1.4/assignment_4.3/VersdOS.c create mode 100644 report-2/week_1.4/assignment_4.3/VersdOS.h create mode 100644 report-2/week_1.4/assignment_4.3/VersdOS_ll.s create mode 100644 report-2/week_1.4/assignment_4.3/main.c create mode 100644 report-2/week_1.4/assignment_4.4/VersdOS.c create mode 100644 report-2/week_1.4/assignment_4.4/VersdOS.h create mode 100644 report-2/week_1.4/assignment_4.4/VersdOS_ll.s create mode 100644 report-2/week_1.4/assignment_4.4/main.c diff --git a/report-2/week_1.3.md b/report-2/week_1.3.md index 81683c1..7616008 100644 --- a/report-2/week_1.3.md +++ b/report-2/week_1.3.md @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/report-2/week_1.4.md b/report-2/week_1.4.md index b61b9dc..3d34909 100644 --- a/report-2/week_1.4.md +++ b/report-2/week_1.4.md @@ -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. diff --git a/report-2/week_1.4/assignment_4.3/VersdOS.c b/report-2/week_1.4/assignment_4.3/VersdOS.c new file mode 100644 index 0000000..1da4a35 --- /dev/null +++ b/report-2/week_1.4/assignment_4.3/VersdOS.c @@ -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 +#include +#include +#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(); +} + + diff --git a/report-2/week_1.4/assignment_4.3/VersdOS.h b/report-2/week_1.4/assignment_4.3/VersdOS.h new file mode 100644 index 0000000..b1a0405 --- /dev/null +++ b/report-2/week_1.4/assignment_4.3/VersdOS.h @@ -0,0 +1,27 @@ +/* + * VersOS.h + * + * Created on: 12 nov. 2013 + * Author: VersD @ hr.nl + * + */ + +#ifndef VERSDOS_H_ +#define VERSDOS_H_ + +#include + +// 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_ */ diff --git a/report-2/week_1.4/assignment_4.3/VersdOS_ll.s b/report-2/week_1.4/assignment_4.3/VersdOS_ll.s new file mode 100644 index 0000000..a3720d9 --- /dev/null +++ b/report-2/week_1.4/assignment_4.3/VersdOS_ll.s @@ -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 diff --git a/report-2/week_1.4/assignment_4.3/main.c b/report-2/week_1.4/assignment_4.3/main.c new file mode 100644 index 0000000..e5607ce --- /dev/null +++ b/report-2/week_1.4/assignment_4.3/main.c @@ -0,0 +1,115 @@ +#include +#include +#include +#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); +} diff --git a/report-2/week_1.4/assignment_4.4/VersdOS.c b/report-2/week_1.4/assignment_4.4/VersdOS.c new file mode 100644 index 0000000..1da4a35 --- /dev/null +++ b/report-2/week_1.4/assignment_4.4/VersdOS.c @@ -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 +#include +#include +#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(); +} + + diff --git a/report-2/week_1.4/assignment_4.4/VersdOS.h b/report-2/week_1.4/assignment_4.4/VersdOS.h new file mode 100644 index 0000000..b1a0405 --- /dev/null +++ b/report-2/week_1.4/assignment_4.4/VersdOS.h @@ -0,0 +1,27 @@ +/* + * VersOS.h + * + * Created on: 12 nov. 2013 + * Author: VersD @ hr.nl + * + */ + +#ifndef VERSDOS_H_ +#define VERSDOS_H_ + +#include + +// 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_ */ diff --git a/report-2/week_1.4/assignment_4.4/VersdOS_ll.s b/report-2/week_1.4/assignment_4.4/VersdOS_ll.s new file mode 100644 index 0000000..a3720d9 --- /dev/null +++ b/report-2/week_1.4/assignment_4.4/VersdOS_ll.s @@ -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 diff --git a/report-2/week_1.4/assignment_4.4/main.c b/report-2/week_1.4/assignment_4.4/main.c new file mode 100644 index 0000000..b13530a --- /dev/null +++ b/report-2/week_1.4/assignment_4.4/main.c @@ -0,0 +1,114 @@ +#include +#include +#include +#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); +}