/* *************************************************************************** ; 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