Kernel porting notes

This section briefly describes the issues involved in porting eCos to a new target platform and/or architecture.

Porting overview

The effort required to port eCos to a new target varies. Adding support for a new platform/board may require almost no effort, while adding support for a new architecture is more demanding. Additionally, new device drivers may have to be written if there is no existing support for the target’s devices.

Given that there are usually more target platforms using the same microprocessor or microcontroller, adding eCos support for a new target would often be a question of adding support for the new target platform. The architectures supported by eCos include the following: ARM7, MIPS (TX39), MN10300, PowerPC (MPC8xx), and SPARClite.

Adding a new architecture support is a bigger job and also requires tool support (GCC, GDB and binutils) which is a big undertaking in itself.

Platform support

Adding support for a new platform requires (a subset of):

  1. Adding eCos configuration information.

  2. Memory layout description.

  3. Memory controller initialization.

  4. Interrupt controller handling.

  5. Minimal serial device driver for GDB interaction and simple diagnostics output.

    System timer initialization and control.

    Wallclock driver.

    A wallclock emulation based on the system timer is provided with the standard eCos distribution. For those hardware platforms where a battery backed-up clock device or other means of determining actual wallclock time exists, a wallclock driver may be implemented more fully.

If the architecture in question is a microcontroller (as opposed to a microprocessor), the job of porting may be as simple as adding configuration information and defining a new memory layout (items one and two). Currently eCos supports the following microcontrollers: MN10300, MPC8xx, and TX39.

Architectural support

Adding support for a new architecture requires:

  1. Adding eCos configuration information.

  2. Writing a HAL for the CPU core"s register model, interrupt and exception model, cache model, and possibly simple handling for the MMU model.

  3. For microcontrollers the HAL should also support the memory controller, interrupt controller and a possible on-MCP serial controller for GDB interaction and simple diagnostics output, system timer initialization and control, and a wallclock driver.

If there is already support for a member of the same architecture family, the porting job may just consist of adding extra feature support to the existing HAL. Or if the new target architecture only defines a subset of the architecture family, the HAL may need additional configuration control, allowing parts of the existing HAL code to be disabled.

Adding configuration information

Architecture and platform configuration information resides in two top-level files targets and packages as well as in architecture and platform specific configuration files (hal/<arch>/arch/current/include/pkgconf/hal_<arch>.h and hal/<arch>/<platform>/current/include/pkgconf/hal_<arch>_<platform>.h. Furthermore, each platform must define memory layouts for each startup type.

targets

Architecture and platform information must be added to the targets file.

target powerpc {
    alias               { PowerPC powerpc-eabi }
    command_prefix      powerpc-eabi
    packages            { CYGPKG_HAL_POWERPC }
    hal                 hal/powerpc/arch

    cflags {
        ARCHFLAGS       "-mcpu=860 -D_SOFT_FLOAT"
        ERRFLAGS        "-Wall -Wpointer-arith -Wstrict-prototypes -Winline -Wundef"
        CXXERRFLAGS     "-Woverloaded-virtual"
        LANGFLAGS       "-ffunction-sections -fdata-sections"
        DBGFLAGS        "-g -O2"
        CXXLANGFLAGS    "-fno-rtti -fno-exceptions -fvtable-gc -finit-priority"
        LDLANGFLAGS     "-Wl,--gc-sections -Wl,-static"
    }

    platform cogent {
        alias           { "Cogent board" }
        startup         { ram rom stubs }
        packages        {
            CYGPKG_HAL_POWERPC_COGENT
            CYGPKG_DEVICES_WALLCLOCK
            CYGPKG_DEVICES_WATCHDOG
        }
    }
}           

pkgconf uses the entries in targets to create a build tree. The --target option matches target name (powerpc) or its aliases (PowerPC powerpc-eabi), just as the --platform option matches platform name (cogent) or its aliases (Cogent board). The same is true for the --startup option which matches on the list of valid startup types (ram, rom and stubs).

The command_prefix is the prefix on the cross compiler tools, usually the same target triplet used when configuring the tools (powerpc-eabi).

packages lists the hardware-related packages that should be enabled if this target is selected. Typically this will just be the appropriate architectural HAL package provided for this architecture (CYGPKG_HAL_POWERPC), while hal specifies the relative path of the source files.

cflags specifies the compiler and linker flags. The -finit-priority flag is required for proper initialization of eCos, while -ffunction-sections, -fdata-sections, and -Wl,--gc-sections are required to provide linker garbage collection which removes functions and initialized data that are not going to be used. The other FLAGS definitions can be set according to preference, taking care to ensure that ARCHFLAGS contains all necessary flags for the particular architecture.

The platform option is used to define a new target platform. There can be several of these for each architecture. The name and startup types are defined using platform, alias, and startup as described above. packages defines the set of packages supported by this particular platform. This set must include the platform HAL package (CYGPKG_HAL_POWERPC_COGENT), but can name other packages (CYGPKG_DEVICES_WALLCLOCK and CYGPKG_DEVICES_WATCHDOG) which will be enabled per default when selecting this architecture/platform configuration.

packages

The individual packages must be defined in the packages file.

package CYGPKG_HAL_POWERPC {
        alias           { "PowerPC common HAL" hal_powerpc powerpc_hal powerpc_arch_hal }
        directory       hal/powerpc/arch
        include_dir     cyg/hal
        hardware
}

package CYGPKG_HAL_POWERPC_COGENT {
        alias           { "PowerPC Cogent board support" hal_powerpc_cogent powerpc_cogent_hal }
        directory       hal/powerpc/cogent
        include_dir     cyg/hal
        hardware
} 

These are the definitions of the two packages named in the targets file. The aliases can be used with the --disable- and --enable- options of pkgconf.

directory specifies the relative path of the source files, include_dir where header files provided by the package should be copied to in the install directory, and hardware specifies that these packages is normally associated with specific hardware and should only be enabled for the appropriate hardware.

Package-specific configuration

The package-specific configuration files provide presentation information used by the Configuration Tool, dependencies on other packages and of course additional fine-grained options that are architecture and/or target specific. See the two files hal/powerpc/arch/current/include/pkgconf/hal_powerpc.h and hal/powerpc/cogent/current/include/pkgconf/hal_powerpc_cogent.h for an example.

Memory layout information

For each target platform must be defined the memory layout used for any given startup type. This information resides in two files mlt_<arch>_<platform>_<startup>.ldi and mlt_<arch>_<platform>_<startup>.mlt in the directory hal/<arch>/<platform>/current/include/pkgconf/. The former is a linker script fragment, the latter a file describing the layout for the eCos Configuration Tool.

Redefining the memory layout can be done in the Configuration Tool, which will create the linker script (the .ldi file). It is also possible to do by hand, in which case only the linker script should be created; when no .mlt file exists, the Configuration Tool will not overwrite the default linker script.

Platform porting

Platform porting basically consists of making a copy of an existing platform directory and changing the code to match the new platform. The header and source files in the platform directory and their contents are described in the section called Architectural HAL files.

In particular the configuration information and memory layout need changing, as may the board initialization code and the minimal serial drivers used by hal_diag.c and plf_stub.c.

Another useful reference for porting to a new platform is the GNUPro documentation on gdb stubs, which can be found at http://www.cygnus.com/pubs/gnupro/3_GNUPro_Debugging_Tools/b_Debugging_with_GDB/gdbThe_GDB_remote_serial_protocol.html

Architectural porting

The easiest way to make a new architectural port of eCos is to make a copy of an existing HAL and change the code to suit the new CPU. This guide will use the PowerPC Cogent board as an example. Wherever powerpc, ppc, or cogent is mentioned in this guide or in the source files, you should replace the strings with appropriate architecture and platform names. There are also a few files that need renaming.

If there is simulator support for the new CPU it is possible to test big parts of the HAL and the rest of the eCos kernel before a port to a specific platform is attempted. This is an advantage as doing a platform port can cause problems of it own, making it difficult to determine whether the architectural or platform parts of the port in progress are to blame when something is not working properly.

When no simulator support exists, the starting point of a port is to produce a minimal GDB stub for the target platform, which will allow code to be downloaded, executed and/or debugged on the board. This guide is based on a situation where no simulator exists as it would be the most likely scenario.

Writing an eCos GDB stub

A GDB stub has both a architectural part (description of the CPUs registers, exception decoding, breakpoint and stepping model, etc.) and a platform part (board initialization and simple serial driver).

Writing a stub is a subset of the work required to a full architectural and platform port of the HAL (and thus eCos). The below sections will be a rough list of minimal requirements for a stub; remaining elements of the files can be fleshed out when extending the port to include full eCos functionality. The files and their contents are described in the section called Architectural HAL files.

Tip: If the target board has an existing download stub (not necessarily GDB compliant), the GDB stub can be tested by changing it to run from RAM rather than ROM (using ram startup instead of stubs startup).

After downloading the stub and starting it, it should be possible to connect GDB to the target. Note that trying to download another application may cause the memory of the stub to be overwritten, so some consideration is required when defining the memory layout.

If the target board does not have an existing download stub and requires a new EPROM to be burned for each testing cycle, you may want to start with writing a minimal stub which can only be used for downloading data to the target board.

For this purpose you can skip the exception support code in vectors.S and hack hal/common/current/src/stubrom/stubrom.c to jump directly to the stub code without using a breakpoint.

Tip: While working on improving the stub code or other parts of the HAL you can use the simple diagnostics output functions (by way of diag_printf) as a crude way of providing debugging feedback until you get full GDB stub functionality in place.

Tip: A good way of debugging the stub itself is to enable remote debugging in GDB (set remotedebug 1). This makes GDB display any communication between itself and the stub on the target. Consult the GDB file remote.c for details on the protocol.

Architecture files

include/basetype.h

Implement in full. Little effort.

include/hal_arch.h

The following macros are required for the stub: HAL_SavedRegisters, HAL_BREAKPOINT, HAL_BREAKINST, HAL_BREAKINST_SIZE, HAL_GET_GDB_REGISTERS, and HAL_SET_GDB_REGISTERS.

include/hal_cache.h

The macros in this file can be left as empty if caches are kept disabled. This is definitely the best way to start porting, avoiding cache problems entirely. The cache is not of much use until eCos can be used with applications anyway.

include/hal_intr.h

It is necessary to implement enough exception handling code to properly handle breakpoints.

As the porting job progresses, asynchronous break points (CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT) may come in handy. These require a minimal interrupt system to be in place.

include/hal_io.h

Should be fully implemented. Usually zero effort.

include/<arch>_regs.h

Can be filled in piecemeal as the porting job progresses.

include/<arch>_stub.h

Redefine NUMREGS, REGSIZE, and regnames using the same register layout as GDB. The register definitions can be found in the config/<arch>/tm-<arch>.h file in the GDB sources. The definitions for the PowerPC were found in config/rs6000/tm-rs6000.h.

Discrepancies between what GDB expects and what is defined in the stub will show up when you use the info reg command in GDB (and know what the register contents on the target should be). Be careful to get the REGSIZE macro defined correctly.

src/context.c

Nothing here required by the stub.

src/hal_misc.c

The two functions cyg_hal_invoke_constructors and cyg_hal_exception_handler must be implemented. The former is the same on most architectures and the latter just needs to call __handle_exception.

src/<arch>.ld

The linker script must be properly defined.

src/<arch>_stub.c

This file must be fully implemented.

__computeSignal can be defined to just return SIGTRAP as a minimal implementation. Proper signal decoding may help debugging though.

Single-stepping can be implemented in one of two ways. Some architectures (such as the PowerPC) have hardware support to control single-stepping making it simple to implement. Other architectures require use of breakpoints to implement the functionality, which requires instruction decoding. Examples of the latter approach can be found in the ARM, MIPS, and MN10300 stubs. Implementing instruction decoding obviously requires more effort.

src/vectors.S

This is the core file of the architecture HAL. It is hard to define what the minimal implementation requirements are for stubs to work. It may be worth and/or necessary to do a full implementation of this file to start with, but here are some pointers anyway.

_start as defined for the PowerPC is about the minimum requirement, but you can ignore MMU and cache setup while working on the stub.

__default_exception_vsr and restore_state must preserve enough state to allow breakpoints without trashing CPU state for the application code. If you need asynchronous GDB breakpoints __default_interrupt_vsr must also be defined well enough to allow interrupts without trashing the CPU state of the interrupted application code.

Assorted tables also need to be defined, depending on how much of the exception and interrupt handlers is implemented.

Platform files

include/hal_diag.h

Shouldn"t require any changes.

include/plf_stub.h

This file provides the interface to the platform stub functions for the generic-stub.c code.

The minimal stub (no asynchronous GDB breakpoints) only requires HAL_STUB_PLATFORM_INIT_SERIAL, HAL_STUB_PLATFORM_GET_CHAR, and HAL_STUB_PLATFORM_PUT_CHAR and the matching functions in plf_stub.c to be defined.

src/<platform>.c

This file defines hal_hardware_init which takes care of initializing the board. For the Cogent board this includes watchdog initialization and memory controller setup. Other boards may have different requirements.

src/hal_diag.c

This file defines three functions that provide simple diagnostics output; hal_diag_init, hal_diag_write_char, and hal_diag_read_char. Normally these would implement a very simple serial driver. They could also address an LCD or just some LEDs.

The simple serial driver for the Cogent board is implemented in a separate file, cma_ser.c, which is shared with the plf_stub.c file.

src/plf_stub.c

This file implements the serial driver needed by the GDB stub. The minimal stub only requires init, putc, and getc functions. A stub which supports asynchronous breakpoints also requires functions to handle serial interrupts. For example implementations see cma_ser.c or the plf_stub.c file for the MN10300 stdeval1 board.

Building the stub

  1. Prepare a build directory, configuring eCos for stubs startup.

  2. Disable all packages except eCos common HAL, infrastructure, <arch> common HAL, and <arch> <platform> board support.

  3. Disable the HAL common options CYGFUN_HAL_COMMON_KERNEL_SUPPORT and CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT.

    Enable the HAL common option CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS.

  4. Build libtarget.

  5. Change to the directory hal/common/current/src/stubrom and type make. This should result in an eCos GDB stub image file called stubrom. This can be converted to SRECord or binary format (using objcopy) which can be used by EPROM burner or PROM emulator software.

Filling in the blanks

When a GDB stub has been written and is working, finishing the HAL port is pretty much a question of completing the header files and writing the functions that were not needed for the stub.