389 lines
14 KiB
C
389 lines
14 KiB
C
/**
|
|
* @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
|