Vous êtes sur la page 1sur 35

Chapter 25 Embedded systems programming

Bjarne Stroustrup


This lecture provides a brief overview of what distinguishes embedded systems programming from ordinary programming. It then touches upon facilities that become prominent or problems when working close to the hardware such as free store use, bit manipulation, and coding standards. Remember: not all computers are little grey boxes hiding under desks in offices.
Stroustrup/Programming 2


Embedded systems Whats special/different

predictability memory Absolute addresses Bits unsigned

Resource management

Access to hardware

Coding standards


Embedded systems

Hard real time

Response must occur before the deadline

Response should occur before the deadline most of the time

Soft real time

Often there are plenty of resources to handle the common cases

But crises happen and must be handled

Predictability is key Correctness is even more important than usual

correctness is not an abstract concept but I assumed that the hardware worked correctly is no excuse

Over a long time and over a large range of conditions, it simply doesnt


Embedded systems

Computers used as part of a larger system

That usually doesnt look like a computer That usually controls physical devices Critical as in if the system fails someone might die

Often reliability is critical

Often resources (memory, processor capacity) are limited Often real-time response is essential
Stroustrup/Programming 5

Embedded systems

What are we talking about?

Assembly line quality monitors Bar code readers Bread machines Cameras Car assembly robots Cell phones Centrifuge controllers CD players Disk drive controllers Smart card processors

Fuel injector controls Medical equipment monitors PDAs Printer controllers Sound systems Rice cookers Telephone switches Water pump controllers Welding machines Windmills Wrist watches


Do You Need to Know This Stuff ?

Computer Engineers You will build and oversee the building of these systems

All close to he hardware code resembles this The concern for correctness and predictability of embedded systems code is simply a more critical form of what we want for all code

Electrical Engineers You will build and oversee the building of these systems.

You have to work with the computer guys You have to be able to talk to them You may have to teach them You may have to take over for them

Computer scientists youll know to do this or only work on web applications (and the like)
Stroustrup/Programming 7


C++ operations execute in constant, measurable time

E.g., you can simply measure the time for an add operation or a virtual function call and thatll be the cost of every such add operation and every virtual function call (pipelining, caching, implicit concurrency makes this somewhat trickier on some modern processors)
Free store allocation (new) Exception throw

With the exception of:

So throw and new are typically banned in hard real-time applications

Today, I wouldnt fly in a plane that used those

In 5 years, well have solved the problem for throw

Each individual throw is predictable

Not just in C++ programs

Similar operations in other languages are similarly avoided

Stroustrup/Programming 8


Given the constraints

Keep the highest level of abstraction

Dont write glorified assembler code Represent your ideas directly in code

As always, try to write the clearest, cleanest, most maintainable code Dont optimize until you have to

People far too often optimize prematurely John Bentley's rules for optimization

First law: Dont do it Second law (for experts only): Dont do it yet


Embedded systems programming

You (usually) have to be much more aware of the resources consumed in embedded systems programming than you have to in ordinary programs

Time Space Communication channels Files ROM (Read-Only Memory) Flash memory

You must take the time to learn about the way your language features are implemented for a particular platform

Hardware Operating system Libraries

Stroustrup/Programming 10

Embedded systems programming

A lot of this kind of programming is

Looking at specialized features of an RTOS (Real Time Operating System) Using a Non-hosted environment (thats one way of saying a language right on top of hardware without an operating system) Involving (sometimes complex) device driver architectures Dealing directly with hardware device interfaces Thats what specific courses and manuals are for

We wont go into details here



How to live without new

old object Free space old object

Whats the problem

C++ code refers directly to memory

Once allocated, an object cannot be moved (or can it?)

New object

Allocation delays

The effort needed to find a new free chunk of memory of a given size depends on what has already been allocated


If you have a hole (free space) of size N and you allocate an object of size M where M<N in it, you now have a fragment of size N-M to deal with After a while, such fragments constitute much of the memory
Stroustrup/Programming 12

How to live without new

Solution: pre-allocate


Global objects

Allocated at startup time

Sets aside a fixed amount of memory


Grow and shrink only at the top

No fragmentation Constant time operations

Pools of fixed sized objects

We can allocate and deallocate

No fragmentation Constant time operations

Top of stack
Stroustrup/Programming 13


How to live without new

No new (of course)

And no malloc() (memory allocation during runtime) either (for those of you who speak C)

No standard library containers (they use free store indirectly) Instead

Define (or borrow) fixed-sized Pools Define (or borrow) fixed-sized Stacks

Do not regress to using arrays and lots of pointers



Pool example
// Note: element type known at compile time // allocation times are completely predictable (and short) // the user has to pre-calculate the maximum number of elements needed template<class T, int N>class Pool { public: Pool(); // make pool of N Ts construct pools only during startup T* get(); // get a T from the pool; return 0 if no free Ts void free(T*); // return a T given out by get() to the pool private: // keep track of T[N] array (e.g., a list of free objects) }; Pool<Small_buffer,10> sb_pool; Pool<Status_indicator,200> indicator_pool;
Stroustrup/Programming 15

Stack example
// Note: allocation times completely predictable (and short) // the user has to pre-calculate the maximum number of elements needed template<int N>class Stack { public: Stack(); // make an N byte stack construct stacks only during startup void* get(int N); // allocate n bytes from the stack; return 0 if no free space void free(void* p); // return the last block returned by get() to the stack private: // keep track of an array of N bytes (e.g. a top of stack pointer) }; Stack<50*1024> my_free_store; // 50K worth of storage to be used as a stack void* pv1 = my_free_store.get(1024); int* pi = static_cast<int*>(pv1); // you have to convert memory to objects void* pv2 = my_free_store.get(50); Pump_driver* pdriver = static_cast<Pump_driver*>(pv2);
Stroustrup/Programming 16


Excellent for embedded systems work

No runtime overhead for inline operations

Sometimes performance matters In embedded systems memory is often critical (limited)

No memory used for unused operations



How to live with failing hardware

Failing how?

In general, we cannot know In practice, we can assume that some kinds of errors are more common than others

But sometimes a memory bit just decides to change


Power surges/failure The connector vibrated out of its socket Falling debris Falling computer X-rays E.g., only when the temperature exceeds 100 F. and the cabinet door is closed E.g., on Mars

Transient errors are the worst

Errors that occur away from the lab are the worst



How to live with failing hardware


In emergency, use a spare Know when the program (or hardware) is misbehaving


Have a quick way out of misbehaving code

Make systems modular Have some other module, computer, part of the system responsible for serious errors

In the end, maybe a person i.e., manual override Remember HAL ?

Monitor (sub)systems

In case they cant/dont notice problems themselves

Stroustrup/Programming 19

Absolute addresses

Physical resources (e.g., control registers for external devices) and their most basic software controls typically exist at specific addresses in a low-level system We have to enter such addresses into our programs and give a type to such data For example
Device_driver* p = reinterpret_cast<Device_driver*>(0xffb8); Serial_port_base *COM1 = reinterpret_cast<Serial_port_base*>(0x3f8);



Bit manipulation: Unsigned integers

How do you represent a set of bits in C++?

unsigned char uc; unsigned short us; unsigned int ui;

unsigned long int ul;

// 8 bits // typically 16 bits // typically 16 bits or 32 bits // (check before using) // many embedded systems have 16-bit ints // typically 32 bits or 64 bits

std::vector<bool> vb(93); // 93 bits Use only if you really need more than 32 bits std::bitset bs(314); // 314 bits Use only if you really need more than 32 bits Typically efficient for multiples of sizeof(int)
Stroustrup/Programming 21

Bit manipulation
a: b: 1 0 1 0 1 0 1 0 0xaa 0 0 0 0 1 1 1 1 0x0f

& | ^ << >> ~

and inclusive or exclusive or left shift right shift ones complement

a&b: 0 0 0 0 1 0 1 0 0x0a a|b: a^b: a<<1: b>>2: ~b: 1 0 1 0 1 1 1 1 0xaf 1 0 1 0 0 1 0 1 0xa5 0 1 0 1 0 1 0 0 0x54 0 0 0 0 0 0 1 1 0x03 1 1 1 1 0 0 0 0 0xf0



Bit manipulation

Bitwise operations

Sign bit

8 bits == 1 byte & (and) val | (or) 0 1 1 0 0 0 1 1 0 1 0 0 1 1 0 1 ^ (exclusive or xor) << (left shift) >> (right shift) 0xff: 1 1 1 1 1 1 1 1 ~ (one's complement) 0 1 0 0 1 1 0 1 Basically, what the hardware provides right: true false

For example
void f(unsigned short val) // assume 16-bit, 2-byte short integer { unsigned char right = val & 0xff ; // rightmost (least significant) byte unsigned char left = (val>>8) & 0xff ; // leftmost (most significant) byte bool negative = val & 0x8000 ; // sign bit (if 2s complement) // }
Stroustrup/Programming 23

Bit manipulation

Or |

0xff: val

1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0

Set a bit

And &

Is a bit set? Select (mask) some bits

For example:
enum Flags { bit4=1<<4, bit3=1<<3, bit2=1<<2, bit1=1<<1, bit0=1 }; unsigned char x = bit3 | bit1; // x becomes 8+2 x |= bit2; // x becomes 8+4+2 if (x&bit3) { // is bit3 set? (yes, it is) // } unsigned char y = x &(bit4|bit2); // y becomes 4 Flags z = Flags(bit2|bit0); // the cast is necessary because the compiler // doesnt know that 5 is in the Flags range



Bit manipulation

Exclusive or (xor) ^

a^b means (a|b) & !(a&b) either a or b but not both

unsigned char a = 0xaa; unsigned char b = 0x0f; unsigned char c = a^b;

Immensely important in graphics and cryptography

a: b: a^b: 1 0 1 0 1 0 1 0 0xaa 0 0 0 0 1 1 1 1 0x0f 1 0 1 0 0 1 0 1 0xa5



Unsigned integers

You can do ordinary arithmetic on unsigned integers

Avoid that when you can

Try never to use unsigned just to get another bit of precision If you need one extra bit, soon, youll need another Dont mix signed and unsigned in an expression

You cant completely avoid unsigned arithmetic


Indexing into standard library containers uses unsigned (in my opinion, thats a design error) vector<int> v; unsigned correct, but pedantic // for (int i = 0; i<v.size(); ++i) for (vector<int>::size_type i = 0; i<v.size(); ++i) for (vector<int>::iterator p = v.begin(); p!=v.end(); ++p)
Stroustrup/Programming 26

Yet another way


One source of errors is complicated problems

Inherent complexity Incidental complexity

Another source of errors is poorly-written code

Reasons for unnecessarily complicated code

Overly clever programmers

Who use features they dont understand Who dont use the most appropriate features

Undereducated programmers

Large variations in programming style



Coding standards

A coding standard is a set of rules for what code should look like

Typically specifying naming and indentation rules

E.g., use Stroustrup layout E.g., dont use new or throw to avoid predictability problems

Typically specifying a subset of a language

Typically specifying rules for commenting

Every function must have a comment explaining what it does

E.g., use <iostream> rather than <stdio.h> to avoid safety problems

Often requiring the use of certain libraries

Organizations often try to manage complexity through coding standards

Often they fail and create more complexity than they manage



Coding standards

A good coding standard is better than no standard

I wouldnt start a major (multi-person, multi-year) industrial project without one C++ coding standards that restrict programming to something like the C subset do harm They are not uncommon Even the good ones All programmers want to write their code exactly their own way

A poor coding standard can be worse than no standard

All coding standards are disliked by programmers

A good coding standard is prescriptive as well as restrictive

Here is a good way of doing things as well as Never do this

And examples
Stroustrup/Programming 29

A good coding standard gives rationales for its rules

Coding standards

Common aims

Reliability Portability Maintainability Testability Reusability Extensibility Readability



Some sample rules

No function shall have more than 200 lines (30 would be even better)

that is, 200 non-comment source lines E.g., int a = 7; x = a+7; f(x,9); using #ifdef and #ifndef // violation!

Each new statement starts on a new line

No macros shall be used except for source control

Identifiers should be given descriptive names

May contain common abbreviations and acronyms When used conventionally, x, y, i, j, etc., are descriptive Use the number_of_elements style rather than the numberOfElements style Type names and constants start with a capital letter

E.g., Device_driver and Buffer_pool E.g., Head and head // violation!

Identifiers shall not differ only by case



Some more sample rules

Identifiers in an inner scope should not be identical to identifiers in an outer scope

E.g., int var = 9; { int var = 7; ++var; }

// violation: var hides var

Declarations shall be declared in the smallest possible scope Variables shall be initialized

E.g., int var;

// violation: var is not initialized

Casts should be used only when essential Code should not depend on precedence rules below the level of arithmetic expressions
E.g., x = a*b+c; if( a<b || c<=d) // ok // violation: parenthesize (a<b) and (c<=d)

Increment and decrement operations shall not be used as subexpressions

E.g., int x = v[++i]; // violation (that increment might be overlooked)



An example of bit manipulation

The Tiny Encryption Algorithm (TEA)

Originally by David Wheeler

Dont look too hard at the code (unless you happen to need a good simple encryption algorithm for an application); its simply to give you the flavor of some bit manipulation code It takes one word (4 bytes at a time)

E.g., 4 characters from a string or an image file

It assumes 4-byte long integers Explanation is at the link (and in the book)

Without the explanation this is just an example of how bit manipulation code can look. This code is not meant to be self-explanatory.
Stroustrup/Programming 33

void encipher( const unsigned long *const v, unsigned long *const w, const unsigned long * const k) { unsigned long y = v[0]; unsigned long z = v[1]; unsigned long sum = 0; unsigned long delta = 0x9E3779B9; unsigned long n = 32; while(n-->0) { y += (z << 4 ^ z >> 5) + z ^ sum + k[sum&3]; sum += delta; z += (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3]; } w[0]=y; w[1]=z; } Stroustrup/Programming


void decipher( const unsigned long *const v, unsigned long *const w, const unsigned long * const k) { unsigned long y = v[0]; unsigned long z = v[1]; unsigned long sum = 0xC6EF3720; unsigned long delta = 0x9E3779B9; unsigned long n = 32; // sum = delta<<5; in general, sum = delta * n while(n-->0) { z -= (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3]; sum -= delta; y -= (z << 4 ^ z >> 5) + z ^ sum + k[sum&3]; } w[0]=y; w[1]=z; } Stroustrup/Programming