EMBEDDED SYSTEM DEVELOPMENT

The most important development goals for embedded system developers are "Low Time to Market" and "High Quality". Probably no customer would dislike a "Zero Defect" product delivered to them in "Zero Time". We will discuss these two constraints later in this section.

Choice of Programming Languages:

Most common programming languages being used in Embedded Systems are "C" and "Assembly Languages". Assembly language enables programmers to write optimized codes (in terms of speed and memory requirements), which is desirable in embedded systems. Howevever recent advancements in compiler technology has greatly improved the performance of compilers. Some of the commercially available compilers can genearate code which are comparable to the hand-written assembly language codes.

Use of High Level (or middle level) programming languages has some distinct advantages over assembly language Codes - Low Development time, Shorter Debug Cycles, Ease of Maintenance. Also, an application developed with a high (or low) level language can be easily ported to a newer processor (moving assembly code from one processor to another processor will require a re-development from scratch). For this reason, in recent years, there has been a great shift from "Assembly Language" programming to a mix Mode programming. In Mix mode programming, both the "C" and "Assembly" langauge are used for application development. A complete application is initially developed using "C". Later, the performance critical modules are replaced by a handwritten assembly code.

Another important factor for emergence of "C" as a preffered programming languages in Embedded System Development is its ability to control the Hardware. Since "C" is a low level language (and not a high level language), it has provisions to program the Hardware (similar to the way it can be programmed using Assembly Language). This feature is not available in other languages like C++.

In addition to "C", Java is also a popular choice for embedded development. It has emerged as fastest growing language in embedded systems (specially for mobile technologies and gaming consoles).

Development tools

Most chip (processor) manufactures provide proprietary tool chains (Compilers, Archiver, Assembler and Linker), which are optimized for their platforms. The compilers have different optimization options (optimize for memory, optimize for speed) and optimization levels. Some developers also use GNU tools chains (gcc, glibc, binutils, gdb etc) for the development under Linux enviroment.

Loader

As discussed under the

section-5, Loader converts an executable to the loadable file. Different Processor Vendors use different Loader file formats. Loader format can also be Operating System Specific.

Development Kits

Processor Designers provide Evaluation and Development kits to their potential customers. If you are starting a new project (or are new to embedded system development field), evaluation kits are the best way to start. These kits come with a Processor and minimum system components. Development kits are a upgraded version of Evlauation kit, which contain domain specific components (e.g. automobile, cellphone, medical electronics). Most vendors provide special discounts on these kits to Students and Universities. Here are some links to the development boards of some popular embedded processors.

  • Microchip PIC Starter kits
  • MIPS Development Boards
  • ARM Development Boards
  • Texas Instruments DSP Development Kits
  • SHARC Development Boards
  • Blackfin Evaluation Kits
  • Blackfin Development Boards
  • Blackfin uclinux development boards

Portability

While developing an application on a given platform, it must be considered that tomorrow the same application might have to run on a different processor, or under a different operating system. Writing a portable (Across different platforms) application greatly increases the code reusability. The main issues which need to be addressed are:

* Endianness : Some processors support Little-Endian word storage whereas, other processors provide Big-Endian storage. As long as the data created by a processor is being consumed by the same processor, there are no Endian-ness issues. However, if there is a difference in the Endianness of two processors (within or outside the system), potential error can be caused. Hence it is customary to define a particular standard for all the data communication (across or within the system).

* Word Sizes: Different processor might have different word sizes. Generally compilers for the given processor assume the data types based on the processor word size. For example (Compiler for) a 16-bit processor will generally treat an integer as 16-bit variable, whereas (Compiler for) a 32-bit processor processor will treat the integer as 32-bit variable. Consider the following code section


int a;
for (a=1; a++; a>0)
{
       instructions;
}
The above code will behave differently for different integer sizes. Hence it is customary to use more meaningful notations for data types. For example INT16, UINT16, INT32, UINT32 provide a better idea about the data types (than short int, long int, int etc). These type of notation also avoids the ambiguity across different operating systems.

Reentrant Functions
A routine is said to be reentrant if, while it is being executed, it can be re-invoked by itself, or by any other routine, by interrupting the present execution for a while. Embedded application are interrupt driven (frequent interrupts change the execution state) and hence reentrant code is highly desirable in such applications. A non-reentrant code can be a potential reason for system crash.
In order to be reentrant, the function must satisy the following conditions:
1. It should never modifies itself (i.e. function once loaded in to system memory, must never change).
2. Any variables changed by the function must be allocated to a particular "instance" of the code's invocation.

Consider the following function which computes sum of all the elements in an array of integers. The Array address is passed as a paramter to the function and the number of elements are stored in a global variable "ArraySize".


int ArraySize;  

float FindSum(int *StartAddress)  
{  
             int i;  
                 float TempSum=0;  
    
                for (i=0; i < ArraySize; i++)  
                 {  
                                  TempSum += *(StartAddress+i);  
                 }  
               return TempSum;  
} 
In the above function, the variable ArraySize which is used by the function is a global variable. If the execution of a thread (program thread) which has just called the function FindSum() is stopped by another thread, and this second thread again makes a call to the FindSum() function, then the value of variable ArraySize would have been lost when the program flow returns to the first function. An easy way to resolve this problem is passing ArraySize as a function parameter (instead of keeping it as a global variable). This will ensure that ArraySize has a local copy for each function call.

float FindSum(int *StartAddress, int ArraySize) 
{ 
              int i; 
             float TempSum=0;      
             for (i=0; i < ArraySize; i++)  
             { 
                              TempSum += *(StartAddress+i);  
              }  
         return TempSum; 
} 
In certain cases, the use of global variables can not be avoided. In such scenarios, the function (which needs to be reentrant) should create a local copy of these global variables for its use.

Use of Global variables and Static variables in a function is first sign of non-reentrancy. These should be avoided.

Memory Leaks
Memory leaks refer to the unused memory in a system, which was allocated to the application but never claimed back, at application exit. Memory leaks can be caused by the application which have malloc() calls, but no free() calls for this allocated memory while (either normally or because of some error conditions) exiting. Memory leaks are a potential danger for the embedded systems (which already have memory constraints). Therefore, any application running on embedded systems must be tested well to have no memory-leaks. There are some commercial as well as open-source tools available for memory leak testing on a code. However these tools can only statistically analyze the application. These tools can not detect any memory leak which might be caused due to sudden crash of application.

Development Goals
As we discussed in the beginning of this section, Low Development Cost and Low Development time are some of the desirable development goals (in addition to High Quality and High Reliability).



User Comments

No Posts found !

Login to Post a Comment.