You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
INTRO<br />
smx ®<br />
TRAINING CLASS<br />
WORKBOOK<br />
TASKS<br />
ITC<br />
v3.6<br />
INTERNALS<br />
A Class on the Theory<br />
Of Using smx<br />
by<br />
Ralph Moore<br />
MEMORY<br />
I/O<br />
© Copyright 1998, 2001, 2004<br />
Micro Digital Associates, Inc.<br />
2900 Bristol Street #G204<br />
Costa Mesa, CA 92626<br />
(714) 437-7333<br />
support@smxinfo.com<br />
www.smxinfo.com<br />
All rights reserved.<br />
TIMING<br />
ERROR<br />
SUMMARY AND<br />
DISCUSSION<br />
NOTES
SMX TRAINING CLASS<br />
APPROXIMATE SCHEDULE<br />
1st day<br />
9:00 AM 1. INTRODUCTION<br />
10:15 AM break<br />
10:30 AM 2. TASKS<br />
12:00 PM lunch<br />
1:00 PM 3. INTERTASK COMMUNICATION<br />
2:45 PM break<br />
3:00 PM 4. INTERNALS<br />
4:00 PM IN-DEPTH Q & A<br />
5:00 PM done<br />
2nd day<br />
9:00 AM 5. MEMORY MANAGEMENT<br />
10:15 AM break<br />
10:30 AM 6. INTERRUPTS & I/O<br />
12:00 PM lunch<br />
1:00 PM 7. TIMING<br />
2:00 PM 8. ERROR MANAGEMENT<br />
2:45 PM break<br />
3:00 PM 9. SUMMARY AND DISCUSSION<br />
4:00 PM 10. DISCUSSION OF OTHER PRODUCTS<br />
5:00 PM done
INTRODUCTION<br />
1. OBJECTIVES<br />
2. DEFINITIONS<br />
3. WHAT IS WRONG WITH SUPERLOOPS?<br />
4. BENEFITS OF MULTITASKING<br />
5. METHODOLOGY OF MULTITASKING<br />
a. NEW MINDSET<br />
b. DIVIDE & CONQUER<br />
6. PRECISION AGRICULTURE EXAMPLE<br />
a. SOIL ANALYSIS MACHINE (SAM)<br />
b. SAM BLOCK DIAGRAM<br />
c. SAM FLOW DIAGRAM<br />
7. WHAT SERVICES DOES A KERNEL PROVIDE?<br />
8. MANUALS<br />
a. REFERENCE MANUAL<br />
b. USER’S GUIDE<br />
c. QUICK START<br />
INTRO-1
OBJECTIVES<br />
TO TEACH:<br />
1. THE PRINCIPLES OF MULTITASKING<br />
2. HOW smx WORKS<br />
3. HOW TO USE smx EFFECTIVELY TO SIMPLIFY APPLICATION<br />
CODE<br />
COURSE FORMAT<br />
1. INFORMAL<br />
2. QUESTIONS ARE WELCOME<br />
REVIEW APPLICATION<br />
1. SO I CAN SLANT COURSE AT IT<br />
2. DRAW EXAMPLES FROM APPLICATION<br />
WHO HAS USED MULTITASKING?<br />
INTRO-2
DEFINITIONS<br />
TASK<br />
A SMALL PART OF A JOB. (SAME AS A THREAD.)<br />
PROCESS<br />
A COMPLETE EXECUTABLE OR APPLICATION.<br />
USUALLY AN EMBEDDED SYSTEM IS A SINGLE PROCESS.<br />
<strong>RTOS</strong><br />
LAN<br />
Network<br />
smx<br />
Kernel<br />
Shell<br />
User Interface<br />
We will be discussing<br />
primarily the kernel<br />
Sensors &<br />
Actuators<br />
Application<br />
File i/o<br />
Disk<br />
BOOTS AND RUNS FROM RAM<br />
LOADS, RUNS, AND MANAGES APPLICATION CODE<br />
USUALLY LARGE, COMPLEX, SLOW & EXPENSIVE<br />
SMX BREAKS THIS MOLD. IT IS A MODULAR <strong>RTOS</strong><br />
REAL-TIME MULTITASKING KERNEL (smx)<br />
TARGETED AT BLACK BOXES W/O STD I/O DEVICES<br />
RUNS FROM ROM ON POWER-UP OR RESET<br />
USUALLY LINKED WITH APPLICATION<br />
SMALL, SIMPLE, FAST, & INEXPENSIVE<br />
HEREAFTER CALLED “KERNEL”<br />
INTRO-3
WHAT IS WRONG WITH SUPERLOOPS?<br />
1<br />
2<br />
1<br />
2<br />
3<br />
3<br />
INTERRUPT<br />
TASK<br />
4<br />
1. LACK OF TASK INDEPENDENCE<br />
2. CRUDE TASK SCHEDULING<br />
a. FIXED EXECUTION ORDER ==> INFLEXIBILITY<br />
b. NO TASK PRIORITIES<br />
c. NO PREEMPTION<br />
3. NO INTER-TASK COMMUNICATION MECHANISM<br />
4. INFLEXIBILITY — HARD TO ADD MORE TASKS<br />
5. POOR HANDLING OF ASYNCHRONOUS EVENTS<br />
6. DIFFICULTY ACHIEVING FAST RESPONSE TIMES<br />
THESE LIMITATIONS EVENTUALLY LEAD TO “SPAGHETTI CODE”<br />
BASIC RULE:<br />
WHAT IS NOT IN THE KERNEL ENDS UP IN THE APPLICATION<br />
EXTREME KERNEL SIMPLICITY LEADS TO APPLICATION COMPLEXITY<br />
INTRO-4
BENEFITS OF MULTITASKING<br />
TASK<br />
ISR<br />
Client<br />
F1<br />
F3<br />
Server<br />
OR<br />
TASK TASK TASK<br />
Output1<br />
ISR<br />
Client<br />
F2<br />
ISR<br />
TASK<br />
TASK<br />
Output2<br />
Initial Design<br />
Added Later<br />
1. TASK INDEPENDENCE — NO SET ORDER OF EXECUTION<br />
2. PRIORITY-BASED TASK SCHEDULING<br />
3. MANY INTER-TASK COMMUNICATION MECHANISMS<br />
4. FLEXIBLE STRUCTURE (ADD NEW TASK EASILY — SEE ABOVE)<br />
5. GOOD HANDLING OF ASYNCHRONOUS EVENTS<br />
6. EASE OF ACHIEVING FAST RESPONSE TIMES<br />
+ 7. OOP PARADIGM: (VERY IMPORTANT BENEFIT)<br />
a. ENCAPSULATION:<br />
— TCB’S & STACKS<br />
— OTHER KERNEL OBJECTS ALSO HAVE CB'S<br />
b. REUSE<br />
c. REPLACEMENT (SEE ABOVE)<br />
8. CLIENT/SERVER PARADIGM (GOOD DESIGN TECHNIQUE)<br />
9. WELL-DEFINED INTERFACES BETWEEN FUNCTIONAL AREAS -<br />
I.E. F1, F2, F3 (MESSAGES, SEMAPHORES, ETC.)<br />
— GOOD FOR MULTI-PROGRAMMER PROJECTS<br />
INTRO-5
NEW MINDSET NEEDED<br />
N<br />
Y<br />
Not Superloop Not Sequential<br />
MULTITASKING REQUIRES DIFFERENT THINKING:<br />
1. PARALLEL NOT SEQUENTIAL<br />
Rx<br />
Tx<br />
CHK<br />
Data<br />
ACK/NAK<br />
Data<br />
FLOW DIAGRAM<br />
CONTROL == DATA<br />
2. FLOW IS CONTROL<br />
T 1<br />
M X M T 2<br />
Task Message Exchange<br />
3. OBJECT-ORIENTATION<br />
TASKS<br />
MESSAGES<br />
EXCHANGES<br />
SEMAPHORES<br />
QUEUES<br />
INTRO-6
DIVIDE AND CONQUER<br />
SYSTEM -> SUBSYSTEMS<br />
1 2<br />
3 4<br />
4a<br />
SUBSYSTEM<br />
-> TASKS<br />
4b<br />
4d<br />
4c<br />
WORK DOWN FROM HIGH LEVEL<br />
GREATER AND GREATER DEFINITION<br />
DETAILING ONE SUBSYSTEM USUALLY HAS LITTLE IMPACT ON<br />
ANOTHER<br />
INCREMENTAL DESIGN, IMPLEMENTATION, AND TEST<br />
USE SKELETON TASKS (E.G. 4a & 4b TO TEST 4d)<br />
INTRO-7
SOIL ANALYSIS MACHINE (SAM)<br />
wireless WAN antenna<br />
GPS antenna<br />
air-conditioned cab<br />
soil samples<br />
soil sampler<br />
Soil<br />
Analysis<br />
Machine<br />
(Soil)<br />
Sample S+1<br />
SAM Inside<br />
Sample S<br />
N K P PH<br />
Fe<br />
Chemical<br />
Analyzers<br />
Returneth<br />
INTRO-8
SAM SPECIFICATION<br />
BLOCK DIAGRAM<br />
Fertilizer<br />
Company<br />
GPS<br />
Receiver<br />
Farmer<br />
Soil<br />
Sampler<br />
Controller<br />
Main<br />
Computer<br />
Wireless<br />
WAN<br />
Soil<br />
Sample<br />
Analyzers<br />
Operator<br />
Interface<br />
File<br />
I/O<br />
PCMCIA<br />
HD<br />
Touchscreen<br />
LCD<br />
Keyboard<br />
Inkjet<br />
Color<br />
Printer<br />
FUNCTIONS<br />
1. CONTROL GPS RECEIVER, REQUEST POSITION & TIME INFO<br />
2. CONTROL SOIL SAMPLING MECHANISM<br />
3. PERFORM SOIL ANALYSIS (N, K, P, Acidity, Fe)<br />
4. PROVIDE TRACTOR DRIVER WITH INFORMATION & DIRECTIONS<br />
5. ACCEPT TOUCHSCREEN COMMANDS FROM TRACTOR DRIVER<br />
6. ACCEPT KEYBOARD INPUT<br />
7. GENERATE GRAPHICAL & TEXT OUTPUT TO LCD<br />
INTRO-9
SAM SPECIFICATION (CONT)<br />
8. PREPARE NUTRIENT MAP<br />
a. PRINT<br />
b. STORE<br />
c. TRANSMIT TO FARMER<br />
9. CALCULATE FERTILIZER NEEDS<br />
10. INTERACT WITH FERTILIZER COMPANY<br />
a. SEND DATA (WEB SITE)<br />
b. RECEIVE INSTRUCTIONS (JAVA VIRTUAL MACHINE (JVM))<br />
11. CHECK & CALIBRATE SUBSYSTEMS<br />
INTRO-10
SAM ARCHITECTURE<br />
FLOW DIAGRAM<br />
GPS<br />
position<br />
Where<br />
slower/<br />
1<br />
2 faster<br />
4<br />
Speed sample GUI<br />
Control<br />
Soil<br />
Sampler<br />
sample<br />
taken<br />
tag #<br />
1<br />
Tagger<br />
2 3<br />
Sample<br />
Correlator<br />
Analyzer<br />
bad sample<br />
warning<br />
tag #<br />
Map<br />
Maker<br />
3<br />
Disk<br />
Store<br />
4<br />
Analyzer<br />
Controls<br />
Soil Analyzers<br />
Iron<br />
Acid 1<br />
Phos 1<br />
Pot 1<br />
Nitro 1<br />
1<br />
Analyzer<br />
Outputs<br />
touch<br />
screen<br />
Opcon<br />
kbd<br />
1<br />
Plotter<br />
3<br />
www.<br />
tractor.<br />
com<br />
WAN<br />
2<br />
1<br />
web<br />
server<br />
TCP/IP<br />
browser<br />
TASK DEFINITION CRITERIA<br />
1. ASYNCHRONOUS FUNCTIONS (NOT THE ONLY CRITERIA!)<br />
2. HIGHER LEVEL (PROCESS OUTPUTS OF MULTIPLE LOWER-<br />
LEVEL TASKS)<br />
3. FUNCTIONALLY DIFFERENT (FOSTER RE-USE &<br />
REPLACEMENT)<br />
4. SERVER (USUALLY MULTIPLE CLIENTS)<br />
INTRO-11
WHAT SERVICES DOES A KERNEL PROVIDE?<br />
1. TASK MANAGEMENT<br />
2. INTERTASK COMMUNICATION (ITC)<br />
3. MEMORY MANAGEMENT<br />
4. INTERRUPT AND I/O MANAGEMENT<br />
5. TIMING<br />
6. ERROR MANAGEMENT<br />
THESE WILL BE COVERED NEXT<br />
SMX MANUALS<br />
REFERENCE MANUAL — 100% DEFINITION OF SMX<br />
USER'S GUIDE — TUTORIAL<br />
QUICK START — INSTALLATION, GETTING STARTED,<br />
APPLICATION DEVELOPMENT<br />
TARGET GUIDE — CPU AND TOOL INFORMATION<br />
INTRO-12
TASKS<br />
1. NATURE OF TASKS<br />
2. TASK STATES<br />
3. TASK SCHEDULING<br />
4. CREATING TASKS<br />
5. STARTING TASKS<br />
6. WHY HAVE DYNAMIC TASKS?<br />
7. TASK PREEMPTION<br />
8. BUMPING TASKS<br />
9. BOUND VS. UNBOUND TASKS<br />
10. TASK CONTROL FUNCTIONS<br />
11. TASK FLAGS<br />
12. LOCATE & DELETE<br />
TASKS-1
NATURE OF TASKS<br />
MORE PRECISE DEFINITION OF SOFTWARE TASK<br />
A PORTION OF THE WORK, WHICH IS NOT CONFINED TO ONE<br />
REPETITION<br />
CONSISTS OF:<br />
1. CODE — TYPICAL:<br />
void atask_main(par)<br />
{<br />
// initialization code<br />
while(1)<br />
{<br />
// main task code<br />
}<br />
}<br />
2. STACK<br />
REQUIRED FOR EVERY ACTIVE TASK<br />
USUALLY ARE FAIRLY LARGE (500-10000 BYTES)<br />
ALLOW FOR:<br />
MAXIMUM NESTING OF FUNCTIONS (P, R, & AV) 1<br />
MAXIMUM NESTING OF ISR’S (R & AV) 2<br />
MAXIMUM LSR REQUIREMENT (P, R, & AV) 2<br />
SPACE FOR RUN-STATE REGISTERS (INCLUDING<br />
COPROCESSOR) WHEN TASK IS SUSPENDED<br />
SPACE NEEDED BY DEBUGGER, IF DEBUGGING<br />
1<br />
P, R, & AV = PARAMETERS, REGISTERS SAVED, &<br />
AUTOVARIABLES<br />
2 ISR’S AND LSR’S SHARE CURRENT TASK’S STACK FOR<br />
SPEED<br />
TASKS-2
NATURE OF TASKS (CONT)<br />
3. TASK CONTROL BLOCK (TCB)<br />
fl<br />
bl<br />
flags TASK<br />
priority<br />
errnum<br />
rv<br />
sv<br />
sfp<br />
sp<br />
ss<br />
thisptr<br />
fun<br />
hook_entry<br />
hook_exit<br />
stp<br />
sbp<br />
ssz<br />
shwm<br />
ctxt<br />
name<br />
forward link<br />
backward link<br />
cbtype and flags<br />
task priority<br />
last error for task<br />
return value<br />
suspend value<br />
stack frame pointer<br />
stack pointer<br />
stack segment (segmented versions only)<br />
this pointer ( smx ++<br />
)<br />
function pointer<br />
hooked entry routine pointer<br />
hooked exit routine pointer<br />
stack top pointer<br />
stack bottom pointer<br />
stack size<br />
stack high-water mark<br />
pointer to task context<br />
task name<br />
4. TIMER<br />
FOR TASK TIMEOUT, WHENEVER IN WAIT STATE<br />
NOTES<br />
1. TCB AND TIMER ARE UNIQUE TO EACH TASK CREATED<br />
2. STACK IS UNIQUE TO EACH ACTIVE TASK<br />
3. CODE CAN BE SHARED BETWEEN TASKS<br />
TASKS ARE OBJECTS, WHEREAS CODE IS JUST CODE.<br />
TASKS-3
TASK STATES<br />
A TASK MAY BE IN ONE OF FOUR STATES<br />
RUN<br />
create = create_task<br />
del = delete_task<br />
Start Here<br />
preempt,<br />
resume, start<br />
dispatch<br />
del<br />
NULL<br />
create<br />
stop, suspend, sleep,<br />
receive, test, count<br />
READY<br />
del<br />
del<br />
stop, suspend<br />
start, resume, timeout, send, signal<br />
WAIT<br />
NULL<br />
WAIT<br />
READY<br />
RUN<br />
TASK NOT YET CREATED<br />
WAITING FOR AN EVENT TO OCCUR<br />
READY TO RUN. (IN THE READY QUEUE.)<br />
ACTUALLY RUNNING. ONLY ONE TASK AT A TIME CAN<br />
ACTUALLY RUN. IT IS THE “CURRENT TASK” (ct).<br />
FROM OUTSIDE IT LOOKS LIKE ALL READY TASKS ARE RUNNING<br />
AT ONCE.<br />
TASKS-4
TASK SCHEDULING<br />
SCHEDULING IS THE METHOD FOR DECIDING WHICH READY TASK TO<br />
DISPATCH (I.E. RUN) NEXT<br />
TYPES<br />
ROUND ROBIN<br />
TIMESLICING<br />
PRIORITY TIMESLICING<br />
PREEMPTIVE<br />
ROUND ROBIN (AKA “COOPERATIVE”)<br />
TASKS MUST BE ALTERED AS<br />
SYSTEM CHANGES<br />
NOT MUCH IMPROVEMENT OVER<br />
A SUPERLOOP<br />
C<br />
B<br />
A<br />
task decides when<br />
to release processor<br />
C<br />
too long<br />
service<br />
event<br />
TIMESLICING<br />
Fixed length<br />
time slices<br />
C<br />
too long<br />
APPROPRIATE FOR MULTI-USER<br />
SYSTEMS (E.G. UNIX) —<br />
CONCEPT OF “FAIRNESS”<br />
C<br />
B<br />
fixed<br />
order<br />
NOT CONSISTENT WITH<br />
EMBEDDED APPLICATIONS — ALL<br />
EVENTS ARE NOT EQUAL<br />
A<br />
TASKS-5
TASK SCHEDULING (CONT)<br />
PRIORITY TIMESLICING<br />
top priority<br />
C<br />
better<br />
INCREASES COMPLEXITY<br />
EVEN THEN, WORST CASE<br />
RESPONSE IS 1 TIMESLICE<br />
A SMALL TIMESLICE RESULTS IN<br />
EXCESSIVE TASK SWITCHING<br />
USED BY Phar Lap ETS, WinCE,<br />
WinNT<br />
C<br />
B<br />
A<br />
variable<br />
order<br />
PREEMPTIVE (smx)<br />
PREEMPTION DUE TO:<br />
HARDWARE INTERRUPT<br />
smx CALL<br />
MOST NATURAL METHOD<br />
RESPONSIVENESS IS ASSURED<br />
(ALMOST NO WAIT)<br />
CAN TUNE SYSTEM BY<br />
CHANGING PRIORITIES OF TASKS<br />
EASY TO ADD, REMOVE, OR<br />
CHANGE TASKS<br />
USED BY MOST “HARD” REAL-<br />
TIME KERNELS & <strong>RTOS</strong>'S<br />
C<br />
B<br />
A<br />
C<br />
optimum<br />
TASKS-6
TASK SCHEDULING (CONT)<br />
READY QUEUE (RQ)<br />
HOLDS TASKS WHICH ARE READY TO RUN<br />
SEQUENTIAL READY QUEUE (NOT USED BY smx):<br />
Task to<br />
Enqueue:<br />
2<br />
Last task<br />
to run<br />
Ready Queue:<br />
4 3 3 2<br />
0<br />
Next task<br />
# = priority<br />
to run<br />
SEARCH AND SEARCH ... AND SEARCH<br />
LAYERED READY QUEUE (smx) — MUCH FASTER:<br />
TWO STEPS TO ENQUEUE A TASK:<br />
1. INDEX TO PRIORITY LEVEL<br />
2. PUT AT END OF LEVEL<br />
4<br />
4<br />
3<br />
3<br />
3<br />
2<br />
index<br />
2<br />
2<br />
enqueue<br />
DOUBLY LINKED LIST<br />
MAKES STEP 2 FAST<br />
(USE BACKLINK FROM QCB)<br />
1<br />
#’s ARE PRIORITIES<br />
0<br />
0<br />
QCB<br />
TCB<br />
TASKS-7
NOTES ON SMX FUNCTIONS<br />
1. RETURN VALUE == 0 ==> FAILURE OR TIMEOUT<br />
!= 0 ==> OK (USUALLY HANDLE OR TRUE)<br />
2. MANY SMX FUNCTIONS RETURN “HANDLES”. THESE ARE<br />
UNSIGNED INTEGERS (UINTS) WHICH IDENTIFY SMX<br />
OBJECTS.<br />
3. WHEN AN SMX OBJECT IS CREATED, ITS HANDLE IS USUALLY<br />
STORED IN A STATIC/GLOBAL VARIABLE BEARING THE NAME<br />
OF THE OBJECT.<br />
TCB_PTR my_task;<br />
my_task = create_task (...)<br />
4. WE WILL SKIP OVER SELDOM-USED SMX FUNCTIONS<br />
5. ALL CAPS ==> MACROS OR INLINE FUNCTIONS (e.g. LOCK(),<br />
TCB_PTR)<br />
6. MOST SMX FUNCTIONS ARE SSR’S (SYSTEM SERVICE<br />
ROUTINES) WHICH ARE “ATOMIC”<br />
7. SMX FUNCTIONS ARE ORTHOGONAL AND SYMMETRIC<br />
A. "ORTHOGONAL" MEANS TASK STATE DOES NOT MATTER<br />
B. "SYMMETRIC" MEANS THAT WHAT CAN BE DONE CAN BE<br />
UNDONE<br />
8. "near" & "far" ARE X86 SEGMENTATION CONCEPTS.<br />
EVERYTHING IS "near" FOR 32-BIT FLAT PROCESSORS.<br />
TASKS-8
CREATING TASKS<br />
FUNCTION<br />
atask = create_task (task_main, priority, stack_size)<br />
DEFINITIONS<br />
atask<br />
task_main()<br />
priority<br />
stack_size<br />
TCB_PTR atask;<br />
[NEAR] POINTER WHICH STORES THE TASK HANDLE<br />
IS THE CODE<br />
0-127 (127 IS HIGHEST)<br />
BYTES<br />
USES<br />
NOTES<br />
CREATES A TASK<br />
STATES: NULL —> WAIT<br />
1. 0 PRIORITY IS RESERVED FOR IDLE TASK OR TIMESLICED<br />
TASKS<br />
2. FIRST-COME, FIRST SERVE IS OFTEN BEST BETWEEN TASKS<br />
OF SIMILAR IMPORTANCE (e.g. SAM ANALYZER TASKS)<br />
USUALLY ONLY A HALF A DOZEN PRIORITY LEVELS ARE<br />
NECESSARY OR USEFUL<br />
3. RECOMMEND NAMES, NOT NUMBERS:<br />
enum priority = { MIN, LOW, NORM, HIGH, MAX }<br />
4. EASY TO ADD PRIORITIES:<br />
enum priority = { MIN, VLOW, LOW, NORM, MHIGH, HIGH, MAX }<br />
5. stack_size == 0 TEMPORARY STACK WILL BE ASSIGNED<br />
FROM STACK POOL WHEN TASK IS<br />
DISPATCHED<br />
!= 0 PERMANENT STACK IS ASSIGNED AT TIME<br />
OF TASK CREATION<br />
TASKS-9
STARTING TASKS<br />
FUNCTIONS<br />
startx (atask)<br />
start_par (atask, par)<br />
VARIABLES<br />
atask<br />
par<br />
TASK HANDLE<br />
UINT PARAMETER PASSED TO atask STARTUP CODE<br />
USES<br />
NOTES<br />
START NEW TASKS<br />
ABORT AND RESTART TASKS<br />
1. MOVES A TASK TO THE END OF ITS PRIORITY LEVEL, IN rq,<br />
AND STARTS IT OVER — IF IN RQ<br />
2. STATES: WAIT, READY, RUN —> READY<br />
3. ACCEPTING A PARAMETER<br />
void atask_main(uint par)<br />
{<br />
do_something_with(par);<br />
// ...<br />
while(1)<br />
{<br />
}<br />
}<br />
TASKS-10
STARTING TASKS (CONT)<br />
EXAMPLE: CREATE TWO TASKS, ONE OF WHICH PREEMPTS THE OTHER<br />
void init(void)<br />
{<br />
TCB_PTR A, B;<br />
A = create_task(A_main, NORM, 500);<br />
B = create task(B_main, HIGH, 500);<br />
startx(A);<br />
}<br />
Case I<br />
void A_main(void)<br />
{<br />
unlockx(); // make preemptible<br />
startx(B);<br />
f1();<br />
} !<br />
void B_main(void)<br />
{<br />
f2();<br />
} !<br />
f2() RUNS BEFORE f1()<br />
// both have permanent<br />
// stacks<br />
Case II<br />
void A_main(void)<br />
{<br />
startx(B); // not preemptible<br />
f1();<br />
unlockx();<br />
} !<br />
void B_main(void)<br />
{<br />
f2();<br />
} !<br />
f1() RUNS BEFORE f2()<br />
! BOTH TASKS AUTO-STOP (RUN —> WAIT)<br />
TASKS-11
WHY HAVE DYNAMIC TASKS?<br />
i.e. atask = create_task (...);<br />
& delete_task (atask);<br />
TASKS CONSUME RESOURCES<br />
BETTER TO CREATE EXPLICITLY IN CODE — EASIER TO MODIFY IN<br />
ONE PLACE THAN TWO (I.E. CODE AND TABLE)<br />
DYNAMIC TASKS ARE NECESSARY FOR DLM’S, DLL’S, APPLETS, ETC.<br />
WE ARE LIVING IN AN INCREASINGLY DYNAMIC WORLD<br />
EXAMPLE: VARIABLE # OF COMM PORTS<br />
PORTS<br />
User Input<br />
or<br />
Autodetect<br />
n = # PORTS<br />
void comm_code (port)<br />
{<br />
}<br />
// same for all ports<br />
TCB_PTR comm_task[]; // array of task handles<br />
comm_task = (TCB_PTR *) callocx(n, sizeof(TCB_PTR));<br />
for (j = 0; j < n; j++) // n = # PORTS<br />
{<br />
comm_task[j] = create_task(comm_code, HI, 500);<br />
start_par(comm_task[j], j);<br />
}<br />
// n = # PORTS<br />
TASKS-12
TASK PREEMPTION<br />
B (HIGH)<br />
A (NORM)<br />
B preempts* A<br />
T 1 T 2<br />
done<br />
done<br />
* DUE TO:<br />
(1) HDW INTERRUPT<br />
(2) SMX CALL FROM A<br />
PROBLEMS WITH PREEMPTION:<br />
NON-REENTRANT SHARED CODE<br />
GLOBAL VARIABLES<br />
NON-SHARABLE RESOURCES<br />
smx TASKS START AS NON-PREEMPTIBLE<br />
void atask_main (void)<br />
{<br />
// …<br />
unlockx(self);<br />
while(1)<br />
{<br />
// …<br />
LOCK();<br />
// …<br />
unlockx(self);<br />
// ..<br />
}<br />
}<br />
nonpre<br />
pre<br />
nonpre<br />
pre<br />
Task can lock itself at any time<br />
METHODS TO PREVENT TASK PREEMPTION PROBLEMS<br />
1. LOCK CURRENT TASK (ABOVE)<br />
2. PUT TASKS AT THE SAME PRIORITY LEVEL* (OFTEN<br />
OVERLOOKED)<br />
3. USE SEMAPHORE (AKA "MUTEX") (TRADITIONAL)<br />
4. USE MESSAGE AS A TOKEN (NICE IF MSG HAS RESOURCE<br />
INFO — I/O PORTS ETC.)<br />
* if not timesliced<br />
TASKS-13
TASK PREEMPTION (CONT)<br />
MUST ALWAYS BE AWARE OF THE POSSIBILITY OF PREEMPTION — IT<br />
CAUSES DIFFICULT BUGS TO FIND<br />
NON-ATOMIC OPERATION:<br />
F1();<br />
F2();<br />
preemption possible<br />
by F3()<br />
F1 sets variable<br />
F2 uses variable<br />
F3 changes variable<br />
ATOMIC OPERATION:<br />
LOCK();<br />
F1();<br />
F2();<br />
unlockx();<br />
F3();<br />
preemption<br />
not possible<br />
SSR'S & LSR'S ARE ALSO ATOMIC:<br />
void F12(void)<br />
{<br />
ENTER_SSR(id)<br />
F1();<br />
F2();<br />
exit_ssr(value)<br />
}<br />
.......<br />
.......<br />
invoke(F12_lsr, par)<br />
.......<br />
.......<br />
OR:<br />
void F12_lsr(uint par)<br />
{<br />
F1();<br />
F2();<br />
}<br />
TASKS-14
BUMPING TASK PRIORITIES<br />
FUNCTION<br />
USES<br />
bump_task (atask, new_priority);<br />
1. CHANGE PRIORITY OF ANOTHER TASK<br />
#define cp ct->priority;<br />
bump_task(atask, cp-1); // put atask 1 below ct priority (so it won't<br />
// preempt)<br />
2. BUMP CURRENT TASK TO THE END OF ITS LEVEL —<br />
PRODUCES ROUND-ROBIN SCHEDULING<br />
bump_task(ct); // moves ct to end of cp level<br />
3. CHANGE CURRENT TASK’S PRIORITY LEVEL TO REFLECT THE<br />
IMPORTANCE OF A SECTION OF CODE OR TO PREVENT<br />
PREEMPTION<br />
// PRI_F is highest priority of any task using F()<br />
old_priority = cp;<br />
bump_task(ct, PRI_F);<br />
F(x);<br />
bump_task(ct, old_priority);<br />
ABOVE IS AN EXAMPLE OF SAME PRIORITY-LEVEL PREEMPTION<br />
BLOCKING. IT IS A GOOD METHOD TO PREVENT PRIORITY<br />
INVERSION.<br />
TASKS-15
BOUND VS. UNBOUND TASKS<br />
(Unique Feature)<br />
1<br />
T<br />
S<br />
CURRENT<br />
TASK<br />
temporary stack<br />
START HERE<br />
permanent stack<br />
STOP<br />
CREATE<br />
TASK<br />
(STACK =)<br />
0<br />
STOP<br />
CREATE<br />
POOL<br />
T<br />
S<br />
1 2<br />
WAIT<br />
T<br />
S<br />
S<br />
1<br />
T<br />
S<br />
T<br />
2<br />
S<br />
S<br />
STACK<br />
POOL<br />
RESUME<br />
START<br />
START<br />
S<br />
permanent<br />
stack<br />
temporary<br />
stack<br />
S<br />
1<br />
T<br />
S<br />
READY<br />
T<br />
2<br />
S<br />
DISPATCH<br />
RUN<br />
1<br />
2<br />
100 tasks @ 1KB/stack = 100KB (not too big a deal in 32-bit systems)<br />
1000 tasks @ 1KB/stack = 1MB<br />
TASKS-16
TASK CONTROL FUNCTIONS<br />
(BASIC TO ALL smx TASK SERVICES)<br />
startx (atask)<br />
STATE: WAIT, READY, RUN —> READY<br />
RESTARTS atask IMMEDIATELY<br />
USE TO START OR RESTART atask<br />
STACK IS RELEASED UNLESS PERMANENTLY BOUND<br />
STACK POINTER IS RESET IF PERMANENTLY BOUND<br />
stop (atask, timeout)<br />
STATE: WAIT, READY, RUN —> WAIT<br />
TASK WILL RESTART AT TIMEOUT<br />
USE TO ABORT atask<br />
STACK IS RELEASED UNLESS PERMANENTLY BOUND<br />
STACK POINTER IS RESET IF PERMANENTLY BOUND<br />
resume (atask)<br />
STATE: WAIT, READY, RUN —> READY<br />
RESUMES A TASK FROM WHERE SUSPENDED OR PREEMPTED<br />
RESTARTS TASK IF IT WAS STOPPED<br />
USE TO RESUME<br />
STACK HELD & STACK POINTER IS UNCHANGED<br />
suspend (atask, timeout)<br />
STATE: WAIT, READY, RUN —> WAIT<br />
TASK WILL RESUME AT TIMEOUT<br />
TASK WILL RESTART AT TIMEOUT IF IT WAS STOPPED<br />
USE TO SUSPEND<br />
STACK HELD & STACK POINTER IS UNCHANGED<br />
TASKS-17
TASK CONTROL FUNCTIONS (CONT)<br />
THE PRECEDING ILLUSTRATES:<br />
ORTHOGONALITY: ANY OPERATION IN ANY STATE (EXCEPT NULL)<br />
AND SYMMETRY<br />
STOP SUSPEND<br />
START<br />
RESUME<br />
ALL SMX FUNCTIONS WHICH AFFECT TASK STATES ARE BUILT<br />
ON THIS BACKBONE<br />
TASKS-18
TASK FLAGS<br />
TASK CBTYPE:<br />
1 F E L S B C H<br />
F<br />
E<br />
L<br />
S<br />
B<br />
C<br />
H<br />
STACK IS FAR<br />
TASK IS IN EVENT QUEUE<br />
TASK IS LOCKED<br />
LOCK (task=ct)<br />
unlockx (task=ct)<br />
PERFORM STACK CHECKING<br />
STACK_CHECK (ON/OFF, task=ct)<br />
PERMANENTLY BOUND STACK<br />
TASK IS USING COPROCESSOR<br />
SAVE COPRO REGISTERS ON SUSPENSION<br />
RESTORE COPRO REGISTERS ON RESUMPTION<br />
copro (ON/OFF, task=ct)<br />
TASK IS HOOKED<br />
RUN EXIT ROUTINE ON SUSPENSION<br />
RUN ENTRY ROUTINE ON RESUMPTION<br />
hook (entry, exit, task=ct)<br />
cPROC<br />
cPROC<br />
_entry<br />
; entry routine code here<br />
ret<br />
_exit<br />
; exit routine code here<br />
ret<br />
HOOKED ROUTINES ACT LIKE EXTENSIONS OF THE SHEDULER.<br />
TASKS-19
TASK LOCATE & DELETE FUNCTIONS<br />
queue = locate (atask)<br />
RETURNS HANDLE OF QUEUE TASK IS IN, OR 0 IF NOT IN QUEUE<br />
delete_task (atask)<br />
RELEASES ALL BLOCKS, MESSAGES, AND TIMERS OWNED BY<br />
atask<br />
DELETES atask<br />
RELEASES STACK BACK TO HEAP OR POOL<br />
RELEASES TCB AND TIMER<br />
CLEARS atask POINTER<br />
STATES: WAIT, READY, RUN —> NULL<br />
(NOTE: FOR A TASK TO DELETE ITSELF REQUIRES A<br />
HELPER TASK smxKillTask WHICH IS PART OF<br />
PROTOSYSTEM)<br />
TASKS-20
INTERTASK COMMUNICATION (ITC)<br />
1. ITC PURPOSE & MECHANISMS<br />
2. EXCHANGES<br />
3. EXAMPLE: MESSAGES TO SAM CORRELATOR TASK<br />
4. MESSAGES<br />
5. MESSAGE PRIORITY<br />
6. TASK PRIORITY<br />
7. MISC EXCHANGE & MESSAGE FUNCTIONS<br />
8. EXAMPLE: MORE DETAIL ON SENDING AND RECEIVING<br />
9. EXAMPLE: RECEIVE STOP<br />
10. SEMAPHORES<br />
11. EXAMPLE: MUTUAL EXCLUSION<br />
12. EVENT QUEUES<br />
13. COUNTING EVENTS<br />
14. EVENT TABLES<br />
15. EXAMPLE: ANALYZER GROUP<br />
16. EXAMPLE: BETTER IMPLEMENTATION<br />
ITC-1
ITC PURPOSE & MECHANISMS<br />
PURPOSE<br />
1. EXCHANGE DATA & CONTROL INFORMATION BETWEEN<br />
TASKS<br />
2. SYNCHRONIZATION OF TASKS<br />
3. CONTROL OF TASKS<br />
MECHANISMS PROVIDED BY SMX:<br />
EXCHANGES<br />
SEMAPHORES<br />
AKA<br />
“MAILBOXES”<br />
“MUTEXES”<br />
EVENT QUEUES<br />
EVENT TABLES<br />
PIPES<br />
“EVENT GROUPS”<br />
“QUEUES”<br />
WITHOUT ITC NOTHING USEFUL CAN BE ACCOMPLISHED<br />
(REFER BACK TO SAM FLOW DIAGRAM, INTRO-11)<br />
MESSAGING IS ONE OF smx's STRONGEST FEATURES<br />
ITC-2
EXCHANGES<br />
PERMIT SENDING MESSAGES BETWEEN TASKS<br />
send()<br />
receive()<br />
T 1<br />
X T 2<br />
T has sent M<br />
1 1<br />
T will receive it<br />
2<br />
M 1<br />
EXCHANGES CAN ENQUEUE MESSAGES<br />
T 1<br />
X M 1 T 2<br />
M 2<br />
T 1 has sent 3 messages<br />
T 2 has received the<br />
first message<br />
M 3<br />
INTRODUCES FLEXIBILITY: T 1 & T 2 ARE NOT IN LOCKSTEP<br />
MESSAGE QUEUE AT X IS T 2 ’s WORKSTREAM<br />
EXCHANGES CAN RECEIVE MESSAGES FROM MANY TASKS<br />
T 2<br />
T 1<br />
T 3<br />
X T 4<br />
T 1, T 2, & T 3 send messages<br />
to X<br />
T 4 receives them<br />
T 4 is a server<br />
EXCHANGES CAN ENQUEUE TASKS<br />
T 2<br />
T 1<br />
T1 will receive M1<br />
T2 will receive the next<br />
message M2<br />
M 2 T 3<br />
M 1 X<br />
ITC-3
EXCHANGES (CONT)<br />
EXCHANGES PROVIDE ANONYMITY<br />
T 2<br />
T 1<br />
X<br />
T2 can be replaced by<br />
a whole subsystem<br />
OR<br />
T 3<br />
OR<br />
T 4 T 5<br />
T2 can be replaced by T3<br />
at any time<br />
ANONYMITY INCREASES TASK INDEPENDENCE<br />
ITC-4
EXAMPLE: MESSAGES TO SAM CORRELATOR TASK<br />
WHERE<br />
TASK<br />
WX<br />
IRON<br />
TASK<br />
IX<br />
CORRELATOR<br />
TASK<br />
SAMPLE XCHG<br />
SAX<br />
NITRO<br />
TASK<br />
NX<br />
XCB_PTR WX, IX, ..., NX, SAX;<br />
void where_main(void)<br />
{<br />
MCB_PTR wmsg;<br />
// ...<br />
while(1)<br />
{<br />
// ... prepare wmsg<br />
sendx(wmsg, WX);<br />
}<br />
}<br />
void iron_main(void)<br />
{<br />
MCB_PTR imsg;<br />
// ...<br />
while(1)<br />
{<br />
// ... prepare imsg<br />
sendx(imsg, IX);<br />
}<br />
}<br />
void correlator_main(void)<br />
{<br />
MCB_PTR wmsg, imsg, ..., nmsg;<br />
MCB_PTR smsg;<br />
// ...<br />
while(1)<br />
{<br />
wmsg = receive(WX, TMO);<br />
imsg = receive(IX, TMO);<br />
...<br />
nmsg = receive(NX, TMO);<br />
// ... prepare smsg<br />
sendx(smsg, SAX);<br />
}<br />
}<br />
CORRELATOR WAITS FOR<br />
SLOWEST MSG<br />
ILLUSTRATES ACHIEVING<br />
DESIRED FUNCTIONALITY VIA<br />
STRUCTURE<br />
ITC-5
MESSAGES<br />
WHAT ARE MESSAGES?<br />
MESSAGE CONTROL BLOCK (MCB)<br />
priority<br />
fl<br />
bl<br />
FMSG/NMSG<br />
onr<br />
mp<br />
size<br />
reply<br />
rxchg<br />
DATA<br />
BLOCK<br />
(ACTUAL<br />
MSG)<br />
DATA BLOCK — STORES ACTUAL MESSAGE. IT CAN BE ANY SIZE<br />
WHERE DO THEY COME FROM?<br />
MESSAGE POOL / RESOURCE EXCHANGE (RX)<br />
T 1<br />
receive()<br />
(”empty”)<br />
send()<br />
(full)<br />
RX<br />
X<br />
send()<br />
(”empty”)<br />
receive()<br />
(full)<br />
T 2<br />
RX = create_xchga(CB_RXCHG);<br />
pool = create_dpool(RX, num, size, dar_struc_ptr);<br />
dar_struc_ptr<br />
min_ptr<br />
max_ptr<br />
DAR structure<br />
DAR<br />
Dynamically<br />
Allocated<br />
Region<br />
CREATES num MESSAGES OF size BYTES IN THE DAR<br />
GETS num MCB'S AND ENQUEUES MSGS AT RX<br />
UPDATES min_ptr<br />
ITC-6
MESSAGES (CONT)<br />
T1 CAN OBTAIN MESSAGES QUICKLY FROM RX<br />
T2 MUST RETURN USED MESSAGES TO RX — DON'T FORGET!<br />
HEAP<br />
heap<br />
T 1<br />
X T 2<br />
heap<br />
void task1_main(void)<br />
{<br />
MCB_PTR msg;<br />
while (...)<br />
{<br />
msg = create_hmsgf (NULL, size);<br />
msg = create_hmsgn (NULL, size);<br />
fill(msg);<br />
sendx(msg, X);<br />
}<br />
}<br />
void task2_main(void)<br />
{<br />
MCB_PTR msg;<br />
while (msg = receive(X, TMO))<br />
{<br />
process(msg);<br />
delete_hmsg (msg);<br />
}<br />
// perform error handling<br />
}<br />
// far heap or<br />
// near heap<br />
// TMO = timeout<br />
ITC-7
MESSAGE PRIORITY<br />
MULTI-MESSAGE-LEVEL EXCHANGES<br />
receive()<br />
X T 2<br />
M 3<br />
2<br />
3<br />
M 2 >= 3<br />
0 T will receive M next, then M & M<br />
M 1 0,1,2<br />
2 2 1 3<br />
p = priority<br />
PERMITS MORE URGENT MESSAGES (E.G. KILL TUNE) TO<br />
BYPASS LESS URGENT MESSAGES (E.G. NOTES)<br />
EACH LEVEL CAN BE A SINGLE PRIORITY OR A RANGE OF<br />
PRIORITIES<br />
PASS EXCHANGE<br />
Client Tasks<br />
Server Task<br />
receive()<br />
PX T 3<br />
p = priority<br />
T 1<br />
4<br />
4<br />
T 2<br />
2 2<br />
T<br />
3<br />
will receive M<br />
1<br />
next and<br />
M 2<br />
1, 2<br />
assume priority level 4<br />
M 1<br />
>= 3<br />
0<br />
Each client task assigns<br />
its own priority to the<br />
messages it sends to PX: bump_msg(msg, cp);<br />
SERVER TASK WILL RUN AT PRIORITY LEVEL OF CLIENT TASK<br />
When T has M :<br />
3 1<br />
M 1<br />
4<br />
T 3<br />
4<br />
ITC-8
TASK PRIORITY EXCHANGES<br />
MULTI-TASK-LEVEL EXCHANGES<br />
T will receive M<br />
3 1<br />
T will receive M<br />
1 2<br />
>= 2<br />
0, 1<br />
2<br />
T 3<br />
0 1<br />
T 1<br />
T 2<br />
T 4<br />
M 1<br />
X<br />
T 5<br />
M 2<br />
MESSAGE IS A TOKEN WHICH CONTROLS ACCESS TO A<br />
RESOURCE (ONE MESSAGE PER RESOURCE)<br />
MESSAGE MAY BE EMPTY OR CONTAIN INFORMATION<br />
PERTAINING TO THE RESOURCE<br />
EXAMPLE:<br />
i/o ports:<br />
M1<br />
out port #1<br />
status port #1<br />
out<br />
status<br />
Line<br />
Printer<br />
1<br />
M2<br />
out port #2<br />
status port #2<br />
out<br />
status<br />
Line<br />
Printer<br />
2<br />
BY USING INFORMATION IN TOKEN MESSAGES, THE CODE IN<br />
EACH TASK CAN BE IDENTICAL.<br />
ITC-9
MISC EXCHANGE & MESSAGE FUNCTIONS<br />
CREATE EXCHANGE X<br />
XCB_PTR X ;<br />
X = create_xchga (type, limp=0);<br />
type = X NORMAL<br />
RX RESOURCE<br />
PX PASS<br />
(0 ==> single level task & msg queues)<br />
>= 0<br />
priority base<br />
limp 0<br />
3<br />
2<br />
0<br />
tasks (1 level)<br />
messages (3 level)<br />
X<br />
>= 3<br />
2<br />
0,1<br />
CLEAR X<br />
clear_q(X);<br />
RETURNS OR FREES MSGS & RESUMES TASKS<br />
DELETE EXCHANGE<br />
delete_xchg (X);<br />
FIRST CLEARS MSG OR TASK QUEUE<br />
RELEASES XCB<br />
CLEARS X<br />
MESSAGE OPERATIONS<br />
bump_msg (msg, new_priority);<br />
bump_msg (msg); // current priority<br />
ITC-10
EXAMPLE: MORE DETAIL ON SENDING AND RECEIVING<br />
USE A MESSAGE TEMPLATE<br />
struct WHERE {<br />
int tag; // sample #<br />
int x; // position x<br />
int y; // position y<br />
};<br />
WHERE SUBSYSTEM<br />
RX<br />
WM<br />
pool<br />
REFER TO ITC-5<br />
RX<br />
SM<br />
pool<br />
Tagger<br />
tag_<br />
sem<br />
Where<br />
WX<br />
X<br />
Correlator<br />
SAX<br />
X<br />
WHERE TASK CODE<br />
XCB_PTR WMpool, WX, SMpool, SAX;<br />
void where_main(void)<br />
{<br />
int<br />
x,y, sample=1;<br />
MCB_PTR wmsg;<br />
struct WHERE *w;<br />
DAR_STRUC *free_RAM;<br />
WMpool = create_xchga(CB_RXCHG);<br />
WX = create_xchga(CB_NXCHG);<br />
create_dpool(WMpool, MAX_SAMPLES, sizeof(struct WHERE), free_RAM);<br />
// ...<br />
while (test(tag_sem, TMO))<br />
{<br />
wmsg = receive(WMpool, TMO);<br />
w = wmsg->mp; // assign template<br />
w->tag = sample++;<br />
gps(&x, &y); // get latest position<br />
w->x = x;<br />
w->y = y;<br />
sendx(wmsg, WX);<br />
}<br />
}<br />
ITC-11
EXAMPLE: MORE DETAIL ON SENDING AND RECEIVING (CONT)<br />
CORRELATOR TASK CODE<br />
struct SAMPLE {<br />
int sampno;<br />
int x;<br />
int y;<br />
int iron;<br />
// ...<br />
}<br />
void correlator_main(void)<br />
{<br />
MCB_PTR wmsg, smsg;<br />
struct WHERE * w;<br />
struct SAMPLE * s;<br />
//...<br />
}<br />
while (wmsg = receive(WX, TMO))<br />
{<br />
smsg = receive (SMpool, TMO);<br />
w = wmsg->mp;<br />
s = smsg->mp;<br />
s->sampno = convert(w->tag);<br />
s->x = w->x;<br />
s->y = w->y;<br />
sendx(wmsg, WMpool);<br />
//...<br />
sendx(smsg, SAX);<br />
}<br />
THE WHERE TASK MUST HAVE HIGH OR MAX PRIORITY FOR<br />
POSITIONAL ACCURACY<br />
CORRELATOR TASK CAN HAVE NORM OR LOW PRIORITY (WX, IX,<br />
ETC. MSG QUEUES WILL MERELY GROW LONGER)<br />
ITC-12
EXAMPLE: RECEIVE STOP<br />
receive_stop()<br />
STACK IS RELEASED IF TEMPORARY<br />
TASK IS RESTARTED WHEN MESSAGE IS RECEIVED OR TIMEOUT<br />
OCCURS<br />
void init(void)<br />
{<br />
TCB_PTR atask;<br />
}<br />
atask = create_task(atask_init, HIGH, 0);<br />
startx(atask);<br />
...<br />
void atask_init(void)<br />
{<br />
// task initialization<br />
ct->fun = atask_main;<br />
receive_stop(xchg, TMO);<br />
}<br />
void atask_main(MCB_PTR msg)<br />
{<br />
if msg<br />
{<br />
// process msg<br />
receive_stop(xchg, TMO);<br />
}<br />
else<br />
// process error<br />
}<br />
// reassign code pointer<br />
// wait for first msg<br />
// wait for next msg<br />
ITC-13
SEMAPHORES<br />
SIMILAR TO EXCHANGES BUT NO MESSAGES ARE USED<br />
signalx()<br />
test()<br />
T 1<br />
S(th) T 2<br />
COUNTING SEMAPHORE<br />
EACH SIGNAL INCREMENTS AN INTERNAL COUNTER, c<br />
A TEST PASSES IF c >= th WHERE th IS THE THRESHOLD OF S.<br />
THEN c = c - th<br />
th == 1 IS USED FOR MUTUAL EXCLUSION (“MUTEX”)<br />
th > 1 IS USED FOR COUNTING EVENTS<br />
A SEMAPHORE CAN ACCUMULATE COUNTS ABOVE ITS THRESHOLD<br />
SEMAPHORES CAN ENQUEUE TASKS<br />
threshold<br />
T 2<br />
test()<br />
T 1<br />
test()<br />
T<br />
1<br />
will be the first task resumed<br />
T will be the next task resumed<br />
2<br />
T 3<br />
signalx()<br />
S(1)<br />
MULTILEVEL TASK SEMAPHORES ARE SUPPORTED<br />
>= 2<br />
T 1<br />
2<br />
T 3<br />
3<br />
0, 1<br />
T 2<br />
0<br />
T 4<br />
signalx()<br />
S(1)<br />
Order resumed: T , T , T<br />
1 3 2<br />
ALLOWS HIGHER PRIORITY TASKS TO GAIN ACCESS TO<br />
RESOURCES FIRST<br />
ITC-14
EXAMPLE: MUTUAL EXCLUSION<br />
void init(void)<br />
{<br />
SCB_PTR in_fct1;<br />
}<br />
in_fct1 = create_sema(1); // threshold = 1<br />
signalx(in_fct1);<br />
// prime semaphore for first use<br />
// ...<br />
startx(taskA);<br />
startx(taskB);<br />
void taskA_main(void)<br />
{<br />
// ...<br />
test(in_fct1, TMO);<br />
fct1(); //non-reentrant<br />
signalx(in_fct1);<br />
// ...<br />
}<br />
void taskB_main(void)<br />
{<br />
// ...<br />
test(in_fct1, TMO);<br />
fct1();<br />
signalx(in_fct1);<br />
// ...<br />
}<br />
taskB (HIGH)<br />
preempt<br />
test<br />
fct1<br />
taskB done<br />
taskA (NORM)<br />
fct1<br />
fct1<br />
signal<br />
A SEMAPHORE IS MORE SPECIFIC THAN LOCKING A TASK<br />
IT BLOCKS ONLY TASKS TRYING TO USE THE SAME RESOURCE<br />
HOWEVER, OVERHEAD IS HIGHER — NOT GOOD FOR SHORT<br />
SECTIONS OF CODE<br />
ITC-15
EVENT QUEUES<br />
COUNT THE NUMBER OF EVENTS SINCE A TASK BEGAN WAITING<br />
signalx()<br />
count()<br />
T 1<br />
EQ T 2<br />
TASKS ARE ENQUEUED BY DIFFERENTIAL COUNT<br />
count (6, EQ)<br />
6<br />
T 4<br />
x<br />
= count<br />
T 1<br />
T 1<br />
EQ<br />
EQ<br />
total count =<br />
T 2<br />
3<br />
5<br />
y<br />
3<br />
8<br />
T 2<br />
T 3<br />
3<br />
T 4 T 3<br />
3 2<br />
= diff count<br />
total count =<br />
3<br />
6 8<br />
ONLY THE COUNT IN THE FIRST TASK IS DECREMENTED BY EQ FOR<br />
EACH EVENT (SIGNAL)<br />
EVENT QUEUES ARE MOST FREQUENTLY USED FOR PRECISE DELAYS:<br />
void init(void)<br />
{<br />
ECB_PTR ticks;<br />
ticks = create_eq(); // signaled each tick<br />
// ...<br />
}<br />
void task_main(void)<br />
{<br />
// ...<br />
while (count(5, ticks)); // runs every 5 ticks<br />
{<br />
// ...<br />
}<br />
}<br />
ITC-16
COUNTING EVENTS<br />
EVENT QUEUES MISS COUNTS IF NO TASK IS WAITING<br />
T 1<br />
signalx()<br />
EQ<br />
Count is lost<br />
(No task counter present)<br />
SEMAPHORES NEVER MISS COUNTS<br />
T 1<br />
signalx()<br />
ctr<br />
S<br />
Counter keeps incrementing<br />
EACH HAS ITS USES<br />
EVENT QUEUE WOULD NOT BE APPROPRIATE FOR SOIL SAMPLE<br />
TAGGER (SEE PAGE ITC-11)<br />
EXAMPLE FOR EVENT QUEUE<br />
void init(void)<br />
{<br />
ECB_PTR revs;<br />
revs = create_eq(); // signaled each revolution<br />
// ...<br />
}<br />
void rev_task_main(void)<br />
{<br />
start_wheel();<br />
count(20, revs);<br />
stop_wheel();<br />
}<br />
ANY REVOLUTIONS BEFORE WHEEL STARTED OR AFTER WHEEL<br />
STOPPED ARE UNIMPORTANT<br />
ITC-17
EVENT TABLES<br />
ALLOW A TASK TO WAIT ON MULTIPLE EVENTS<br />
set_flags()<br />
flags<br />
-<br />
1<br />
0 0 0<br />
31<br />
3<br />
2<br />
1<br />
0<br />
mask0<br />
A<br />
1 1<br />
test_flags()<br />
T 1<br />
mask1<br />
O<br />
1<br />
1<br />
test_flags()<br />
T 2 T 3<br />
unused slot<br />
SETTING FLAG 1 CAUSES ALL THREE TASKS TO RESUME<br />
WIDTH IS 16 (15 FLAGS) FOR 16-BIT SYSTEMS<br />
WIDTH IS 32 (31 FLAGS) FOR 32-BIT SYSTEMS<br />
CREATE EVENT TABLE<br />
et = create_et(num_slots);<br />
SPACE IS ALLOCATED FROM THE [NEAR] HEAP<br />
HENCE, EVENT TABLES CAN BE OF ARBITRARY SIZE<br />
A TASK WAITS ON A SLOT<br />
T1: test_flags(et, AND + 0xA, TMO); // mask0 = AND + 0xA<br />
T2 & T3: test_flags(et, OR + 3, TMO); // mask1 = OR + 3<br />
AND = 0x8000 16-BIT<br />
= 0x80000000 32-BIT<br />
OR = 0<br />
ITC-18
EVENT TABLES (CONT)<br />
TASKS WITH THE SAME MASK ARE ENQUEUED ON THE SAME SLOT<br />
FLAGS MAY BE SET OR CLEARED INDIVIDUALLY OR IN GROUPS:<br />
set_flags(et, 2); // set flag 1 in et<br />
reset_flags(et, ALL); // ALL = 0xff...f<br />
TEST_FLAGS RETURNS STATE OF FLAGS IN ET<br />
int flags;<br />
flags = test_flags(et, mask, TMO);<br />
ITC-19
EXAMPLE: ANALYZER GROUP<br />
#define NITRO 0x10 // 10000b<br />
#define POT 0x08 // 01000b<br />
// ...<br />
#define IRON 0x01 // 00001b<br />
void init(void)<br />
{<br />
ETCB_PTR analyzer;<br />
analyzer = create_et(1);<br />
// ...<br />
}<br />
// 1 slot<br />
void nitro_main(void)<br />
{<br />
// ...<br />
while (test(nitro_sem, TMO))<br />
{<br />
// ...<br />
set_flags(analyzer, NITRO);<br />
}<br />
}<br />
void correlator_main(void)<br />
{<br />
// ...<br />
while (test_flags(analyzer, AND+NITRO+POT+PHOS+PH+IRON, TMO)<br />
{<br />
reset_flags(analyzer, ALL);<br />
get_sample(NITRO);<br />
signalx(nitro_sem); // to start next sample<br />
get_sample(POT);<br />
signalx(pot_sem);<br />
// ...<br />
}<br />
}<br />
FLAGS DO NOT AUTORESET<br />
INTERLOCKING (E.G. nitro_sem) IS NECESSARY TO AVOID LOSING<br />
EVENTS DUE TO SETTING AN ALREADY-SET FLAG<br />
WORKS BUT IS CLUMSY IN THIS CASE<br />
ITC-20
EXAMPLE: BETTER IMPLEMENTATION<br />
TAGGER<br />
WS<br />
WHERE<br />
TASK<br />
WX<br />
IS<br />
IRON<br />
TASK<br />
IX<br />
CORRELATOR<br />
TASK<br />
SAX<br />
NS<br />
NITRO<br />
TASK<br />
NX<br />
while (test(_S))<br />
receive(wx,...)<br />
receive(ix,...)<br />
NOTES<br />
1. TAGGER ISSUES A BATCH OF SIGNALS<br />
2. NO SIGNALS WILL BE LOST — EVEN IF AN ANALYZER FALLS<br />
BEHIND<br />
3. EACH ANALYZER WORKS AT ITS OWN RATE (NOTE HOW<br />
TASKS MIMIC HARDWARE UNITS)<br />
4. EXCHANGES STORE MESSAGES CONTAINING<br />
MEASUREMENTS<br />
5. CORRELATOR WILL WAIT FOR A LATE MESSAGE<br />
ITC-21
EXAMPLE: BETTER IMPLEMENTATION (CONT)<br />
6. DYNAMIC CONFIGURATION: SUPPOSE AN ANALYZER IS<br />
MISSING?<br />
a. TAGGER WOULD NOT SIGNAL ITS SEMAPHORE<br />
b. INSTEAD, TAGGER WOULD SEND A DUMMY MESSAGE TO<br />
ITS EXCHANGE<br />
c. ANALYZER TASK WOULD NEVER RUN<br />
7. PLUG IN DIFFERENT TAGGERS<br />
switch (config)<br />
{<br />
case 1:<br />
tagger = create_task(tagger1_main, ...);<br />
case 2:<br />
tagger = create_task(tagger2_main, ...);<br />
}<br />
8. IT IS GOOD TO MIMIC THE REAL WORLD<br />
a. CODE IS EASIER TO UNDERSTAND<br />
b. EASIER TO ADAPT TO DIFFERENT HARDWARE<br />
CONFIGURATIONS<br />
ITC-22
TASK PIPES<br />
PERMIT SENDING CHARACTERS BETWEEN TASKS<br />
pput_char()<br />
pget_char()<br />
T 1<br />
P<br />
T 2<br />
EACH TASK RUNS AT ITS OWN RATE:<br />
1. IF THE PIPE IS FULL, T1 WILL SUSPEND ON IT AND HOLD THE<br />
CHAR UNTIL T2 REMOVES A CHAR FROM P<br />
2. IF THE PIPE IS EMPTY, T2 WILL SUSPEND ON IT UNTIL T1<br />
SUPPLIES A CHAR<br />
PIPES ARE GOOD FOR SYNCHRONIZING TASKS WHICH NATURALLY<br />
RUN AT DIFFERENT RATES<br />
CREATE PIPE<br />
apipe = (PXCB_PTR) create_cx(CB_PIPE);<br />
// "cx" means char xchg<br />
ASSIGN A BUFFER<br />
put_msg(msg, size, apipe);<br />
DELETE PIPE<br />
delete_cx((CXCB_PTR &) apipe);<br />
RELEASES BUFFER FIRST<br />
void task1_main(void)<br />
{<br />
char ch;<br />
// ...<br />
do<br />
{<br />
// ... produce char<br />
} while(pput_char(ch, apipe, TMO));<br />
}<br />
void task2_main(void)<br />
{<br />
int ch; // in to distinguish NUL from no char<br />
// ...<br />
while (ch = pget_char(apipe, TMO)<br />
{<br />
// ... process 0xff & ch<br />
}<br />
}<br />
ITC-23
INTERNALS<br />
1. WHAT ARE TASKS, MESSAGES, ETC?<br />
2. READY QUEUE<br />
INTERNALS-1
WHAT ARE TASKS, MESSAGES, ETC?<br />
THEY ARE OBJECTS CONSISTING OF:<br />
DATA<br />
IN CONTROL BLOCKS<br />
“HIDDEN” — USUALLY NOT NECESSARY TO ACCESS<br />
DIRECTLY<br />
CODE<br />
SET OF SMX SERVICES ASSOCIATED WITH THE OBJECTS<br />
(E.G. sendx() & receive() ARE ASSOCIATED WITH EXCHANGES)<br />
VIEW APPLICATIONS AS A SET OF INTERACTING OBJECTS<br />
HOW ARE QUEUES FORMED?<br />
XCB 0<br />
XCT<br />
XCB X<br />
MCB y<br />
MCB z<br />
fl<br />
fl<br />
fl<br />
bl<br />
bl<br />
bl<br />
XCB n<br />
MCB 0<br />
MCT<br />
NOTHING MOVES<br />
IT’S ALL SMOKE & MIRRORS!<br />
MCB m<br />
INTERNALS-2
READY QUEUE<br />
qcb's:<br />
tcb's:<br />
4<br />
rq_top<br />
handle<br />
ct<br />
handle<br />
3<br />
3<br />
3<br />
2<br />
(numbers are priorities)<br />
1<br />
1<br />
1<br />
rq<br />
handle<br />
0<br />
0<br />
INTERNALS-3
MEMORY MANAGEMENT<br />
1. HEAPS<br />
2. BLOCK POOLS<br />
3. EXAMPLE: TASK CONTEXT INFO<br />
4. FAST BLOCK POOLS<br />
5. OTHER MEMORY MANAGEMENT<br />
MEMORY-1
HEAPS<br />
REPLACE COMPILER HEAPS<br />
MACROS (AT COMPILE TIME)<br />
TRANSLATION FUNCTIONS (AT LINK TIME)<br />
SAME NAMES AS IN COMPILER RTL<br />
ADVANTAGES OF smx HEAPS<br />
ATOMIC FUNCTIONS (I.E. SSR’S)<br />
BETTER PROTECTION<br />
ATTEMPT TO MINIMIZE FRAGMENTATION<br />
FUNCTIONS PROVIDED<br />
dp = mallocx(size);<br />
dp = callocx(num, size);<br />
dp_new = reallocx(dp_old, size)<br />
freex(dp);<br />
status = heapchkx();<br />
status = heapsetx(fill_char);<br />
status = heapwalkx(hip); // heap info structure pointer<br />
HEAP STRUCTURE AND INITIALIZATION<br />
WHEN AN SMX HEAP IS FIRST INITIALIZED, IT CONSISTS OF<br />
THREE BLOCKS:<br />
cf._heap_min<br />
_heap_srchp<br />
onr<br />
1<br />
0<br />
hcb 0<br />
hcb 1<br />
nbp<br />
data<br />
block 1<br />
(free)<br />
nbp<br />
increasing<br />
memory<br />
cf._heap_max<br />
1<br />
hcb n<br />
(each row is a full<br />
control block)<br />
MEMORY-2
HEAPS (CONT)<br />
AFTER ALLOCATING A BLOCK:<br />
onr<br />
cf._heap_min<br />
task handle<br />
1<br />
h<br />
hcb 0<br />
hcb 1<br />
nbp<br />
_heap_srchp<br />
0<br />
block 1<br />
hcb 2<br />
nbp<br />
increasing<br />
memory<br />
block 2<br />
(free)<br />
nbp<br />
cf._heap_max<br />
1<br />
hcb n<br />
(each row is a full<br />
control block)<br />
TO SPEED ALLOCATION _heap_srchp ALWAYS POINTS AT OR<br />
BELOW THE LOWEST FREE BLOCK<br />
CONTROL BLOCK (32-BIT FLAT VERSIONS)<br />
0x5555AAAA<br />
nbp<br />
onr<br />
blocks are on 8 byte<br />
boundaries<br />
0x5555AAAA<br />
16 bytes<br />
ADVANTAGES OF HEAPS<br />
EASY TO USE<br />
CAN ALLOCATE THE EXACT AMOUNT OF MEMORY NEEDED EACH<br />
TIME<br />
MEMORY NOT USED UNTIL NEEDED<br />
SMX HEAPS ARE SELF-INITIALIZING<br />
DISADVANTAGES OF HEAPS<br />
FRAGMENTATION — “GARBAGE COLLECTION” IS NOT PRACTICAL<br />
IN HARD REAL-TIME SYSTEMS<br />
MIXING OF CONTROL INFORMATION AND DATA IS HAZARDOUS<br />
TO HEAP INTEGRITY<br />
ALLOCATION CAN BE SLOW AND UNPREDICTABLE. THIS IMPACTS<br />
PREEMPTION LATENCY<br />
MEMORY-3
HEAPS (CONT)<br />
DEALING WITH THE DISADVANTAGES<br />
FRAGMENTATION<br />
BLOCKS ARE MERGED WITH ADJACENT WHEN FREED<br />
USE FOR ONE-TIME ALLOCATIONS (E.G. SMX USES FOR<br />
CONTROL TABLES)<br />
DEVELOP A HEAP MANAGEMENT STRATEGY. E.G.:<br />
ALLOCATE STANDARD-SIZE BLOCKS (E.G. N * 64 -16)<br />
APPLICATION-LEVEL GARBAGE COLLECTION<br />
ALLOW FOR WORST-CASE SCENARIO<br />
RETRY, DON’T DIE<br />
MIXING OF CONTROL AND DATA<br />
X86 SEGMENTED PROTECTED MODE FAR HEAP: EVERY<br />
DATA BLOCK IS A SEPARATE SEGMENT<br />
OTHERS: TEST HEAP FENCES BY WALKING HEAPS IN IDLE<br />
TASK<br />
SLOW ALLOCATION<br />
AVOID mallocx() IN HIGH PRIORITY TASKS<br />
MANAGE FRAGMENTATION<br />
MEMORY-4
X86 SEGMENTED HEAPS<br />
NEAR HEAP<br />
FUNCTIONS: nmallocx(), nfreex(), etc<br />
CAN BE CALLED DIRECTLY<br />
NEAR HEAP CONTROL BLOCK<br />
0x5555<br />
nbp<br />
onr<br />
0x5555<br />
8 bytes<br />
blocks are on 8 byte<br />
boundaries<br />
MAY NOT EXCEED (64K - sizeof(all near variables)) FOR 16-BIT<br />
SYSTEMS<br />
FAR HEAP<br />
FUNCTIONS: fmallocx(), ffreex(), etc<br />
CAN BE CALLED DIRECTLY<br />
FAR HEAP CONTROL BLOCK (16 BYTES)<br />
0x5555<br />
(spare)<br />
nbp<br />
blocks are on 16 byte<br />
boundaries<br />
pbp<br />
onr<br />
0x5555<br />
16 bytes<br />
MODEL-INDEPENDENT FUNCTIONS<br />
EXAMPLE: mallocx() MAPS TO nmallocx() OR fmallocx()<br />
DEFAULT TO:<br />
NEAR HEAP FOR SMALL DATA MODELS (SMALL, MEDIUM,<br />
AND FLAT)<br />
FAR HEAP FOR LARGE DATA MODELS (COMPACT AND<br />
LARGE)<br />
MEMORY-5
X86 SEGMENTED HEAPS (CONT)<br />
FREE BLOCK MERGING<br />
nfreex() MERGES UPPER FREE BLOCK ONLY (no pbp)<br />
nfreex()<br />
free<br />
used<br />
nmallocx() MERGES FREE BLOCKS AS ENCOUNTERED<br />
ffreex() MERGES UPPER & LOWER FREE BLOCKS<br />
ffreex()<br />
MEMORY-6
FIXED BLOCK POOLS<br />
ADVANTAGES<br />
FAST<br />
PREDICTABLE<br />
CONTROL INFORMATION AND DATA ARE SEPARATE<br />
DISADVANTAGES<br />
FIXED NUMBER AND SIZE OF BLOCKS<br />
MUST PRE-ALLOCATE WHOLE POOL BEFORE NEEDED<br />
CREATE POOL<br />
FROM DYNAMICALLY ALLOCATED REGION<br />
apool = create_dpool(NULL, bnum, bsize, dar_struc_ptr);<br />
SAME AS CREATING MESSAGE POOL, EXCEPT NO RXCHG (NULL)<br />
apool<br />
pp<br />
BPCB<br />
pool pointer<br />
DAR<br />
pool<br />
control<br />
block<br />
minh<br />
srch<br />
maxh<br />
bsize<br />
bnum<br />
data block n<br />
block<br />
control<br />
block<br />
onr<br />
ph<br />
bp<br />
pool handle<br />
block pointer<br />
NO delete_pool()<br />
MEMORY-7
FIXED BLOCK POOLS (CONT)<br />
FROM HEAP<br />
pool = create_hpooln(num, size);<br />
pool = create_hpoolf(num, size);<br />
delete_hpool(pool);<br />
// near heap<br />
// far heap<br />
GETTING AND RELEASING BLOCKS<br />
USES<br />
blk = get_block(pool);<br />
rel_block(blk);<br />
STACKS IN STACK POOL<br />
SBCB<br />
onr<br />
si<br />
sx<br />
ss<br />
owner handle<br />
stack min<br />
stack max<br />
stack segment<br />
16-bit only<br />
BUFFERS<br />
WORK AREAS — IN LIEU OF STACK SPACE<br />
TASK CONTEXT INFO — E.G. I/O ADDRESSES<br />
MEMORY-8
EXAMPLE: TASK CONTEXT INFO<br />
TCB_PTR comm_task[NUM_PORTS];<br />
PORT_INFO struct {<br />
uint read_port;<br />
uint write_port;<br />
uint ctrl_port;<br />
}<br />
void init(void)<br />
{<br />
PORT_INFO cp;<br />
BCB_PTR cb;<br />
comm_blks = create_hpooln(NUM_PORTS, sizeof(struct PORT_INFO));<br />
// from near heap<br />
for (j = 0; j < NUM_PORTS; j++)<br />
{<br />
cb = get_block(comm_blks);<br />
cp = cb->bp;<br />
cp->read_port = COMM_BASE + 3j;<br />
cp->write_port = COMM_BASE + 3j + 1;<br />
cp->ctrl_port = COMM_BASE + 3j + 2;<br />
comm_task[j] = create_task((CODE_PTR) comm_main, HIGH, 1000);<br />
start_par(comm_task[j], cb);<br />
}<br />
// ...<br />
}<br />
void comm_main(BCB_PTR cb)<br />
{<br />
PORT_INFO cp;<br />
cb->onr = ct; // reassign owner because initially onr = init task<br />
cp = cb->bp;<br />
while(1)<br />
{<br />
outp(cp->write_port, next_char);<br />
// ...<br />
}<br />
}<br />
delete_taskcomm_task[3]);<br />
WILL AUTOMATICALLY RELEASE ITS PORT INFORMATION BLOCK<br />
MEMORY-9
FAST BLOCK POOLS<br />
ADVANTAGES OVER NORMAL BLOCK POOLS<br />
FASTER<br />
SIMPLER — NO PCB’S OR BCB’S<br />
DISADVANTAGES<br />
USES<br />
LESS SAFETY<br />
NO OWNER<br />
NO POOL INFORMATION<br />
AVAILABLE FOR NEAR BLOCKS, ONLY (SEGMENTED X86)<br />
SMX — CONTROL BLOCKS<br />
C++ — OBJECTS (OVERLOAD new AND delete OPERATORS)<br />
APPLICATION — OBJECTS & CONTROL BLOCKS<br />
OPERATIONS<br />
free_ptr = create_fbpool(data_ptr, num, size);<br />
data_ptr —> AVAILABLE MEMORY<br />
bp = get_fblocks(VOID PTR & free_ptr, num, size, clr_sz);<br />
GETS num CONTIGUOUS BLOCKS, IF POSSIBLE (SIZE MUST<br />
BE SPECIFIED BECAUSE THERE IS NO PCB). CLEARS clr_sz<br />
BYTES.<br />
DEFAULTS: num = 1, and size is NOT NEEDED<br />
sort_fblocks(free_ptr);<br />
rel_fblock(bp, & free_ptr);<br />
MEMORY-10
OTHER MEMORY MANAGEMENT<br />
MESSAGE POOLS<br />
SIMILAR TO BLOCK POOLS<br />
MESSAGES EASILY TRANSFERRED BETWEEN TASKS<br />
CAN BE USED FOR BUFFERS AND WORK AREAS, JUST LIKE<br />
BLOCK POOLS<br />
DISADVANTAGE — MORE OVERHEAD<br />
SHARED GLOBAL VARIABLES<br />
STACKS<br />
SIMPLE AND FAST<br />
RISKY DUE TO ACCESS CONFLICTS<br />
WHILE ONE TASK IS USING, A HIGHER PRIORITY TASK CAN<br />
PREEMPT AND CHANGE<br />
DIFFICULT BUGS TO FIND<br />
SHOULD USE SEMAPHORES AND OTHER PROTECTION<br />
MECHANISMS<br />
BEST TO USE MESSAGES AND OTHER SAFE METHODS<br />
FROM:<br />
STACK POOL — BOUND UNTIL TASK STOPPED<br />
NEAR HEAP (ss == ds) — PERMANENTLY BOUND<br />
FAR HEAP (ss != ds) — PERMANENTLY BOUND<br />
MEMORY-11
OTHER MEMORY MANAGEMENT (CONT)<br />
STACKS SHOULD BE USED EXTENSIVELY<br />
REENTRANT ROUTINES:<br />
void task1_main(void)<br />
{<br />
// …<br />
call<br />
f1();<br />
// …<br />
}<br />
task1<br />
Stack<br />
use<br />
void f1(void)<br />
{<br />
int x,y;<br />
// …<br />
}<br />
use<br />
call<br />
void task2_main(void)<br />
{<br />
// …<br />
f1();<br />
// …<br />
}<br />
task2<br />
Stack<br />
y<br />
x<br />
...<br />
y<br />
x<br />
...<br />
TWO ACCESS METHODS:<br />
1. AUTO VARIABLES (bp-i)<br />
2. BEGINNING OF STACK BLOCK (ss:0) (16-BIT ONLY)<br />
ss:0<br />
+<br />
sp<br />
bp<br />
(low address)<br />
BUFFER<br />
top of stack<br />
AUTO VAR’S<br />
stack direction<br />
(high address)<br />
MEMORY-12
INTERRUPTS & I/O<br />
1. INTERRUPTS<br />
2. EXAMPLE: STARTING A TASK DUE TO AN INTERRUPT<br />
3. LSR OPERATION<br />
4. I/O PIPES<br />
5. EXAMPLE: KEYBOARD INPUT<br />
6. BUCKETS<br />
7. EXAMPLE: PACKETIZED DATA OUT<br />
I/O-1
INTERRUPTS<br />
OBJECTIVE: MINIMAL KERNEL INTERRUPT LATENCY<br />
LATENCY = TIME FROM INTERRUPT —> ISR<br />
LATENCIES ADD:<br />
PROCESSOR + KERNEL + APPLICATION<br />
APPLICATION = LONGEST ISR + BACKGROUND<br />
EXCESSIVE LATENCIES CAUSE:<br />
DATA OVER-RUNS<br />
MISSED COUNTS<br />
SLOW RESPONSES —> INSTABILITY<br />
INTERMITTENT FAILURES — THE WORST KIND TO FIND<br />
SMX WORST-CASE INTERRUPT LATENCY<br />
14 PROCESSOR INSTRUCTIONS<br />
104 CLOCKS = 5.2 MICROSEC ON 20 MHZ 386<br />
COMPARABLE TO PROCESSOR LATENCY<br />
TWO-LEVEL FOREGROUND STRUCTURE<br />
1. ISR’S (INTERRUPT SERVICE ROUTINES)<br />
MINIMAL CODE<br />
CAN ONLY DO GET & PUT CHAR OP’S AND INVOKE LSR’S<br />
2. LSR’S (LINK SERVICE ROUTINES) (INT ENABLED 100%)<br />
CAN DO ALL SMX CALLS<br />
BUT CANNOT WAIT<br />
RUN IN THE CONTEXT OF THE CURRENT TASK (USE ITS STACK)<br />
ACT LIKE FOREGROUND TASKS<br />
VERY-LOW OVERHEAD<br />
INTERRUPT<br />
INVOKE() signalx() start()<br />
ISR LSR TASK<br />
TASK<br />
FOREGROUND<br />
BACKGROUND<br />
I/O-2
EXAMPLE: STARTING A TASK FROM AN INTERRUPT<br />
uint x = 0;<br />
void INTERRUPT startA_isr(void)<br />
{<br />
ENTER_ISR();<br />
x++;<br />
// ...<br />
INVOKE(startA_lsr, x); // puts into lq<br />
EXIT_ISR();<br />
// branches to lsr scheduler<br />
}<br />
void startA_lsr(uint pass_num)<br />
{<br />
start_par(taskA, pass_num);<br />
}<br />
void taskA_main(uint pass_num)<br />
{<br />
if(pass_num ...)<br />
// ...<br />
}<br />
RULES FOR USING ISR'S & LSR’S:<br />
1. ENTER_ISR & EXIT_ISR() MAY BE OMITTED ONLY IF:<br />
a. INVOKE() IS NOT USED IN THE ISR, AND<br />
b. ISR PRIORITY > THAT OF ANY ISR USING INVOKE()<br />
2. DO NOT USE CT IN AN LSR<br />
3. NO WAITS (I.E. TIMEOUT PARAMETER = NO_WAIT (= 0))<br />
I/O-3
LSR OPERATION<br />
preempt task1<br />
srnest<br />
0<br />
1<br />
2<br />
3<br />
4<br />
task1<br />
c<br />
ssr1<br />
i<br />
invoke lsr1<br />
ssr1 S lsr1 lsr1 S lsr2 S<br />
c<br />
isr1 isr1 r<br />
ssr2 r<br />
i<br />
isr2 r<br />
invoke lsr2<br />
r<br />
task2<br />
c = smx call i = interrupt r = return S = scheduler<br />
1. WHEN INVOKED, LSR IS PUT INTO LSR QUEUE (LQ) ONLY<br />
2. THE LSR DOES NOT RUN UNTIL:<br />
a. ALL NESTED ISR’S FINISH<br />
b. AN INTERRUPTED SSR FINISHES<br />
c. THE LSR SCHEDULER STARTS IT<br />
3. LSR’S RUN IN FIFO ORDER<br />
4. AFTER ALL INVOKED LSR’S HAVE RUN, A TASK WILL RUN<br />
I/O-4
I/O PIPES<br />
LSR TASK<br />
(NO WAIT)<br />
L 1<br />
pput_char()<br />
P<br />
pget_char()<br />
T 1<br />
pput_char()<br />
pget_char()<br />
T 2<br />
P<br />
L 2<br />
(NO WAIT)<br />
WORKS LIKE TASK —> TASK EXCEPT LSR CANNOT WAIT ON<br />
PIPE<br />
LSR 1 MUST SAVE CHAR SOMEWHERE IF PIPE IS FULL<br />
LSR 2 MUST ABORT IF PIPE IS EMPTY<br />
ISR TASK/LSR (macros only)<br />
PPUT_CHAR()<br />
PGET_CHAR()<br />
I 1<br />
P<br />
T/L<br />
T/L 2 2<br />
PPUT_CHAR()<br />
P<br />
PGET_CHAR()<br />
1 1<br />
I 2<br />
PPUT_CHAR()<br />
MACRO<br />
WILL NOT SUSPEND ON FULL PIPE — RETURNS FALSE<br />
PGET_CHAR()<br />
MACRO<br />
WILL NOT SUSPEND ON EMPTY PIPE — RETURNS 0<br />
0X100 ADDED TO A VALID CHAR TO DISTINGUISH NUL FROM<br />
NO CHAR<br />
I/O-5
EXAMPLE: KEYBOARD INPUT<br />
void init(void)<br />
{<br />
PCB_PTR kbd_pipe;<br />
TCB_PTR opcon;<br />
MCB_PTR pipe_buf;<br />
pipe_buf = create_msg(NULL, 100);<br />
kbd_pipe = (PXCB_PTR) create_cx(CB_PIPE);<br />
put_msg(pipe_buf, 100, kbd_pipe);<br />
opcon = create_task (opcon_main, LOW, 500);<br />
startx(opcon);<br />
}<br />
void INTERRUPT kbd_isr(void)<br />
{<br />
byte ch;<br />
ENTER_ISR();<br />
ch = inp(KBD_PORT);<br />
INVOKE(kbd_lsr, ch);<br />
EXIT_ISR();<br />
}<br />
void kbd_lsr(byte ch)<br />
{<br />
pput_char(ch, kbd_pipe);<br />
}<br />
void opcon_main(void)<br />
{<br />
int ch;<br />
// ...<br />
while(ch = pget_char(kbd_pipe, TMO))<br />
{<br />
switch (ch & 0xFF)<br />
// ...<br />
}<br />
}<br />
I/O-6
I/O PIPES<br />
ISR/LSR TASK (macros + ssr’s)<br />
PPUT_CHAR()<br />
P<br />
pget_char()<br />
T 1<br />
INVOKE()<br />
T 1<br />
I 1<br />
I 1<br />
resume_pget()<br />
pput_char()<br />
P<br />
L 1<br />
PGET_CHAR()<br />
L 1<br />
INVOKE()<br />
resume_pput()<br />
ISR/LSR —> TASK:<br />
ISR 1 PUTS CHARS IN PIPE RAPIDLY WITH PPUT_CHAR()<br />
INVOKES LSR 1 TO WAKEUP THE TASK WITH resume_pget()<br />
(DON’T USE pput_char(). COULD CAUSE SEQUENCING<br />
ERROR)<br />
TASK —> ISR/LSR:<br />
TASK 2 PUTS CHARS IN PIPE WITH pput_char()<br />
ISR 2 GETS CHARS RAPIDLY WITH PGET_CHAR()<br />
INVOKES LSR 2 TO WAKEUP TASK 2 WITH resume_pput()<br />
(DON’T USE pget_char(). COULD CAUSE SEQUENCING<br />
ERROR)<br />
I/O-7
EXAMPLE: SERIAL INPUT<br />
void init(void)<br />
{<br />
PCB_PTR inpipe;<br />
MCB_PTR pipe_buf;<br />
TCB_PTR intask;<br />
pipe_buf = create_msg(NULL, 100);<br />
inpipe = (PXCB_PTR) create_cx(CB_PIPE);<br />
put_msg(pipe_buf, 100, inpipe);<br />
intask = create_task (intask_main, HI, 500);<br />
startx(intask);<br />
}<br />
void INTERRUPT serial_isr(void)<br />
{<br />
byte ch; int count = 0;<br />
ENTER_ISR();<br />
while (inp(PORT_STATUS) & RXRDY)<br />
{<br />
ch = inp(PORT_DATA);<br />
PPUT_CHAR(ch, inpipe);<br />
if (count++ >= 20)<br />
{<br />
INVOKE(wakeup_lsr); /* ensure intask is awake, getting chars */<br />
count = 0;<br />
}<br />
}<br />
EXIT_ISR();<br />
}<br />
void wakeup_lsr(void)<br />
{<br />
resume_pget(inpipe); /* wakeup task waiting on inpipe, if any */<br />
}<br />
void intask_main(void)<br />
{<br />
int ch;<br />
while(ch = pget_char(inpipe, TMO))<br />
// ...<br />
}<br />
I/O-8
BUCKETS<br />
CONVERT SERIAL STREAMS MESSAGE STREAMS<br />
IN<br />
PUT_CHAR()<br />
RX<br />
I 1<br />
T 1<br />
put_msg()<br />
B<br />
get_msg()<br />
OR<br />
send()<br />
X<br />
L 1<br />
X<br />
receive()<br />
T 2<br />
OR<br />
put_msg()<br />
B<br />
L 2<br />
GET_CHAR()<br />
I 2<br />
OUT<br />
CREATE A BUCKET<br />
bucket = (BXCB_PTR) create_cx(CB_BUCKET);<br />
DELETE A BUCKET<br />
delete_cx((CXCB_PTR &) bucket)<br />
GOOD FOR HIGH SPEED PACKETIZED SERIAL I/O<br />
I/O-9
EXAMPLE: PACKETIZED DATA OUT<br />
void init(void)<br />
{<br />
BXCB_PTR out_bucket;<br />
// bucket<br />
SCB_PTR next_packet;<br />
// semaphore<br />
XCB_PTR free_packets;<br />
// resource exchange<br />
static struct DAR near ndar;<br />
ndar.dnext = ADDR1;<br />
// in near memory<br />
ndar.dmax = ADDR2;<br />
// in near memory<br />
out_bucket = (BXCB_PTR) create_cx(CB_BUCKET);<br />
next_packet = create_sema(1);<br />
signalx(next_packet);<br />
// prime semaphore<br />
free_packets = create_xchg(CB_RXCHG);<br />
create_dpool(free_packets, 2, 500, (DAR_PTR)&fdar);<br />
}<br />
void out_task_main(void)<br />
{<br />
MCB_PTR packet;<br />
char first_char;<br />
// ...<br />
while(test(next_packet, TMO))<br />
{<br />
packet = receive(free_packets, TMO);<br />
fill(packet);<br />
put_msg(packet, 500, out_bucket);<br />
outp(out_port, first_char); // start sending<br />
}<br />
}<br />
void INTERRUPT out_isr(void)<br />
{<br />
int ch;<br />
ENTER_ISR();<br />
if (ch = GET_CHAR(out_bucket))<br />
outp(out_port, ch);<br />
else<br />
INVOKE(out_lsr, 0);<br />
EXIT_ISR();<br />
}<br />
void out_lsr(void)<br />
{<br />
MCB_PTR packet;<br />
if (packet = get_msg(out_bucket))<br />
sendx(packet, free_packets);<br />
signalx(next_packet);<br />
}<br />
I/O-10
TIMING<br />
1. TIMEOUTS<br />
2. DELAYS<br />
3. TIMERS<br />
4. EXAMPLE: PERIODIC TESTS OF SOIL ANALYSIS<br />
MACHINE<br />
TIMING-1
TIMEOUTS<br />
AVAILABLE IN ALL SMX CALLS CAUSING WAITS<br />
CAN BE USED TO PREVENT “LOSING” TASKS<br />
TRY TO SPECIFY A REASONABLE MAXIMUM WAIT TIME FOR<br />
EACH OPERATION<br />
while (msg = receive(xchg, 20));<br />
{<br />
// ... normal processing<br />
}<br />
if (!xerrno)<br />
// deal with timeout<br />
TIMEOUTS ARE SPECIFIED IN TICKS<br />
receive (xchg, NO_WAIT);<br />
receive (xchg, 20);<br />
receive (xchg, INF);<br />
// don’t wait<br />
// wait 20 ticks<br />
// wait forever<br />
HOWEVER THE TIMEOUT TASK DETERMINES TIMEOUTS<br />
IT MAY RUN ONLY ONCE PER SECOND, AND<br />
IT CAN BE DELAYED BY HIGHER PRIORITY TASKS<br />
TIMEOUTS ARE NOT INTENDED FOR PRECISE DELAYS<br />
TIMEOUTS CAN BE UP TO 248 DAYS @ 100 Hz TICK RATE FOR 16-BIT<br />
SYSTEMS<br />
TIMING-2
DELAYS<br />
LONG DELAYS<br />
sleepx(stime + 3600); // 1 hour<br />
stime IS KEPT IN SECONDS<br />
TIME FROM LAST COLDSTART, IF NOT SET<br />
IF SET, IS TIME FROM A SPECIFIC TIME (E.G. 00:00:00 GMT 1/1/80)<br />
APPLICATION MUST SET<br />
USEFUL FOR LONG TIME DELAYS AND REAL TIME OPERATIONS<br />
1 SEC ACCURACY. DEPENDENT UPON TIMEOUT TASK<br />
SLEEPS CAN BE OVER 100 YEARS IN 16-BIT SYSTEMS<br />
PRECISE DELAYS<br />
while (count(29, ticks, INF)) {<br />
do_op(); }<br />
EVENT QUEUES WERE PREVIOUSLY DISCUSSED<br />
SHORT DELAYS<br />
UP TO 10 MIN (100 Hz TICK RATE) FOR 16-BIT SYSTEMS<br />
+0/-1 TICK ACCURACY<br />
TICK RATE IS LIMITED BY OVERHEAD<br />
TIMING-3
TIMERS<br />
ARE DYNAMIC OBJECTS<br />
TMCB<br />
fl<br />
bl<br />
forward link<br />
backward link<br />
TMR<br />
onr<br />
interval<br />
diff_count<br />
lsr<br />
par<br />
owner handle<br />
for cyclic operation<br />
differential counter<br />
lsr to invoke<br />
parameter to lsr<br />
tmrA = start_timer(lsr, par, t, i)<br />
t i creates tmrA<br />
0 0 no-op<br />
0 i now + i, & i cyclic<br />
t 0 now + t one-shot<br />
t i now + t, & i cyclic<br />
TMCB IS ENQUEUED IN TQ DIFFERENTIALLY:<br />
TMR X<br />
10<br />
TQ TMR 1<br />
TMR 2<br />
TMR 3<br />
5 3 9<br />
total time = 5 8 17<br />
TQ TMR 1<br />
TMR 2<br />
TMR X<br />
TMR 3<br />
5 3 2 7<br />
total time =<br />
5 8 10 17<br />
TIMING-4
TIMERS (CONT)<br />
FIRST TIMER IS DECREMENTED EVERY TICK (OR A<br />
DIFFERENT PERIODIC INTERRUPT CAN BE USED)<br />
LOW OVERHEAD<br />
WHEN TIMER TIMES OUT:<br />
THE LSR IS INVOKED WITH PARAMETER, PAR<br />
IF ONE-SHOT, TIMER IS DELETED<br />
IF CYCLIC, TIMER IS IMMEDIATELY PUT BACK IN TQ FOR i<br />
TICKS<br />
NO ACCUMULATION OF TIMING ERROR<br />
TIMERS CAN BE READ WITHOUT DISTURBING<br />
read_timer (tmrA, &time_left);<br />
TIMERS CAN BE STOPPED<br />
stop_timer (tmrA, &time_left);<br />
LSR DOES NOT RUN<br />
TIMER IS DELETED<br />
TIMING-5
EXAMPLE: PERIODIC TESTS OF SOIL ANALYSIS MACHINE<br />
void init(void)<br />
{<br />
static TMCB_PTR samtst1_tmr, samtst2_tmr, samtst3_tmr, ...;<br />
samtst1_tmr = start_timer(samtst_lsr, ST1, 0, ONE_MIN);<br />
samtst2_tmr = start_timer(samtst_lsr, ST2, 0, FIVE_MIN);<br />
samtst3_tmr = start_timer(samtst_lsr, ST3, 0, ONE_HR);<br />
// ...<br />
}<br />
void samtst_lsr(int testno)<br />
{<br />
switch (testno)<br />
{<br />
case ST1: startx(samtst1);<br />
case ST2: startx(samtst2);<br />
case ST3: startx(samtst3);<br />
}<br />
}<br />
— OR —<br />
case ST1: signalx(samtst1_sem);<br />
case ST2: signalx(samtst2_sem);<br />
case ST3: signalx(samtst3_sem);<br />
FIRST EXAMPLE:<br />
NON-PRE TASKS<br />
ABORT TEST IF NOT DONE AND RESTART<br />
SECOND EXAMPLE:<br />
NORMAL TASKS<br />
COMPLETE ALL TESTS<br />
TIMING-6
ERROR MANAGEMENT<br />
1. ERROR MANAGEMENT<br />
2. EXAMPLE: ANALYZER FAILURE<br />
ERROR-1
ERROR MANAGEMENT<br />
WHY?<br />
BUGS ARE INEVITABLE<br />
MALFUNCTIONS AND FAILURES ARE UNAVOIDABLE<br />
DAMAGE CONTROL IS ESSENTIAL<br />
GOOD ERROR DETECTION HELPS DEBUGGING<br />
SMX PROVIDES EXCELLENT ERROR DETECTION<br />
WELL-DEFINED PATHS (E.G. SMX CALLS)<br />
LIMITED NUMBER OF POSSIBILITIES<br />
TESTING IS INDEPENDENT OF APPLICATION<br />
MINIMAL IMPACT UPON CODE SIZE AND SPEED<br />
OVER 70 ERROR TYPES MONITORED<br />
ERROR HANDLING<br />
POINT OF CALL<br />
CENTRAL (BY ERROR TYPE)<br />
BOTH ARE PROVIDED<br />
if (result = smxcall(...))<br />
// do op<br />
else<br />
// handle error or timeout<br />
DISPLAY<br />
SCREEN<br />
task<br />
code<br />
ssr<br />
code<br />
jump table<br />
esr 1<br />
ERROR<br />
BUFFER<br />
smx call<br />
point of<br />
call error<br />
handler<br />
error<br />
errno<br />
minor:<br />
continue<br />
esr 2<br />
std or<br />
userwritten<br />
++<br />
major:<br />
restart task<br />
or reboot<br />
ERROR<br />
CTRS<br />
ERROR-2
ERROR MANAGEMENT (CONT)<br />
CENTRAL ERROR HANDLING — COMMON<br />
LOG ERROR INFORMATION INTO ERROR BUFFER (EB)<br />
KEEP COUNTS OF ERRORS — ERROR COUNTER PER ERROR<br />
TYPE<br />
OUTPUT ERROR MESSAGE<br />
INVOKE USER-WRITTEN ERROR SERVICE ROUTINE<br />
POC ERROR HANDLING — SPECIAL<br />
RECOVERY VS. SEVERITY<br />
MINOR — RETRY, ABORT, OR IGNORE (POINT OF CALL)<br />
SERIOUS — ABORT & RESTART TASK(S) (ESR)<br />
CATASTROPHIC — SHUT DOWN SYSTEM & RESTART, IF<br />
POSSIBLE (ESR)<br />
ERROR-3
EXAMPLE: ANALYZER FAILURE<br />
void correlator_main(void)<br />
{<br />
XCB_PTR IX, EX;<br />
MCB_PTR iron_msg, error_msg;<br />
}<br />
while(1)<br />
{<br />
// ...<br />
if (iron_msg = receive(IX, ONE_SEC)<br />
process(iron_msg);<br />
else<br />
{<br />
error_msg = receive(EX, NO_WAIT);<br />
load(error_msg, “NO IRON SAMPLE”);<br />
sendx(error_msg, X911);<br />
}<br />
// ...<br />
}<br />
void alarm_mgr_main(void)<br />
{<br />
// ...<br />
while (error_msg = receive(X911, INF))<br />
process(error_msg);<br />
}<br />
ERROR-4
SUMMARY<br />
BENEFITS OF MULTITASKING<br />
REDUCE TIME TO MARKET<br />
LESS CODE TO DEVELOP<br />
LESS DEBUGGING<br />
EXPLICIT RELATIONSHIPS — FLOW DIAGRAM<br />
NOT BURIED IN CODE<br />
RELIABILITY<br />
EXPANSIBILITY<br />
FLEXIBILITY<br />
EASILY UNDERSTOOD<br />
ALLOWS MULTIPLE PROGRAMMERS TO WORK WITHOUT<br />
EXCESSIVE INTERACTION — WELL DEFINED INTERFACES<br />
programmer 1 programmer 2<br />
T 1<br />
X<br />
T 2<br />
message with well-defined format<br />
USE MANY TASKS, NOT JUST A FEW<br />
SUMMARY-1
SUMMARY (CONT)<br />
FLEXIBILITY<br />
Valve<br />
Test 1<br />
2ms<br />
Timer<br />
lsr<br />
Main<br />
Task<br />
M 1<br />
X 1<br />
Valve 1<br />
Control<br />
DAC1<br />
V1<br />
M 2<br />
Valve 2<br />
X 2<br />
DAC2<br />
Control<br />
V2<br />
Valve<br />
Test 2<br />
TIMER CAN BE STOPPED AND VALVE TEST STARTED TO<br />
PERIODICALLY RUN THE VALVE THROUGH A TEST SEQUENCE TO<br />
VERIFY THAT IT IS OK.<br />
INSTEAD OF:<br />
DAC1<br />
V1<br />
2ms<br />
Timer<br />
lsr<br />
2ms<br />
task<br />
DAC2<br />
V2<br />
SUMMARY-2
SUMMARY (CONT)<br />
WHY ARE TASKS BETTER THAN SUBROUTINES?<br />
TRUE OBJECTS<br />
RELATIONSHIPS ARE MORE FORMAL & EXPLICIT<br />
— E.G. MESSAGES<br />
BETTER ISOLATION<br />
MORE INDEPENDENT — GOOD FOR MULTI-PROGRAMMER<br />
PROJECTS<br />
DYNAMIC INSTANTIATION & LINKING — GOOD FOR SELF-<br />
CONFIGURATION, TESTING, ETC.<br />
MUCH BETTER RUN-TIME ERROR CHECKING<br />
IN SUMMARY<br />
MULTITASKING PROVIDES OBJECT-ORIENTED PROGRAMMING,<br />
LIKE C++, WITH LESS COMPLEXITY AND BETTER SUITABILITY TO<br />
EMBEDDED SYSTEMS.<br />
SUMMARY-3