Académique Documents
Professionnel Documents
Culture Documents
3.1 Tasks 1
Issue Scheduler/Task signal exchange for block-unblock of tasks via function
calls
Issue All tasks are blocked and scheduler idles forever (not desirable!)
Issue Two or more tasks with same priority levels in Ready state (time-slice,
FIFO)
Example: scheduler switches from processor-hog vLevelsTask to vButtonTask
of extern in one task to point to another task that declares the shared data
Shared data caused the shared-data problem without solutions discussed in
Chp4 or use of Reentrancy characterization of functions
(See Fig 6.5, Fig 6.6, Fig 6.7, and Fig 6.8)
3.2 Tasks 2
Reentrancy A function that works correctly regardless of the number of tasks that
stack
A reentrant function calls only reentrant functions
A reentrant function uses system hardware (shared resource) atomically
reentrant?
If shared variables are not protected, could they be accessed using single
Race condition
Assume the scenario in which
process A reads the free slot (e.g.
7) but before printing, CPU
switches to process B and process
B reads the free slot ( again 7 )
and prints its file name there and
updates it to 8. In that case when
CPU switches back to process A , it
starts from the point it left off
and writes its file in 7. Thus
process B never gets the print of
its file.
Situations like this , when two
processes are using the shared
data and result depends on who
runs precisely when are called race
conditions.
20
Race condition
Semaphore is one of the ways for providing
mutual exclusion by protecting critical regions.
21
Semaphores
22
Semaphore Definition
A semaphore is a data structure that is
shared by several processes. Semaphores are
most often used to synchronize operations (to
avoid race conditions) when multiple processes
access a common, non-shareable resource.
By using semaphores, we attempt to avoid
other multi-programming problems such as:
Starvation
Occurs when a process is habitually denied access
to a resource it needs.
Deadlock
23
Semaphore Definition
To indicate a process has gained access to the resource,
the process decrements the semaphore.
For events to progress correctly, the test and
decrement operation on the semaphore must be atomic
(i.e., no interruptible/indivisible).
There are two kinds of Semaphores:
Binary semaphores
Control access to a single resource, taking
the value of 0 (resource is in use) or 1
(resource is available).
Counting semaphores
Control access to multiple resources, thus
assuming a range of nonnegative values.
24
Semaphore Definition
Semaphore is a nonnegative integer that is
stored in the kernel.
Access to the semaphore is provided by a
series of semaphore system calls.
25
26
27Figure
28
Return
Manual Section
Success
Failure
Sets errno
-1
Yes
problems in RTOS
(See Fig 6.12 and Fig 6.13)
section
Each shared data (resource/device) requires a separate semaphore for individual
place
The symmetry of takes and releases must match or correspond each
take must have a corresponding release somewhere in the ES application
Taking the wrong semaphore unintentionally (issue with multiple
semaphores)
Holding a semaphore for too long can cause waiting tasks deadline to be
missed
Priorities could be inverted and usually solved by priority
inheritance/promotion
for resources)
Counting semaphores multiple instances of resources, increase/decrease
of integer semaphore variable
Mutex protects data shared while dealing with priority inversion problem
Summary Protecting shared data in RTOS
Disabling/Enabling interrupts (for task code and interrupt routines), faster
Contents
Message Queues, Mailboxes and Pipes
Timer Functions
Events
Memory Management
Interrupt Routines in an RTOS Environment
if (!!problem arises)
vLogError(ERROR_TYPE_X);
if (!!problem arises)
vLogError(ERROR_TYPE_Y);
pOseQueue = OSQCreate(apvQueue,
SIZEOF_QUEUE);
!!Start Task1
!!Start Task2
}
void Task1(void) {
}
void Task2(void) {
}
void ErrorTask(void)
{
while(FOREVER)
{
iErrorType = (int) OSQPend(pOseQueue,
WAIT_FOREVER, &byErr);
}
void vMainTask(void)
{
int *pTemperatures;
BYTE byErr;
while(TRUE)
{
pTemperatures = (int*)OSQPend(pOseQueueTemp,
WAIT_FOREVER, &byErr);
if (pTemperatures[0] != pTemperatures[1])
!! Set Off howling alarm;
}
While(TRUE) {
pTemperatures =
(int*)malloc(2*sizeof*pTemperatures);
pTemperatures[0] =
pTemperatures[1] =
OSQPost(pOseQueueTemp,
(void*)pTemperatures);
}
}
Mailboxes
Much like queues.
RTOS can
Create, write ,check and read from mail boxes.
The number of messages in a mailbox is limited.
User can prioritize mailbox messages.
MultiTask!
sndmsg, rcvmsg, chkmsg
Pipes
Much like queues.
RTOS can
Create, write ,check and read from pipes.
Byte-oriented.
Use fread(), fwrite()
Pitfalls
Passing Pointers through a queue may create
shared data bugs
void vReadTemperaturesTask(void)
{
int *pTemperatures;
void vReadTemperaturesTask(void)
{
int iTemperatures[2];
While(TRUE) {
pTemperatures =
(int*)malloc(2*sizeof*pTemperatures);
While(TRUE) {
iTemperatures[0] =
pTemperatures[0] =
pTemperatures[1] =
iTemperatures[1] =
OSQPost(pOseQueueTemp,
OSQPost(pOseQueueTemp,
(void*)pTemperatures);
}
}
(void*)iTemperatures);
}
}
Timer Functions
Must Keep track of the passage of time
Offer taskDelay() functions.
void vMakePhoneCallTask(void)
{
taskDelay(100);
vDialingToneOn(*p_chPhoneNumber 0);
taskDelay(100);
vDialingToneOff();
Events
Procedure
1.
2.
3.
Memory Management
Interrupt Routines must not call any RTOS functions thatMight block the caller.
2. Might cause the RTOS to switching tasks without fair
warning
1.
RTOS
TaskHigh
Send message
to mailbox
TaskLow
Time
ISR
RTOS
TaskHigh
Send Message
to mailbox
TaskLow
Time
Return
RTOS
TaskHigh
Send Message
to mailbox
TaskLow
Time
TaskHigh
Enter
interrupt
routine.
Send Message
to mailbox
TaskLow
Time
OSSemPost
OSISRSemPost for interrupt routines
Nested Interrupts
The RTOS must know
when the lower-priority interrupt routine.
RTOS schedler goes to TaskHigh
instead of finishing low-priority ISR.
High-priority
ISR
Low-priority
ISR
RTOS
TaskHigh
TaskLow
High-priority
interrupt
occurs.
Send Message
to mailbox
Time
architecture
Key RTOS mechanisms used include tasks, task management, inter
task communication mechanisms (semaphores, queues, mailboxes,
pipes), and interrupts
Design considerations
ES design technique: Create all needed tasks, get them into blocked-state or idle state
waiting on interrupts (to be generated by an external event, e.g., frame-arrival at a network
port)
(See Fig 8.1 network port and serial port comm via tasks that implement DDP and ADSP
protocol stack)
Principles 1
Principles 2
Principles 3
Principles 3
quicker response time using task prioritization high priority for time-critical ones, and low priority for
others
Principles 4
Other Tasks ?
Need many small, simple tasks? But worry about data-sharing, intertask comm
Need a task per stimuli? Same problems!
Tasks wait on RTOS for an event (expected in each tasks independent message queue)
Tasks block on in one place (RTOS signal), and not any other semaphore, no data sharing
Principles 5
them forever)
Rule-of-thumb: Create all tasks needed at start, and keep them if memory is cheap!
Principles 6
directly) improves portability since only the shell may be rewritten fro RTOS to RTOS
An Examples
An Example
Which Architecture?
If RTOS, meeting deadlines depends on dealing with the 4-5 secs time required to calculate the # of gallons
requires task suspensions, perhaps, with less IRs usage; and above all, the microprocessor must support some
RTOS
If not RTOS, meeting deadlines requires the use of several interrupts (and IRs)
An Example
System Decomposition for Tasks
One low priority task that handles all # gallons calculations and detects leaks as well (for all
tanks 1 at a time)
A high priority overflow-detection task (higher than a leak-detection task)
A high priority float-hardware task, using semaphores to make the level-calc and overflowdetection task wait on it for reading (semaphores will be simpler, faster than queuing requests to
read levels)
A high priority button handling tasks need a state-machine model (an IR? with internal static
data structures, a simple wait on button-signal, and an action which is predicated on sequence of
button signals) since semaphores wont work
(See Fig 8.8)
A high priority display task to handle contention for LCD use
[Turning the alarm bell on/off by the level-calc, overflow, and user-button is typically noncontentious an atomic op hence do not need a separate alarm-bell task] However, need a
module with BellOn(), BellOff() functions to encapsulate the alarm hardware
Low priority task to handle report formatting (one line at a time), and handle report queue
(See Table 8.2)
An Example
[dt]
UBT
BHI
BIR
BHT
[pt]
DT
PT
User presses printer button, print IR signals print-formatting task -> which sends first line to printer; printer
interrupts for print IR to send next line to printer; when all lines (for report) are done, print IR signals printformatting task for next report
A level task need to read, it interrupts the level-read-hardware routine; the level is read by the hardware and the
IR interrupts the task to read the new float level
See Fig 8.9 and code listing in Chap 11 Black magic and wizardry!! SEng, an Art!
Encapsulating Semaphores:
Dont assume that all tasks will use semaphore correctly (take/release), leading to errors
Protect semaphores and associated data encapsulate/hide them in a task
Let all tasks call a separate module (acting as an intermediary) to get to the CS - this separate
module/function will in turn call the task which encapsulates the semaphore
(See Fig 8.10 the correct code)
(See Fig 8.11 the incorrect alternative, which bypasses the intermediate function
Encapsulating Queues:
Writing to or reading from a flash memory using queues to enqueue messages, the correctness of Fig 8.4
intermediate task vHandleFlashTask, which is supported by auxiliary functions vReadFlash and vWriteFlash. [The
handle-task provides an interface for all other tasks to get to the queue]
(See Fig 8.13)
Guaranteeing that the system will meet hard deadlines comes from writing fast code
Issues: fast algorithms, efficient data structures, code in assembly (if possible)
access time of data structures/buffers, semaphore blocking any operation that cant be done in the same
time units on each execution/access
local variables, parameters, function nesting-level, worst-case nesting of interrupt routines, space for the
RTOS (or select features) from the manual
B. Experimental runs of the code not easy and wont reflect worst-case behavior
Techniques / Suggestions:
Substitute or eliminate large functions, watch for repeated calls to large functions
Consider writing your own function to replace RTOS functions, watch RTOS functions that call several others
Configure or customize the RTOS functions to suit only the needs of the ES
Study assembly listing of cross-compilers, and rework your C code or write your own assembly unit/task
Use static variable instead of relying on stack variables (push/pop and pointering takes space)
Copy data structures passed to a function, via a pointer, into the functions local, static variables process the data and copy
Saving Power
Some embedded systems run on battery, turning battery off for some or all devices is good
Generally, how to do you save power?
Look for the power-saving modes (enablers) which the manufacturers provide
Software can put microprocessor in one the modes via special instruction or writing a code to special register in the
Distinction
Host: Where the embedded software is developed, compiled, tested, debugged, optimized,
and prior to its translation into target device. (Because the host has keyboards, editors,
monitors, printers, more memory, etc. for development, while the target may have not of
these capabilities for developing the software.)
Target: After development, the code is cross-compiled, translated cross-assembled, linked
(into target processor instruction set) and located into the target
Cross-Compilers
Native tools are good for host, but to port/locate embedded code to target, the host must have a tool-chain that
includes a cross-compiler, one which runs on the host but produces code for the target processor
Cross-compiling doesnt guarantee correct target code due to (e.g., differences in word sizes, instruction sizes, variable
declarations, library functions)
code that can be linked and located into the target processor
(See Fig 9.1)
Native linkers are different from cross-linkers (or locators) that perform additional tasks to locate embedded
binary code into target processors
Address Resolution
Native Linker: produces host machine code on the hard-drive (in a named file), which the loader loads into RAM, and then
In RAM, the application program/codes logical addresses for, e.g., variable/operands and function calls, are ordered or
organized by the linker. The loader then maps the logical addresses into physical addresses a process called address
resolution. The loader then loads the code accordingly into RAM (see Fig 9.2). In the process the loader also resolves the
addresses for calls to the native OS routines
Locator: produces target machine code (which the locator glues into the RTOS) and the combined code (called map) gets
copied into the target ROM. The locator doesnt stay in the target environment, hence all addresses are resolved, guided by
locating-tools and directives, prior to running the code (See Fig 9.3 and Fig 9.4)
Unchanging embedded program (binary code) and constants must be kept in ROM to be remembered even
on power-off
Chain tools (for embedded systems) also require a start-up code to be in a separate segment and located
at a microprocessor-defined location where the program starts execution
Some cross-compilers have default or allow programmer to specify segments for program parts, but crossassemblers have no default behavior and programmer must specify segments for program parts
Segments with initialized values in ROM are shadowed (or copied into RAM) for correct reset of
initialized variables, in RAM, each time the system comes up (esp. for initial values that are take
#define constants, and which can be changed)
In C programs, a host compiler may set all uninitialized variable to zero or null, but this is not generally
the case for embedded software cross-compilers (unless the startup code in ROM does so
If part(s) of a constant string is(are) expected to be changed during run-time, the cross-compiler must
generate a code to allow shadowing of the string from ROM
An advanced locator is capable of running (albeit slowly) a startup code in ROM, which (could
decompress and) load the embedded code from ROM into RAM to execute quickly since RAM is faster,
especially for RISC microprocessors
Moving maps into ROM or PROM, is to create a ROM using hardware tools or a PROM programmer
(for small and changeable software, during debugging)
If PROM programmer is used (for changing or debugging software), place PROM in a socket (which
makes it erasable for EPROM, or removable/replaceable) rather than burnt into circuitry
PROMs can be pushed into sockets by hand, and pulled using a chip puller
The PROM programmer must be compatible with the format (syntax/semantics) of the Map
ROM Emulators Another approach is using a ROM emulator (hardware) which emulates the target
system, has all the ROM circuitry, and a serial or network interface to the host system. The locator
loads the Map into the emulator, especially, for debugging purposes.
Software on the host that loads the Map file into the emulator must understand (be compatible with)
the Maps syntax/semantics
No need to pull the flash (unlike PROM) for debugging different embedded code
New versions of embedded software (supplied by vendor) can be loaded into flash memory by customers over a network Requires a) protecting the flash programmer, saving it in RAM and executing from there, and reloading into flash after new
version is written and b) the ability to complete loading new version even if there are crashes and protecting the startup code as in
(a)
Modifying and/or debugging the flash programming software requires moving it into RAM, modify/debug, and reloading it into
target flash memory using above methods
DEBUGGING TECHNIQUES
Introduction
Rule of Thumb: Write good, bug-free code from start if you could
Post-shipment application problems are more tolerable than embedded (real-time or life-critical)
software
Some reasons why you cant test (much, if any) on target machine:
Exercise all code, including exceptions (real situations may be difficult to exercise)
Develop reusable, repeatable test (difficult to do in target environment, and likelihood of hitting the
same bug is low)
Store test results (target may not even have disk drive to store results)
Basic Techniques
Fig 10.1
Target system on the left: (hardware-indep code, hardware-dep code, hw)
Test system (on host) on the right: (hardware-indep code same, scaffold rest)
Scaffold provides (in software) all functionalities and calls to hardware as in the hardware-dep and hardware
Basic Techniques
Fig 10.2
Radio.c -- hardware independent code
Radiohw.c hardware dependent code (only interface to hw: inp() and outp() supporting
program stub!!
(continued)
B) a component of the routine which deals with the rest of the system
2) To test, structure the routine such that the hardware-dependent component (A) calls the hardware-independent part
(B).
3) Write component B in C-language, so that the test scaffold can call it
(continued)
BYTE *p_by;
.
.
/* Send each of the characters in a_byTestCommand */
p_by = a_byTestCommand;
while (*p_by)
{
/* Send a single character as though received by the interrupt */
vHandleRxByte (*p_by);
/* Go to the next character */
++p_by;
}
.
.
}
1) Hard to test parts which are truly hardware dependent, until the target system is operational. Yet,
good to test most sw-independent parts on host (see Fig 10.8)
3) Having the scaffold run on the host and its RTOS scaffold can run as low priority task within the
RTOS and have nicely integrated testing environment
4) The hard to justify limitations cant tell in scaffold until the actual test
Writing to the wrong hardware address software/hardware interactions
Realistic interrupt latency due to differences in processor speeds (host v. target)
Real interrupts that cause shared-data problems, where real enable/disable is the key
Differences in network addressing, size of data types, data packing schemes portability issues
Simulator takes the Map as input, reads the instructions from simulated ROM, reads/writes from/to
simulated registers
Provide a user interface to simulator for I/O, debugging (using, e.g., a macro language)
Capabilities of Simulators:
Collect statistics on # instructions executed, bus cycles for estimating actual times
Easier to test assembly code (for startup software and interrupt routines) in simulator
Easier to test for portability since simulator takes same Map as the target
Other parts, e.g., timers and built-in peripherals, can be tested in the corresponding simulated versions in the
I/O to support the scaffold; and scripts to format and reformat files between the simulator, simulated memory, and
the scaffold)
If the expression is TRUE nothing happens, if FALSE, a message is printed and the program crashes
Assert works well in finding bugs early, when testing in the host environment
On failure, assert causes a return to the host operating systems (cant do on target, and cant print such
message on target may not have the display unit)
Assert macro that runs on the target are useful for spotting problems:
1) disabling interrupts and spin in infinite loop effectively stopping the system
2) turn on some pattern of LEDs or blinking device
3) write special code memory for logic analyzer to read
4) write location of the instruction that cause problem to specific memory for logic analyzer to read (the Map can
triggering mechanism to indicate start of monitoring, adjust vertical to know ground-signal, used as voltmeter (flat
graph at some vertical relative to ground signal), test if a device/part is working is graph flat? Is the digital signal
coming through expecting a quick rising/falling edge (from 0 VCC or VCC 0) if not, scope will show slow
rising/falling indicating loading, bus fight, or other hardware problem
(See Fig 10.10, Fig 10.11, Fig 10.12, Fig 10.13, Fig 10.14)
Logic Analyzer
Like storage scopes that (first) capture many signals and displays them simultaneously
It knows only of VCC and ground voltage levels (displays are like timing diagrams) Real scopes display exact
purposes (e.g., writing data to wrong address, tracking a rarely occurring bug, filtering signals for select devices or
events)
LA cant capture all signals, e.g., on fetch from caches, registers, un-accessed memory
LAs have better trace and filtering mechanism, and easier to detail and find problems
LAs support many but select signals to attach, ICE requires connecting ALL signals
Software-Only Monitors
Monitors allow running an embedded system in the target environment, while providing debugging interfaces on both
The codes receives programs from serial port, network, copies into targets RAM, and run it with full debugging capabilities to
test/debug the programs
Another portion of monitor resides on host provides debugging capability and communicates with the debugging