550 likes | 744 Views
Application Development for Embedded Systems. Andrea Marongiu, Carlo Caione { a.marongiu , carlo.caione }@ unibo.it. Application Cross-Development. Cross development is the separation of the build environment from the target environment .
E N D
Application Development for Embedded Systems Andrea Marongiu, Carlo Caione {a.marongiu, carlo.caione}@unibo.it
ApplicationCross-Development • Cross development is the separation of the build environmentfrom the target environment. • Embedded computers where a device has extremely limited resources, are typically not powerful enough to run a compiler, a file system, or a development environment. • Since debugging and testing may also require more resources than are available on an embedded system, cross-compilation can be less involved and less prone to errors than native compilation.
Application Cross-Development Target architecture .c ToolChain Favourite Editor .o Target HW Simulator HW Debugger Development Board
Toolchain • The first and mostessentialproducttodevelopapplications on any embedded deviceis a cross-toolchain • A set oftoolsrunning on a hostmachine, usedto • Compile high-level source code into target object code • Link pre-existingcollectionsofobjectfiles (libraries) • Assemble the wholethingintoanexecutableobjectby the target machine • Letus take a closer look..
GNU Toolchain • A collection of programming tools produced by the GNU Project. These tools form a toolchain (suite of tools used in a serial manner) used for developing applications and operating systems. • It plays a vital role in development of Linux kernel, and software for embedded systems. • Projects included in the GNU toolchain are: • GNU Compiler Collection (GCC): Suite of compilers for several programming languages; • GNU Binutils: Suite of tools including linker, assembler and other tools; • GNU Debugger (GDB): Code debugging tool; • GNU make: Automation tool for compilation and build; • GNU build system (autotools):
Compiling code compiler header files source code pre-processor compiler assembler .s object code assembler binutils linker library executable file
Compiling code #include <stdio.h> #define TRUE 1 #define FALSE 0 main() { int i; i = 5 * 2; printf(“5 times 2 is %d.\n”, i); printf(“TRUE is %d.\n”, TRUE); printf(“FALSE is %d.\n”, FALSE); } Handled by the pre-processor Handledby the compiler Implemented in C library
Compiling code • The pre-processor handles • Macros (#define) • Inclusions (#include) • Conditional code inclusion (#ifdef, #if) • Language extensions (#pragma). • The compiler processes source code and turns it into assembler modules. • The assembler converts them to target binary code. • The linker takes the object files and searches library files to find the routines it calls. It calculates the address references and incorporates any symbolic information to create an executable file format.
GCC (GNU Compiler Collection) • The GNU Compiler Collection (usually shortened to GCC) is a set of compilers produced for various programming languages by the GNU Project. • It is a tool used by nearly every embedded engineer, even those who don’t target Linux • When starting an embedded project, the first tool needed is a cross-compiler, a compiler that generates code intended to work on a machine different from the one on which the code generation occurred. • When used in an embedded project, GCC capably does cross-compilation, without complaint • GCC is available for most embedded platforms, for example Symbian, FreescalePower Architecture-based chips, Playstation and Sega Dreamcast
GCC Architecture Common intermediate representation Multiple front-ends Retargetable!
GNU Binutils • The GNU Binary Utilities, or binutils, is a collection of programming tools for the manipulation of object code in various object file formats. • The most important tools are • as – Assembler: Transforms the assembly code produced by the compiler into a binary format executable by the target machine • ld – Linker: Takes as input several object files and generates a single executable, resolving interactions between symbols • ar – Archiver: Combines together (possibly) multiple object files into a library. This could be later linked to other applications either statically or dynamically • objdump – Dumper: It allows recreating an assembler file from an object file. It can also dump additional information about different segments of the output file format
ObjectArchives (Libraries) • In computer science, a library is a collection of subroutines or classes used to develop software • Libraries contain code and data that provide services to independent programs. This allows the sharing and changing of code and data in a modular fashion • Executables and libraries make references known as links to each other through the process known as linking, which is typically done by a linker • Two types of libraries: • Static libraries • Dynamic / shared libraries
Runtime Libraries • Compilers only generate a small subset of high-level languages facilities and commands from built-in routines. • It relies on libraries to provide the full range of functions that the language offers: • Processor dependent: mathematical functions, string manipulation and similar features that use the processor and don’t need to communicate with peripherals; • I/O dependent: defines the hardware that the software need to access. The library routine either drives directly the hardware or calls the operating system to perform its task; • System calls: typical routines are those which dinamically allocate memory, task control commands, use semaphores, etc; • Exit routines: used to terminate programs free up the memory used by the application.
Newlib • Newlib is a C standard library implementation intended for use on embedded systems. • It is a conglomeration of several library parts, all under free software licenses that make them easily usable on embedded products. • The section System Calls of the newlib documentation describes how it can be used with many operating systems. • Its primary use is on embedded systems that lack any kind of operating system; in that case it calls a "board support package" that can do things like write a byte of output on a serial port, or read a sector from a disk or other memory device. • As of 2007, devkitARM, a popular toolchain for programming homebrew software for Nintendo DS and Game Boy Advance systems, includes Newlib as its C library
Newlib C Code GCC atoi(), strtol(), bzero(), strcat(), ... Newlib _exit(), close(), fork(), kill(), ... BSP / Kernel Hardware MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Newlib read() BSP / Kernel BSP Kernel read() read() Interrupt 0x80 UART_read() kernel + BSP UART UART MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Building a ToolchainAn example: arm-elf-gcc • We’ve introduced the three main softwareblocks composing a toolchain • Binutils – Target specific tools • Gcc – Compiler for target machine • C library – Target specific C library • We now describe the entire process of building a complete toolchain for a ARM7 processor
Building the toolchain for ARM • The source code for binutils, gcc and newlib can be downloaded at • http://www.gnu.org/software/binutils/ • http://gcc.gnu.org/mirrors.html • ftp://sources.redhat.com/pub/newlib/index.html • The standard procedure to build and install each of the products consists of three steps • Configuration • Build • Install
Building the toolchain for ARM • For each of the product, create a separate source and build directory $ROOT> mkdir binutils-src $ROOT> mkdir gcc-src $ROOT> mkdir newlib-src $ROOT> mkdir binutils-build $ROOT> mkdir gcc-build $ROOT> mkdir newlib-build • Then create an INSTALL folder for the entire toolchain $ROOT> mkdir INSTALL
Building the toolchain for ARM • Build and install the binutils specifying the arm-elf target $ROOT> cd binutils-build $ROOT> ../binutils-src/configure --target=arm-elf \ --prefix=../INSTALL \ --program-prefix=arm-elf- $ROOT> make all install • At the end of the process, in the folder $ROOT/INSTALL/bin we’ll find the cross-tools $ROOT> ll INSTALL/bin
Building the toolchain for ARM • Add to your $PATH environment variable the path to the binutils just installed, so that the compiler can use them to build the cross-compiler $ROOT> export PATH=$ROOT/INSTALL/bin:$PATH • Build and install the cross-compiler specifying the arm-elf target and the location of the C library headers $ROOT> cd gcc-build $ROOT> ../gcc-src/configure --target=arm-elf \ --prefix=../INSTALL \ --program-prefix=arm-elf- \ --enable-languages=c,c++ \ --with-newlib \ --with-headers=../newlib-src/newlib/libc/include $ROOT> make all-gcc install-gcc
Building the toolchain for ARM • Build and install the C library $ROOT> cd newlib-build $ROOT> ../newlib-src/configure --target=arm-elf \ --prefix=../INSTALL $ROOT> make all install • Finally, install the entire compiler $ROOT> cd gcc-build $ROOT> make all install
Building the toolchain for ARM • Take a look at the $ROOT/INSTALL/bin folder
Embedded ApplicationProgramming Complexity Microcontroller • SW & HW Low Complexity • The application may use directly the HW • The programmer know the HW and read and write directly internal register Multimedia SoC Custom middleware • Complex HW • Programmer needs abstraction layers. • Embedded O.S. • Application use HW facilities throught system call & driver • HW programmed by statically linking API • Standard / Custom API • Microkernel
Embedded ApplicationProgramming Complexity Microcontroller • SW & HW Low Complexity • The application may use directly the HW • The programmer know the HW and read and write directly internal register Multimedia SoC Custom middleware • Complex HW • Programmer needs abstraction layers. • Embedded O.S. • Application use HW facilities throught system call & driver • HW programmed by statically linking API • Standard / Custom API • Microkernel
Application Cross-Development Target architecture .c Application ToolChain 0x0…0 App. Favourite Editor CPU .o MEM Target HW Simulator HW Debugger Development Board
Embedded ApplicationProgramming Complexity Microcontroller • SW & HW Low Complexity • The application may use directly the HW • The programmer know the HW and read and write directly internal register Multimedia SoC Custom middleware • Complex HW • Programmer needs abstraction layers. • Embedded O.S. • Application use HW facilities throught system call & driver • HW programmed by statically linking API • Standard / Custom API • Microkernel
O.S. Embedded • WhyweneedanO.S. on embedded system: • Depends on complexity • Hardware complexity, programmerneedsabstractionlayers • Multi-processexecution => scheduler • MemoryManagmentUnit • Store data => File system • DifferentsO.S.: • Windows CE • Linux embedded : based on generalpurpose linux + BSP • ucLinux : Lite versionof Linux embedded (No MMU support) • RTems : (No File System, No MMU )
Application Cross-Development Target architecture .c .c .c Microkernel source code ToolChain 0x0…0 App. Favourite Editor CPU .o MEM Target HW Simulator HW Debugger Development Board
Embedded ApplicationProgramming Complexity Microcontroller • SW & HW Low Complexity • The application may use directly the HW • The programmer know the HW and read and write directly internal register Multimedia SoC Custom middleware • Complex HW • Programmer needs abstraction layers. • Embedded O.S. • Application use HW facilities throught system call & driver • HW programmed by statically linking API • Standard / Custom API • Microkernel
Application Cross-Development Target architecture .c .c .c O.S. kernel source code ToolChain 0x0…0 O.S.kernel Favourite Editor CPU .o MEM Target HW Simulator HW Debugger Development Board
Application Cross-Development Target architecture .c Application ToolChain 0x0…0 O.S.kernel Favourite Editor App. CPU .o MEM Target HW Simulator HW Debugger Development Board
[carlo@naomi linux-2.6.34-ARCH]$ ls -altotal 6614 drwxr-xr-x 21 root root 624 Jul 6 15:01 . drwxr-xr-x 4 root root 120 Jul 6 15:01 .. drwxr-xr-x 4 root root 120 Jul 5 23:07 arch drwxr-xr-x 2 root root 104 Jul 5 23:07 block -rw-r--r-- 1 root root 111523 Jul 5 23:06 .config drwxr-xr-x 3 root root 96 Jul 5 23:07 crypto drwxr-xr-x 3 root root 72 Jul 5 23:06 Documentation drwxr-xr-x 80 root root 2016 Jul 5 23:07 drivers drwxr-xr-x 62 root root 1560 Jul 5 23:06 fs drwxr-xr-x 16 root root 400 Jul 5 23:06 include drwxr-xr-x 2 root root 72 Jul 5 23:07 init drwxr-xr-x 6 root root 296 Jul 5 23:06 kernel drwxr-xr-x 2 root root 176 Jul 5 23:07 lib -rw-r--r-- 1 root root 53184 Jul 5 23:06 Makefile drwxr-xr-x 2 root root 104 Jul 5 23:07 mm -rw-r--r-- 1 root root 646765 Jul 5 23:06 Module.symvers drwxr-xr-x 41 root root 1040 Jul 5 23:07 net drwxr-xr-x 2 root root 72 Jul 5 23:06 samples drwxr-xr-x 12 root root 2544 Jul 5 22:51 scripts drwxr-xr-x 6 root root 176 Jul 5 23:07 security drwxr-xr-x 19 root root 480 Jul 5 23:06 sound drwxr-xr-x 2 root root 48 Jul 5 23:06 .tmp_versions drwxr-xr-x 2 root root 72 Jul 5 23:07 usr drwxr-xr-x 3 root root 72 Jul 5 23:07 virt -rw-r--r-- 1 root root 5941605 Jul 5 23:05 vmlinux [carlo@naomi linux-2.6.34-ARCH]$ Linux kernel patching + kernel vanilla BSP patches • Download the new kernel from www.kernel.org • Kernel untar: • $ tar -xvf <dir>/linux-2.6.33.tar.bz2 • Kernel patch: • $ bunzip2 -c <BSP>/linux-2.6.33-BSP.patch.bz2 | patch -p1 MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Linux kernel configuration configuration Generic linux kernel Specific linux kernel (hw) • Kernel configuration: • $ make ARCH=arm CROSS_COMPILE=${CROSS_COMPILER} target_defconfig • $ make ARCH=arm CROSS_COMPILE=${CROSS_COMPILER} menuconfig • Decide if module or not MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Linux kernel cross-compiling modules .ko ./linux-2.6.x/driver/... cross-compiling linux kernel zImage ./linux-2.6.x/arch/boot/zImage • Kernel compiling: • $ make ARCH=arm CROSS_COMPILE=${CROSS_COMPILER} • In computing, a loadable kernel module (or LKM) is an object file that contains code to extend the running kernel, or so-called base kernel, of an operating system • Without loadable kernel modules, an operating system would have to have all possible anticipated functionality already compiled directly into the base kernel. Much of that functionality would reside in memory without being used, wasting memory, and would require that users rebuild and reboot the base kernel every time new functionality is desired • Module loading • $ insmod ${MODULE}.ko • Module unloading • $ rmmod ${MODULE}.ko MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Host and Target zImage + rootfs host target • Cross-compiling the kernel • Cross-compiling the root filesystem • Executing ETHERNET RS232 BOOTLOADER JTAG MEMORY MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Transfer zImage + rootfs to target • How to transfer zImage and rootfs into target? • JTAG • USB/UART/ETH... connection in bootloader mode JTAG Programmer ELF/BIN/HEX Flash ELF/BIN/HEX USB/UART/... Bootloader Flash MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
TFTP • Trivial File Transfer Protocol (TFTP) is a file transfer protocol, with the functionality of a very basic form of File Transfer Protocol (FTP) • Due to its simple design, TFTP could be implemented using a very small amount of memory • TFTP is designed to be small and easy to implement, therefore, lacks most of the features of a regular FTP. TFTP only reads and writes files (or mail) from/to a remote server. It cannot list directories, and currently has no provisions for user authentication • TFTP used the ethernet port MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
zModem • Old communication protocol used in communication over serial connection • Dramatically improved performance compared to older protocols, ZMODEM also offered restartable transfers, auto-start by the sender, an expanded 32-bit CRC, and control character quoting MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Rootfs and wear leveling • Wear leveling (also written wear levelling) is a technique for prolonging the service life of some kinds of erasable computer storage media, such as Flash memory used in solid-state drives and USB Flash drives • Wear leveling attempts to work around limitations of these storage medias by arranging data so that erasures and re-writes are distributed evenly across the medium. In this way, no single erase block prematurely fails due to a high concentration of write cycles • CRAMFS • The compressed ROM file system (or cramfs) is a free (GPL'ed) read-only Linux file system designed for simplicity and space-efficiency. It is mainly used in embedded systems and small-footprint systems • JFFS • The Journalling Flash File System (or JFFS) is a log-structured file system for use on NOR flash memory devices on the Linux operating system • JFFS2 • Journalling Flash File System version 2 or JFFS2 is a log-structured file system for use in flash memory devices. It is the successor to JFFS. JFFS2 has been included in the Linux kernel since the 2.4.10 release. JFFS2 is also available for Open Firmware, the eCos RTOS and the RedBootbootloader MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
NFS • Flash the whole rootfs every time we change something is a waste of time Network File System (NFS) is a network file system protocol originally developed by Sun Microsystems in 1984, allowing a user on a client computer to access files over a network in a manner similar to how local storage is accessed Host Target Flash rootfs rootfs NFS + ETH cross-tools MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Loadable Kernel Modules • The Linux kernel is what's known as a monolithic kernel, which means that the majority of the operating system functionality is called the kernel and runs in a privileged mode. This differs from a micro-kernel, which runs only basic functionality as the kernel and pushes other functionality outside the privileged space. • Linux can be dynamically altered at run time through the use of Linux kernel modules (LKMs) • Dynamically alterable means that you can load new functionality into the kernel, unload functionality from the kernel, and even add new LKMs that use other LKMs. The advantage to LKMs is that you can minimize the memory footprint for a kernel, loading only those elements that are needed (which can be an important feature in embedded systems) Applications syscalls Kernel LKM CPU Memory Devices MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
LKM • The skeleton of a LKM: #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> static int hello3_data __initdata = 3; static int __init hello_3_init(void) { printk(KERN_ALERT "Hello, world %d\n", hello3_data); return 0; } static void __exit hello_3_exit(void) { printk(KERN_ALERT "Goodbye, world 3\n"); } module_init(hello_3_init); module_exit(hello_3_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR(“Author”); Module constructor /destructor Entry/exit macros Module macros MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
LKM lifecycle insmod rmmod utilities User-Space init_module delete_module System Calls sys_init_module sys_delete_module Kernel-Space Kernel Functions ... ... MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
user-space / kernel-space communication • How it it possible to transfer data between user-space and kernel-space? • Procfs / Sysfs • Character / block devices • Socket • Ioctl • Syscalls • Signals • Mmap MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
Procfs [carlo@naomi ~]$ cat /proc/version Linux version 2.6.34-ARCH (tobias@T-POWA-LX) (gcc version 4.5.0 20100610 (prerelease) (GCC) ) #1 SMP PREEMPT Mon Jul 5 21:03:38 UTC 2010 #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/utsname.h> static intversion_proc_show(structseq_file *m, void *v){ seq_printf(m, linux_proc_banner, utsname()->sysname, utsname()->release, utsname()->version); return 0; } static intversion_proc_open(structinode *inode, struct file *file){ return single_open(file, version_proc_show, NULL); } static const structfile_operationsversion_proc_fops = { .open = version_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init proc_version_init(void){ proc_create("version", 0, NULL, &version_proc_fops); return 0; } module_init(proc_version_init); MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
ioctl • In computing, ioctl, short for input/output control, is a system call for device-specific operations and other operations which cannot be expressed by regular system calls • Uses: • The most common use of ioctls is to control hardware devices • One use of ioctls exposed to end-user applications is terminal I/O • When applications need to extend the kernel, for instance to accelerate network processing, ioctl calls provide a convenient way to bridge userspace code to kernel extensions • A Unix ioctl call takes as parameters: • An open file descriptor • A request code number • Either an integer value, possibly unsigned (going to the driver) or a pointer to data (either going to the driver, coming back from the driver, or both). MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
ioctl in kernel-space #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #include "chardev.h“ #define SUCCESS 0 #define DEVICE_NAME "char_dev“ #define BUF_LEN 80 ... structfile_operations Fops = { .read = device_read, .write = device_write, .ioctl = device_ioctl, .open = device_open, .release = device_release, /* a.k.a. close */ }; ... intinit_module(){ intret_val; ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); if (ret_val < 0) { printk(KERN_ALERT "%s failed with %d\n“,"Sorry, registering the character device ", ret_val); return ret_val; } return 0; } MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
ioctl in kernel-space intdevice_ioctl(structinode *inode, struct file *file, unsigned intioctl_num, unsigned long ioctl_param) { inti; char *temp; char ch; switch (ioctl_num) { case IOCTL_SET_MSG: temp = (char *)ioctl_param; get_user(ch, temp); for (i = 0; ch && i < BUF_LEN; i++, temp++) get_user(ch, temp); device_write(file, (char *)ioctl_param, i, 0); break; case IOCTL_GET_MSG: i = device_read(file, (char *)ioctl_param, 99, 0); put_user('\0', (char *)ioctl_param + i); break; case IOCTL_GET_NTH_BYTE: return Message[ioctl_param]; break; } return SUCCESS; } MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>
ioctl in user-space #include "chardev.h“ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> ... ioctl_set_msg(intfile_desc, char *message) { intret_val; ret_val = ioctl(file_desc, IOCTL_SET_MSG, message); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); exit(-1); } } ... main(){ intfile_desc, ret_val; char *msg = "Message passed by ioctl\n"; file_desc = open(DEVICE_FILE_NAME, 0); if (file_desc < 0) { printf("Can't open device file: %s\n", DEVICE_FILE_NAME); exit(-1); } ioctl_get_nth_byte(file_desc); ioctl_get_msg(file_desc); ioctl_set_msg(file_desc, msg); close(file_desc); } MPHS - AA. 2010/2011 Carlo Caione <carlo.caione@unibo.it>