all
This commit is contained in:
388
RTOS_Labs_common/OS.h
Normal file
388
RTOS_Labs_common/OS.h
Normal file
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* @file OS.h
|
||||
* @brief Real Time Operating System for Labs 1, 2, 3, 4 and 5
|
||||
* @details ECE445M
|
||||
* @version V1.0
|
||||
* @author Valvano
|
||||
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
|
||||
* @warning AS-IS
|
||||
* @note For more information see http://users.ece.utexas.edu/~valvano/
|
||||
* @date January 10, 2026
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __OS_H
|
||||
#define __OS_H 1
|
||||
#include <stdint.h>
|
||||
/**
|
||||
* \brief Set which lab is active so I can use the same OS.c for all Labs
|
||||
*/
|
||||
#define LAB1 0
|
||||
#define LAB2 0
|
||||
#define LAB3 0
|
||||
#define LAB4 0
|
||||
#define LAB5 0
|
||||
#define LAB6 1
|
||||
/**
|
||||
* \brief Times assuming a 80 MHz
|
||||
*/
|
||||
#define TIME_1MS 80000
|
||||
#define TIME_2MS (2*TIME_1MS)
|
||||
#define TIME_500US (TIME_1MS/2)
|
||||
#define TIME_250US (TIME_1MS/4)
|
||||
|
||||
// Thread constants
|
||||
#define MAXNUMTHREADS 10
|
||||
#define STACKSIZE 256
|
||||
#define NUMPRIORITIES 8
|
||||
#define MAXEVENTS 200
|
||||
|
||||
// Fifo size
|
||||
#define MAXFIFOSIZE 256
|
||||
|
||||
// Forward declaration so Sema4 can hold TCB pointers
|
||||
typedef struct TCB TCB_t;
|
||||
|
||||
/**
|
||||
* \brief Semaphore structure. Feel free to change the type of semaphore, there are lots of good solutions
|
||||
*/
|
||||
struct Sema4{
|
||||
int32_t Value; // >0 means free, otherwise means busy
|
||||
TCB_t* blockedList; // head of per-semaphore blocked queue (oldest waiter)
|
||||
TCB_t* blockedTail; // tail of per-semaphore blocked queue (newest waiter)
|
||||
};
|
||||
typedef struct Sema4 Sema4_t;
|
||||
|
||||
// TCB state
|
||||
typedef enum {FREE, ACTIVE} State_t;
|
||||
// Thread Control Block structure
|
||||
struct TCB{
|
||||
int32_t* sp;
|
||||
TCB_t* next;
|
||||
TCB_t* prev;
|
||||
State_t state;
|
||||
uint8_t id;
|
||||
uint32_t sleep;
|
||||
uint32_t priority;
|
||||
Sema4_t* blocked;
|
||||
};
|
||||
|
||||
// Periodic thread structure
|
||||
typedef struct Event Event_t;
|
||||
struct Event{
|
||||
void (*task)(void);
|
||||
uint32_t timeToNext;
|
||||
uint32_t period;
|
||||
uint32_t priority;
|
||||
};
|
||||
|
||||
// Mailbox structure
|
||||
typedef struct Mailbox{
|
||||
uint32_t data;
|
||||
Sema4_t free;
|
||||
Sema4_t dataValid;
|
||||
} Mailbox_t;
|
||||
|
||||
// Fifo structure
|
||||
typedef struct Fifo{
|
||||
uint32_t PutI;
|
||||
uint32_t GetI;
|
||||
uint32_t data[MAXFIFOSIZE];
|
||||
Sema4_t CurrentSize;
|
||||
uint32_t MaxSize;
|
||||
uint32_t LostData;
|
||||
} Fifo_t;
|
||||
|
||||
/**
|
||||
* @details Initialize operating system, disable interrupts until OS_Launch.
|
||||
* Initialize OS controlled I/O: serial, ADC, systick, LaunchPad I/O and timers.
|
||||
* Interrupts not yet enabled.
|
||||
* @param none
|
||||
* @return none
|
||||
* @brief Initialize OS
|
||||
*/
|
||||
void OS_Init(void);
|
||||
|
||||
// ******** OS_InitSemaphore ************
|
||||
// initialize semaphore
|
||||
// input: pointer to a semaphore
|
||||
// output: none
|
||||
void OS_InitSemaphore(Sema4_t *semaPt, int32_t value);
|
||||
|
||||
// ******** OS_Wait ************
|
||||
// decrement semaphore
|
||||
// Lab2 spinlock
|
||||
// Lab3 block if less than zero
|
||||
// input: pointer to a counting semaphore
|
||||
// output: none
|
||||
void OS_Wait(Sema4_t *semaPt);
|
||||
|
||||
// ******** OS_Signal ************
|
||||
// increment semaphore
|
||||
// Lab2 spinlock
|
||||
// Lab3 wakeup blocked thread if appropriate
|
||||
// input: pointer to a counting semaphore
|
||||
// output: none
|
||||
void OS_Signal(Sema4_t *semaPt);
|
||||
|
||||
// ******** OS_bWait ************
|
||||
// Lab2 spinlock, set to 0
|
||||
// Lab3 block if less than zero
|
||||
// input: pointer to a binary semaphore
|
||||
// output: none
|
||||
void OS_bWait(Sema4_t *semaPt);
|
||||
|
||||
// ******** OS_bSignal ************
|
||||
// Lab2 spinlock, set to 1
|
||||
// Lab3 wakeup blocked thread if appropriate
|
||||
// input: pointer to a binary semaphore
|
||||
// output: none
|
||||
void OS_bSignal(Sema4_t *semaPt);
|
||||
|
||||
//******** OS_AddThread ***************
|
||||
// add a foreground thread to the scheduler
|
||||
// Inputs: pointer to a void/void foreground task
|
||||
// number of bytes allocated for its stack
|
||||
// priority, 0 is highest, 255 is the lowest
|
||||
// Priorities are relative to other foreground threads
|
||||
// Outputs: 1 if successful, 0 if this thread can not be added
|
||||
// In Lab 2, you can ignore both the stackSize and priority fields
|
||||
// In Lab 3, you can ignore the stackSize fields
|
||||
// In Lab 4, the stackSize can be 128, 256, or 512 bytes
|
||||
int OS_AddThread(void(*task)(void),
|
||||
uint32_t stackSize, uint32_t priority);
|
||||
|
||||
//******** OS_Id ***************
|
||||
// returns the thread ID for the currently running thread
|
||||
// Inputs: none
|
||||
// Outputs: Thread ID, number greater than zero
|
||||
uint32_t OS_Id(void);
|
||||
|
||||
//******** OS_AddPeriodicThread ***************
|
||||
// Add a background periodic thread
|
||||
// typically this function receives the highest priority
|
||||
// Inputs: task is pointer to a void/void background function
|
||||
// period in ms
|
||||
// priority 0 is the highest, 3 is the lowest
|
||||
// Priorities are relative to other background periodic threads
|
||||
// Outputs: 1 if successful, 0 if this thread can not be added
|
||||
// You are free to select the resolution of period
|
||||
// It is assumed that the user task will run to completion and return
|
||||
// This task can not spin, block, loop, sleep, or kill
|
||||
// This task can call OS_Signal OS_bSignal OS_AddThread
|
||||
// This task does not have a Thread ID
|
||||
// In lab 2, this command will be called 0 or 1 times
|
||||
// In lab 3, this command will be called 0 to 4 times
|
||||
// In labs 3-7, there will be 0 to 4 background periodic threads, and this priority field
|
||||
// determines the relative priority of these threads
|
||||
// For Lab 3, it ok to make reasonable limits to reduce the complexity. E.g.,
|
||||
// - You can assume there are 0 to 4 background periodic threads
|
||||
// - You can assume the priorities are sequential 0,1,2,3
|
||||
// - You can assume a maximum thread execution time, e.g., 50us
|
||||
// - You can assume a slowest period, e.g., 50ms
|
||||
// - You can limit possible periods, e.g., 1,2,4,5,10,20,25,50ms
|
||||
// - You can assume (E0/T0)+(E1/T1)+(E2/T2)+(E3/T3) is much less than 1
|
||||
int OS_AddPeriodicThread(void(*task)(void),
|
||||
uint32_t period, uint32_t priority);
|
||||
|
||||
// ******** OS_AddS1Task ***************
|
||||
// add a background task to run whenever the S1 (PA18) button is pushed
|
||||
// Inputs: pointer to a void/void background function
|
||||
// priority 0 is the highest, 1 is the lowest
|
||||
// Outputs: 1 if successful, 0 if this thread can not be added
|
||||
// It is assumed that the user task will run to completion and return
|
||||
// This task can not spin, block, loop, sleep, or kill
|
||||
// This task can call OS_Signal OS_bSignal OS_AddThread
|
||||
// This task does not have a Thread ID
|
||||
// Because of the pin conflict with TFLuna, this command will not be called
|
||||
// In lab 2, the priority field can be ignored
|
||||
// In labs 3-7, there will be many background threads, and this priority field
|
||||
// determines the relative priority of these threads
|
||||
int OS_AddS1Task(void(*task)(void), uint32_t priority);
|
||||
|
||||
// ******** OS_AddS2Task ***************
|
||||
// add a background task to run whenever the S2 (PB21) button is pushed
|
||||
// Inputs: pointer to a void/void background function
|
||||
// priority 0 is highest, 1 is lowest
|
||||
// Outputs: 1 if successful, 0 if this thread can not be added
|
||||
// It is assumed user task will run to completion and return
|
||||
// This task can not spin block loop sleep or kill
|
||||
// This task can call issue OS_Signal, it can call OS_AddThread
|
||||
// This task does not have a Thread ID
|
||||
// In labs 3-7, this command will be called will be called 0 or 1 times
|
||||
// In labs 3-7, there will be many background threads, and this priority field
|
||||
// determines the relative priority of these threads
|
||||
int OS_AddS2Task(void(*task)(void), uint32_t priority);
|
||||
|
||||
// ******** OS_AddPA28Task ***************
|
||||
// add a background task to run whenever the bump (PA28) button is pushed
|
||||
// Inputs: pointer to a void/void background function
|
||||
// priority 0 is the highest, 1 is the lowest
|
||||
// Outputs: 1 if successful, 0 if this thread can not be added
|
||||
// It is assumed that the user task will run to completion and return
|
||||
// This task can not spin, block, loop, sleep, or kill
|
||||
// This task can call OS_Signal OS_bSignal OS_AddThread
|
||||
// This task does not have a Thread ID
|
||||
// In lab 3, this command will be called 0 or 1 times
|
||||
// In lab 2, not implemented
|
||||
// In lab 3, there will be many background threads, and this priority field
|
||||
// determines the relative priority of these four threads
|
||||
int OS_AddPA28Task(void(*task)(void), uint32_t priority);
|
||||
|
||||
// ******** OS_Sleep ************
|
||||
// place this thread into a dormant state
|
||||
// input: number of msec to sleep
|
||||
// output: none
|
||||
// You are free to select the time resolution for this function
|
||||
// OS_Sleep(0) implements cooperative multitasking
|
||||
void OS_Sleep(uint32_t sleepTime);
|
||||
|
||||
// ******** OS_Kill ************
|
||||
// kill the currently running thread, release its TCB and stack
|
||||
// input: none
|
||||
// output: none
|
||||
void OS_Kill(void);
|
||||
|
||||
// ******** OS_Suspend ************
|
||||
// suspend execution of currently running thread
|
||||
// scheduler will choose another thread to execute
|
||||
// Can be used to implement cooperative multitasking
|
||||
// Same function as OS_Sleep(0)
|
||||
// input: none
|
||||
// output: none
|
||||
void OS_Suspend(void);
|
||||
|
||||
// temporarily prevent foreground thread switch (but allow background interrupts)
|
||||
uint32_t OS_LockScheduler(void);
|
||||
// resume foreground thread switching
|
||||
void OS_UnLockScheduler(uint32_t previous);
|
||||
|
||||
// ******** OS_Fifo_Init ************
|
||||
// Initialize the Fifo to be empty
|
||||
// Inputs: size
|
||||
// Outputs: none
|
||||
// In Lab 2, you can ignore the size field
|
||||
// In Lab 3, you should implement the user-defined fifo size
|
||||
// In Lab 3, you can put whatever restrictions you want on size
|
||||
// e.g., 4 to 64 elements
|
||||
// e.g., must be a power of 2,4,8,16,32,64,128
|
||||
void OS_Fifo_Init(uint32_t size);
|
||||
|
||||
// ******** OS_Fifo_Put ************
|
||||
// Enter one data sample into the Fifo
|
||||
// Called from the background, so no waiting
|
||||
// Inputs: data
|
||||
// Outputs: true if data is properly saved,
|
||||
// false if data not saved, because it was full
|
||||
// Since this is called by interrupt handlers
|
||||
// this function can not disable or enable interrupts
|
||||
int OS_Fifo_Put(uint32_t data);
|
||||
|
||||
// ******** OS_Fifo_Get ************
|
||||
// Remove one data sample from the Fifo
|
||||
// Called in foreground, will spin/block if empty
|
||||
// Inputs: none
|
||||
// Outputs: data
|
||||
uint32_t OS_Fifo_Get(void);
|
||||
|
||||
// ******** OS_Fifo_Size ************
|
||||
// Check the status of the Fifo
|
||||
// Inputs: none
|
||||
// Outputs: returns the number of elements in the Fifo
|
||||
// greater than zero if a call to OS_Fifo_Get will return right away
|
||||
// zero or less than zero if the Fifo is empty
|
||||
// zero or less than zero if a call to OS_Fifo_Get will spin or block
|
||||
int32_t OS_Fifo_Size(void);
|
||||
|
||||
// ******** OS_MailBox_Init ************
|
||||
// Initialize communication channel
|
||||
// Inputs: none
|
||||
// Outputs: none
|
||||
void OS_MailBox_Init(void);
|
||||
|
||||
// ******** OS_MailBox_Send ************
|
||||
// enter mail into the MailBox
|
||||
// Inputs: data to be sent
|
||||
// Outputs: none
|
||||
// This function will be called from a foreground thread
|
||||
// It will spin/block if the MailBox contains data not yet received
|
||||
void OS_MailBox_Send(uint32_t data);
|
||||
|
||||
// ******** OS_MailBox_Recv ************
|
||||
// remove mail from the MailBox
|
||||
// Inputs: none
|
||||
// Outputs: data received
|
||||
// This function will be called from a foreground thread
|
||||
// It will spin/block if the MailBox is empty
|
||||
uint32_t OS_MailBox_Recv(void);
|
||||
|
||||
// ******** OS_Time ************
|
||||
// return the system time counting up
|
||||
// Inputs: none
|
||||
// Outputs: time in 12.5ns units, 0 to 4294967295
|
||||
// The time resolution should be less than or equal to 1us, and the precision 32 bits
|
||||
// It is ok to change the resolution and precision of this function as long as
|
||||
// this function and OS_TimeDifference have the same resolution and precision
|
||||
uint32_t OS_Time(void);
|
||||
|
||||
// ******** OS_TimeDifference ************
|
||||
// Calculates difference between two times
|
||||
// Inputs: two times measured with OS_Time
|
||||
// Outputs: time difference in 12.5ns units
|
||||
// The time resolution should be less than or equal to 1us, and the precision at least 12 bits
|
||||
// It is ok to change the resolution and precision of this function as long as
|
||||
// this function and OS_Time have the same resolution and precision
|
||||
uint32_t OS_TimeDifference(uint32_t start, uint32_t stop);
|
||||
|
||||
// ******** OS_ClearMsTime ************
|
||||
// sets the system time to zero (from Lab 1)
|
||||
// Inputs: none
|
||||
// Outputs: none
|
||||
// You are free to change how this works
|
||||
void OS_ClearMsTime(void);
|
||||
|
||||
// ******** OS_MsTime ************
|
||||
// reads the current time in msec
|
||||
// Inputs: none
|
||||
// Outputs: time in ms units
|
||||
// You are free to select the time resolution for this function
|
||||
// It is ok to make the resolution to match the first call to OS_AddPeriodicThread
|
||||
uint32_t OS_MsTime(void);
|
||||
|
||||
//******** OS_Launch ***************
|
||||
// start the scheduler, enable interrupts
|
||||
// Inputs: number of 12.5ns clock cycles for each time slice
|
||||
// you may select the units of this parameter
|
||||
// Outputs: none (does not return)
|
||||
// In Lab 2, you can ignore the theTimeSlice field
|
||||
// In Lab 3, you should implement the user-defined TimeSlice field
|
||||
// It is ok to limit the range of theTimeSlice to match the 24-bit SysTick
|
||||
void OS_Launch(uint32_t theTimeSlice);
|
||||
|
||||
// Program has these fields
|
||||
struct Program{
|
||||
uint32_t StartOffset; // offset to starting location
|
||||
uint32_t CodeSize; // size of code segment
|
||||
uint32_t StackSize; // size of stack segment
|
||||
uint32_t DataSize; // size of data segment (globals)
|
||||
char Name[8]; // ASCII string
|
||||
};
|
||||
typedef struct Program Program_t;
|
||||
|
||||
// Load program from disk and launch process
|
||||
// open file for reading
|
||||
// read StartOffset,CodeSize,StackSize,DataSize,Name
|
||||
// Allocate spaces in RAM for data, stack, and code segments
|
||||
// Reads the object code from file into the code segment
|
||||
// Closes the file
|
||||
// Add program as a process, create a thread for it, and execute
|
||||
// SP => stack segment
|
||||
// R7 => data segment
|
||||
// PC => code segment (entry point at first location)
|
||||
// Inputs: name is the name of the file on SDC
|
||||
// priority is the thread priority
|
||||
// Output: 1 success, 0 failure
|
||||
int OS_LoadProgram(char *name, uint32_t priority);
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user