0% found this document useful (0 votes)
4 views35 pages

Embedded System Design Module 4

This document provides an overview of embedded firmware design and development, outlining key learning objectives related to firmware steps, languages, and design approaches. It discusses the importance of firmware in controlling embedded hardware and details various design methodologies, including the Super Loop and OS-based approaches. Additionally, it covers the use of assembly and high-level languages in firmware development, highlighting their respective advantages and limitations.

Uploaded by

Tmkoc Fan boy
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views35 pages

Embedded System Design Module 4

This document provides an overview of embedded firmware design and development, outlining key learning objectives related to firmware steps, languages, and design approaches. It discusses the importance of firmware in controlling embedded hardware and details various design methodologies, including the Super Loop and OS-based approaches. Additionally, it covers the use of assembly and high-level languages in firmware development, highlighting their respective advantages and limitations.

Uploaded by

Tmkoc Fan boy
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

308 Introduc on to Embedded Systems

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.

9.1 EMBEDDED FIRMWARE DESIGN APPROACHES


The firmware design approaches for embedded product is purely dependent on
LO 1 Understand
the complexity of the functions to be performed, the speed of operation required,
the different
etc. Two basic approaches are used for Embedded firmware design. They are
steps involved in
‘Conventional Procedural Based Firmware Design’ and ‘Embedded Operating
the design and
System (OS) Based Design’. The conventional procedural based design is also
development
known as ‘Super Loop Model’. We will discuss each of them in detail in the
of firmware for
following sections.
embedded systems
9.1.1 The Super Loop Based Approach
The Super Loop based firmware development approach is adopted for applications that are not time critical
and where the response time is not so important (embedded systems where missing deadlines are acceptable).
It is very similar to a conventional procedural programming where the code is executed task by task. The
task listed at the top of the program code is executed first and the tasks just below the top are executed after
completing the first task. This is a true procedural one. In a multiple task based system, each task is executed
in serial in this approach. The firmware execution flow for this will be
1. Configure the common parameters and perform initialisation for various hardware components memory,
registers, etc.
310 Introduc on to Embedded Systems

2. Start the first task and execute it


3. Execute the second task
4. Execute the next task
5. :
6. :
7. Execute the last defined task
8. Jump back to the first task and follow the same flow
From the firmware execution sequence, it is obvious that the order in which the tasks to be executed are
fixed and they are hard coded in the code itself. Also the operation is an infinite loop based approach. We can
visualise the operational sequence listed above in terms of a ‘C’ program code as
void main ()
{
Configurations ();
Initialisations ();
while (1)
{
Task 1 ();
Task 2 ();
:
:
Task n ();
}
}
Almost all tasks in embedded applications are non-ending and are repeated infinitely throughout the
operation. From the above ‘C’ code you can see that the tasks 1 to n are performed one after another and when
the last task (nth task) is executed, the firmware execution is again re-directed to Task 1 and it is repeated
forever in the loop. This repetition is achieved by using an infinite loop. Here the while (1) { } loop. This
approach is also referred as ‘Super loop based Approach’.
Since the tasks are running inside an infinite loop, the only way to come out of the loop is either a
hardware reset or an interrupt assertion. A hardware reset brings the program execution back to the main
loop. Whereas an interrupt request suspends the task execution temporarily and performs the corresponding
interrupt routine and on completion of the interrupt routine it restarts the task execution from the point where
it got interrupted.
The ‘Super loop based design’ doesn’t require an operating system, since there is no need for scheduling
which task is to be executed and assigning priority to each task. In a super loop based design, the priorities
are fixed and the order in which the tasks to be executed are also fixed. Hence the code for performing these
tasks will be residing in the code memory without an operating system image.
This type of design is deployed in low-cost embedded products and products where response time is not
time critical. Some embedded products demands this type of approach if some tasks itself are sequential. For
example, reading/writing data to and from a card using a card reader requires a sequence of operations like
checking the presence of card, authenticating the operation, reading/writing, etc. it should strictly follow a
specified sequence and the combination of these series of tasks constitutes a single task-namely data read/
write. There is no use in putting the sub-tasks into independent tasks and running them parallel. It won’t work
at all.
Embedded Firmware Design and Development 311

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.

9.1.2 The Embedded Operating System (OS) Based Approach


The Operating System (OS) based approach contains operating systems, which can be either a General
Purpose Operating System (GPOS) or a Real Time Operating System (RTOS) to host the user written
application firmware. The General Purpose OS (GPOS) based design is very similar to a conventional PC
based application development where the device contains an operating system (Windows/Unix/ Linux, etc.
for Desktop PCs) and you will be creating and running user applications on top of it. Example of a GPOS used
in embedded product development is Microsoft® Windows Embedded 8.1 which offers customisation to use
with a range of industry devices like Handhelds, Point of Sale Terminals, Patient Monitoring Systems, etc.
Use of GPOS in embedded products merges the demarcation of Embedded Systems and general computing
systems in terms of OS. For Developing applications on top of the OS, the OS supported APIs are used.
Similar to the different hardware specific drivers, OS based applications also require ‘Driver software’ for
different hardware present on the board to communicate with them.
Real Time Operating System (RTOS) based design approach is employed in embedded products demanding
Real-time response. RTOS respond in a timely and predictable manner to events. Real Time operating system
contains a Real Time kernel responsible for performing pre-emptive multitasking, scheduler for scheduling tasks,
multiple threads, etc. A Real Time Operating System (RTOS) allows flexible scheduling of system resources
312 Introduc on to Embedded Systems

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.

9.2 EMBEDDED FIRMWARE DEVELOPMENT LANGUAGES


As mentioned in Chapter 2, you can use either a target processor/controller
LO 2 Discuss the specific language (Generally known as Assembly language or low level
different languages for language) or a target processor/controller independent language (Like
embedded firmware C, C++, JAVA, etc. commonly known as High Level Language) or a
development and the combination of Assembly and High level Language. We will discuss where
merits and limitations each of the approach is used and the relative merits and de-merits of each,
of each in the following sections.

9.2.1 Assembly Language based Development


‘Assembly language’ is the human readable notation of ‘machine language’, whereas ‘machine language’
is a processor understandable language. Processors deal only with binaries (1s and 0s). Machine language
is a binary representation and it consists of 1s and 0s. Machine language is made readable by using specific
symbols called ‘mnemonics’. Hence machine language can be considered as an interface between processor
and programmer. Assembly language and machine languages are processor/controller dependent and an
assembly program written for one processor/controller family will not work with others.
Assembly language programming is the task of writing processor specific machine code in mnemonic
form, converting the mnemonics into actual processor instructions (machine language) and associated
data using an assembler.
Assembly Language program was the most common type of programming adopted in the beginning of
software revolution. If we look back to the history of programming, we can see that a large number of
programs were written entirely in assembly language. Even in the 1990s, the majority of console video games
were written in assembly language, including most popular games written for the Sega Genesis and the
Super Nintendo Entertainment System. The popular arcade game NBA Jam released in 1993 was also coded
entirely using the assembly language.
Even today also almost all low level, system related, programming is carried out using assembly language.
Some Operating System dependent tasks require low-level languages. In particular, assembly language is
often used in writing the low level interaction between the operating system and the hardware, for instance
in device drivers.
The general format of an assembly language instruction is an Opcode followed by Operands. The Opcode
tells the processor/controller what to do and the Operands provide the data and information required to
perform the action specified by the opcode. It is not necessary that all opcode should have Operands following
them. Some of the Opcode implicitly contains the operand and in such situation no operand is required. The
operand may be a single operand, dual operand or more. We will analyse each of them with the 8051 ASM
instructions as an example.
MOV A, #30
Embedded Firmware Design and Development 313

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)

Object to Hex File Linker/


Absolute Object File
Converter Locator

Machine Code
(Hex File)

Fig. 9.1 Assembly language to machine language conversion process

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

*** WARNING L1: UNRESOLVED EXTERNAL SYMBOL


SYMBOL: PUT_CRLF
MODULE: [Link] (SAMPLE)

[Link] Library File Crea on and Usage


Libraries are specially formatted, ordered program collections of object modules that may be used by the
linker at a later time. When the linker processes a library, only those object modules in the library that are
necessary to create the program are used. Library files are generated with extension ‘.lib’. Library file is some
kind of source code hiding technique. If you don’t want to reveal the source code behind the various functions
you have written in your program and at the same time you want them to be distributed to application
developers for making use of them in their applications, you can supply them as library files and give them
the details of the public functions available from the library (function name, function input/output, etc). For
using a library file in a project, add the library to the project.
If you are using a commercial version of the assembler/compiler suite for your development, the vendor of
the utility may provide you pre-written library files for performing multiplication, floating point arithmetic,
etc. as an add-on utility or as a bonus☺.
‘LIB51’ from Keil Software is an example for a library creator and it is used for creating library files for
A51 Assembler/C51 Compiler for 8051 specific controller.
[Link] Linker and Locator
Linker and Locator is another software utility responsible for “linking the various object modules in a multi-
module project and assigning absolute address to each module”. Linker generates an absolute object module
by extracting the object modules from the library, if any and those obj files created by the assembler, which is
generated by assembling the individual modules of a project. It is the responsibility of the linker to link any
external dependent variables or functions declared on various modules and resolve the external dependencies
among the modules. An absolute object file or module does not contain any re-locatable code or data. All
code and data reside at fixed memory locations. The absolute object file is used for creating hex files for
dumping into the code memory of the processor/controller.
‘BL51’ from Keil Software is an example for a Linker & Locator for A51 Assembler/C51 Compiler for
8051 specific controller.
[Link] Object to Hex File Converter
This is the final stage in the conversion of Assembly language (mnemonics) to machine understandable
language (machine code). Hex File is the representation of the machine code and the hex file is dumped
into the code memory of the processor/controller. The hex file representation varies depending on the target
processor/controller make. For Intel processors/controllers the target hex file format will be ‘Intel HEX’
and for Motorola, the hex file should be in ‘Motorola HEX’ format. HEX files are ASCII files that contain
a hexadecimal representation of target application. Hex file is created from the final ‘Absolute Object File’
using the Object to Hex File Converter utility.
‘OH51’ from Keil software is an example for Object to Hex File Converter utility for A51 Assembler/C51
Compiler for 8051 specific controller.
[Link] Advantages of Assembly Language Based Development
Assembly Language based development was (is☺) the most common technique adopted from the beginning
of embedded technology development. Thorough understanding of the processor architecture, memory
organisation, register sets and mnemonics is very essential for Assembly Language based development. If
318 Introduc on to Embedded Systems

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.”

9.2.2 High Level Language Based Development


As we have seen in the earlier section, Assembly language based programming is highly time consuming,
tedious and requires skilled programmers with sound knowledge of the target processor architecture. Also
applications developed in Assembly language are non-portable. Here comes the role of high level languages.
Any high level language (like C, C++ or Java) with a supported cross compiler (for converting the application
developed in high level language to target processor specific assembly code – We will discuss cross-compilers
in detail in a later section) for the target processor can be used for embedded firmware development. The
most commonly used high level language for embedded firmware application development is ‘C’. You may
be thinking why ‘C’ is used as the popular embedded firmware development language. The answer is “C is
the well defined, easy to use high level language with extensive cross platform development tool support”.
Nowadays Cross-compilers for C++ is also emerging out and embedded developers are making use of C++
for embedded application development.
The various steps involved in high level language based embedded firmware development is same as
that of assembly language based development except that the conversion of source file written in high level
language to object file is done by a cross-compiler, whereas in Assembly language based development it is
carried out by an assembler. The various steps involved in the conversion of a program written in high level
language to corresponding binary file/machine language is illustrated in Fig. 9.2.
The program written in any of the high level language is saved with the corresponding language extension
(.c for C, .cpp for C++, etc). Any text editor like ‘notepad’ or ‘WordPad’ from Microsoft® or the text editor
provided by an Integrated Development (IDE) tool supporting the high level language in use can be used
for writing the program. Most of the high level languages support modular programming approach and
hence you can have multiple source files called modules written in corresponding high level language. The
source files corresponding to each module is represented by a file with corresponding language extension.
Translation of high level source code to executable object code is done by a cross-compiler. The cross-
compilers for different high level languages for the same target processor are different. It should be noted
that each high level language should have a cross-compiler for converting the high level source code into
the target processor machine code. Without cross-compiler support a high level language cannot be used
for embedded firmware development. C51 Cross-compiler from Keil software is an example for Cross-
compiler. C51 is a popular cross-compiler available for ‘C’ language for the 8051 family of micro controller.
Conversion of each module’s source code to corresponding object file is performed by the cross compiler.
Rest of the steps involved in the conversion of high level language to target processor’s machine code are
same as that of the steps involved in assembly language based development.
As an example of high level language based embedded firmware development, we will discuss how
‘Embedded C’ is used for embedded firmware development, in a later section of this chapter.
320 Introduc on to Embedded Systems

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)

Object to Hex File Linker/


Absolute Object File
Converter Locator

Machine Code
(Hex File)
Fig. 9.2 High level language to machine language conversion process

[Link] Advantages of High Level Language Based Development


Reduced Development Time Developer requires less or little knowledge on the internal hardware details
and architecture of the target processor/controller. Bare minimal knowledge of the memory organisation and
register details of the target processor in use and syntax of the high level language are the only pre-requisites
for high level language based firmware development. Rest of the things will be taken care of by the cross-
compiler used for the high level language. Thus the ramp up time required by the developer in understanding
the target hardware and target machine’s assembly instructions is waived off by the cross compiler and
it reduces the development time by significant reduction in developer effort. High level language based
development also refines the scope of embedded firmware development from a team of specialised architects
to anyone knowing the syntax of the language and willing to put little effort on understanding the minimal
hardware details. With high level language, each task can be accomplished by lesser number of lines of code
compared to the target processor/controller specific Assembly language based development.
Developer Independency The syntax used by most of the high level languages are universal and a program
written in the high level language can easily be understood by a second person knowing the syntax of the
language. Certain instructions may require little knowledge of the target hardware details like register set,
memory map etc. Apart from these, the high level language based firmware development makes the firmware,
developer independent. High level languages always instruct certain set of rules for writing the code and
commenting the piece of code. If the developer strictly adheres to the rules, the firmware will be 100%
developer independent.
Embedded Firmware Design and Development 321

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.

9.2.3 Mixing Assembly and High Level Language


Certain embedded firmware development situations may demand the mixing of high level language with
Assembly and vice versa. High level language and assembly languages are usually mixed in three ways;
namely, mixing Assembly Language with High Level Language, mixing High Level Language with Assembly
and In-line Assembly programming.
[Link] Mixing Assembly with High level language (e.g. Assembly Language with ‘C’)
Assembly routines are mixed with ‘C’ in situations where the entire program is written in ‘C’ and the cross
compiler in use do not have a built in support for implementing certain features like Interrupt Service Routine
functions (ISR) or if the programmer wants to take advantage of the speed and optimised code offered by
machine code generated by hand written assembly rather than cross compiler generated machine code. When
accessing certain low level hardware, the timing specifications may be very critical and a cross compiler
generated binary may not be able to offer the required time specifications accurately. Writing the hardware/
peripheral access routine in processor/controller specific Assembly language and invoking it from ‘C’ is the
most advised method to handle such situations.
Mixing ‘C’ and Assembly is little complicated in the sense—the programmer must be aware of how
parameters are passed from the ‘C’ routine to Assembly and values are returned from assembly routine to ‘C’
and how ‘Assembly routine’ is invoked from the ‘C’ code.
Passing parameter to the assembly routine and returning values from the assembly routine to the caller ‘C’
function and the method of invoking the assembly routine from ‘C’ code is cross compiler dependent. There
is no universal written rule for this. You must get these informations from the documentation of the cross
322 Introduc on to Embedded Systems

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

E.g. #pragma asm


MOV A, #13H
#pragma endasm

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.

9.3 PROGRAMMING IN EMBEDDED C


LO 3 Understand Whenever the conventional ‘C’ Language and its extensions are used for
the fundamentals programming embedded systems, it is referred as ‘Embedded C’ programming.
of embedded Programming in ‘Embedded C’ is quite different from conventional Desktop
firmware design application development using ‘C’ language for a particular OS platform. Desktop
using Embedded computers contain working memory in the range of Giga bytes and storage
‘C’ memory in the range of Giga/Tera bytes. For a desktop application developer, the
resources available are surplus in quantity and the developer is not restricted in
terms of the memory usage. This is not the case for embedded application developers. Almost all embedded
systems are limited in both storage and working memory resources. Embedded application developers should
be aware of this fact and should develop applications in the best possible way which optimises the code
memory and working memory usage as well as performance. In other words, the hands of an embedded
application developer are always tied up in the memory usage context☺.

9.3.1 ‘C’ v/s. ‘Embedded C’


‘C’ is a well structured, well defined and standardised general purpose programming language with extensive
bit manipulation support. ‘C’ offers a combination of the features of high level language and assembly and
helps in hardware access programming (system level programming) as well as business package developments
(Application developments like pay roll systems, banking applications, etc). The conventional ‘C’ language
follows ANSI standard and it incorporates various library files for different operating systems. A platform
(operating system) specific application, known as, compiler is used for the conversion of programs written in
‘C’ to the target processor (on which the OS is running) specific binary files. Hence it is a platform specific
development.
Embedded ‘C’ can be considered as a subset of conventional ‘C’ language. Embedded ‘C’ supports all
‘C’ instructions and incorporates a few target processor specific functions/instructions. It should be noted
that the standard ANSI ‘C’ library implementation is always tailored to the target processor/controller library
files in Embedded ‘C’. The implementation of target processor/controller specific functions/instructions
depends upon the processor/controller as well as the supported cross-compiler for the particular Embedded
‘C’ language. A software program called ‘Cross-compiler’ is used for the conversion of programs written in
Embedded ‘C’ to target processor/controller specific instructions (machine language).
598 Introduc on to Embedded Systems

Windows Embed- Platform Builder Microsoft.


ded Compact [Link]
RTOS Available as bundled package with Visual Studio IDE
VxWorks RTOS Wind River Workbench Wind River Systems.
[Link]
QNX RTOS QNX Momentics QNX Software Systems
[Link]
3rd Party Tools MULTI IDE Green Hills Software Supports a variety of family of proces-
sors. Visit
[Link] for more details

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.

13.2 TYPES OF FILES GENERATED ON CROSS-COMPILATION


Cross-compilation is the process of converting a source code written
LO 2 Identify the different in high level language (like ‘Embedded C’) to a target processor/
types of files (List File, controller understandable machine code (e.g. ARM processor or
Preprocessor output file, Object 8051 microcontroller specific machine code). The conversion of
File, Map File, Hex File, etc.) the code is done by software running on a processor/controller
generated during the cross (e.g. x86 processor based PC) which is different from the target
compilation of a source file processor. The software performing this operation is referred as
written in high level language the ‘Cross-compiler’. In a single word cross-compilation is the
like Embedded C and during the process of cross platform software/firmware development. Cross
cross assembling of a source file assembling is similar to cross-compiling; the only difference is that
written in Assembly Language the code written in a target processor/controller specific Assembly
code is converted into its corresponding machine code. The
application converting Assembly instruction to target processor/controller specific machine code is known
as cross-assembler. Cross-compilation/cross-assembling is carried out in different steps and the process
generates various types of intermediate files. Almost all compilers provide the option to select whatever
intermediate files needs to be retained after cross-compilation. The various files generated during the cross-
compilation/cross-assembling process are:
List File (.lst), Hex File (.hex), Pre-processor Output file, Map File (File extension linker dependent),
Object File (.obj)

13.2.1 List File (.LST File)


Listing file is generated during the cross-compilation process and it contains an abundance of information
about the cross compilation process, like cross compiler details, formatted source text (‘C’ code), assembly
code generated from the source file, symbol tables, errors and warnings detected during the cross-compilation
process. The type of information contained in the list file is cross-compiler specific. As an example let’s
consider the cross-compilation process of the file sample.c given as the first illustrative embedded C program
under Keil µVision5 IDE discussion. The ‘list file’ generated contains the following sections.
The Embedded System Development Environment 599

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

28 =1 extern char getchar (void);


29 =1 extern char ungetchar (char);
30 =1 extern char putchar (char);
31 =1 extern int printf (const char *, ...);
32 =1 extern int sprintf (char *, const char *, ...);
33 =1 extern int vprintf (const char *, char *);
34 =1 extern int vsprintf (char *, const char *, char *);
35 =1 extern char *gets (char *, int n);
36 =1 extern int scanf (const char *, ...);
37 =1 extern int sscanf (char *, const char *, ...);
38 =1 extern int puts (const char *);
39 =1
40 =1 #pragma RESTORE
41 =1
42 =1 #endif
43 =1
4
5 void main()
6 {
7 1 printf(“Hello World!\n”);
8 1 }
9
Assembly Lis ng Assembly listing contains the assembly code generated by the cross compiler for the
‘C’ source code. Assembly code generated can be excluded from the list file by using special compiler
directives.
ASSEMBLY LISTING OF GENERATED OBJECT CODE
; FUNCTION main (BEGIN)
; SOURCE LINE # 5
; SOURCE LINE # 6
; SOURCE LINE # 7
0000 7BFF MOV R3, #0FFH
0002 7A00 R MOV R2, #HIGH ?SC_0
0004 7900 R MOV R1, #LOW ?SC_0
0006 020000 E LJMP _printf

; FUNCTION main (END)


Symbol Lis ng The symbol listing contains symbolic information about the various symbols present in the
cross compiled source file. Symbol listing contains the sections symbol name (NAME) symbol classification
(CLASS (Special Function Register (SFR), structure, typedef, static, public, auto, extern, etc.)), memory
space (MSPACE (code memory or data memory)), data type (TYPE (int, char, Procedure call, etc.)), offset
((OFFSET from code memory start address)) and size in bytes (SIZE). Symbol listing in list file output can
be turned on or off by cross-compiler directives.
NAME CLASS MSPACE TYPE OFFSET SIZE
==== ===== ====== ==== ====== ====
The Embedded System Development Environment 601

size_t . . . . . TYPEDEF ----- U_INT ----- 2


main . . . . . . .PUBLIC CODE PROC 0000H -----
_printf. . . . . .EXTERN CODE PROC ----- -----
Module Informa on The module information provides the size of initialised and un-initialised memory
areas defined by the source file
MODULE INFORMATION: STATIC OVERLAYABLE
CODE SIZE = 9 ----
CONSTANT SIZE = 13 ----
XDATA SIZE = ---- ----
PDATA SIZE = ---- ----
DATA SIZE = ---- ----
IDATA SIZE = ---- ----
BIT SIZE = ---- ----
END OF MODULE INFORMATION.
Warnings and Errors Warnings and Errors section of list file records the errors encountered or any
statement that may create issues in application (warnings), during cross compilation. The warning levels
can be configured before cross compilation. You can ignore certain warnings, e.g. a local variable is declared
within a function and it is not used anywhere in the program. Certain warnings require prompt attention.
C51 COMPILATION COMPLETE. 0 WARNING(S), 0 ERROR(S)
List file is a very useful tool for application debugging in case of any cross compilation issues.

13.2.2 Preprocessor Output File


The preprocessor output file generated during cross-compilation contains the preprocessor output for the
preprocessor instructions used in the source file. Preprocessor output file is used for verifying the operation
of macros and conditional preprocessor directives. The preprocessor output file is a valid C source file. File
extension of preprocessor output file is cross compiler dependent.

13.2.3 Object File (.OBJ File)


Cross-compiling/assembling each source module (written in C/Assembly) converts the various Embedded C/
Assembly instructions and other directives present in the module to an object (.OBJ) file. The format (internal
representation) of the .OBJ file is cross compiler dependent. OMF51 or OMF2 are the two objects file formats
supported by C51 cross compiler. The object file is a specially formatted file with data records for symbolic
information, object code, debugging information, library references, etc. The list of some of the details stored
in an object file is given below.
1. Reserved memory for global variables.
2. Public symbol (variable and function) names.
3. External symbol (variable and function) references.
4. Library files with which to link.
5. Debugging information to help synchronise source lines with object code.
The object code present in the object file are not absolute, meaning, the code is not allocated fixed memory
location in code memory. It is the responsibility of the linker/locater to assign an absolute memory location
to the object code. During cross-compilation process, the cross compiler sets the address of references to
external variables and functions as 0. The external references are resolved by the linker during the linking
602 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.

13.2.4 Map File (.MAP)


As mentioned above, the cross-compiler converts each source code module into a re-locatable object (OBJ)
file. Cross-compiling each source code module generates its own list file. In a project with multiple source
files, the cross-compilation of each module generates a corresponding object file. The object files so created
are re-locatable codes, meaning their location in the code memory is not fixed. It is the responsibility of a
linker to link all these object files. The locater is responsible for locating absolute address to each module in
the code memory. Linking and locating of re-locatable object files will also generate a list file called ‘linker
list file’ or ‘map file’. Map file contains information about the link/locate process and is composed of a
number of sections. The different sections listed in a map file are cross compiler dependent. The information
generally held by map files is listed below. It is not necessary that the map files generated by all linkers/
locaters should contain all these information. Some may contain less information compared to this or others
may contain more information than given in this. It all depends on the linker/locater.
Page Header A header on each page of the linker listing (MAP) file which indicates the linker version
number, date, time, and page number.
e.g. BL51 BANKED LINKER/LOCATER V6.22 10/16/2014 15:47:10 PAGE 1

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)

VALUE TYPE NAME


---------------------------------------------------------
------- MODULE ?C_STARTUP
C:0C1EH SEGMENT ?C_C51STARTUP
I:0022H SEGMENT ?STACK
C:0000H PUBLIC ?C_STARTUP
D:00E0H SYMBOL ACC
D:00F0H SYMBOL B
D:0083H SYMBOL DPH
D:0082H SYMBOL DPL
Inter Module Cross Reference The cross reference listing includes the section name, memory type and the
name of the modules in which it is defined and all modules in which it is accessed.
e.g.
NAME………………USAGE MODULE NAMES
---------------------------------------------------------------------
?C?CCASE …………CODE; ?C?CCASE PRINTF
?C?CLDOPTR …….. CODE; ?C?CLDOPTR PRINTF
?C?CLDPTR….......... CODE; ?C?CLDPTR PRINTF
?C?CSTPTR...............CODE; ?C?CSTPTR PRINTF
?C?PLDIIDATA…….CODE; ?C?PLDIIDATA PRINTF
604 Introduc on to Embedded Systems

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

13.2.5 HEX File (.HEX)


Hex file is the binary executable file created from the source code. The absolute object file created by the
linker/locater is converted into processor understandable binary code. The utility used for converting an
object file to a hex file is known as Object to Hex file converter. Hex files embed the machine code in a
particular format. The format of Hex file varies across the family of processors/controllers. Intel HEX and
Motorola HEX are the two commonly used hex file formats in embedded applications. Intel HEX file is an
ASCII text file in which the HEX data is represented in ASCII format in lines. The lines in an Intel HEX
file are corresponding to a HEX Record. Each record is made up of hexadecimal numbers that represent
machine-language code and/or constant data. Individual records are terminated with a carriage return and a
linefeed. Intel HEX file is used for transferring the program and data to a ROM or EPROM which is used as
code memory storage.
[Link] Intel HEX File Format
As mentioned, Intel HEX file is composed of a number of HEX records. Each record is made up of five fields
arranged in the following format:
:llaaaattdd...cc
Each group of letters corresponds to a different field, and each letter represents a single hexadecimal digit.
Each field is composed of at least two hexadecimal digits (which make up a byte) as described below:

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.

Memory Address in Hex Data in Hex


0000H 02
0001H 0C
0002H 1F
If you are familiar with 8051 Machine code you can easily identify that 02 is the machine code for
the instruction LJMP and the next two bytes represent the 16bit address of the location to which the jump is
intended. Obviously the instruction is LJMP 0C1F. The last two digits (cc) of the record holds the checksum
of the values present in the record. The checksum is calculated by adding all the bytes in the record and then
taking modulo 256 of the result. The resultant is 2’s complemented and represented as checksum in the record
field. Above example it is 0xD0. Intel hex files end with an end of file record indicating the end of records in
the hex file. Let’s examine the end of record structure.

: 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 SIMULATORS, EMULATORS AND DEBUGGING


Simulators and emulators are two important tools used in embedded system
LO 4 Explain development. Both the terms sound alike and are little confusing. Simulator
Simulators, In Circuit is a software tool used for simulating the various conditions for checking
Emulators (ICE), and the functionality of the application firmware. The Integrated Development
Debuggers and their Environment (IDE) itself will be providing simulator support and they help
role in embedded in debugging the firmware for checking its required functionality. In certain
firmware debugging scenarios, simulator refers to a soft model (GUI model) of the embedded
product. For example, if the product under development is a handheld
device, to test the functionalities of the various menu and user interfaces, a soft form model of the product
with all UI as given in the end product can be developed in software. Soft phone is an example for such a
simulator. Emulator is hardware device which emulates the functionalities of the target device and allows real
time debugging of the embedded firmware in a hardware environment.

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

1. Purely software based


2. Doesn’t require a real target system
3. Very primitive (Lack of featured I/O support. Everything is a simulated one)
4. Lack of Real-time behaviour
[Link] Advantages of Simulator Based Debugging
Simulator based debugging techniques are simple and straightforward. The major advantages of simulator
based firmware debugging techniques are explained below.
No Need for Original Target Board Simulator based debugging technique is purely software oriented.
IDE’s software support simulates the CPU of the target board. User only needs to know about the memory
map of various devices within the target board and the firmware should be written on the basis of it. Since the
real hardware is not required, firmware development can start well in advance immediately after the device
interface and memory maps are finalised. This saves development time.
Simulate I/O Peripherals Simulator provides the option to simulate various I/O peripherals. Using simulator’s
I/O support you can edit the values for I/O registers and can be used as the input/output value in the firmware
execution. Hence it eliminates the need for connecting I/O devices for debugging the firmware.
Simulates Abnormal Condi ons With simulator’s simulation support you can input any desired value for
any parameter during debugging the firmware and can observe the control flow of firmware. It really helps the
developer in simulating abnormal operational environment for firmware and helps the firmware developer to
study the behaviour of the firmware under abnormal input conditions.
[Link] Limita ons of Simulator based Debugging
Though simulation based firmware debugging technique is very helpful in embedded applications, they
possess certain limitations and we cannot fully rely upon the simulator-based firmware debugging. Some of
the limitations of simulator-based debugging are explained below.
Devia on from Real Behaviour Simulation-based firmware debugging is always carried out in a development
environment where the developer may not be able to debug the firmware under all possible combinations of
input. Under certain operating conditions we may get some particular result and it need not be the same when
the firmware runs in a production environment.
Lack of Real Timeliness The major limitation of simulator based debugging is that it is not real-time in
behaviour. The debugging is developer driven and it is no way capable of creating a real time behaviour.
Moreover in a real application the I/O condition may be varying or unpredictable. Simulation goes for
simulating those conditions for known values.

13.4.2 Emulators and Debuggers


What is debugging and why debugging is required? Debugging in embedded application is the process of
diagnosing the firmware execution, monitoring the target processor’s registers and memory while the firmware
is running and checking the signals from various buses of the embedded hardware. Debugging process in
embedded application is broadly classified into two, namely; hardware debugging and firmware debugging.
Hardware debugging deals with the monitoring of various bus signals and checking the status lines of the
target hardware. The various tools used for hardware debugging will be explaining in detail in a later section
of this chapter. Firmware debugging deals with examining the firmware execution, execution flow, changes to
various CPU registers and status registers on execution of the firmware to ensure that the firmware is running
as per the design. This section deals with the debugging of firmware.
The Embedded System Development Environment 609

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

[Link] Inline Breakpoint Based Firmware Debugging


Inline breakpoint based debugging is another primitive method of firmware debugging. Within the firmware
where you want to ensure that firmware execution is reaching up to a specified point, insert an inline debug
code immediately after the point. The debug code is a printf() function which prints a string given as per
the firmware. You can insert debug codes (printf()) commands at each point where you want to ensure the
firmware execution is covering that point. Cross-compile the source code with the debug codes embedded
within it. Burn the corresponding hex file into the EEPROM. You can view the printf() generated data on the
a ‘Terminal Program’ like PUTTy or TeraTerm. Configure the serial communication settings of the ‘Terminal
Program’ connection to the same as that of the serial communication settings configured in the firmware (Say
Baudrate = 9600; Parity = None; Stop Bit = 1; Flow Control = None); Connect the target board’s serial port
(COM) to the development PC’s COM Port using an RS232 Cable. Power up the target board. Depending on
the execution flow of firmware and the inline debug codes inserted in the firmware, you can view the debug
information on the ‘Terminal Program’. Typical usage of inline debug codes and the debug info retrieved on
the HyperTerminal is illustrated below.
//First Inline Debug Code
printf (“Starting Configuration…\n”);
Configurations……
……………………
//Inline Debug code ensuring execution of Configuration section
printf (“End of Configuration…\n”);
printf (“Beginning of Firmware Execution…\n”);
Code segment1….
………………….
//Inline Debug code ensuring execution of Code Segment 1
printf (“End of Code segment 1…\n”);
Code segment2….
………………….
//Inline Debug code ensuring execution of Code Segment 2
printf (“End of Code segment 2…\n”);
If the firmware is error free and the execution occurs properly, you will get all the debug messages on the
Terminal Program. Based on this debug info you can check the firmware for errors (Fig. 13.38).

Fig. 13.38 Inline Breakpoint Based Firmware Debugging with HyperTerminal


The Embedded System Development Environment 611

[Link] Monitor Program Based Firmware Debugging


Monitor program based firmware debugging is the first adopted invasive method for firmware debugging
(Fig. 13.39). In this approach a monitor program which acts as a supervisor is developed. The monitor
program controls the downloading of user code into the code memory, inspects and modifies register/memory
locations; allows single stepping of source code, etc. The monitor program implements the debug functions
as per a pre-defined command set from the debug application interface. The monitor program always listens
to the serial port of the target device and according to the command received from the serial interface it
performs command specific actions like firmware downloading, memory inspection/modification, firmware
single stepping and sends the debug information (various register and memory contents) back to the main
debug program running on the development PC, etc. The first step in any monitor program development is
determining a set of commands for performing various operations like firmware downloading, memory/
register inspection/modification, single stepping, etc. Once the commands for each operation is fixed, write
the code for performing the actions corresponding to these commands. As mentioned earlier, the commands
may be received through any of the external interface of the target processor (e.g. RS-232C serial interface/
parallel interface/USB, etc.). The monitor program should query this interface to get commands or should
handle the command reception if the data reception is implemented through interrupts. On receiving a
command, examine it and perform the action corresponding to it. The entire code stuff handling the command
reception and corresponding action implementation is known as the “monitor program”. The most common
type of interface used between target board and debug application is RS-232/USB Serial interface. After the
successful completion of the ‘monitor program’ development, it is compiled and burned into the FLASH
memory or ROM of the target board. The code memory containing the monitor program is known as the
‘Monitor ROM’.

Target CPU

Monitor ROM
Debugger RS-232 Serial link
application

Host PC
Target board
Von-Neumann RAM

Fig. 13.39 Monitor Program Based Target Firmware Debug Setup

The monitor program contains the following set of minimal features.


1. Command set interface to establish communication with the debugging application
2. Firmware download option to code memory
3. Examine and modify processor registers and working memory (RAM)
4. Single step program execution
5. Set breakpoints in firmware execution
6. Send debug information to debug application running on host machine
612 Introduc on to Embedded Systems

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.

Debugger application RS-232/USB cable Emulator POD

Target board interface


(Device adaptor)

In Circuit Emulator

Signal lines
PC COM/USB port
(Flat Cable)
PC Target Board

Fig. 13.40 In Circuit Emulator (ICE) Based Target Debugging

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.

13.5 TARGET HARDWARE DEBUGGING


Even though the firmware is bug free and everything is intact in the board,
LO 5 Discuss the your embedded product need not function as per the expected behaviour in
different tools and the first attempt for various hardware related reasons like dry soldering of
techniques used for components, missing connections in the PCB due to any un-noticed errors
embedded hardware in the PCB layout design, misplaced components, signal corruption due to
debugging noise, etc. The only way to sort out these issues and figure out the real
problem creator is debugging the target board. Hardware debugging is not similar to firmware debugging.
Hardware debugging involves the monitoring of various signals of the target board (address/data lines, port
pins, etc.), checking the inter-connection among various components, circuit continuity checking, etc. The
various hardware debugging tools used in Embedded Product Development are explained below.

You might also like