Files
2026-06-12 02:55:04 -07:00

258 lines
9.3 KiB
ArmAsm

/* ***************************************************************************
; osasm.s: low-level OS commands, written in assembly
; derived from uCOS-II
;****************************************************************************
;OS Lab2/3/4/5/6
;Students will implement these functions as part of ECE445M Lab
;Starter to labs 1,2,3,4
; Jonathan Valvano
; December 21, 2025
;
;Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu
; You may use, edit, run or distribute this file
; as long as the above copyright notice remains
; THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
; OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
; VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
; OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
; For more information about my classes, my research, and my books, see
; http://users.ece.utexas.edu/~valvano/
;
*/
.data
.align 2
// Declare global variables here if needed
// with the .space assembly directive
.text
.thumb
.align 2
.global RunPt // currently running thread
.global NextThreadPt // next thread to run, set by schedule
.global Scheduler
.global SysTick_Handler
.global TimeMs
.global OS_MsTime
.global OS_ClearMsTime
.global Scheduler
.global ReadyLists
.global RunPts
.global StartOS
.global ContextSwitch2
.global PendSV_Handler
.global OSDisableInterrupts
.global OSEnableInterrupts
.global SVC_Handler
.global StartCritical
.global EndCritical
.include "../inc/msp.s" // For profiling
.macro TogglePB22
LDR R0, =GPIOB_DOUTTGL31_0
LDR R1, =(1 << 22)
STR R1, [R0]
.endm
.equ POINTERSIZE, 4 // bytes
.equ TCB_NEXT, 4 // offset within struct
OSDisableInterrupts:
CPSID I
BX LR
OSEnableInterrupts:
CPSIE I
BX LR
// *********** StartCritical ************************
//make a copy of previous I bit, disable interrupts
// inputs : none
// outputs : R0=previous I bit
StartCritical:
MRS R0, PRIMASK // save old status
CPSID I // mask all (except faults)
BX LR
// *********** EndCritical ************************
// using the copy of previous I bit, restore I bit to previous value
// inputs : R0=previous I bit
// outputs : none
EndCritical:
MSR PRIMASK, R0
BX LR
// ******** OS_ClearMsTime ************
// sets the system time to zero (solve for Lab 1), and start a periodic interrupt
// Inputs: none
// Outputs: none
// You are free to change how this works
OS_ClearMsTime:
PUSH {LR}
BL StartCritical // Save I bit
LDR R2, =TimeMs
MOVS R1, #0
STR R1, [R2]
BL EndCritical // Restore I bit
LDR R0, =10000 //period
MOVS R1, 4 //prescale
MOVS R2, 1 //priority
BL TimerG8_IntArm //40MHz Clock - will arm @ 1kHz
POP {PC}
// ******** OS_MsTime ************
// reads the current time in msec (solve for Lab 1)
// Inputs: none
// Outputs: time in ms units
// You are free to select the time resolution for this function
// For Labs 2 and beyond, it is ok to make the resolution to match the first call to OS_AddPeriodicThread
OS_MsTime:
LDR R0, =TimeMs
LDR R0, [R0]
BX LR
// Scheduler handles the algorithm for switching threads
Scheduler:
MOVS R0, #0 // priorityReady
LDR R1, =ReadyLists
readyListLoop: // Traverse ReadyLists array looking for highest priority thread that is ready
LDR R2, [R1, R0]
CMP R2, #0
BNE readyFound
ADDS R0, R0, #POINTERSIZE // Keep looking if NULL, will break if no ready threads
B readyListLoop
readyFound:
LDR R1, =RunPts
ADDS R1, R0 // R1 = RunPts + priorityReady Pointer to TCB pointer
LDR R0, [R1] // R0 = RunPts[priorityReady] Pointer to actual TCB
LDR R0, [R0, #TCB_NEXT] // R0 = RunPts[priorityReady]->next
LDR R2, =RunPt
STR R0, [R2] // RunPt = RunPts[priorityReady]->next
STR R0, [R1] // RunPts[priorityReady] = RunPt
BX LR
StartOS:
// put your code here
LDR R0, =RunPt // Pointer to currently running thread
LDR R1, [R0] // R1 = value of RunPt
LDR R2, [R1] // new thread SP
MOV SP, R2 // Switch stack pointer
POP {R4-R7} // Restore R4-R7
POP {R0-R3} // Restore R0-R3
ADD SP, SP, #8 // Discard R12 and LR
POP {R0} // start location
ADD SP, SP, #4 // Discard PSR
CPSIE I // Enable interrupts
BX R0 // Start first thread
OSStartHang:
B OSStartHang // Should never get here
/* *******************************************************************************************************
; HANDLE PendSV EXCEPTION
; void PendSV_Handler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M. This is because the Cortex-M auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R7 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) disable interrupts so context switch is atomic
; b) Push remaining R4-R7 on stack;
; c) Save the SP in its TCB,
; d) Get next thread to run from the OS.c
; e) Load SP of next thread into SP
; f) Pop R4-R7 from new stack;
; g) enable interrupts
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) RunPt points to the tcb of the task to suspend
; c) NextThreadPt points to the tcb of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system, we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was a user thread and not an ISR.
;
; 5) We will assume (hope) the compiler does not use R8-R11
;
;********************************************************************************************************/
.type PendSV_Handler, %function
// Only ISR running at priority 3 guarantees it interrupts only main/foreground code
PendSV_Handler:
// put your code here
TogglePB22 // For profiling
TogglePB22 // For profiling
CPSID I // Prevent interrupt during switch
PUSH {R4-R7}
LDR R4, =RunPt
LDR R1, [R4]
MOV R2, SP
STR R2, [R1] // Save SP into old thread's TCB
MOV R5, LR // Preserve LR when calling scheduler
BL Scheduler // Call scheduler to pick the next thread
MOV LR, R5 // Restore LR
LDR R1, [R4] // R1 = RunPt
LDR R2, [R1] // SP of new thread
MOV SP, R2
POP {R4-R7}
CPSIE I // Re-enable interrupts
TogglePB22 // For profiling
BX LR // Exception return will restore remaining context
/* *******************************************************************************************************
; HANDLE SVC EXCEPTION
; void SVC_Handler(void)
;
; Note(s) : SVC is a software-triggered exception to make OS kernel calls from user land.
; The function ID to call is encoded in the instruction itself, the location of which can be
; found relative to the return address saved on the stack on exception entry.
; Function-call paramters in R0..R3 are also auto-saved on stack on exception entry.
;
;********************************************************************************************************/
.type OS_Id, %function
.type OS_Kill, %function
.type OS_Sleep, %function
.type OS_Time, %function
.type OS_AddThread, %function
.type ST7735_Message, %function
.global OS_Id
.global OS_Kill
.global OS_Sleep
.global OS_Time
.global OS_AddThread
.global ST7735_Message
.type SVC_Handler, %function
SVC_Handler:
PUSH {R4-R5,LR}
POP {R4-R5,PC}
SVCJumpTable:
.long OS_Id
.long OS_Kill
.long OS_Sleep
.long OS_Time
.long OS_AddThread
.long ST7735_Message
.end