BSP and Device Driver Development Guide¶
BSP and Device Driver Development Guide¶
COPYRIGHT (c) 1988 - 2015.On-Line Applications Research Corporation (OAR).
The authors have used their best efforts in preparing this material. These efforts include the development, research, and testing of the theories and programs to determine their effectiveness. No warranty of any kind, expressed or implied, with regard to the software or the material contained in this document is provided. No liability arising out of the application or use of any product described in this document is assumed. The authors reserve the right to revise this material and to make changes from time to time in the content hereof without obligation to notify anyone of such revision or changes.
The RTEMS Project is hosted at http://www.rtems.org. Any inquiries concerning RTEMS, its related support components, or its documentation should be directed to the Community Project hosted at http://www.rtems.org.
RTEMS Online Resources
Home | https://www.rtems.org/ |
Developers | https://devel.rtems.org/ |
Documentation | https://docs.rtems.org/ |
Bug Reporting | https://devel.rtems.org/query |
Mailing Lists | https://lists.rtems.org/ |
Git Repositories | https://git.rtems.org/ |
1. Introduction¶
Before reading this documentation, it is strongly advised to read the RTEMS Development Environment Guide to get acquainted with the RTEMS directory structure. This document describes how to do a RTEMS Board Support Package, i.e. how to port RTEMS on a new target board. Discussions are provided for the following topics:
- RTEMS Board Support Package Organization
- Makefiles and the Linker Command Script
- Board Initialization Sequence
- Device Drivers:
- Console Driver
- Clock Driver
- Timer Driver
- Real-Time Clock Driver
- Non-Volatile Memory Driver
- Networking Driver
- Shared Memory Support Driver
- Analog Driver
- Discrete Driver
The original version of this manual was written by Geoffroy Montel <g_montel@yahoo.com>. When he started development of the gen68340 BSP, this manual did not exist. He wrote the initial version of this manual as the result of his experiences. At that time, this document was viewed internally as the most important “missing manual” in the RTEMS documentation set.
The gen68340 BSP is a good example of the life of an RTEMS BSP. It is based upon a part not recommended for new designs and none of the core RTEMS Project team members have one of these boards. Thus we are unlikely to perform major updates on this BSP. So as long as it compiles and links all tests, it will be available.
The RTEMS Project team members are always trying to identify common code across BSPs and refactoring the code into shared routines. As part of this effort, the we will enhance the common BSP Framework. Not surprisingly, not every BSP takes advantage of every feature in the framework. The gen68340 does not take advantage of as many features as the ERC32 BSP does. So in many ways, the ERC32 is a better example BSP at this point. But even the ERC32 BSP does not include examples of every driver template and framework available to the BSP author. So in this guide we will try to point out good examples from other BSPs.
Our goal is for you to be able to reuse as much code as possible and write as little board specific code as possible.
2. Target Dependent Files¶
RTEMS has a multi-layered approach to portability. This is done to maximize the amount of software that can be reused. Much of the RTEMS source code can be reused on all RTEMS platforms. Other parts of the executive are specific to hardware in some sense. RTEMS classifies target dependent code based upon its dependencies into one of the following categories.
- CPU dependent
- Board dependent
- Peripheral dependent
2.1. CPU Dependent¶
This class of code includes the foundation routines for the executive proper such as the context switch and the interrupt subroutine implementations. Sources for the supported processor families can be found in cpukit/score/cpu
. A good starting point for a new family of processors is the no_cpu
directory, which holds both prototypes and descriptions of each needed CPU dependent function.
CPU dependent code is further subcategorized if the implementation is dependent on a particular CPU model. For example, the MC68000 and MC68020 processors are both members of the m68k CPU family but there are significant differences between these CPU models which RTEMS must take into account.
The source code found in the cpukit/score/cpu
is required to only depend upon the CPU model variations that GCC distinguishes for the purposes of multilib’ing. Multilib is the term the GNU community uses to refer to building a single library source multiple times with different compiler options so the binary code generated is compatible. As an example, from GCC’s perspective, many PowerPC CPU models are just a PPC603e. Remember that GCC only cares about the CPU code itself and need not be aware of any peripherals. In the embedded community, we are exposed to thousands of CPU models which are all based upon only a relative small number of CPU cores.
Similarly for the SPARC/ERC32 BSP, the RTEMS_CPU
is specified as erc32
which is the name of the CPU model and BSP for this SPARC V7 system on chip. But the multilib variant used is actually v7
which indicates the ERC32 CPU core is a SPARC V7.
2.2. Board Dependent¶
This class of code provides the most specific glue between RTEMS and a particular board. This code is represented by the Board Support Packages and associated Device Drivers. Sources for the BSPs included in the RTEMS distribution are located in the directory c/src/lib/libbsp
. The BSP source directory is further subdivided based on the CPU family and BSP.
Some BSPs may support multiple board models within a single board family. This is necessary when the board supports multiple variants on a single base board. For example, the Motorola MVME162 board family has a fairly large number of variations based upon the particular CPU model and the peripherals actually placed on the board.
2.3. Peripheral Dependent¶
This class of code provides a reusable library of peripheral device drivers which can be tailored easily to a particular board. The libchip library is a collection of reusable software objects that correspond to standard controllers. Just as the hardware engineer chooses a standard controller when designing a board, the goal of this library is to let the software engineer do the same thing.
The source code for the reusable peripheral driver library may be found in the directory c/src/lib/libchip
. The source code is further divided based upon the class of hardware. Example classes include serial communications controllers, real-time clocks, non-volatile memory, and network controllers.
2.4. Questions to Ask¶
When evaluating what is required to support RTEMS applications on a particular target board, the following questions should be asked:
- Does a BSP for this board exist?
- Does a BSP for a similar board exists?
- Is the board’s CPU supported?
If there is already a BSP for the board, then things may already be ready to start developing application software. All that remains is to verify that the existing BSP provides device drivers for all the peripherals on the board that the application will be using. For example, the application in question may require that the board’s Ethernet controller be used and the existing BSP may not support this.
If the BSP does not exist and the board’s CPU model is supported, then examine the reusable chip library and existing BSPs for a close match. Other BSPs and libchip provide starting points for the development of a new BSP. It is often possible to copy existing components in the reusable chip library or device drivers from BSPs from different CPU families as the starting point for a new device driver. This will help reduce the development effort required.
If the board’s CPU family is supported but the particular CPU model on that board is not, then the RTEMS port to that CPU family will have to be augmented. After this is done, development of the new BSP can proceed.
Otherwise both CPU dependent code and the BSP will have to be written.
This type of development often requires specialized skills and there are people in the community who provide those services. If you need help in making these modifications to RTEMS try a search in a search engine with something like “rtems support”. The RTEMS Project encourages users to use support services however we do not endorse any providers.
2.5. CPU Dependent Executive Files¶
The CPU dependent files in the RTEMS executive source code are found in the following directory:
cpukit/score/cpu/<CPU>
where <CPU> is replaced with the CPU family name.
Within each CPU dependent directory inside the executive proper is a file named <CPU>.h
which contains information about each of the supported CPU models within that family.
2.6. CPU Dependent Support Files¶
The CPU dependent support files contain routines which aid in the development of applications using that CPU family. For example, the support routines may contain standard trap handlers for alignment or floating point exceptions or device drivers for peripheral controllers found on the CPU itself. This class of code may be found in the following directory:
c/src/lib/libcpu/<CPU>
CPU model dependent support code is found in the following directory:
c/src/lib/libcpu/<CPU>/<CPU_MODEL>
<CPU_MODEL> may be a specific CPU model name or a name indicating a CPU core or a set of related CPU models. The file configure.ac
in each c/src/lib/libcpu/<CPU>
directory contains the logic which enables the appropriate subdirectories for the specific CPU model your BSP has.
2.7. Board Support Package Structure¶
The BSPs are all under the c/src/lib/libbsp
directory. Below this directory, there is a subdirectory for each CPU family. Each BSP is found under the subdirectory for the appropriate processor family (arm, powerpc, sparc, etc.). In addition, there is source code available which may be shared across all BSPs regardless of the CPU family or just across BSPs within a single CPU family. This results in a BSP using the following directories:
c/src/lib/libbsp/shared
c/src/lib/libbsp/<CPU>/shared
c/src/lib/libbsp/<CPU>/<BSP>
Under each BSP specific directory, there is a collection of subdirectories. For commonly provided functionality, the BSPs follow a convention on subdirectory naming. The following list describes the commonly found subdirectories under each BSP.
console
: is technically the serial driver for the BSP rather than just a console driver, it deals with the board UARTs (i.e. serial devices).clock
: support for the clock tick - a regular time basis to the kernel.timer
: support of timer devices.rtc
ortod
: support for the hardware real-time clock.nvmem
: support for non-volatile memory such as EEPROM or Flash.network
: the Ethernet driver.shmsupp
: support of shared memory driver MPCI layer in a multiprocessor system,include
: include files for this BSP.gnatsupp
: BSP specific support for the GNU Ada run-time. Each BSP that wishes to have the possibility to map faults or exceptions into Ada language exceptions or hardware interrupts into Ada interrupt tasks must provide this support.
There may be other directories in the BSP tree and the name should be indicative of the functionality of the code within that directory.
The build order of the BSP is determined by the Makefile structure. This structure is discussed in more detail in the Chapter 3 - Makefiles chapter.
This manual refers to the gen68340 BSP for numerous concrete examples. You should have a copy of the gen68340 BSP available while reading this piece of documentation. This BSP is located in the following directory:
c/src/lib/libbsp/m68k/gen68340
Later in this document, the $BSP340_ROOT label will be used to refer to this directory.
3. Makefiles¶
This chapter discusses the Makefiles associated with a BSP. It does not describe the process of configuring, building, and installing RTEMS. This chapter will not provide detailed information about this process. Nonetheless, it is important to remember that the general process consists of four phases as shown here:
bootstrap
configure
build
install
During the bootstrap phase, you are using the configure.ac
and Makefile.am
files as input to GNU autoconf and automake to generate a variety of files. This is done by running the bootstrap
script found at the top of the RTEMS source tree.
During the configure phase, a number of files are generated. These generated files are tailored for the specific host/target combination by the configure script. This set of files includes the Makefiles used to actually compile and install RTEMS.
During the build phase, the source files are compiled into object files and libraries are built.
During the install phase, the libraries, header files, and other support files are copied to the BSP specific installation point. After installation is successfully completed, the files generated by the configure and build phases may be removed.
3.1. Makefiles Used During The BSP Building Process¶
RTEMS uses the GNU automake and GNU autoconf automatic configuration package. Consequently, there are a number of automatically generated files in each directory in the RTEMS source tree. The bootstrap
script found in the top level directory of the RTEMS source tree is executed to produce the automatically generated files. That script must be run from a directory with a configure.ac
file in it. The bootstrap
command is usually invoked in one of the following manners:
bootstrap
to regenerate all files that are generated by autoconf and automake.bootstrap -c
to remove all files generated by autoconf and automake.bootstrap -p
to regeneratepreinstall.am
files.
There is a file named Makefile.am
in each directory of a BSP. This file is used by automake to produce the file named Makefile.in
which is also found in each directory of a BSP. When modifying a Makefile.am
, you can probably find examples of anything you need to do in one of the BSPs.
The configure process specializes the Makefile.in
files at the time that RTEMS is configured for a specific development host and target. Makefiles are automatically generated from the Makefile.in
files. It is necessary for the BSP developer to provide the Makefile.am
files and generate the Makefile.in
files. Most of the time, it is possible to copy the Makefile.am
from another similar directory and edit it.
The Makefile
files generated are processed when configuring and building RTEMS for a given BSP.
The BSP developer is responsible for generating Makefile.am
files which properly build all the files associated with their BSP. Most BSPs will only have a single Makefile.am
which details the set of source files to build to compose the BSP support library along with the set of include files that are to be installed.
This single Makefile.am
at the top of the BSP tree specifies the set of header files to install. This fragment from the SPARC/ERC32 BSP results in four header files being installed.
include_HEADERS = include/bsp.h
include_HEADERS += include/tm27.h
include_HEADERS += include/erc32.h
include_HEADERS += include/coverhd.h
When adding new include files, you will be adding to the set of include_HEADERS
. When you finish editing the Makefile.am
file, do not forget to run bootstrap -p
to regenerate the preinstall.am
.
The Makefile.am
also specifies which source files to build. By convention, logical components within the BSP each assign their source files to a unique variable. These variables which define the source files are collected into a single variable which instructs the GNU autotools that we are building libbsp.a
. This fragment from the SPARC/ERC32 BSP shows how the startup related, miscellaneous support code, and the console device driver source is managed in the Makefile.am
.
startup_SOURCES = ../../sparc/shared/bspclean.c ../../shared/bsplibc.c \
../../shared/bsppredriverhook.c \
../../shared/bsppost.c ../../sparc/shared/bspstart.c \
../../shared/bootcard.c ../../shared/sbrk.c startup/setvec.c \
startup/spurious.c startup/erc32mec.c startup/boardinit.S
clock_SOURCES = clock/ckinit.c
...
noinst_LIBRARIES = libbsp.a
libbsp_a_SOURCES = $(startup_SOURCES) $(console_SOURCES) ...
When adding new files to an existing directory, do not forget to add the new files to the list of files to be built in the corresponding XXX_SOURCES
variable in the Makefile.am
and run``bootstrap``.
Some BSPs use code that is built in libcpu
. If you BSP does this, then you will need to make sure the objects are pulled into your BSP library. The following from the SPARC/ERC32 BSP pulls in the cache, register window management and system call support code from the directory corresponding to its RTEMS_CPU
model.
libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/cache.rel \
../../../libcpu/@RTEMS_CPU@/reg_win.rel \
../../../libcpu/@RTEMS_CPU@/syscall.rel
The Makefile.am
files are ONLY processed by bootstrap
and the resulting Makefile.in
files are only processed during the configure process of a RTEMS build. Therefore, when developing a BSP and adding a new file to a Makefile.am
, the already generated Makefile
will not automatically include the new references unless you configured RTEMS with the --enable-maintainer-mode
option. Otherwise, the new file will not being be taken into account!
3.2. Creating a New BSP Make Customization File¶
When building a BSP or an application using that BSP, it is necessary to tailor the compilation arguments to account for compiler flags, use custom linker scripts, include the RTEMS libraries, etc.. The BSP must be built using this information. Later, once the BSP is installed with the toolset, this same information must be used when building the application. So a BSP must include a build configuration file. The configuration file is make/custom/BSP.cfg
.
The configuration file is taken into account when building one’s application using the RTEMS template Makefiles (make/templates
). These application template Makefiles have been included with the RTEMS source distribution since the early 1990’s. However there is a desire in the RTEMS user community to move all provided examples to GNU autoconf. They are included in the 4.9 release series and used for all examples provided with RTEMS. There is no definite time table for obsoleting them. You are free to use these but be warned they have fallen out of favor with many in the RTEMS community and may disappear in the future.
The following is a slightly shortened version of the make customization file for the gen68340 BSP. The original source for this file can be found in the make/custom
directory.
# The RTEMS CPU Family and Model
RTEMS_CPU=m68k
RTEMS_CPU_MODEL=m68340
include $(RTEMS_ROOT)/make/custom/default.cfg
# This is the actual bsp directory used during the build process.
RTEMS_BSP_FAMILY=gen68340
# This contains the compiler options necessary to select the CPU model
# and (hopefully) optimize for it.
CPU_CFLAGS = -mcpu=cpu32
# optimize flag: typically -O2
CFLAGS_OPTIMIZE_V = -O2 -g -fomit-frame-pointer
The make customization files have generally grown simpler and simpler with each RTEMS release. Beginning in the 4.9 release series, the rules for linking an RTEMS application are shared by all BSPs. Only BSPs which need to perform a transformation from linked ELF file to a downloadable format have any additional actions for program link time. In 4.8 and older, every BSP specified the “make executable” or make-exe
rule and duplicated the same actions.
It is generally easier to copy a make/custom
file from a BSP similar to the one being developed.
4. Linker Script¶
4.1. What is a “linkcmds” file?¶
The linkcmds
file is a script which is passed to the linker at linking time. This file describes the memory configuration of the board as needed to link the program. Specifically it specifies where the code and data for the application will reside in memory.
The format of the linker script is defined by the GNU Loader ld
which is included as a component of the GNU Binary Utilities. If you are using GNU/Linux, then you probably have the documentation installed already and are using these same tools configured for native use. Please visit the Binutils project http://sourceware.org/binutils/ if you need more information.
4.2. Program Sections¶
An embedded systems programmer must be much more aware of the placement of their executable image in memory than the average applications programmer. A program destined to be embedded as well as the target system have some specific properties that must be taken into account. Embedded machines often mean average performances and small memory usage. It is the memory usage that concerns us when examining the linker command file.
Two types of memories have to be distinguished:
- RAM - volatile offering read and write access
- ROM - non-volatile but read only
Even though RAM and ROM can be found in every personal computer, one generally doesn’t care about them. In a personal computer, a program is nearly always stored on disk and executed in RAM. Things are a bit different for embedded targets: the target will execute the program each time it is rebooted or switched on. The application program is stored in non-volatile memory such as ROM, PROM, EEPROM, or Flash. On the other hand, data processing occurs in RAM.
This leads us to the structure of an embedded program. In rough terms, an embedded program is made of sections. It is the responsibility of the application programmer to place these sections in the appropriate place in target memory. To make this clearer, if using the COFF object file format on the Motorola m68k family of microprocessors, the following sections will be present:
- code (
.text
) section: is the program’s code and it should not be modified. This section may be placed in ROM. - non-initialized data (
.bss
) section: holds uninitialized variables of the program. It can stay in RAM. - initialized data (
.data
) section: holds the initialized program data which may be modified during the program’s life. This means they have to be in RAM. On the other hand, these variables must be set to predefined values, and those predefined values have to be stored in ROM.
Note
Many programs and support libraries unknowingly assume that the .bss
section and, possibly, the application heap are initialized to zero at program start. This is not required by the ISO/ANSI C Standard but is such a common requirement that most BSPs do this.
That brings us up to the notion of the image of an executable: it consists of the set of the sections that together constitute the application.
4.3. Image of an Executable¶
As a program executable has many sections (note that the user can define their own, and that compilers define theirs without any notice), one has to specify the placement of each section as well as the type of memory (RAM or ROM) the sections will be placed into. For instance, a program compiled for a Personal Computer will see all the sections to go to RAM, while a program destined to be embedded will see some of his sections going into the ROM.
The connection between a section and where that section is loaded into memory is made at link time. One has to let the linker know where the different sections are to be placed once they are in memory.
The following example shows a simple layout of program sections. With some object formats, there are many more sections but the basic layout is conceptually similar.
.text | RAM or ROM |
.data | RAM |
.bss | RAM |
4.4. Example Linker Command Script¶
The GNU linker has a command language to specify the image format. This command language can be quite complicated but most of what is required can be learned by careful examination of a well-documented example. The following is a heavily commented version of the linker script used with the the gen68340
BSP This file can be found at $BSP340_ROOT/startup/linkcmds.
/*
* Specify that the output is to be coff-m68k regardless of what the
* native object format is.
*/
OUTPUT_FORMAT(coff-m68k)
/*
* Set the amount of RAM on the target board.
*
* NOTE: The default may be overridden by passing an argument to ld.
*/
RamSize = DEFINED(RamSize) ? RamSize : 4M;
/*
* Set the amount of RAM to be used for the application heap. Objects
* allocated using malloc() come from this area. Having a tight heap
* size is somewhat difficult and multiple attempts to squeeze it may
* be needed reducing memory usage is important. If all objects are
* allocated from the heap at system initialization time, this eases
* the sizing of the application heap.
*
* NOTE 1: The default may be overridden by passing an argument to ld.
*
* NOTE 2: The TCP/IP stack requires additional memory in the Heap.
*
* NOTE 3: The GNAT/RTEMS run-time requires additional memory in
* the Heap.
*/
HeapSize = DEFINED(HeapSize) ? HeapSize : 0x10000;
/*
* Set the size of the starting stack used during BSP initialization
* until first task switch. After that point, task stacks allocated
* by RTEMS are used.
*
* NOTE: The default may be overridden by passing an argument to ld.
*/
StackSize = DEFINED(StackSize) ? StackSize : 0x1000;
/*
* Starting addresses and length of RAM and ROM.
*
* The addresses must be valid addresses on the board. The
* Chip Selects should be initialized such that the code addresses
* are valid.
*/
MEMORY {
ram : ORIGIN = 0x10000000, LENGTH = 4M
rom : ORIGIN = 0x01000000, LENGTH = 4M
}
/*
* This is for the network driver. See the Networking documentation
* for more details.
*/
ETHERNET_ADDRESS =
DEFINED(ETHERNET_ADDRESS) ? ETHERNET_ADDRESS : 0xDEAD12;
/*
* The following defines the order in which the sections should go.
* It also defines a number of variables which can be used by the
* application program.
*
* NOTE: Each variable appears with 1 or 2 leading underscores to
* ensure that the variable is accessible from C code with a
* single underscore. Some object formats automatically add
* a leading underscore to all C global symbols.
*/
SECTIONS {
/*
* Make the RomBase variable available to the application.
*/
_RamSize = RamSize;
__RamSize = RamSize;
/*
* Boot PROM - Set the RomBase variable to the start of the ROM.
*/
rom : {
_RomBase = .;
__RomBase = .;
} >rom
/*
* Dynamic RAM - set the RamBase variable to the start of the RAM.
*/
ram : {
_RamBase = .;
__RamBase = .;
} >ram
/*
* Text (code) goes into ROM
*/
.text : {
/*
* Create a symbol for each object (.o).
*/
CREATE_OBJECT_SYMBOLS
/*
* Put all the object files code sections here.
*/
*(.text)
. = ALIGN (16); /* go to a 16-byte boundary */
/*
* C++ constructors and destructors
*
* NOTE: See the CROSSGCC mailing-list FAQ for
* more details about the "\[......]".
*/
__CTOR_LIST__ = .;
[......]
__DTOR_END__ = .;
/*
* Declares where the .text section ends.
*/
etext = .;
_etext = .;
} >rom
/*
* Exception Handler Frame section
*/
.eh_fram : {
. = ALIGN (16);
*(.eh_fram)
} >ram
/*
* GCC Exception section