13.07.2014 Views

Smx - RTOS

Smx - RTOS

Smx - RTOS

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!