Transcript Lecture9au
Embedded Software
Architectures
• No Operating System
– Round robin: sequential polling for events
– Round robin w/ interrupts
– Function Queue Scheduling
• Real Time Operating Systems
Overall Architecture
Serial ISR
Main
PC
Tone ISR
FIFO Queue
• Main
– If (not empty) get item, move “tail” pointer
else what?
• Serial ISR
– If (not full) put item, move “head” pointer,
and acknowledge received byte
else what?
• When writing this code:
– Think of ISR and MAIN are parallel processes running
on separate computers using shared memory to
communicate: why?
FIFO
• Empty Condition
– Tail = Head
• Tail is place to get next byte if != head
• Initial Condition
– Same as empty condition
• Full Condition
– Head = (Tail –1)
• Head is place to put next byte if not right behind tail
Example
• Queue size is SIZE
– In main
while (1) {
while (head == tail); // wait until queue not empty
data = queue[tail];
tail = tail + 1
if (tail == SIZE) tail = 0;
process(data);
}
• Do we have a problem?
• Solution?
Safe Queue?
while (1) {
while (head == tail); // wait until queue not empty
data = queue[tail];
ES = 0;
// disable serial interrupts
tail = next(tail);
ES = 1
// enable serial interrupts
process(data);
}
unsigned char next(unsigned char ptr) {
if (++ptr == SIZE) ptr = 0;
return(ptr);
}
Consider the compiler
C code for blocking queue
Compiler output?
while (1) {
MOV R0,TAIL
while (head == tail); // wait until
MOV R1,HEAD
queue not empty
LOOP: MOV A,R1
data = queue[tail];
SUBB A,R0
ES = 0;
// disable serial
JZ
LOOP
interrupts
MOV R3,#QUEUE
tail = next(tail);
ADD R3,R0
ES = 1
// enable
MOV R4, @R3
serial interrupts
CLR ET0
process(data);
ACALL NEXT
}
MOV TAIL,A
unsigned char next(unsigned char ptr) {
SETB ET0
if (++ptr == SIZE) ptr = 0;
return(ptr);
Assume next() operates on R0, returns in A
}
I think we’re safe now
volatile data unsigned char head;
…
while (1) {
while (head == tail);
data = queue[tail];
ES = 0;
tail = next(tail);
ES = 1
process(data);
}
unsigned char next(unsigned char ptr) {
if (++ptr == SIZE) ptr = 0;
return(ptr);
}
MOV R0,TAIL
LOOP: MOV R1,HEAD
MOV A,R1
SUBB A,R0
JZ
LOOP
MOV R3,#QUEUE
ADD R3,R0
MOV R4, @R3
CLR ET0
ACALL NEXT
MOV TAIL,A
SETB ET0
*compiler knows that sfr’s are volatile (ports, flags)
M-BOX in Round Robin Arch.
void main (void) {
if (TF0) music_task();
if (R1) serial_input_task();
}
Would this work for the M-BOX?
How do we know?
// process timer overflow
// process serial input
Task Diagram
Worst case: character arrives one cycle before TF0
Worst Case Latency = max run time of all other tasks
serial task
main
serial
music
main
w.c. latency
timer0 overflow occurs (TF0)
char arrives
deadline
What is the worst
case serial processing
time?
Depends on response message
length--could be bad!
How much latency can we
tolerate? Practically none
Round Robin w/ Interrupts
volatile bit fEvent;
void isr(void) {
time_critical_processing();
fEvent = TRUE;
}
void main (void) {
if (R1) serial_input_task();
if (fEvent) {
housekeeping();
fEvent = FALSE;
}
}
Why not put housekeeping into the ISR too?
Then our worst case latency to a separate time critical event
would be poor
Would this work for the M-BOX? See next slide
M-BOX in RR+INT
volatile bit fEndOfSlice, fSerial;
void isr(void) interrupt … {
process_tones();
if (!--sliceCount) {
changeTones();
sliceCount = SliceSize
fEndOfSlice = TRUE;
}
}
void serial(void) interrupt …{
timeCritical();
fSerial = TRUE;
}
What are the time critical
functions?
Flipping channels, change
tones at end of timeslice
What are the
housekeeping functions?
TNE count down and
stream processing (TNE,
Tones)
main () {
if (fSerial) {process_serial_data(); fSerial = FALSE;} Do these have hard time
if (fEndOfSlice) {
constraints too?
if (--TNE==0)
Yes, one time slice
process_next_event();
fEndOfSlice = FALSE;
(18ms)…good enough?
}
Not necessarily…serial is
}
undefined!
Task Diagram
Worst case analysis: character comes one cycle before TF0
Worst case latency: Sum of max of all task functions.
Advantage over RR: Critical Stuff happens in ISR
time slice start
time slice end
serial_isr
isr
serial
housekeeping
main
deadline
timer0 overflow occurs (TF0)
char arrives
Can we do better?
Function Queue
void isr(void) interrupt … {
process_tones();
if (!--sliceCount) {
changeTones();
sliceCount = SliceSize;
enq(process_time_slice);
}
}
void serial(void) interrupt …{
SerialTimeCritical();
enq(process_serial_data);
}
void main(void) {
while (1) if (f = deq()) { *f());
}
You get a scheduling opportunity
every time a task completes.
What is the
advantage of this?
Programmer can
set priority for task
functions.
Worst case latency
for priority n task
function? Sum of
max execution time
for all task
functions of
priority > n + max
current task
Task Diagram
Worst case analysis: character comes one cycle before TF0
Worst case latency: Sum of max of all task functions.
Advantage over RR: Task functions can have easier deadlines
time slice start
time slice end
serial_isr
isr
serial
housekeeping
main
deadline
timer0 overflow occurs (TF0)
char arrives
Can we do better?
Comparison Non OS Architectures
• See Chapter 5, table 5.1 Simon
Real Time Operating Systems
• What is the basic thing we want the OS to do to help
us improve worst case latency? Enable multithreading
• How? Define an OS time-slice (tick) at which highest
priority ‘runnable’ task is continued. Priority function
determines response behavior.
• Simplest Scheduling algorithm: each task gets at most
1 tick at a time to run. Round Robin Scheduling. Worst
case task latency = #tasks*tick. Worst case run time =
ticks/task * #tasks
• Some properties of such system: liveness, safety,
fairness, latency, overhead.
• Other niceties: Device Drivers, Synchronization,
Message passing, Memory Management
Programmers View
void tone_isr(void) interrupt … {
Advantages:
process_tones();
if (!--sliceCount) {
•Deterministic response time
changeTones();
even w/ non deterministic
sliceCount = SliceSize
isr_send_signal(MUSIC);
tasks lengths.
}
• Incremental development
}
void serial_isr(void) interrupt …{
timeCritical();
Resources:
os_send_signal(SERIAL);
}
•Task switching overhead
void play(void) _task_ MUSIC {
•Memory overhead
os_create(SERIAL);
while (1) {os_wait();
•Use of system timer
process_next_event();}
•Degrades best case response
}
void serial(void) _task_ SERIAL {
time.
while (1) {os_wait();
process_serial_data();} // os_create(MUSIC)?
}
Tasks are threads
Another Solution
• Multiprocessor: Dedicate one processor to
each (or a few) tasks.
• Still need synchronization and
communication.
• Our player network could be an example of
a multiprocessor system