;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; $Workfile: os_cpu_a.s $
; $Revision: 1.0 $
; $Author: WellsK $
; $Date: Jun 30 2003 15:18:32 $
;
; Project: Lh7A400 MicroCos-II assembly code
;
; Description:
; Assembly language files for the MicroCos-II OS and the LH7A400
; port.
;
; Revision history:
; $Log: //smaicnt2/pvcs/VM/sharpmcu/archives/sharpmcu/software/csps/lh7a400/ports/ucosii/os_cpu_a.s-arc $
;
; Rev 1.0 Jun 30 2003 15:18:32 WellsK
; Initial revision.
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SHARP MICROELECTRONICS OF THE AMERICAS MAKES NO REPRESENTATION
; OR WARRANTIES WITH RESPECT TO THE PERFORMANCE OF THIS SOFTWARE,
; AND SPECIFICALLY DISCLAIMS ANY RESPONSIBILITY FOR ANY DAMAGES,
; SPECIAL OR CONSEQUENTIAL, CONNECTED WITH THE USE OF THIS SOFTWARE.
;
; SHARP MICROELECTRONICS OF THE AMERICAS PROVIDES THIS SOFTWARE SOLELY
; FOR THE PURPOSE OF SOFTWARE DEVELOPMENT INCORPORATING THE USE OF A
; SHARP MICROCONTROLLER OR SYSTEM-ON-CHIP PRODUCT. USE OF THIS SOURCE
; FILE IMPLIES ACCEPTANCE OF THESE CONDITIONS.
;
; COPYRIGHT (C) 2001 SHARP MICROELECTRONICS OF THE AMERICAS, INC.
; CAMAS, WA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXPORT ucos_irq_handler
EXPORT OSStartHighRdy
EXPORT OS_TASK_SW
EXPORT OSCtxSw
EXPORT OSIntCtxSw
IMPORT OSRunning
IMPORT OSTaskSwHook
IMPORT OSTCBHighRdy
IMPORT OSTCBCur
IMPORT OSPrioHighRdy
IMPORT OSPrioCur
IMPORT OSIntNesting
IMPORT OSIntEnter
IMPORT OSTimeTick
IMPORT OSIntExit
IMPORT irq_func_ptrs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Private defines and data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MODE_SVC EQU 0x00000013
MODE_DIS_IRQ EQU 0x00000080
INTC_BASE_ADDR EQU 0x80000500
INTC_SOURCE_OFF EQU 0x00000000
INTC_TIMER1_INT_MASK EQU 0x00000100
TIMER1_INT_CLR EQU 0x80000C0C
INTC_FIRST_IRQ EQU 5
AREA |C$$code|, CODE, READONLY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Function: OSStartHighRdy
;
; Purpose: Set highest priority uCos-II task ready to run
;
; Description:
; Call the OSTaskSwHook() function. Set OSRunning to TRUE. Get the
; address of the highest priority task control block. Get the
; stack pointer for the highest prior task (from the fetched TCB).
; Set the current stack pointer to the fetched stack pointer. Set
; the current task control block pointer to the highest priority
; task control block pointer. Restore the new task's SPSR and CPSR
; registers. Restore all ARM registers for the task and continue
; the task.
;
; Parameters: NA
;
; Outputs; NA
;
; Returns: NA
;
; Notes: This function should never be called from the IRQ level.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
OSStartHighRdy
BL OSTaskSwHook ; Call OSTaskSwHook
LDR r0, =OSRunning
MOV r1, #1
STRB r1, [r0] ; OSRunning = TRUE
LDR r0, =OSTCBHighRdy ; Get highest priority TCB
LDR r0, [r0] ; Get waiting task TCB address
LDR sp, [r0] ; Switch to the new task stack
B OSCtxSw_from_start ; Start first task
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Function: OS_TASK_SW (also known as OSCtxSw)
;
; Purpose: Task level context switch
;
; Description:
; Save the current task registers and status on the task stack.
; Save the current task stack pointer in the current TCB. Call
; the OSTaskSwHook() function. Set OSPrioCur = OSPrioHighRdy.
; Branch to the switch_tcb() function to restore the context of
; the new task.
;
; Parameters: NA
;
; Outputs; NA
;
; Returns: NA
;
; Notes: This function should never be called from the IRQ level.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
OS_TASK_SW
OSCtxSw
STMFD sp!, {lr} ; Save resume address
STMFD sp!, {r0-r12, lr} ; Save current task registers
MRS r0, cpsr
STMFD sp!, {r0} ; Save current CPSR
LDR r0, =OSTCBCur
LDR r0, [r0] ; Get pointer to current TCB
STR sp, [r0] ; Save stack pointer of cur task
BL OSTaskSwHook ; Call OSTaskSwHook
OSCtxSw_from_int
LDR r0, =OSTCBHighRdy ; Get highest priority TCB
LDR r2, [r0] ; Get waiting task TCB address
LDR r0, =OSTCBCur ; Get current TCB
STR r2, [r0] ; OSTCBCur = OSTCBHighRdy
LDR sp, [r2] ; Get new task stack pointer
LDR r0, =OSPrioHighRdy ; Get highest priority address
LDRB r1, [r0] ; Get highest priority
LDR r0, =OSPrioCur ; Get current priority address
STRB r1, [r0] ; OSPrioCur = OSPrioHighRdy
OSCtxSw_from_start
LDMFD sp!, {r0} ; Get CPSR value - this will be
MSR spsr_cf, r0 ; restored on function resume
LDMFD sp!, {r0-r12, lr, pc}^ ; Load task registers and continue
; task
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Function: OSTickISR
;
; Purpose: OS timer tick
;
; Description:
; See function comments.
;
; Parameters: NA
;
; Outputs; NA
;
; Returns: NA
;
; Notes:
; This function will always be called from ucos_irq_handler() on
; a timer interrupt. All other interrupts are handled directly in
; the ucos_irq_handler() function.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
OSTickISR
; At this point, the real handling of the interrupt is done and the
; context change for the task and ARM core may need to be done. The ARM
; core is presently in the ARM IRQ mode and may have nested interrupts.
; Call the OSIntEnter(), OSTimeTick(), and OSIntExit() functions.
BL OSIntEnter ; Call OSIntEnter()
BL OSTimeTick ; Call OSTimeTick()
BL OSIntExit ; Call OSIntExit()
; A task context is not needed if the current and highest priority
; tasks are the same. Check the TCB pointers. If they are the same,
; just return from the interrupt and continue the task that was
; already running.
LDR r8, =OSTCBCur
LDR r9, =OSTCBHighRdy
LDR r8, [r8]
LDR r9, [r9]
CMP r8, r9 ; Are current and next TCB's
; the same?
BEQ no_switch ; If the same, then just exit IRQ
; The ARM core is still in IRQ mode. The task's r0-12 registers and the
; task resume address (lr) are saved on the IRQ mode stack, but need to
; be saved on the task's stack. The task's original link register (lr
; in the task's mode) and the task's ARM core mode (CSPR) also need to
; be saved on the task's stack).
BL OSTaskSwHook ; Call OSTaskSwHook()
MRS r0, spsr ; Get interrupted task's mode
STMFD sp!, {r0} ; Save task's CPSR on IRQ stack
; so task mode can use it
; Restore the IRQ stack pointer
ADD sp, sp, #4 ; Restore stack pointer from CPSR
LDMFD sp!, {r0-r12, lr} ; Restore registers to task state
; Go into task mode with interrupts disabled
MRS r0, spsr ; Get interrupted task's mode
ORR r0, r0, #MODE_DIS_IRQ ; Disable interrupts in task mode
MOV r1, sp ; Get IRQ stack pointer - this is
; need for task mode
MSR cpsr_cf, r0 ; Switch to task mode with
; interrupts disabled
; Get task's PC from IRQ stack
LDR r0, [r1, #-4] ; Get task's resume address
STMFD sp!, {r0} ; Save resume address
STMFD sp!, {r2-r12, lr} ; Save task's registers
LDR r0, [r1, #-60] ; Get task's CPSR
LDR r2, [r1, #-52] ; Restore task's r1
LDR r1, [r1, #-56] ; Restore task's r0
STMFD sp!, {r0-r2} ; Save r0, r1, and CPSR
; The task state is now saved on the task stack. Adjsut the stack size
; by one more word to account for the saved resume address and save
; the task stack pointer
LDR r0, =OSTCBCur
LDR r0, [r0] ; Get pointer to current TCB
STR sp, [r0] ; Save stack pointer of cur task
B OSCtxSw_from_int ; Switch to new task
no_switch
LDMFD sp!, {r0-r12, pc}^ ; Restore and exit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Function: OSIntCtxSw
;
; Purpose: OS interrupt level task context switch
;
; Description:
; This function does nothing. The real interrupt level task context
; switch is done after the return from the OSIntExit() function in
; OSTickISR() function. It's a bit easier to do it there, makes
; register restoration easier, and the IRQ mode stack pointer is
; already in the correct position. The call to OSTaskSwHook() is
; still performed here.
;
; Parameters: NA
;
; Outputs; NA
;
; Returns: NA
;
; Notes:
; This function is called from OSIntExit() to perform a task level
; context switch.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
OSIntCtxSw
MOV pc, lr
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Function: ucos_irq_handler
;
; Purpose: Main interrupt (IRQ) handler for uCos-II
;
; Description:
; See function comments.
;
; Parameters: NA
;
; Outputs; NA
;
; Returns: NA
;
; Notes:
; This function works with the LH7A400 interrupt driver and uses
; data initialized and maintained with that driver.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ucos_irq_handler
; Save registers we use for the interrupt
SUB lr, lr, #4 ; Adjust lr to real return address
STMFD sp!, {r0-r12, lr}
; Read the interrupt controller pending register
LDR r1, =INTC_BASE_ADDR
LDR r0, [r1, #INTC_SOURCE_OFF]
; Was it a timer 1 interrupt?
LDR r2, =INTC_TIMER1_INT_MASK
AND r3, r0, r2 ; Mask off timer 1 interrupt bit
CMP r3, r2 ; Was it a timer 1 interrupt?
BNE intc_not_timer ; Not a timer interrupt
; This was a timer interrupt, so clear the timer and perform ThreadX
; context switching
LDR r3, =TIMER1_INT_CLR ; Timer 1 interrupt clear address
STR r0, [r3] ; Clear the timer 1 interrupt
B OSTickISR ; Perform OS timer tick
intc_not_timer
; Loop through all the enabled interrupts and jump to the function
; for the first pending interrupt
LDR r1, =INTC_FIRST_IRQ ; Get first interrupt to check
MOV r2, #0x00000001 ; Interrupt bit mask
; Start checking pending interrupt states
int_check
MOV r3, r2, LSL r1 ; Build interrupt mask
AND r4, r0, r3 ; Check just one bit at a time
CMP r4, r3 ; Is interrupt pending?
BEQ int_found ; Interrupt was found, handle it
ADD r1, r1, #1 ; Check next interrupt bit
CMP r1, #0x1F ; Was this the last interrupt?
BLE int_check ; Check next interrupt
; If the program made it to here, then the interrupt was not found,
; so just return to the caller, as we can do nothing
int_not_found
B int_exit
; A pending interrupt was found, so jump to it's handler
int_found
LDR r0, =irq_func_ptrs ; Get address of jump table
ADD r0, r0, r1, LSL #2 ; Add by interrupt offset
LDR r0, [r0] ; Get handler address
CMP r0, #0 ; Is handler address NULL?
BEQ int_exit ; If null, the exit
MOV lr, pc ; Will return to int_exit
; handler, it is best to save all
; registers, so none get corrupted
MOV pc, r0 ; Jump to handler
; Exit the interrupt function
int_exit
LDMFD sp!, {r0-r12, pc}^ ; Restore and exit
END