avrx_tasking.s
来自「血凝仪检测系统,硬件电路部分由正弦波产生模块、前级放大与滤波模块、检测线圈、锁相」· S 代码 · 共 462 行
S
462 行
#include "avrx.inc"
/*
Copyright 1999-2001, Larry Barello
larry@barello.net
Revision History
20010515- Added support for IAR c_stack (initialize Y in task def)
20010403- Ported to avrx-ctoasm.inc
20010307- Moved AvrXStack into RAM variable so it can be set from main().
- Added function AvrXSetKernelStack
*/
_MODULE(avrx_tasking)
;+
;----------------------------------------------------
; Critical Data Structures
;
; - RunQueue is the head of the run queue.
; - Running is current running task - once inside a Prolog/Epilog
; section, the RunQueue might get re-arranged so we need to track
; who we are.
; - Syslevel is a counter tracking how many times we have reentered
; the kernel, or, how many interrupts are nested (same thing).
;-
_DATASECTION
_GLOBAL(RunQueue, 2) ; Head of the run queue
_GLOBAL(Running, 2) ; Current running task
_GLOBAL(AvrXStack, 2) ; Pointer to the kernel stack
#ifdef __IAR_SYSTEMS_ASM__
_GLOBAL(AvrXCStack, 2) ; Pointer to C parameter stack
#endif
_GLOBAL(SysLevel, 1)
_CODESECTION
;+
; --------------------------------------------------
; void IntProlog(void)
;
; Pushes entire register context onto the stack, returning a frame pointer
; to the saved context. If running in user mode upon entry (SysLevel == 0xFF)
; then switches the stack to the kernel and stores the frame pointer in the
; current processes PID.
;
; PASSED: Nothing
; RETURN: Y = Frame Pointer (See AvrX.inc)
; R0 = pid.state (Single Step Flag) or 0
; ASSUMES:
; USES: R14-R18, R26-R32, SysLevel
; CALLS:
; NOTES: Does not muck with R16-R25 so passed parameters from C to
; AvrX functions can be accessed directly after a call to Prolog.
;
;-
_FUNCTION(IntProlog)
IntProlog: ; 3 cycles
push R29
push R28
push R27
push R26
push R25
push R24
push R23
push R22
push R21
push R20
push R19
push R18
push R17
push R16
push R15
push R14
push R13
push R12
push R11
push R10
push R9
push R8
push R7
push R6
push R5
push R4
push R3
push R2
push R1
push R0
in R0, SREG
push R0
in Yl, SPL
in Yh, SPH ; Grab frame pointer
ldd Xl, Y+_RetLo
ldd Xh, Y+_RetHi
std Y+_R30, Zl
std Y+_R31, Zh ; Swap return address with Z
#ifdef SINGLESTEPSUPPORT
clr R0
#endif /* SINGLESTEPSUPPORT */
lds R1, SysLevel
inc R1
sts SysLevel, R1 ; (80)
brne AlreadyInKernel ; Check if first time in kernel
lds Zh, Running+NextH ; Point to running task
lds Zl, Running+NextL
std Z+PidSP+NextH, Yh
std Z+PidSP+NextL, Yl
#ifdef SINGLESTEPSUPPORT
ldd tmp0, Z+PidState ; Return to caller SingleStepFlag.
sbrs tmp0, SingleStep
rjmp SkipDequeue
ldd R1, Z+PidNext+NextH ; Dequeue current task
sts RunQueue+NextH, R1
ldd R1, Z+PidNext+NextL
sts RunQueue+NextL, R1
mov R0, tmp0 ; Return to caller SingleStepFlag.
cbr tmp0, BV(SingleStep)
std Z+PidState, tmp0 ; Clear flag in PID
SkipDequeue:
#endif /* SINGLESTEPSUPPORT */
lds tmp0, AvrXStack+NextL
out SPL, tmp0
lds tmp0, AvrXStack+NextH
out SPH, tmp0 ; Swap to kernel stack
AlreadyInKernel: ; (85/102)
clr R1 ; R1 = __Zero__ for Avr-gcc
mov Zl, Xl ; scratch in IARICC
mov Zh, Xh
ijmp ; Return to caller (89/106)
_ENDFUNC
/*+
; --------------------------------------------------
; _Epilog
;
; Restore previous context (kernel or user).
; If task has SingleStep flag set, then generate an interrupt
; before returning to the task.
;
; PASSED:
; RETURN:
; ASSUMES: SysLevel >= 0 (running on kernel stack)
; USES: SysLevel
; CALLS:
-*/
_EXTERN(GenerateInterrupt)
_FUNCTION(Epilog)
Epilog:
pop R30
pop R30
_ENDFUNC
_FUNCTION(_Epilog)
_Epilog:
BeginCritical
lds R16, SysLevel ; Interrupts off..
dec R16
brge SkipTaskSwap
lds Zh, RunQueue+NextH
lds Zl, RunQueue+NextL
adiw Zl, 0
breq _IdleTask
sts Running+NextH, Zh
sts Running+NextL, Zl ; Update current running task
ldd Yh, Z+PidSP+NextH
ldd Yl, Z+PidSP+NextL
out SPL, Yl
out SPH, Yh ; 20 cycles
#ifdef SINGLESTEPSUPPORT
ldd R0, Z+PidState ; X, Y and R0 available
sbrc R0, SingleStep
rcall GenerateInterrupt
#endif
SkipTaskSwap: ; 20/6
sts SysLevel, R16
pop R0
out SREG, R0
pop R0
pop R1
pop R2
pop R3
pop R4
pop R5
pop R6
pop R7
pop R8
pop R9
pop R10
pop R11
pop R12
pop R13
pop R14
pop R15
pop R16
pop R17
pop R18
pop R19
pop R20
pop R21
pop R22
pop R23
pop R24
pop R25
pop R26
pop R27
pop R28
pop R29
pop R30
pop R31
EndCriticalReturn ; 97/83 cycles with interrupts off
; Jump here if there are no entries in the _RunQueue.
_IdleTask:
BeginIdle ; Any interrupt will EndIdle
EndCritical ; Enable interrupts
sleep ; Power Down..
EndIdle
rjmp _Epilog
_ENDFUNC
/*+
;-------------------------------------------------
; void * AvrXSetKernelStack(char * newstack);
;
; PASSED: Pointer to end of new stack or NULL
; RETURN: pointer to end of stack
;
-*/
_FUNCTION(AvrXSetKernelStack)
AvrXSetKernelStack:
pop R31
pop R30
subi p1l, 0
sbci p1h, 0
brne sks1
in p1l, SPL
in p1h, SPH
sks1:
sts AvrXStack+NextL, p1l
sts AvrXStack+NextH, p1h
ijmp
_ENDFUNC
/*+
; --------------------------------------------------
; *PID AvrXInitProcess(*TaskControlBlock)
; *PID AvrXRunTask(*TaskControlBlock)
;
; Initialize the PID based upon the TCB table. Run Task
; "resumes" the process as well
;
; PASSED: R25:R24 = TCB
; RETURNS: R25:R24 = PID
; USES: X, Z and R0
; CALLS:
; NOTES: TCB, Layout, below:
; 2 PID
; 1 Flags
; 2 STACK
; 2 Entry
-*/
_FUNCTION(AvrXRunTask)
AvrXRunTask:
rcall AvrXInitTask
rjmp AvrXResume
_ENDFUNC
_FUNCTION(AvrXInitTask)
AvrXInitTask:
#ifdef __IAR_SYSTEMS_ASM__
mov tmp0, Xl
mov tmp1, Xh
#endif
mov Zl, p1l
mov Zh, p1h
rcall lpm_inc ; Get Stack pointer
mov Xl, R0
rcall lpm_inc
mov Xh, R0
#ifdef TASKEXIT
_EXTERN(AvrXTaskExit)
ldi tmp2, lo8_pm(AvrXTaskExit)
st X, tmp2 ; !Pre-dec is not a PUSH!
ldi tmp2, hi8_pm(AvrXTaskExit)
st -X, tmp2 ; Push task exit onto stack
rcall lpm_inc
st -X, R0
rcall lpm_inc
st -X, R0 ; Push task entry point
#else
rcall lpm_inc
st X, R0
rcall lpm_inc
st -X, R0 ; Push task entry point
#endif /* TASKEXIT */
ldi tmp2, 0
#ifdef __IAR_SYSTEMS_ASM__
st -x, tmp2 ; R31
st -x, tmp2 ; R30
rcall lpm_inc
mov tmp3, R0
rcall lpm_inc
st -x, R0 ; Frame is stored in reverse order
st -x, tmp3 ; lo8(c_stack)
ldi tmp3, 29 ; R0-R27 and SREG get "pushed"
#else
ldi tmp2, 0
ldi tmp3, 33 ; R0-R31 and SREG get "pushed"
#endif
PushRegisters:
st -X, tmp2
dec tmp3 ; Fill rest of context with Zeros
brne PushRegisters ; Decrement stack pointer
sbiw Xl, 1 ; because pre dec is not a push!
rcall lpm_inc
mov r1l, R0 ; Get PID -> R23:22
rcall lpm_inc
mov r1h, R0
rcall lpm_inc ; Priority -> R0
mov Zl, r1l
mov Zh, r1h
std Z+PidSP+NextL, Xl
std Z+PidSP+NextH, Xh ; Stash context frame pointer
std Z+PidPriority, R0
ldi Xl, BV(SuspendedBit) | BV(SuspendBit)
std Z+PidState, Xl ; Mark state so we can "resume" pid
std Z+PidNext+NextH, tmp2
std Z+PidNext+NextL, tmp2; Clear pid.next
#ifdef __IAR_SYSTEMS_ASM__
mov Xh, tmp1
mov Xl, tmp0
#endif
ret
lpm_inc: ; Tiny helper...
lpm
adiw Zl, 1
ret
_ENDFUNC
/*+
; --------------------------------------------------
; AvrXResume
;
; Takes a PID and inserts it into the run queue.
;
; Uses two flags, Suspended and Suspend to determine if a task is
; actually suspended, as opposed to just being marked for suspension.
; Mark _SUSP to make QueuePid not queue, Mark _SUSPENDED when attempt
; to queue is made (e.g. not blocked on a semaphore). This is because
; we can't "resume" something that is blocked on a semaphore, even though
; it is marked for suspension.
;
; PASSED: R25:24 = PID to queue
; RETURNS: R24 : 0 = Ok, 1 = Already queued (error)
; USES:
; ASSUMES:
; NOTES:
-*/
_FUNCTION(AvrXResume)
AvrXResume: ; User Entry Point
AVRX_Prolog
mov Zl, p1l
mov Zh, p1h
ldd tmp0, Z+PidState
cbr tmp0, BV(SuspendBit)
std Z+PidState, tmp0 ; clear Suspend flag
sbrs tmp0, SuspendedBit
rjmp ar00
cbr tmp0, BV(SuspendedBit) ; If removed from run queue,
std Z+PidState, tmp0 ; Clear flag and queue it up.
rcall _QueuePid ; If found, then add to run queue
ar00:
rjmp _Epilog
_ENDFUNC
/*+
; --------------------------------------------------
; _QueuePid
;
; Takes a PID and inserts it into the run queue. The run queue is sorted
; by priority. Lower numbers go first. If there are multiple tasks of equal
; priority, then the new task is appended to the list of equals (round robin)
;
; PASSED: p1h:p1l = PID to queue
; RETURNS:
; USES: Z, tmp0-3 and SREG, RunQueue
; ASSUMES:
; NOTES: Returns with interrupts on.
;
-*/
_FUNCTION(_QueuePid)
_QueuePid: ; Kernel entry point only
mov Zl, p1l
mov Zh, p1h
ldd tmp0, Z+PidState ; Xh = Priority & Flags
andi tmp0, (BV(SuspendBit) | BV(IdleBit)) ; if marked Suspended or idle
brne _qpSUSPEND
ldd tmp2, Z+PidPriority
mov tmp0, Yl
mov tmp1, Yh ; Preserve Y through this routine
ldi Yl, lo8(RunQueue)
ldi Yh, hi8(RunQueue)
BeginCritical
_qp00:
mov Zl, Yl
mov Zh, Yh
ldd Yl, Z+PidNext+NextL
ldd Yh, Z+PidNext+NextH ; Z = current, X = Next
adiw Yl, 0
breq _qp01 ; End of queue, continue
ldd tmp3, Y+PidPriority
cp tmp2, tmp3
brsh _qp00 ; Loop until pri > PID to queue
_qp01:
std Z+NextH, p1h
std Z+NextL, p1l ; Prev->Next = Object
mov Zh, p1h
mov Zl, p1l
std Z+NextH, Yh ; Object->Next = Next
std Z+NextL, Yl
mov Yl, tmp0
mov Yh, tmp1
EndCriticalReturn
_qpSUSPEND:
sbr tmp0, BV(SuspendedBit) ; Mark suspended and return
std Z+PidState, tmp0
EndCriticalReturn
_ENDFUNC
_END
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?