Name

HAL Port — Implementation Details

Description

This documentation explains how the eCos HAL specification has been mapped onto the ARM hardware and should be read in conjunction with the relevant Architecture Reference Manual and the Technical Reference Manual for the revision of the ARM architecture being used. It should be noted that the architectural HAL is usually complemented by a variant HAL and a platform HAL, and those may affect or redefine some parts of the implementation.

Exports

The architectural HAL provides header files cyg/hal/hal_arch.h, cyg/hal/hal_intr.h, cyg/hal/hal_io.h and cyg/hal/hal_mmu.h. These header files export the functionality provided by all the ARM HALs for a given target, automatically including headers from the lower-level HALs as appropriate. For example the platform HAL may provide a header cyg/hal/plf_io.h containing additional I/O functionality, but that header will be automatically included by cyg/hal/hal_io.h so there is no need to include it directly.

Additionally, the architecture HAL provides the cyg/hal/basetype.h header, which defines the basic properties of the architecture, including endianness, data type sizes and alignment constraints.

Startup

The architectural HAL provides a default implementation of the low-level startup code which will be appropriate in nearly all scenarios. For a ROM startup this includes copying initialized data from flash to RAM. For all startup types it will involve zeroing BSS regions and setting up the general C environment. It will also set up the initial exception priorities, switches the CPU into the correct execution mode, enables the debug monitor and enables error exception handling.

In addition to the setup it does itself, the initialization code calls out to the variant and platform HALs to perform their own initialization via the hal_hardware_init() function.

The architectural HAL also initializes the VSR and virtual vector tables, sets up HAL diagnostics, and invokes C++ static constructors, prior to calling the first application entry point cyg_start. This code resides in src/vectors.S.

Interrupts and Exceptions

The eCos interrupt and exception architecture is built around a table of pointers to Vector Service Routines that translate hardware exceptions and interrupts into the function calls expected by eCos. The ARM vector table provides exactly this functionality, so it is used directly as the eCos VSR table. The HAL_VSR_GET and HAL_VSR_SET macros therefore manipulate the vector table directly. The hal_intr.h header provides definitions for all the standard ARM exception vectors.

The vector table is constructed at runtime. For ROM, ROMRAM and SRAM startup all entries are initialized. For RAM startup only the interrupt vectors are (re-)initialized to point to the VSR in the loaded code, the exception vectors are left pointing to the VSRs of the loading software, usually RedBoot or GDB stubs.

When an exception occurs it is delivered via the relevant handler provided in vectors.S. The handler will save the CPU state and call exception_handler in hal_misc.c, which passes the exception on to either the kernel or the GDB stub handler. If it returns then the CPU state is restored and the code continued.

When an interrupt occurs it is delivered to a shared VSR, hal_default_irq_vsr, which saves some state and calls hal_IRQ_handler.

The architectural HAL provides default implementations of HAL_DISABLE_INTERRUPTS, HAL_RESTORE_INTERRUPTS, HAL_ENABLE_INTERRUPTS and HAL_QUERY_INTERRUPTS. These involve manipulation of the status register I flag. Similarly there are default implementations of the interrupt controller macros HAL_INTERRUPT_MASK, HAL_INTERRUPT_UNMASK, HAL_INTERRUPT_ACKNOWLEDGE and HAL_INTERRUPT_CONFIGURE macros.

HAL_INTERRUPT_SET_LEVEL manipulates the relevant interrupt priority registers. The valid range of interrupts supported depends on the number of interrupt priority bits supported by the CPU variant.

Stacks and Stack Sizes

cyg/hal/hal_arch.h defines values for minimal and recommended thread stack sizes, CYGNUM_HAL_STACK_SIZE_MINIMUM and CYGNUM_HAL_STACK_SIZE_TYPICAL. These values depend on a number of configuration options.

A number of system stacks are provided, and their properties controlled in this package's configuration. By default, the ARM HAL will use a separate stack for calling interrupt handlers. This separate interrupt stack means that the worst case overhead of interrupt handling does not need to be considered when determining each thread's maximum stack usage, which reduces overall stack overhead. The size of this interrupt stack is controlled by the common HAL's configuration (CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE) or can be disabled entirely by turning off CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK.

System startup code will also run on the interrupt stack, if enabled, as it is usually sufficiently large for this. Optionally, a separate startup stack can be enabled in this HAL by disabling CYGIMP_HAL_ARM_INT_STACK_IS_STARTUP_STACK, in which case when control is passed to the application by the cyg_start() or cyg_user_start() entry points, this startup stack will then be used. Alternatively, if the interrupt stack has been disabled entirely then a startup stack must be present, and will be used for all initialisation. Its size can be set with CYGNUM_HAL_ARM_STARTUP_STACK_SIZE. Note that global C++ object constructors defined by either the system, or in application code, will have their constructors run on the interrupt stack. Using the C library startup package's "Invoke default static constructors" option (CYGSEM_LIBC_INVOKE_DEFAULT_STATIC_CONSTRUCTORS) would instead ensure the user application constructors are called in the context of main(), which can be more appropriate.

If including GDB stubs in the application, then a separate GDB stub stack is required in order to guarantee that application problems with stack use will not prevent the GDB stub being able to debug the application. Again the size is controlled via this package's CDL (CYGNUM_HAL_ARM_GDB_STACK_SIZE)

Separate small stacks are also created to do the initial handling of Abort Prefetch, Abort Data, Undefined Instruction exceptions, as well as IRQ and FIQ interrupts. Assuming the default eCos VSRs are in place for these exceptions/interrupts, these small stacks are only used very temporarily until the context is switched to supervisor (SVC) mode.

At that point, in the case of the first three exceptions, if GDB stubs are included, the stack then used will be the GDB stack mentioned above. Alternatively, in the case of the first three exceptions without GDB stubs, the stack used will be that of the supervisor mode (SVC) context at the time of the exception. This is usually the running thread, but can also be a DSR running on the interrupt stack.

In the case of the IRQ and FIQ interrupts, these small stacks are only used by the default eCos VSRs temporarily until the stack is switched to the interrupt stack (or if that is disabled, the stack of the interrupted thread).

The above describes the situation when using the normal eCos VSRs for handling the Abort Data, Abort Prefetch, Undefined Instruction, IRQ and FIQ exceptions/interrupts. However if the user overrides the eCos VSRs with their own VSRs, then it may be necessary to change the stack sizes for these contexts depending on the stack use by those new VSRs. Therefore each of the stack sizes corresponding to these exception/interrupt contexts can be changed in the ARM HAL package configuration.

Thread Contexts and setjmp/longjmp

cyg/hal/hal_arch.h defines a thread context data structure, the context-related macros, and the setjmp/longjmp support. The implementations can be found in src/context.S.

Bit Indexing

The architectural HAL provides implementations in the source file hal_misc.c that are referenced by the HAL_LSBIT_INDEX and HAL_MSBIT_INDEX macros.

Idle Thread Processing

Normally the variant HAL provides the HAL_IDLE_THREAD_ACTION implementation. It usually implements code that can be used to put the CPU into a low power mode ready to respond quickly to the next interrupt.

Clock Support

The architectural HAL provides default implementations of the various system clock macros such as HAL_CLOCK_INITIALIZE. The variant or platform HAL are responsible for providing the necessary implementation routines.

HAL I/O

The ARM architecture does not have a separate I/O bus. Instead all hardware is assumed to be memory-mapped. Further it is assumed that all peripherals on the memory bus will switch endianness with the processor and that there is no need for any byte swapping. Hence the various HAL macros for performing I/O simply involve pointers to volatile memory.

The variant and platform files included by the cyg/hal/hal_io.h header will typically also provide details of some or all of the peripherals, for example register offsets and the meaning of various bits in those registers.

Cache Handling

The architecture HAL does not provide direct support for dealing with caches, since there is no common mechanism for doing this. The cache support is the responsibility of the variant HAL to, which will supply the cyg/hal/hal_cache.h header.

Linker Scripts

The architectural HAL will generate the linker script for eCos applications. This involves the architectural file src/arm.ld and a .ldi memory layout file, typically provided by the platform HAL. It is the .ldi file which places code and data in the appropriate places for the startup type, but most of the hard work is done via macros in the arm.ld file.

Diagnostic Support

The architectural HAL implements diagnostic support for DCC output if available, or for discarding all output. However, by default, the diagnostics output is left to the variant or platform HAL, depending on whether suitable peripherals are available on-chip or off-chip. The CYGHWR_HAL_ARM_DIAGNOSTICS_INTERFACE can be configured to direct the diagnostic output support used to the appropriate destination.

SMP Support

The ARM architectural HAL provides SMP support for Cortex-A class processors. If the configuration option CYGPKG_HAL_SMP_SUPPORT is enabled then the hal_smp.h header defines the standard SMP macros described in the HAL documentation. The architectural HAL only provides the SMP components that are common to all CPUs. It is the responsibility of variant and platform HALs to complete SMP support.

The variant HAL needs to supply a number of services for SMP. Access to the interrupt controller needs to be multi-core safe. The design of the standard ARM GIC provides this by default, but other controllers may need a spinlock. MMU and cache support are linked since any memory containing a spinlock must be cached and marked shareable. The variant HAL should also contain cyg_hal_cpu_start(), which is used to start up the secondary CPUs and cyg_hal_smp_start(), which is the initial entry point for secondary CPUs. It must also supply cyg_hal_cpu_message(), and associated ISR and DSR, which are used to pass scheduling messages between CPUs.

The platform HAL (which may comprise more than one layer of hardware specific HALs) is responsible for the memory map and initialization. Initialization will usually involve starting clocks, setting up pin multiplexing and configuring the CPU state. Most of this is common to single and multi-core configurations, although there will be some SMP specific settings. This HAL will also need to supply the PLATFORM_SETUP_CPU macro to initialize the secondary CPUs.

Debug Support

The architectural HAL provides basic support for gdb stubs using the debug monitor exceptions. Breakpoints are implemented using a fixed-size list of breakpoints, as per the configuration option CYGNUM_HAL_BREAKPOINT_LIST_SIZE. When a JTAG device is connected to a ARM device, it will steal breakpoints and other exceptions from the running code. Therefore debugging from RedBoot or the GDB stubs can only be done after detaching any JTAG debugger and power-cycling the board.

HAL_DELAY_US() Macro

The variant or platform HAL is responsible for providing an implementation of the HAL_DELAY_US macro. The system timer must be initialized before this macro is used. The include/hal_intr.h defined HAL_CLOCK_INITIALIZE() macro is called during initialization after the variant and platform initialization functions are called, but before constructors are invoked.

Profiling Support

When using local memory based profiling the ARM architectural HAL implements the mcount function, allowing profiling tools like gprof to determine the application's call graph. It does not implement the profiling timer. Instead that functionality needs to be provided by the variant or platform HAL.