KEMBAR78
3 Embedded System Software Building Process | PDF | Assembly Language | Computer Architecture
0% found this document useful (0 votes)
4 views11 pages

3 Embedded System Software Building Process

The document outlines the embedded system building process, detailing the roles of assemblers, interpreters, compilers, libraries, linkers, and debugging tools. It emphasizes the importance of specialized compilers for embedded C, the use of libraries for functionality, and the challenges of debugging due to limited resources and real-time constraints. Additionally, it discusses the JTAG interface for testing and debugging embedded systems, as well as the role of Makefiles in automating the build process.

Uploaded by

chandrimamanik
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 views11 pages

3 Embedded System Software Building Process

The document outlines the embedded system building process, detailing the roles of assemblers, interpreters, compilers, libraries, linkers, and debugging tools. It emphasizes the importance of specialized compilers for embedded C, the use of libraries for functionality, and the challenges of debugging due to limited resources and real-time constraints. Additionally, it discusses the JTAG interface for testing and debugging embedded systems, as well as the role of Makefiles in automating the build process.

Uploaded by

chandrimamanik
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
You are on page 1/ 11

Embedded system building process

1) Assembler

Sample assembly language


ORG 00H ; Start assembly at memory location 00H

MOV R0, #50H ; Load register R0 with the address 50H

MOV R1, #51H ; Load register R1 with the address 51H

MOV A, @R0 ; Move the content of memory location pointed by R0 (50H) into Accumulator (A)

ADD A, @R1 ; Add the content of memory location pointed by R1 (51H) to Accumulator (A)

; The result is stored in Accumulator (A)

MOV R0, #52H ; Load register R0 with the address 52H

MOV @R0, A ; Move the content of Accumulator (A) to the memory location pointed by R0 (52H)

END ; End of the assembly program

An assembler is a computer program that translates assembly language code into machine code.
Machine code is the low-level language that a computer's processor can directly understand and
execute. Essentially, an assembler acts as a translator, converting human-readable assembly
instructions into binary code that the CPU can process.

2) interpreter

In computer science, an interpreter is a program that translates and executes high-level


programming language code line by line, directly without converting the entire code into machine
code first. This is in contrast to a compiler, which translates the entire code at once.

3) Compiler

Sample C code
#include <reg51.h> // Include the header file for 8051 SFRs

// Function to create a delay

void delay(unsigned int ms) {

unsigned int i, j;

for (i = 0; i < ms; i++) {

for (j = 0; j < 1200; j++); // Adjust this value for desired delay length

}
void main(void) {

// Make P1.0 an output pin (optional, as it's default output)

// P1 = 0x00; // This would set all pins of Port 1 to low

while (1) { // Infinite loop for continuous operation

P1_0 = 1; // Turn LED ON (set P1.0 high)

delay(500); // Delay for 500ms

P1_0 = 0; // Turn LED OFF (set P1.0 low)

delay(500); // Delay for 500ms

An Embedded C compiler is a specialized compiler designed to translate Embedded C code into


machine code that a specific microcontroller or embedded system can understand and execute.
Unlike general-purpose C compilers, these compilers are tailored to the unique constraints and
hardware architectures of embedded systems, often including features like support for bit-level
manipulation and direct hardware access.

Specialized for Hardware:

Embedded C compilers are designed to work with specific hardware architectures like 8051, ARM,
PIC, etc. They generate machine code optimized for the target microcontroller's instruction set and
memory layout.

Bit-Level Manipulation:

Embedded C often requires direct control over individual bits within registers or memory locations.
Embedded C compilers provide specific keywords and data types (like sbit and bits) to facilitate this
bit-level manipulation, which is crucial for interacting with hardware peripherals.

Memory Addressing:

These compilers handle memory addressing in a way that is compatible with the specific memory
map of the target microcontroller. They might support direct access to special function registers
(SFRs) used for controlling hardware components.

Optimization for Resource Constraints:

Embedded systems typically have limited memory and processing power. Embedded C compilers are
designed to generate efficient code that minimizes code size and execution time, optimizing for
these resource constraints.

Examples of Embedded C Compilers:

Popular examples include Keil Compiler, Code Composer Studio, and Arm Compiler for Embedded.
4) Libraries

In embedded C programming, libraries are collections of pre-written code (functions, variables, etc.)
that provide specific functionalities, reducing development time and effort. Unlike general-purpose
C, embedded systems often require specialized libraries due to resource constraints and direct
hardware interaction.

Types of Libraries in Embedded C:

Standard C Libraries (Adapted):

While the full ANSI C library is often too large for embedded systems, subsets like Newlib and
Picolibc offer essential functionalities (e.g., printf, malloc, string manipulation) with a smaller
footprint.

Newlib-nano: is an even more reduced version, often used in resource-constrained environments


like STM32 microcontrollers.

These libraries often require syscall stubs to be implemented by the developer to interface with the
underlying hardware (e.g., for printf to output to a serial port).

Hardware Abstraction Layer (HAL) Libraries:

Provided by microcontroller manufacturers (e.g., STM32Cube HAL, Microchip Harmony), these


libraries offer a standardized interface to interact with peripherals (GPIO, ADC, SPI, I2C, UART) across
different devices from the same vendor.

They abstract away low-level register programming, simplifying development and improving code
portability within a vendor's ecosystem.

Middleware Libraries:

These libraries provide higher-level functionalities built upon the HAL, such as:

RTOS (Real-Time Operating System) kernels: FreeRTOS, Zephyr, RT-Thread for managing tasks and
scheduling.

Communication stacks: TCP/IP, USB, Bluetooth, CAN for network and peripheral communication.

Filesystems: FatFs for managing data on storage devices.

Graphics libraries: LVGL, emWin for creating user interfaces on displays.

Custom Libraries:

Developers often create their own libraries for project-specific functionalities, encapsulating
reusable code modules (e.g., sensor drivers, motor control algorithms, data processing routines).

Considerations when using libraries in Embedded C:

Footprint: Memory usage (Flash and RAM) is crucial in embedded systems. Choose libraries
optimized for size.

Performance: Code efficiency and execution speed are vital for real-time applications.

Reentrancy: Ensure libraries are reentrant if used in multi-threaded or interrupt-driven


environments.
Licensing: Be aware of the licensing terms of open-source or commercial libraries.

Portability: Consider the ease of porting the library to different hardware platforms if needed.

Static libraries in embedded C are collections of pre-compiled object files (.o files) that are linked
directly into the final executable at compile time. This means that when you build your embedded C
application, the relevant code from the static library is copied and embedded within the
application's binary.

Key characteristics and implications of static libraries in embedded C:

Compilation and Linking:

The source code of the library functions is compiled into object files.

These object files are then bundled together into a single archive file, typically with a .a extension
(e.g., libmylibrary.a). The ar command is commonly used to create and manage these archives.

During the linking phase of your embedded application's build process, the linker extracts the
necessary object code from the static library and incorporates it directly into your application's
executable.

Dynamic libraries, also known as shared libraries, in C for embedded systems present a more
complex scenario compared to their use in general-purpose operating systems like Linux. While the
core concept of linking at runtime remains, the constraints and lack of a full-fledged operating
system in many embedded environments necessitate careful consideration.

Key Aspects of Dynamic Libraries in Embedded C:

Runtime Linking:

Dynamic libraries are not fully integrated into the executable during compilation. Instead, the
executable contains references to the library's functions and data, and the actual linking occurs
when the program is loaded or executed. This differs from static libraries, where the entire library
code is copied into the executable.

Reduced Executable Size:

A primary advantage is that the executable size is smaller, as it only contains references to the
library, not the full code. This can be crucial in memory-constrained embedded systems.

5) Linker

In embedded C programming, the linker is a crucial tool in the build process, responsible for
combining various compiled components into a single executable or firmware image that can be
loaded onto the target hardware.

Key roles of the linker in embedded C:

Symbol Resolution:

The linker resolves references to symbols (function and variable names) across different object files
and libraries, assigning them their actual memory addresses. This ensures that when one part of the
code calls a function or accesses a variable defined in another part, the correct memory location is
accessed.
Memory Mapping and Allocation:

Guided by a linker script, the linker determines how different sections of the program (e.g., code,
initialized data, uninitialized data, stack, heap) are mapped to the specific memory regions of the
embedded system (e.g., Flash, RAM). This ensures efficient memory utilization and proper
placement of critical components like the interrupt vector table.

Combining Object Files and Libraries:

It takes the object files generated by the compiler (which contain machine code) and combines them
with any necessary libraries (e.g., C standard library, peripheral drivers) to create a complete
program.

Relocation:

The linker handles relocations, which are adjustments made to addresses within the code and data
to reflect their final positions in memory after linking.

Executable Generation:

The ultimate output of the linker is the final executable file (e.g., .elf, .bin, .hex), which is then ready
to be programmed onto the microcontroller or embedded device.

Linker Scripts:

Linker scripts are text files that provide instructions to the linker about the memory layout of the
target hardware and how different sections of the program should be placed within that memory.
They define memory regions (e.g., FLASH, RAM) with their origin addresses and lengths, and then
specify how input sections (e.g., .text for code, .data for initialized data, .bss for uninitialized data)
are mapped to these output memory regions. This level of control is essential in embedded systems
due to the constrained and often non-uniform memory architectures.

6) Building process of embedded system

The embedded system building process involves compiling source code into object files, linking these
object files, and then locating and assigning physical memory addresses. Common file formats
include .hex, .bin, and .elf, used for programming microcontrollers.

Compilation:

Source code (typically written in C/C++) is compiled into object files (.o or .obj).

A cross-compiler is used, as the target system might be different from the development machine.

The linker resolves external references between object files and assigns memory addresses.

Locating involves assigning physical memory addresses to code and data.

Linking:

The linker combines multiple object files into a single executable file (e.g., .elf).

It resolves references between different object files and libraries.

The linker also handles relocation, adjusting addresses in the code to their final locations in memory.

Common File Formats:


.hex: Intel HEX format, a human-readable format for representing binary data in ASCII characters.

.bin: Raw binary image, a simple format containing just the machine code.

.elf: Executable and Linkable Format, a widely used standard format for executables, object code,
shared libraries, and core dumps.

.s19: Motorola S-record, another common format for representing binary data.

.map: A map file that provides information about the memory layout and addresses of different
code and data sections.

Toolchain:

A toolchain is a set of compatible tools (compiler, linker, assembler, debugger) used in the
embedded system build process.

These tools are typically installed on a host machine (e.g., PC) and used to generate code for the
target device.

The target device (e.g., microcontroller) has limited resources and cannot run these tools directly.

Debugging:

Debugging embedded software can be challenging as it requires specialized tools.

Remote debuggers, emulators, simulators, logic analyzers, and oscilloscopes are commonly used.

Debug information (e.g., DWARF) is often included in the binary or in separate files to help
debuggers map machine code to the original source code.

Directives

In Embedded C, directives, also known as preprocessor directives, are special instructions to the C
preprocessor, which is a program that processes the source code before it is passed to the compiler.
These directives begin with a hash symbol (#) and are not executable statements themselves but
rather modify the source code during the pre-processing stage.
Common Preprocessor Directives in Embedded C:

#include: This directive is used to include the content of a specified header file into the current
source file. In embedded systems, this is crucial for including device-specific header files that define
registers, bit-fields, and other hardware-related information for a particular microcontroller.

#define: This directive is used to define macros, which are essentially symbolic names or constants
that are replaced by their defined value or code snippet before compilation. Macros are frequently
used in embedded systems for:

Defining constant values (e.g., #define F_CPU 16000000UL).

Creating function-like macros for repetitive code segments.

Conditional compilation based on hardware configurations.

Conditional Compilation Directives (#if, #ifdef, #ifndef, #else, #elif, #endif): These directives allow
parts of the code to be conditionally compiled based on whether certain macros are defined or
specific conditions are met. This is highly useful in embedded systems for:

Supporting multiple hardware platforms with a single codebase.

Including or excluding debugging code.

Optimizing code for different memory or performance constraints.

In embedded C programming, the #asm and #endasm directives are used to embed assembly
language instructions directly within C code. This is particularly useful in embedded systems for
performance-critical sections, direct hardware manipulation, or accessing features not directly
supported by C.

Key aspects of #asm directives in embedded C:

Syntax: The assembly code block is typically enclosed between #asm and #endasm directives, like
this:

Why debugging embedded system difficult

Debugging embedded systems is notoriously difficult due to limited resources, real-time constraints,
and the complex interaction between hardware and software. These factors make it challenging to
gain visibility into the system's behavior and pinpoint the root cause of issues.

Here's a more detailed breakdown:

1. Limited Resources:

Embedded systems often operate with limited memory, processing power, and storage.

Traditional debugging tools can be resource-intensive, making them impractical to use on resource-
constrained embedded systems.

This limitation can make it difficult to implement extensive logging or use powerful debuggers.

2. Real-time Constraints:

Many embedded systems must respond to events within strict timing requirements.
Traditional debugging methods that involve pausing or slowing down the system can disrupt real-
time behavior and mask the problem.

Debugging in real-time requires specialized tools and techniques to ensure accurate analysis without
affecting system performance.

3. Hardware-Software Interaction:

Embedded systems involve the complex interplay between hardware and software.

Issues can arise from the interaction between these two domains, making it difficult to determine if
the problem lies in the code or the hardware.

Debugging often requires knowledge of both the hardware architecture and the software code.

4. Limited Visibility:

Accessing the internal state of an embedded system can be challenging due to the lack of
sophisticated debugging interfaces.

Traditional debuggers and logging mechanisms might not be available or might be too disruptive.

This lack of visibility makes it harder to pinpoint the source of errors.

5. Other Factors:

Remote Deployment:

Embedded systems are often deployed in remote or inaccessible locations, making physical access
for debugging difficult.

Lack of Standardization:

The diverse nature of embedded systems with different architectures and development
environments adds to the complexity of debugging.

Integration Challenges:

Debugging embedded systems that are part of larger systems requires understanding how they
interact with other components.
JTAG (Joint Test Action Group)

is a standardized interface used in embedded systems for testing, debugging, and programming. It
allows access to internal components of devices, like microcontrollers and FPGAs, without physically
probing individual pins. This capability is crucial during the development, production, and field
service phases of embedded systems.

Key aspects of JTAG in embedded systems:

Testing and Debugging:

JTAG enables boundary scan testing to verify connections and detect faults like open circuits or
shorts. It also facilitates debugging by allowing access to internal registers and memory, aiding in
identifying and fixing software or hardware issues.

Programming:
JTAG can be used to program flash memory, FPGAs, and other programmable devices, often
enabling in-system programming (ISP) without needing to remove the device from the system.

Access and Control:

JTAG provides a means to access and control internal components of a device, allowing for tasks like
halting execution, inspecting variables, and modifying memory contents during debugging.

Boundary Scan:

A key feature of JTAG, boundary scan allows for testing connections between components on a PCB
by observing and controlling the pins of each integrated circuit.

Four-Wire Interface:

The standard JTAG interface uses four wires: Test Data In (TDI), Test Data Out (TDO), Test Clock
(TCK), and Test Mode Select (TMS).

Test Access Port (TAP):

JTAG devices include a TAP, which is a dedicated interface for accessing the JTAG features.

Daisy-Chaining:

Multiple JTAG-enabled devices can be connected in a chain, allowing for testing and programming of
multiple components using the same JTAG interface.

Benefits of using JTAG in embedded systems:

Reduced Development Time:

JTAG debugging tools can significantly reduce development time by providing powerful debugging
capabilities.

Improved Product Quality:

JTAG enables thorough testing and verification of embedded systems, leading to higher quality
products.

Cost-Effective Production:

JTAG facilitates efficient programming and testing during production, reducing manufacturing costs.

Remote Debugging:

JTAG can be used for remote debugging, allowing developers to diagnose issues in the field without
needing physical access to the device.

MAKE FILE

In embedded systems development, a Makefile serves as a crucial component for automating and
managing the build process of software projects. It acts as a set of instructions for the make utility,
defining how source code files should be compiled, linked, and ultimately transformed into an
executable or deployable image for the target embedded device.

Key aspects of Makefiles in embedded systems:


Automation of Compilation and Linking:

Embedded projects often involve numerous source files, libraries, and specific compiler/linker
options tailored for the target microcontroller or processor. Makefiles automate the complex
sequence of commands required to compile C/C++ source files into object files and then link them
together with necessary libraries and startup code to create the final executable.

Dependency Management:

Makefiles define dependencies between files. If a source file is modified, make intelligently
recompiles only the affected files and their dependents, saving significant build time compared to
manual compilation of the entire project.

Cross-Compilation Support:

Embedded development frequently involves cross-compilation, where the code is compiled on a


host machine (e.g., a PC) for a different target architecture (e.g., ARM Cortex-M). Makefiles are
essential for specifying the correct cross-compilation toolchains (compiler, assembler, linker, etc.)
and their respective flags.

Integration with Development Tools:

Makefiles can be integrated with various embedded development tools, such as debuggers,
programmers, and flash utilities, allowing for automated programming of the target device after a
successful build.

Configuration and Customization:

Makefiles can be parameterized using variables to allow for easy configuration of build options, such
as optimization levels, inclusion of specific features, or selection of different hardware platforms.

Size Tracking:

Makefiles can be configured to output information about the compiled code's size (flash and RAM
usage), which is critical for embedded systems with limited resources.

Basic Structure of a Makefile:

A Makefile consists of rules, each defining a target, its dependencies, and the commands to execute
to build the target.

Sample make file


#######################################################3333

# Define compiler and linker

CC = arm-none-eabi-gcc

LD = arm-none-eabi-ld

# Define source files

SRCS = main.c peripheral.c


# Define object files

OBJS = $(SRCS:.c=.o)

# Define output executable

TARGET = program.elf

all: $(TARGET)

$(TARGET): $(OBJS)

$(LD) -o $@ $^

%.o: %.c

$(CC) -c $< -o $@

clean:

rm -f $(OBJS) $(TARGET)

##################################################333

You might also like