Introduction to Real Time Systems

Report
Interrupt and Time Management
in µC/OS-III
Akos Ledeczi
EECE 354, Fall 2010
Vanderbilt University
Interrupt Basics
•
•
•
•
•
•
Context
Interrupt Service Routine (ISR)
Enable/Disable
Nesting
Interrupt disable time
Interrupt response: time between interrupt and
start of user ISR execution
• Interrupt recovery: time required to return to
interrupted code (or higher priority task)
• Task latency: time to return to task level code
Interrupt Handling
• Interrupt controller handles many issues including
providing the address of the required ISR in many cases
• Two models:
– All interrupts vector to a single interrupt handler
– Each interrupt vector directly to its own ISR
µC/OS-III ISR Pseudo Code
MyISR:
Disable all interrupts;
Save CPU registers;
OSIntNestingCtr++;
if (OSIntNestingCtr == 1)
OSTCBCurPtr->StkPtr =
current task’s CPU stackpointer register value
Clear interrupting device;
Re-enable interrupts (optional);
Call user ISR;
OSIntExit();
Restore CPU registers;
Return from interrupt;
•
•
•
•
•
ISR Prologue
ISR Epilogue
Written in assembly
Context: some CPUs do this automatically
Some CPUs have separate stack (and SP) for interrupts (µC/OS-III port can
implement this in software saving space on all task stacks)
User code typically needs to call one of the Post() functions
Must call OSIntExit()
Short ISR
MyShortISR:
Disable all interrupts;
Save necessary CPU registers;
Clear interrupting device;
DO NOT re-enable interrupts;
Call user ISR;
Restore saved CPU registers;
Return from interrupt;
• When no Post() call is needed
• No OSIntExit() call is needed
• Should be the exception as the OS does not know
about this at all…
One Common Master ISR
ISR Prologue;
while (there are still interrupts to process) {
Get vector address from interrupt controller;
Call interrupt handler;
}
ISR Epilogue;
•
•
•
The while loop above can be part of a user ISR written in C
Interrupt controller may provide an actual address of the required ISR or just an
index that can be used to get the address from a table in memory
ISR needs to look like this (one for each kind of interrupt):
void MyISRHandler(void)
•
Note that there is no nested interrupts even though it does handle multiple
interrupts, so ISR Prologue and Epilogue need to be called only once
Direct Post
Interrupt Latency =
maximum interrupt disable time;
Interrupt Response =
interrupt latency +
vectoring to ISR +
ISR prologue;
Interrupt Recovery =
handling of device +
Post() +
ISR epilogue;
Task Latency =
interrupt response +
interrupt recovery +
scheduler lock time
Deferred Post
Interrupt Latency =
maximum interrupt disable time;
Interrupt Response =
interrupt latency +
vectoring to ISR +
ISR prologue;
Interrupt Recovery =
handling of device +
Post() +
ISR epilogue;
Task Latency =
interrupt response +
interrupt recovery +
re-issue Post() +
context switch to task +
scheduler lock time
Direct vs. Deferred
Tick Handling
void
{
OS_CPU_SysTickHandler (void)
/* ISR */
CPU_CRITICAL_ENTER();
OSIntNestingCtr++;
/* Tell uC/OS-III that we are starting an ISR
CPU_CRITICAL_EXIT();
*/
OSTimeTick();
OSIntExit();
*/
*/
/* Call uC/OS-III's OSTimeTick()
/* Tell uC/OS-III that we are leaving the ISR
}
void OSTimeTick(void)
{
OSTimeTickHook();
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
Get timestamp;
Post “time tick” to the interrupt queue;
#else
Signal the Tick Task;
Run round robin scheduling if enabled;
Signal the Timer Task;
#end
}
• Hook is called first to give the application more precise timing
• Your low power app may not need tick processing at all: it is OK, but no
time delays and timeouts can be used.
Pend Lists
• The first few fields are the same: OS_PEND_OBJ
• Type: four characters for easy debugging
• Head and Tail pointer point to OS_PEND_DATA and not TCB
Pend Lists cont’d.
• Prev and Next: doubly linked
list
• TCBPtr: corresponding TCB
• PenObjPtr: object task is
pending on
• Rest: only needed when
pending on multiple objects
Pend List example
Time Management: Relative Delay
• OS_OPT_TIME_DELAY
• Timing is not precise:
– The only thing guaranteed is that the task will be
delayed at least: ((ticks – 1) * tick-period)
Absolute Delay
Void MyTask(void *p_arg)
{
OS_ERR err;
OS_TICK current;
:
:
current = OSTimeGet();
while (1) {
current = current + 10;
OSTimeDly(current, OS_OPT_TIME_MATCH, &err);
if (err == OS_ERR_NONE) {
/* code resumes here after waiting for 10 ticks or resumed by another task */
}
else {
/* some error in Dly function call */
}
}
}
• Regardless of CPU load, the task remains “synchronized”:
for example, it will be called 100 times during 1000 ticks. In
relative mode under heavy CPU load, it may miss ticks, so it
might get called less than 100 times. A time-of-day clock
may be “late” if relative mode is used.
Time Management
• OSTimeDlyHMSM() only works in relative mode
– OS_OPT_TIME_HMSM_STRICT : only valid ranges
– OS_OPT_TIME_HMSM_NON_STRICT: hr: 0-999, sec: 09999
• OSTimeDlyResume()
• OSTimeSet()
• OSTimeGet()
Timers
• Counters that perform an action via a callback function when
0 is reached
• OS_CFG_TMR_EN
• Timer rate is typically (much) lower than tick rate
• Callback is run from the context of the Timer Task: must not
make blocking calls
void
OSTmrCreate (OS_TMR
CPU_CHAR
OS_TICK
OS_TICK
OS_OPT
OS_TMR_CALLBACK_PTR
void
OS_ERR
*p_tmr,
*p_name,
dly,
period,
opt,
p_callback,
*p_callback_arg,
*p_err)
/* initial delay */
/* repeat period */
One-shot Timers
•
•
Can be retriggered
Can be used to implement
watchdog timers
Periodic Timers
Timer States
Timing
• Tick ISR signals Timer Task at the timer rate
• Timer Task eventually runs and calls callback
functions of all timers that have expired
Timer wheel
• The exact same concept as the tick wheel: only a
fraction of the timers need to be looked at at any one
time

similar documents