Embedded System Design Module 4
Embedded System Design Module 4
Embedded Firmware
9 Design and
Development
LEARNING OBJECTIVES
LO 1 Understand the different steps involved in the design and development of firmware
for embedded systems
Learn about the different approaches for embedded firmware design and
development, the merits and limita ons of each
LO 2 Discuss the different languages for embedded firmware development and the merits
and limita ons of each
Learn about Assembly Language and instruc on mnemonics
Learn the steps involved in conver ng an Assembly Language program to machine
executable code
Learn about the assembler, linker, locator and object to hex file converter
Learn the advantages and drawbacks of Assembly Language based firmware
development
Learn the various steps involved in the conversion of a program wri en in high
level language to machine executable code
Learn about the advantages and limita ons of high level language based embedded
firmware development
Learn the different ways of mixing assembly language with high level language for
embedded applica on development
LO 3 Understand the fundamentals of embedded firmware design using Embedded ‘C’
Learn the similari es and differences between conven onal ‘C’ programming and
‘C’ programming for Embedded applica on development
Learn the difference between na ve and cross-pla orm development
Learn about Keywords and Iden fiers, Data types, Storage Classes, Arithme c
and Logic Opera ons, Rela onal Opera ons, Branching Instruc ons, Looping
Instruc ons, Arrays and Pointers, Characters and Strings, Func ons, Func on
Pointers, Structures and Unions, Preprocessors and Macros, Constant Declara ons,
Vola le Variables, Delay genera on and Infinite loops, Bit manipula on opera ons,
Coding Interrupt Service Rou nes, Recursive and Reentrant func ons, and
Dynamic memory alloca on in Embedded C
Embedded Firmware Design and Development 309
The embedded firmware is responsible for controlling the various peripherals of the embedded hardware
and generating response in accordance with the functional requirements mentioned in the requirements
for the particular embedded product. Firmware is considered as the master brain of the embedded system.
Imparting intelligence to an Embedded system is a one time process and it can happen at any stage, it can be
immediately after the fabrication of the embedded hardware or at a later stage. Once intelligence is imparted
to the embedded product, by embedding the firmware in the hardware, the product starts functioning properly
and will continue serving the assigned task till hardware breakdown occurs or a corruption in embedded
firmware occurs. In case of hardware breakdown, the damaged component may need to be replaced by a
new component and for firmware corruptions the firmware should be re-loaded, to bring back the embedded
product to the normal functioning. For most of the embedded products the embedded firmware is stored
at a permanent memory (ROM) and they are nonalterable by end users. Some of the embedded products
used in the Control and Instrumentation domain are adaptive. This adaptability is achieved by making use
configurable parameters which are stored in the alterable permanent memory area (like NVRAM/FLASH).
The parameters get updated in accordance with the deviations from expected behaviour and the firmware
makes use of these parameters for creating the response next time for similar variations.
Designing embedded firmware requires understanding of the particular embedded product hardware, like
various component interfacing, memory map details, I/O port details, configuration and register details of
various hardware chips used and some programming language (either target processor/controller specific low
level assembly language or a high level language like C/C++/JAVA).
Embedded firmware development process starts with the conversion of the firmware requirements into
a program model using modelling tools like UML or flow chart based representation. The UML diagrams
or flow chart gives a diagrammatic representation of the decision items to be taken and the tasks to be
performed. Once the program model is created, the next step is the implementation of the tasks and actions
by capturing the model using a language which is understandable by the target processor/controller. The
following sections are designed to give an overview of the various steps involved in the embedded firmware
design and development.
A typical example of a ‘Super loop based’ product is an electronic video game toy containing keypad and
display unit. The program running inside the product may be designed in such a way that it reads the keys to
detect whether the user has given any input and if any key press is detected the graphic display is updated.
The keyboard scanning and display updating happens at a reasonably high rate. Even if the application
misses a key press, it won’t create any critical issues; rather it will be treated as a bug in the firmware ☺.
It is not economical to embed an OS into low cost products and it is an utter waste to do so if the response
requirements are not crucial.
The ‘Super loop based design’ is simple and straight forward without any OS related overheads. The
major drawback of this approach is that any failure in any part of a single task may affect the total system.
If the program hangs up at some point while executing a task, it may remain there forever and ultimately the
product stops functioning. There are remedial measures for overcoming this. Use of Hardware and software
Watch Dog Timers (WDTs) helps in coming out from the loop when an unexpected failure occurs or when
the processor hangs up. This, in turn, may cause additional hardware cost and firmware overheads.
Another major drawback of the ‘Super loop’ design approach is the lack of real timeliness. If the number
of tasks to be executed within an application increases, the time at which each task is repeated also increases.
This brings the probability of missing out some events. For example in a system with Keypads, according to
the ‘Super loop design’, there will be a task for monitoring the keypad connected I/O lines and this need not
be the task running while you press the keys (That is key pressing event may not be in sync with the keypad
press monitoring task within the firmware). In order to identify the key press, you may have to press the keys
for a sufficiently long time till the keypad status monitoring task is executed internally by the firmware. This
will really lead to the lack of real timeliness. There are corrective measures for this also. The best advised
option in use interrupts for external events requiring real time attention. Advances in processor technology
brings out low cost high speed processors/controllers, use of such processors in super loop design greatly
reduces the time required to service different tasks and thereby are capable of providing a nearly real time
attention to external events.
Throughout this book under the title ‘Embedded Firmware Design and Development’, we will be
discussing only the ‘Super loop based design’. Again the discussion is narrowed to super loop based firmware
development for 8051 controller.
like the CPU and memory and offers some way to communicate between tasks. We will discuss the basics of
RTOS based system design in a later chapter titled ‘Designing with Real Time Operating Systems (RTOS)’.
‘Windows Embedded Compact’, ‘pSOS’, ‘VxWorks’, ‘ThreadX’, ‘MicroC/OS-III’, ‘Embedded Linux’,
‘Symbian’ etc are examples of RTOS employed in embedded product development.
This instruction mnemonic moves decimal value 30 to the 8051 Accumulator register. Here MOV A is the
Opcode and 30 is the operand (single operand). The same instruction when written in machine language will
look like
01110100 00011110
where the first 8 bit binary value 01110100 represents the opcode MOV A and the second 8 bit binary value
00011110 represents the operand 30.
The mnemonic INC A is an example for instruction holding operand implicitly in the Opcode. The machine
language representation of the same is 00000100. This instruction increments the 8051 Accumulator register
content by 1.
The mnemonic MOV A, #30 explained above is an example for single operand instruction.
LJMP 16bit address is an example for dual operand instruction. The machine language for the same is
00000010 addr_bit15 to addr_bit 8 addr_bit7 to addr_bit 0
The first binary data is the representation of the LJMP machine code. The first operand that immediately
follows the opcode represents the bits 8 to 15 of the 16bit address to which the jump is required and the
second operand represents the bits 0 to 7 of the address to which the jump is targeted.
Assembly language instructions are written one per line. A machine code program thus consists of a
sequence of assembly language instructions, where each statement contains a mnemonic (Opcode + Operand).
Each line of an assembly language program is split into four fields as given below
LABEL OPCODE OPERAND COMMENTS
LABEL is an optional field. A ‘LABEL’ is an identifier used extensively in programs to reduce the
reliance on programmers for remembering where data or code is located. LABEL is commonly used for
representing
A memory location, address of a program, sub-routine, code portion, etc.
The maximum length of a label differs between assemblers. Assemblers insist strict formats for
labelling. Labels are always suffixed by a colon and begin with a valid character. Labels can contain
number from 0 to 9 and special character _ (underscore).
Labels are used for representing subroutine names and jump locations in Assembly language programming.
It is to be noted that ‘LABEL’ is not a mandatory field; it is optional only.
The sample code given below using 8051 Assembly language illustrates the structured assembly language
programming.
;####################################################################
; SUBROUTINE FOR GENERATING DELAY
; DELAY PARAMETR PASSED THROUGH REGISTER R1
; RETURN VALUE NONE
; REGISTERS USED: R0, R1
;####################################################################
DELAY: MOV R0, #255 ; Load Register R0 with 255
DJNZ R1, DELAY ; Decrement R1 and loop till
; R1= 0
RET ; Return to calling program
314 Introduc on to Embedded Systems
The Assembly program contains a main routine which starts at address 0000H and it may or may not
contain subroutines. The example given above is a subroutine, where in the main program the subroutine is
invoked by the Assembly instruction
LCALL DELAY
Executing this instruction transfers the program flow to the memory address referenced by the ‘LABEL’
DELAY.
It is a good practice to provide comments to your subroutines before the beginning of it by indicating the
purpose of that subroutine, what the input parameters are and how they are passed to the subroutines, which
are the return values, how they are returned to the calling function, etc. While assembling the code a ‘;’
informs the assembler that the rest of the part coming in a line after the ‘;’ symbol is comments and simply
ignore it. Each Assembly instruction should be written in a separate line. Unlike C and other high level
languages, more than one ASM code lines are not allowed in a single line.
In the above example the LABEL DELAY represents the reference to the start of the subroutine DELAY.
You can directly replace this LABEL by putting the desired address first and then writing the Assembly code
for the routine as given below.
ORG 0100H
MOV R0, #255 ; Load Register R0 with 50H
DJNZ R1, 0100H ; Decrement R1 and loop till R1= 0
RET ; Return to calling program
The advantage of using a label is that the required address is calculated by the assembler at the time of
assembling the program and it replaces the Label. Hence even if you add some code above the LABEL
‘DELAY’ at a later stage, it won’t create any issues like code overlapping, whereas in the second method
where you are implicitly telling the assembler that this subroutine should start at the specified address (in the
above example 0100H). If the code written above this subroutine itself is crossing the 0100H mark of the
program memory, it will be over written by the subroutine code and it will generate unexpected results☺.
Hence for safety don’t assign any address by yourself, let us refer the required address by using labels and let
the assembler handle the responsibility for finding out the address where the code can be placed. In the above
example you can find out that the label DELAY is used for calling the subroutine as well as looping (using
jumping instruction based on decision-DJNZ). You can also use the normal jump instruction to jump to the
label by calling LJMP DELAY.
The statement ORG 0100H in the above example is not an assembly language instruction; it is an assembler
directive instruction. It tells the assembler that the Instructions from here onward should be placed at location
starting from 0100H. The Assembler directive instructions are known as ‘pseudo-ops’. They are used for
1. Determining the start address of the program (e.g. ORG 0000H)
2. Determining the entry address of the program (e.g. ORG 0100H)
3. Reserving memory for data variables, arrays and structures (e.g. var EQU 70H
4. Initialising variable values (e.g. val DATA 12H)
The EQU directive is used for allocating memory to a variable and DATA directive is used for initialising
a variable with data. No machine codes are generated for the ‘pseudo-ops’.
Till now we discussed about Assembly language and how it is used for writing programs. Now let us have
a look at how assembly programs are organised and how they are translated into machine readable codes.
The Assembly language program written in assembly code is saved as .asm (Assembly file) file or an .src
(source) file or an extension format supported by the tool chain/assembler. Any text editor like ‘notepad’ or
‘WordPad’ from Microsoft® or the text editor provided by an Integrated Development (IDE) tool can be used
for writing the assembly instructions.
Embedded Firmware Design and Development 315
Similar to ‘C’ and other high level language programming, you can have multiple source files called
modules in assembly language programming. Each module is represented by an ‘.asm’ or ‘.src’ or a file with
an extension format specific to the ttol chain/assembler used similar to the ‘.c’ files in C programming. This
approach is known as ‘Modular Programming’. Modular programming is employed when the program is too
complex or too big. In ‘Modular Programming’, the entire code is divided into submodules and each module
is made re-usable. Modular Programs are usually easy to code, debug and alter. Conversion of the assembly
language to machine language is carried out by a sequence of operations, as illustrated below.
[Link] Source File to Object File Transla on
Translation of assembly code to machine code is performed by assembler. The assemblers for different target
machines are different and it is common that assemblers from multiple vendors are available in the market
for the same target machines. Some target processor’s/controller’s assembler may be proprietary and is
supplied by a single vendor only. Some assemblers are freely available in the internet for downloading. Some
assemblers are commercial and requires licence from the vendor. A51 Macro Assembler from Keil software
is a popular assembler for the 8051 family microcontroller. The various steps involved in the conversion
of a program written in assembly language to corresponding binary file/machine language is illustrated in
Fig. 9.1.
Library Files
Source File 1
(.asm or .src file) Module Assembler Object File 1
(Module-1)
Source File 2
(.asm or .src file) Module Assembler Object File 2
(Module-2)
Machine Code
(Hex File)
Each source module is written in Assembly and is stored as .src file or .asm file. Each file can be assembled
separately to examine the syntax errors and incorrect assembly instructions. On successful assembling of
each .src/.asm file a corresponding object file is created with extension ‘.obj’. The object file does not contain
the absolute address of where the generated code needs to be placed on the program memory and hence it
is called a re-locatable segment. It can be placed at any code memory location and it is the responsibility
316 Introduc on to Embedded Systems
of the linker/locator to assign absolute address for this module. Absolute address allocation is done at the
absolute object file creation stage. Each module can share variables and subroutines (functions) among them.
Exporting a variable/function from a module (making a variable/function from a module available to all other
modules) is done by declaring that variable/function as PUBLIC in the source module.
Importing a variable or function from a module (taking a variable or function from any one of other
modules) is done by declaring that variable or function as EXTRN (EXTERN) in the module where it is going
to be accessed. The ‘PUBLIC’ Keyword informs the assembler that the variables or functions declared as
‘PUBLIC’ needs to be exported. Similarly the ‘EXTRN’ Keyword tells the assembler that the variables or
functions declared as ‘EXTRN’ needs to be imported from some other modules. While assembling a module,
on seeing variables/functions with keyword ‘EXTRN’, the assembler understands that these variables or
functions come from an external module and it proceeds assembling the entire module without throwing any
errors, though the assembler cannot find the definition of the variables and implementation of the functions.
Corresponding to a variable or function declared as ‘PUBLIC’ in a module, there can be one or more modules
using these variables or functions using ‘EXTRN’ keyword. For all those modules using variables or functions
with ‘EXTRN’ keyword, there should be one and only one module which exports those variables or functions
with ‘PUBLIC’ keyword. If more than one module in a project tries to export variables or functions with the
same name using ‘PUBLIC’ keyword, it will generate ‘linker’ errors.
Illustrative example for A51 Assembler–Usage of ‘PUBLIC’ for importing variables with same name
on different modules. The target application (Simulator) contains three modules namely ASAMPLE1.
A51, ASAMPLE2.A51 and ASAMPLE3.A51 (The file extension .A51 is the .asm extension specific to A51
assembler). The modules ASAMPLE2.A51 and ASAMPLE3.A51 contain a function named PUTCHAR. Both
of these modules try to export this function by declaring the function as ‘PUBLIC’ in the respective modules.
While linking the modules, the linker identifies that two modules are exporting the function with name
PUTCHAR. This confuses the linker and it throws the error ‘MULTIPLE PUBLIC DEFINITIONS’.
Build target ‘Simulator’
assembling ASAMPLE1.A51...
assembling ASAMPLE2.A51...
assembling ASAMPLE3.A51...
linking...
*** ERROR L104: MULTIPLE PUBLIC DEFINITIONS
SYMBOL: PUTCHAR
MODULE: [Link] (CHAR_IO)
If a variable or function declared as ‘EXTRN’ in one or two modules, there should be one module defining
these variables or functions and exporting them using ‘PUBLIC’ keyword. If no modules in a project export
the variables or functions which are declared as ‘EXTRN’ in other modules, it will generate ‘linker’ warnings
or errors depending on the error level/warning level settings of the linker.
Illustrative example for A51 Assembler–Usage of EXTRN without variables exported. The target
application (Simulator) contains three modules, namely, ASAMPLE1.A51, ASAMPLE2.A51 and ASAMPLE3.
A51 (The file extension .A51 is the .asm extension specific to A51 assembler). The modules ASAMPLE1.A51
imports a function named PUT_CRLF which is declared as ‘EXTRN’ in the current module and it expects any
of the other two modules to export it using the keyword ‘PUBLIC’. But none of the other modules export this
function by declaring the function as ‘PUBLIC’ in the respective modules. While linking the modules, the
linker identifies that there is no function exporting for this function. The linker generates a warning or error
message ‘UNRESOLVED EXTERNAL SYMBOL’ depending on the linker ‘level’ settings.
Embedded Firmware Design and Development 317
you master one processor architecture and its assembly instructions, you can make the processor as flexible
as a gymnast. The major advantages of Assembly Language based development is listed below.
Efficient Code Memory and Data Memory Usage (Memory Op misa on) Since the developer is well
versed with the target processor architecture and memory organisation, optimised code can be written
for performing operations. This leads to less utilisation of code memory and efficient utilisation of data
memory. Remember memory is a primary concern in any embedded product (Though silicon is cheaper and
new memory techniques make memory less costly, external memory operations impact directly on system
performance).
High Performance Optimised code not only improves the code memory usage but also improves the total
system performance. Through effective assembly coding, optimum performance can be achieved for a target
application.
Low Level Hardware Access Most of the code for low level programming like accessing external device
specific registers from the operating system kernel, device drivers, and low level interrupt routines, etc. are
making use of direct assembly coding since low level device specific operation support is not commonly
available with most of the high-level language cross compilers.
Code Reverse Engineering Reverse engineering is the process of understanding the technology behind a
product by extracting the information from a finished product. Reverse engineering is performed by ‘hackers’
to reveal the technology behind ‘Proprietary Products’. Though most of the products employ code memory
protection, if it may be possible to break the memory protection and read the code memory, it can easily be
converted into assembly code using a dis-assembler program for the target machine.
[Link] Drawbacks of Assembly Language Based Development
Every technology has its own pros and cons. From certain technology aspects assembly language development
is the most efficient technique. But it is having the following technical limitations also.
High Development Time Assembly language is much harder to program than high level languages. The
developer must pay attention to more details and must have thorough knowledge of the architecture, memory
organisation and register details of the target processor in use. Learning the inner details of the processor
and its assembly instructions is highly time consuming and it creates a delay impact in product development.
One probable solution for this is use a readily available developer who is well versed in the target processor
architecture assembly instructions. Also more lines of assembly code are required for performing an action
which can be done with a single instruction in a high-level language like ‘C’.
Developer Dependency There is no common written rule for developing assembly language based
applications whereas all high level languages instruct certain set of rules for application development. In
assembly language programming, the developers will have the freedom to choose the different memory
location and registers. Also the programming approach varies from developer to developer depending on his/
her taste. For example moving data from a memory location to accumulator can be achieved through different
approaches. If the approach done by a developer is not documented properly at the development stage, he/
she may not be able to recollect why this approach is followed at a later stage or when a new developer is
instructed to analyse this code, he/she also may not be able to understand what is done and why it is done.
Hence upgrading an assembly program or modifying it on a later stage is very difficult. Well documenting
the assembly code is a solution for reducing the developer dependency in assembly language programming.
If the code is too large and complex, documenting all lines of code may not be productive.
Embedded Firmware Design and Development 319
Non-Portable Target applications written in assembly instructions are valid only for that particular family
of processors (e.g. Application written for Intel x86 family of processors) and cannot be re-used for another
target processors/controllers (Say ARM Cortex M family of processors). If the target processor/controller
changes, a complete re-writing of the application using the assembly instructions for the new target processor/
controller is required. This is the major drawback of assembly language programming and it makes the
assembly language applications non-portable.
“Though Assembly Language programming possesses lots of drawback, as a developer, from my
personal experience I prefer assembly language based development. Once you master the internals of
a processor/controller, you can really perform magic with the processor/controller and can extract the
maximum out of it.”
Library Files
Source File 1
Module
(.c /.c++ etc.) Object File 1
Cross-compiler
(Module-1)
Source File 2
Module
(.c /.c++ etc.) Object File 2
Cross-compiler
(Module-2)
Machine Code
(Hex File)
Fig. 9.2 High level language to machine language conversion process
Portability Target applications written in high level languages are converted to target processor/controller
understandable format (machine codes) by a cross-compiler. An application written in high level language
for a particular target processor can easily be converted to another target processor/controller specific
application, with little or less effort by simply re-compiling/little code modification followed by re-compiling
the application for the required target processor/controller, provided, the cross-compiler has support for the
processor/controller selected. This makes applications written in high level language highly portable. Little
effort may be required in the existing code to replace the target processor specific header files with new
header files, register definitions with new ones, etc. This is the major flexibility offered by high level language
based design.
[Link] Limita ons of High Level Language Based Development
The merits offered by high level language based design take advantage over its limitations. Some cross-
compilers available for high level languages may not be so efficient in generating optimised target processor
specific instructions. Target images created by such compilers may be messy and non-optimised in terms
of performance as well as code size. For example, the task achieved by cross-compiler generated machine
instructions from a high level language may be achieved through a lesser number of instructions if the same
task is hand coded using target processor specific machine codes. The time required to execute a task also
increases with the number of instructions. However modern cross-compilers are tending to adopt designs
incorporating optimisation techniques for both code size and performance. High level language based code
snippets may not be efficient in accessing low level hardware where hardware access timing is critical (of the
order of nano or micro seconds).
The investment required for high level language based development tools (Integrated Development
Environment incorporating cross-compiler) is high compared to Assembly Language based firmware
development tools.
compiler you are using. Different cross compilers implement these features in different ways depending on
the general purpose registers and the memory supported by the target processor/controller. Let’s examine this
by taking Keil C51 cross compiler for 8051 controller. The objective of this example is to give an idea on how
C51 cross compiler performs the mixing of Assembly code with ‘C’.
1. Write a simple function in C that passes parameters and returns values the way you want your assembly
routine to.
2. Use the SRC directive (#PRAGMA SRC at the top of the file) so that the C compiler generates an .SRC
file instead of an .OBJ file.
3. Compile the C file. Since the SRC directive is specified, the .SRC file is generated. The .SRC file
contains the assembly code generated for the C code you wrote.
4. Rename the .SRC file to .A51 file.
5. Edit the .A51 file and insert the assembly code you want to execute in the body of the assembly function
shell included in the .A51 file.
As an example consider the following sample code (Extracted from Keil C51 documentation)
#pragma SRC
unsigned char my_assembly_func (unsigned int argument)
{
return (argument + 1); // Insert dummy lines to access all args and
// retvals
}
This C function on cross compilation generates the following assembly SRC file.
NAME TESTCODE
?PR?_my_assembly_func?TESTCODE SEGMENT CODE
PUBLIC _my_assembly_func
; #pragma SRC
; unsigned char my_assembly_func (
RSEG ?PR?_my_assembly_func?TESTCODE
USING 0
_my_assembly_func:
;---- Variable ‘argument?040’ assigned to Register ‘R6/R7’ ----
; SOURCE LINE # 2
; unsigned int argument)
; {
; SOURCE LINE # 4
; return (argument + 1); // Insert dummy lines to access all args
; and retvals
; SOURCE LINE # 5
MOV A,R7
INC A
MOV R7,A
; }
; SOURCE LINE # 6
?C0001:
RET
; END OF _my_assembly_func
END
Embedded Firmware Design and Development 323
The special compiler directive SRC generates the Assembly code corresponding to the ‘C’ function and
each lines of the source code is converted to the corresponding Assembly instruction. You can easily identify
the Assembly code generated for each line of the source code since it is implicitly mentioned in the generated
.SRC file. By inspecting this code segments you can find out which registers are used for holding the variables
of the ‘C’ function and you can modify the source code by adding the assembly routine you want.
[Link] Mixing High Level Language with Assembly (e.g. ‘C’ with Assembly Language)
Mixing the code written in a high level language like ‘C’ and Assembly language is useful in the following
scenarios:
1. The source code is already available in Assembly language and a routine written in a high level language
like ‘C’ needs to be included to the existing code.
2. The entire source code is planned in Assembly code for various reasons like optimised code, optimal
performance, efficient code memory utilisation and proven expertise in handling the Assembly, etc.
But some portions of the code may be very difficult and tedious to code in Assembly. For example
16bit multiplication and division in 8051 Assembly Language.
3. To include built in library functions written in ‘C’ language provided by the cross compiler. For
example Built in Graphics library functions and String operations supported by ‘C’.
Most often the functions written in ‘C’ use parameter passing to the function and returns value/s to the
calling functions. The major question that needs to be addressed in mixing a ‘C’ function with Assembly is
that how the parameters are passed to the function and how values are returned from the function and how the
function is invoked from the assembly language environment. Parameters are passed to the function and values
are returned from the function using CPU registers, stack memory and fixed memory. Its implementation is
cross compiler dependent and it varies across cross compilers. A typical example is given below for the Keil
C51 cross compiler
C51 allows passing of a maximum of three arguments through general purpose registers R2 to R7. If the
three arguments are char variables, they are passed to the function using registers R7, R6 and R5 respectively.
If the parameters are int values, they are passed using register pairs (R7, R6), (R5, R4) and (R3, R2). If the
number of arguments is greater than three, the first three arguments are passed through registers and rest is
passed through fixed memory locations. Refer to C51 documentation for more details. Return values are
usually passed through general purpose registers. R7 is used for returning char value and register pair (R7,
R6) is used for returning int value. The ‘C’ subroutine can be invoked from the assembly program using the
subroutine call Assembly instruction (Again cross compiler dependent).
E.g. LCALL _Cfunction
Where Cfunction is a function written in ‘C’. The prefix _ informs the cross compiler that the parameters to
the function are passed through registers. If the function is invoked without the _ prefix, it is understood that
the parameters are passed through fixed memory locations.
[Link] Inline Assembly
Inline assembly is another technique for inserting target processor/controller specific Assembly instructions at
any location of a source code written in high level language ‘C’. This avoids the delay in calling an assembly
routine from a ‘C’ code (If the Assembly instructions to be inserted are put in a subroutine as mentioned in the
section mixing assembly with ‘C’). Special keywords are used to indicate that the start and end of Assembly
instructions. The keywords are cross-compiler specific. C51 uses the keywords #pragma asm and #pragma
endasm to indicate a block of code written in assembly.
324 Introduc on to Embedded Systems
Important Note:
The examples used for illustration throughout the section Mixing Assembly & High Level Language is Keil
C51 cross compiler specific. The operation is cross compiler dependent and it varies from cross compiler
to cross compiler. The intention of the author is just to give an overall idea about the mixing of Assembly
code and High level language ‘C’ in writing embedded programs. Readers are advised to go through the
documentation of the cross compiler they are using for understanding the procedure adopted for the cross
compiler in use.
And…. The list continues. There are thousands of IDEs available in the market as either commercial or non-
commercial and as either Open source tools or proprietary tools. Listing all of them is out of the scope of this
book. The intention is to just make the readers familiar with some of the popular IDEs for some commonly
used processors/controllers and RTOSs for embedded development.
Page Header A header on each page of the listing file which indicates the compiler version number, source
file name, date, time, and page number.
C51 COMPILER V9.53.0.0 SAMPLE 10/16/2014 15:47:10 PAGE 1
Command Line Represents the entire command line that was used for invoking the compiler.
C51 COMPILER V9.53.0.0, COMPILATION OF MODULE SAMPLE OBJECT MODULE PLACED
IN [Link]
COMPILER INVOKED BY: C:\Keil_v5\C51\BIN\[Link] sample.c OPTIMISE(8,SPEED)
BROWSE DEBUG OBJECTEXTEND CODE LISTINCLUDE SYMBOLS TABS(2) PREPRINT
Source Code The source code listing outputs the line number as well as the source code on that line. Special
cross compiler directives can be used to include or exclude the conditional codes (code in #if blocks) in the
source code listings. Apart from the source code lines, the list file will include the comments in the source file
and depending on the list file generation settings the entire contents of all include files may also be included.
Special cross compiler directives can be used to include the entire contents of the include file in the list file.
line level source
1 //Sample.c for printing Hello World!
2 //Written by xyz
3 #include <stdio.h>
1 =1 /*--------------------------------------------------------------------------
2 =1 STDIO.H
3 =1
4 =1 Prototypes for standard I/O functions.
5 =1 Copyright © 1988–2002 Keil Elektronik GmbH and Keil Software, Inc.
6 =1 All rights reserved.
7 =1 --------------------------------------------------------------------------*/
8 =1
9 =1 #ifndef __STDIO_H__
10 =1 #define __STDIO_H__
11 =1
12 =1 #ifndef EOF
13 =1 #define EOF -1
14 =1 #endif
15 =1
16 =1 #ifndef NULL
17 =1 #define NULL ((void *) 0)
18 =1 #endif
19 =1
20 =1 #ifndef _SIZE_T
21 =1 #define _SIZE_T
22 =1 typedef unsigned int size_t;
23 =1 #endif
24 =1
25 =1 #pragma SAVE
26 =1 #pragma REGPARMS
27 =1 extern char _getkey (void);
600 Introduc on to Embedded Systems
process. Hence it is obvious that the code generated by the cross-compiler is not executable without linking
it for resolving external references.
Command Line Represents the entire command line that was used for invoking the linker.
e.g. BL51 BANKED LINKER/LOCATER V6.22, INVOKED BY: C:\KEIL_V5\C51\
BIN\[Link] [Link], [Link] TO Sample
CPU Details Details about the target CPU and memory model (internal data memory, external data memory,
paged data memory, etc.) come under this category.
e.g. MEMORY MODEL: SMALL
Input Modules This section includes the names of all object modules, and library files and modules that are
included in the linking process. This section can be checked for ensuring all the required modules are lined
in the linking process
e.g.
INPUT MODULES INCLUDED:
[Link] (?C_STARTUP)
[Link] (SAMPLE)
C:\KEIL_V5\C51\LIB\[Link] (PRINTF)
C:\KEIL_V5\C51\LIB\[Link] (?C?CLDPTR)
C:\KEIL_V5\C51\LIB\[Link] (?C?CLDOPTR)
C:\KEIL_V5\C51\LIB\[Link] (?C?CSTPTR)
C:\KEIL_V5\C51\LIB\[Link] (?C?PLDIIDATA)
C:\KEIL_V5\C51\LIB\[Link] (?C?CCASE)
C:\KEIL_V5\C51\LIB\[Link] (PUTCHAR)
Memory Map Memory map lists the starting address, length, relocation type and name of each segment in
the program.
The Embedded System Development Environment 603
e.g.
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * ****** D A T A M E M O R Y * * * * * * ********
REG 0000H 0008H ABSOLUTE “REG BANK 0”
DATA 0008H 0014H UNIT _DATA_GROUP_
001CH 0004H *** GAP ***
BIT 0020H.0 0001H.1 UNIT _BIT_GROUP_
0021H.1 0000H.7 *** GAP ***
IDATA 0022H 0001H UNIT ?STACK
* * * * * * * ***** C O D E M E M O R Y * * * * * * ********
CODE 0000H 0003H ABSOLUTE
0003H 07FDH *** GAP ***
CODE 0800H 035CH UNIT ?PR?PRINTF?PRINTF
CODE 0B5CH 008EH UNIT ?C?LIB_CODE
CODE 0BEAH 0027H UNIT ?PR?PUTCHAR?PUTCHAR
CODE 0C11H 000DH UNIT ?CO?SAMPLE
CODE 0C1EH 000CH UNIT ?C_C51STARTUP
CODE 0C2AH 0009H UNIT ?PR?MAIN?SAMPLE
Symbol Table It contains the value, type and name for all symbols from the different input modules
e.g.
SYMBOL TABLE OF MODULE: sample (?C_STARTUP)
Program Size Program size information contain the size of various memory areas as well as constant and
code space for the entire application
e.g. Program Size: data=30.1 xdata=0 code=1078
Warnings and Errors Errors and warnings generated while linking a program are written to this section. It
is very useful in debugging link errors.
e.g. LINK/LOCATE RUN COMPLETE. 0 WARNING(S), 0 ERROR(S)
NB: The file extension for MAP files generated by different linkers/locaters need not be the same. It varies
across linker/locater in use. For example the map file generated for BL51 Linker/locater is with extension
.M51
Field Description
: The colon indicating the start of every Intel HEX record
ll: Record length field representing the number of data bytes (dd) in the record
aaaa: Address field representing the starting address for subsequent data in the record
tt: Field indicating the HEX record type. According to its value it can be of the following types
00: Data Record
01: End of File Record
02: 8086 Segment Address Record
04: Extended Linear Address record
dd: Data field that represents one byte of data. A record can have number of data bytes. The number
of data bytes in the record must match to the number specified by the‘ll’ field
The Embedded System Development Environment 605
cc: Checksum field representing the checksum of the record. Checksum is calculated by adding the
values of all hexadecimal digit pairs in the record and taking modulo 256. Resultant o/p is 2’s
complemented to get the checksum.
An extract from the Intel hex file generated for “Hello World” application example is given below.
:03000000020C1FD0
:0C0C1F00787FE4F6D8FD758121020C2BD3
:0E0C110048656C6C6F20576F726C64210A008E
:090C2B007BFF7A0C7911020862CA
:10080000E517240BF8E60517227808300702780B65
:10081000E475F001120BB4020B5C2000EB7F2ED2CA
:10082000008018EF540F2490D43440D4FF30040BD0
:10083000EF24BFB41A0050032461FFE518600215CD
:1008400018051BE51B7002051A30070D7808E475C2
:100BFA00B8130CC2983098FDA899C298B811F6306B
:070C0A0099FDC299F5992242
:00000001FF
Let’s analyse the first record
: l l a a a a t t d d d d d d c c
: 0 3 0 0 0 0 0 0 0 2 0 C 1 F D 0
: field indicates the start of a new record. 03 (ll) gives the number of data bytes in the record. For this
record,‘ll’ is 03 and the number of data bytes in the corresponding record is 03. The start address (aaaa) of
data in the record is 0000H. The record type byte (tt) for this record is 00 and it indicates that this record is a
data record. The data for the above record is 02, 0C and 1F. They are supposed to place at three consecutive
memory locations in the EEPROM with starting address 0000H. The arrangement is given below.
: l l a a a a t t c c
: 0 0 0 0 0 0 0 1 F F
End of record also starts with the start of record symbol ‘:’. Since End of record does not contain any data
bytes, field ‘ll’ will be 00. The field ‘aaaa’ is not significant since the number of data bytes are zero. Field ‘tt’
606 Introduc on to Embedded Systems
will hold the value 01 to indicate that this record is an End of record. Field ‘cc’ holds the checksum of all the
bytes present in the record and it is calculated as 2’s complement of Modulo 256 of (0 + 0 + 0 + 1) = 0xFF
[Link] Motorola HEX File Format
Similar to the Intel HEX file, Motorola HEX file is also an ASCII text file where the HEX data is represented
in ASCII format in lines. The lines in Motorola HEX file represent a HEX Record. Each record is made up
of hexadecimal numbers that represent machine-language code and/or constant data. The general form of
Motorola Hex record is given below.
SOR RT Length Start Address Data/Code Checksum
In other words it can be represented as Stllaaaaddddd…cc
The fields of the record are explained below.
Field Description
SOR Stands for Start of record. The ASCII Character ‘S’ is used as the Start of Record. Every
record begins with the character ‘S’
RT Stands for Record type. The character‘t’ represents the type of record in the general for-
mat. There are different meanings for the record depending on the value of ‘t’
0: Header. Indicates the beginning of Hex File
1: Data Record with 16bit start address
2: Data record with 24bit start address
9: End of File Record
Length (ll): Stands for the count of the character pairs in the record, excluding the type and record length
(Count includes the number of data/code bytes, data bytes representing start address and
character pair representing the checksum). Two ASCII characters ‘ll’ represent the length
field .Each ‘l’ in the representation can take values 0 to 9 and A to F.
Start Address (aaaa): Address field representing the starting address for subsequent data in the record.
Code/Data (dd): Data field that represents one byte of data. A record can have number of data bytes. The
number of data bytes in the record must match to the number specified by (ll - no. of char-
acter pairs for start address–1)
Checksum (cc): Checksum field representing the checksum of the record. Checksum is calculated by adding
the values of all hexadecimal digit pairs in the record and taking modulo 256. Resultant o/p
is 1’s complemented to get the checksum.
Typical example of a Motorola Hex File format is given below.
S011000064656D6F5F68637331322E616273E5
S11311000002000800082629001853812341001812
S9030000FC
You can see that each Record starts with the ASCII character ‘S’. For the first record, the value for field
‘t’ is 0 and it implies that this record is the first record in the hex file (Header Record). The second record is
a data record. The field ‘t’ for second record is 1. Number of character pairs held by second record is 0x13
(19 in decimal). Out of this two character pairs are used for holding the start address and one character pair
for holding the checksum. Rest 16 bytes are the data bytes. The start address for placing the data bytes is
0x1100. The data bytes that are going to be placed in 16 consecutive memory locations starting from address
0x1100 are 0x00, 0x02, 0x00, 0x08, 0x00, 0x08, 0x26, 0x29, 0x00, 0x18, 0x53, 0x81, 0x23, 0x41, 0x00 and
0x18. The last two digits (here 0x12) represent the checksum of the record. Checksum is calculated as the
The Embedded System Development Environment 607
least significant byte of the one’s complement of the sum of the values represented by the pairs of characters
making up the record length, address, and data fields. The third record represents the End of File record. The
value for field ‘t’ for this record is 9 and it is an indicative of End of Hex file. Number of character pairs held
by this record is 03; two for address and one for the checksum. The address is insignificant here since the
record does not contain any values to dump into memory. Only one End of File Record is allowed per file
and it must be the last line of the file.
13.3 DISASSEMBLER/DECOMPILER
Disassembler is a utility program which converts machine codes into target
LO 3 Discuss about
processor specific Assembly codes/instructions. The process of converting
disassembler and
machine codes into Assembly code is known as ‘Disassembling’.
decompiler, and their
In operation, disassembling is complementary to assembling/cross-
role in embedded
assembling. Decompiler is the utility program for translating machine codes
firmware development
into corresponding high level language instructions. Decompiler performs
the reverse operation of compiler/cross-compiler. The disassemblers/
decompilers for different family of processors/controllers are different. Disassemblers/Decompilers are
deployed in reverse engineering. Reverse engineering is the process of revealing the technology behind the
working of a product. Reverse engineering in Embedded Product development is employed to find out the secret
behind the working of popular proprietary products. Disassemblers/decompilers help the reverse-engineering
process by translating the embedded firmware into Assembly/high level language instructions. Disassemblers/
Decompilers are powerful tools for analysing the presence of malicious codes (virus information) in an
executable image. Disassemblers/Decompilers are available as either freeware tools readily available for free
download from internet or as commercial tools. It is not possible for a disassembler/decompiler to generate
an exact replica of the original assembly code/high level source code in terms of the symbolic constants and
comments used. However disassemblers/decompilers generate a source code which is somewhat matching to
the original source code from which the binary code is generated.
13.4.1 Simulators
In a previous section of this chapter, describing the Integrated Development Environment, we discussed about
simulators for embedded firmware debugging. Simulators simulate the target hardware and the firmware
execution can be inspected using simulators. The features of simulator based debugging are listed below.
608 Introduc on to Embedded Systems
Why is debugging required? Well the counter question why you go for diagnosis when you are ill answers
this query. Firmware debugging is performed to figure out the bug or the error in the firmware which creates
the unexpected behaviour. Firmware is analogous to the human body in the sense it is widespread and/or
modular. Any abnormalities in any area of the body may lead to sickness. How is the region causing illness
identified correctly when you are sick? If we look back to the 1900s, where no sophisticated diagnostic
techniques were available, only a skilled doctor was capable of identifying the root cause of illness, that
too with his solid experience. Now, with latest technologies, the scenario is totally changed. Sophisticated
diagnostic techniques provide offline diagnosis like Computerised Tomography (CT), MRI and ultrasound
scans and online diagnosis like micro camera based imaging techniques. With the intrusion of a micro camera
into the body, the doctors can view the internals of the body in real time.
During the early days of embedded system development, there were no debug tools available and the only
way was “Burn the code in an EEPROM and pray for its proper functioning”. If the firmware does not crash,
the product works fine. If the product crashes, the developer is unlucky and he needs to sit back and rework
on the firmware till the product functions in the expected way. Most of the time the developer had to seek the
help of an expert to figure out the exact problem creator. As technology has achieved a new dimension from
the early days of embedded system development, various types of debugging techniques are available today.
The following section describes the improvements over firmware debugging starting from the most primitive
type of debugging to the most sophisticated On Chip Debugging (OCD).
[Link] Incremental EEPROM Burning Technique
This is the most primitive type of firmware debugging technique where the code is separated into different
functional code units. Instead of burning the entire code into the EEPROM chip at once, the code is burned in
incremental order, where the code corresponding to all functionalities are separately coded, cross-compiled
and burned into the chip one by one. The code will incorporate some indication support like lighting up an
“LED (every embedded product contains at least one LED). If not, you should include provision for at least
one LED in the target board at the hardware design time such that it can be used for debugging purpose)” or
activate a “BUZZER (In a system with BUZZER support)” if the code is functioning in the expected way. If
the first functionality is found working perfectly on the target board with the corresponding code burned into
the EEPROM, go for burning the code corresponding to the next functionality and check whether it is working.
Repeat this process till all functionalities are covered. Please ensure that before entering into one level up,
the previous level has delivered a correct result. If the code corresponding to any functionality is found not
giving the expected result, fix it by modifying the code and then only go for adding the next functionality for
burning into the EEPROM. After you found all functionalities working properly, combine the entire source
for all functionalities together, re-compile and burn the code for the total system functioning.
Obviously it is a time-consuming process. But remember it is a onetime process and once you test
the firmware in an incremental model you can go for mass production. In incremental firmware burning
technique we are not doing any debugging but observing the status of firmware execution as a debug method.
The very common mistake committed by firmware developers in developing non-operating system-based
embedded application is burning the entire code altogether and fed up with debugging the code. Please don’t
adopt this approach. Even though you need to spend some additional time on incremental burning approach,
you will never lose in the process and will never mess up with debugging the code. You will be able to
figure out at least ‘on which point of firmware execution the issue is arising’–“A stitch in time saves nine”.
Incremental firmware burning technique is widely adopted in small, simple system developments and in
product development where time is not a big constraint (e.g. R&D projects). It is also very useful in product
development environments where no other debug tools are available.
610 Introduc on to Embedded Systems
Target CPU
Monitor ROM
Debugger RS-232 Serial link
application
Host PC
Target board
Von-Neumann RAM
The monitor program usually resides at the reset vector (code memory 0000H) of the target processor.
The monitor program is commonly employed in development boards and the development board supplier
provides the monitor program in the form of a ROM chip. The actual code memory is downloaded into a
RAM chip which is interfaced to the processor in the Von-Neumann architecture model. The Von-Neumann
architecture model is achieved by ANDing the PSEN\ and RD\ signals of the target processor (In case of
8051) and connecting the output of AND Gate to the Output Enable (RD\) pin of RAM chip. WR\ signal of
the target processor is interfaced to The WR\ signal of the Von Neumann RAM. Monitor ROM size varies
in the range of a few kilo bytes. An address decoder circuit maps the address range allocated to the monitor
ROM and activates the Chip Select (CS\) of the ROM if the address is within the range specified for the
Monitor ROM. A user program is normally loaded at locations 0x4000 or 0x8000. The address decoder
circuit ensures the enabling of the RAM chip (CS\) when the address range is outside that allocated to the
ROM monitor. Though there are two memory chips (Monitor ROM Chip and Von-Neumann RAM), the
total memory map available for both of them will be 64K for a processor/controller with 16bit address space
and the memory decoder units take care of avoiding conflicts in accessing both. While developing user
program for monitor ROM-based systems, special care should be taken to offset the user code and handling
the interrupt vectors. The target development IDE will help in resolving this. During firmware execution and
single stepping, the user code may have to be altered and hence the firmware is always downloaded into a
Von-Neumann RAM in monitor ROM-based debugging systems. Monitor ROM-based debugging is suitable
only for development work and it is not a good choice for mass produced systems. The major drawbacks of
monitor based debugging system are
1. The entire memory map is converted into a Von-Neumann model and it is shared between the monitor
ROM, monitor program data memory, monitor program trace buffer, user written firmware and
external user memory. For 8051, the original Harvard architecture supports 64K code memory and
64K external data memory (Total 128K memory map). Going for a monitor based debugging shrinks
the total available memory to 64K Von-Neumann memory and it needs to accommodate all kinds of
memory requirement (Monitor Code, monitor data, trace buffer memory, User code and External User
data memory).
2. The communication link between the debug application running on Development PC and monitor
program residing in the target system is achieved through a serial link and usually the controller’s On-
chip UART is used for establishing this link. Hence one serial port of the target processor becomes
dedicated for the monitor application and it cannot be used for any other device interfacing. Wastage
of a serial port! It is a serious issue in controllers or processors with single UART.
[Link] In Circuit Emulator (ICE) Based Firmware Debugging
The terms ‘Simulator’ and ‘Emulator’ are little bit confusing and sounds similar. Though their basic
functionality is the same – “Debug the target firmware”, the way in which they achieve this functionality is
totally different. As mentioned before, ‘Simulator’ is a software application that precisely duplicates (mimics)
the target CPU and simulates the various features and instructions supported by the target CPU, whereas
an ‘Emulator’ is a self-contained hardware device which emulates the target CPU. The emulator hardware
contains necessary emulation logic and it is hooked to the debugging application running on the development
PC on one end and connects to the target board through some interface on the other end. In summary, the
simulator ‘simulates’ the target board CPU and the emulator ‘emulates’ the target board CPU.
There is a scope change that has happened to the definition of an emulator. In olden days emulators
were defined as special hardware devices used for emulating the functionality of a processor/controller
and performing various debug operations like halt firmware execution, set breakpoints, get or set internal
RAM/CPU register, etc. Nowadays pure software applications which perform the functioning of a hardware
The Embedded System Development Environment 613
emulator is also called as ‘Emulators’ (though they are ‘Simulators’ in operation). The emulator application
for emulating the operation of a PDA phone for application development is an example of a ‘Software
Emulator’. A hardware emulator is controlled by a debugger application running on the development PC. The
debugger application may be part of the Integrated Development Environment (IDE) or a third party supplied
tool. Most of the IDEs incorporate debugger support for some of the emulators commonly available in the
market. The emulators for different families of processors/controllers are different. Figure 13.40 illustrates
the different subsystems and interfaces of an ‘Emulator’ device.
In Circuit Emulator
Signal lines
PC COM/USB port
(Flat Cable)
PC Target Board
The Emulator POD forms the heart of any emulator system and it contains the following functional
units.
Emula on Device Emulation device is a replica of the target CPU which receives various signals from the
target board through a device adaptor connected to the target board and performs the execution of firmware
under the control of debug commands from the debug application. The emulation device can be either a
standard chip same as the target processor (e.g. AT89C51) or a Programmable Logic Device (PLD) configured
to function as the target CPU. If a standard chip is used as the emulation device, the emulation will provide
real-time execution behaviour. At the same time the emulator becomes dedicated to that particular device and
cannot be re-used for the derivatives of the same chip. PLD-based emulators can easily be re-configured to
use with derivatives of the target CPU under consideration. By simply loading the configuration file of the
derivative processor/controller, the PLD gets re-configured and it functions as the derivative device. A major
drawback of PLD-based emulator is the accuracy of replication of target CPU functionalities. PLD-based
emulator logic is easy to implement for simple target CPUs but for complex target CPUs it is quite difficult.
Emula on Memory It is the Random Access Memory (RAM) incorporated in the Emulator device. It acts
as a replacement to the target board’s EEPROM where the code is supposed to be downloaded after each
firmware modification. Hence the original EEPROM memory is emulated by the RAM of emulator. This is
known as ‘ROM Emulation’. ROM emulation eliminates the hassles of ROM burning and it offers the benefit
of infinite number of reprogrammings (Most of the EEPROM chips available in the market supports only
a few 1000 re-program cycles). Emulation memory also acts as a trace buffer in debugging. Trace buffer
is a memory pool holding the instructions executed/registers modified/related data by the processor while
debugging. The trace buffer size is emulator dependent and the trace buffer holds the recent trace information
614 Introduc on to Embedded Systems
when the buffer overflows. The common features of trace buffer memory and trace buffer data viewing are
listed below:
∑ Trace buffer records each bus cycle in frames
∑ Trace data can be viewed in the debugger application as Assembly/Source code
∑ Trace buffering can be done on the basis of a Trace trigger (Event)
∑ Trace buffer can also record signals from target board other than CPU signals (Emulator dependent)
∑ Trace data is a very useful information in firmware debugging
Emulator Control Logic Emulator control logic is the logic circuits used for implementing complex
hardware breakpoints, trace buffer trigger detection, trace buffer control, etc. Emulator control logic circuits
are also used for implementing logic analyser functions in advanced emulator devices. The ‘Emulator POD’
is connected to the target board through a ‘Device adaptor’ and signal cable.
Device Adaptors Device adaptors act as an interface between the target board and emulator POD. Device
adaptors are normally pin-to-pin compatible sockets which can be inserted/plugged into the target board
for routing the various signals from the pins assigned for the target processor. The device adaptor is usually
connected to the emulator POD using ribbon cables. The adaptor type varies depending on the target
processor’s chip package. DIP, PLCC, etc. are some commonly used adaptors.
The above-mentioned emulators are almost dedicated ones, meaning they are built for emulating a
specific target processor and have little or less support for emulating the derivatives of the target processor
for which the emulator is built. This type of emulators usually combines the entire emulation control logic
and emulation device (if present) in a single board. They are known as ‘Debug Board Modules (DBMs)’.
An alternative method of emulator design supports emulation of a variety of target processors. Here the
emulator hardware is partitioned into two, namely, ‘Base Terminal’ and ‘Probe Card’. The Base terminal
contains all the emulator hardware and emulation control logic except the emulation chip (Target board
CPU’s replica). The base terminal is connected to the Development PC for establishing communication with
the debug application. The emulation chip (Same chip as the target CPU) is mounted on a separate PCB
and it is connected to the base terminal through a ribbon cable. The ‘Probe Card’ board contains the device
adaptor sockets to plug the board into the target development board. The board containing the emulation chip
is known as the ‘Probe Card’. For emulating different target CPUs the ‘Probe Card’ will be different and the
base terminal remains the same. The manufacturer of the emulator supplies ‘Probe Card’ for different CPUs.
Though these emulators are capable of emulating different CPUs, the cost for ‘Probe Cards’ is very high.
Communication link between the emulator base unit/ Emulator POD and debug application is established
through a Serial/Parallel/USB interface. Debug commands and debug information are sent to and from the
emulator using this interface.
[Link] On Chip Firmware Debugging (OCD)
Advances in semiconductor technology has brought out new dimensions to target firmware debugging. Today
almost all processors/controllers incorporate built in debug modules called On Chip Debug (OCD) support.
Though OCD adds silicon complexity and cost factor, from a developer perspective it is a very good feature
supporting fast and efficient firmware debugging. The On Chip Debug facilities integrated to the processor/
controller are chip vendor dependent and most of them are proprietary technologies like Background Debug
Mode (BDM), OnCE, etc. Some vendors add ‘on chip software debug support’ through JTAG (Joint Test
Action Group) port. Processors/controllers with OCD support incorporate a dedicated debug module to the
existing architecture. Usually the on-chip debugger provides the means to set simple breakpoints, query
the internal state of the chip and single step through code. OCD module implements dedicated registers for
controlling debugging. An On Chip Debugger can be enabled by setting the OCD enable bit (The bit name
The Embedded System Development Environment 615
and register holding the bit varies across vendors). Debug related registers are used for debugger control
(Enable/disable single stepping, Freeze execution, etc.) and breakpoint address setting. BDM and JTAG are
the two commonly used interfaces to communicate between the Debug application running on Development
PC and OCD module of target CPU. Some interface logic in the form of hardware will be implemented
between the CPU OCD interface and the host PC to capture the debug information from the target CPU
and sending it to the debugger application running on the host PC. The interface between the hardware and
PC may be Serial/Parallel/USB. The following section will give you a brief introduction about Background
Debug Mode (BDM) and JTAG interface used in On Chip Debugging.
Background Debug Mode (BDM) interface is a proprietary On Chip Debug solution from Motorola.
BDM defines the communication interface between the chip resident debug core and host PC where the
BDM compatible remote debugger is running. BDM makes use of 10 or 26 pin connector to connect to the
target board. Serial data in (DSI), Serial data out (DSO) and Serial clock (DSCLK) are the three major signal
lines used in BDM. DSI sends debug commands serially to the target processor from the remote debugger
application and DSO sends the debug response to the debugger from the processor. Synchronisation of
serial transmission is done by the serial clock DSCLK generated by the debugger application. Debugging is
controlled by BDM specific debug commands. The debug commands are usually 17-bit wide. 16 bits are used
for representing the command and 1 bit for status/control.
Chips with JTAG debug interface contain a built-in JTAG port for communicating with the remote
debugger application. JTAG is the acronym for Joint Test Action Group. JTAG is the alternate name for
IEEE 1149.1 standard. Like BDM, JTAG is also a serial interface. The signal lines of JTAG protocol are
explained below.
Test Data In (TDI): It is used for sending debug commands serially from remote debugger to the target
processor.
Test Data Out (TDO): Transmit debug response to the remote debugger from target CPU.
Test Clock (TCK): Synchronises the serial data transfer.
Test Mode Select (TMS): Sets the mode of testing.
Test Reset (TRST): It is an optional signal line used for resetting the target CPU.
The serial data transfer rate for JTAG debugging is chip dependent. It is usually within the range of 10 to
1000 MHz.