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.
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.
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
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
cyg_start. This code resides
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
HAL_VSR_SET macros therefore manipulate the
directly. The hal_intr.h
header provides definitions for all the standard ARM exception
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
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
The architectural HAL provides default implementations of
HAL_QUERY_INTERRUPTS. These involve manipulation
of the status register I flag. Similarly there are
default implementations of the interrupt controller
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
Stacks and Stack Sizes
values for minimal and recommended thread stack
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
CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE) or can be
disabled entirely by turning off
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
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
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
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
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
support. The implementations can be found
The architectural HAL provides implementations in the source
file hal_misc.c that are referenced by
Idle Thread Processing
Normally the variant HAL provides
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.
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.
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.
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.
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.
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.
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
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.
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
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.
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
HAL_CLOCK_INITIALIZE() macro is called
during initialization after the variant and platform initialization
functions are called, but before constructors are invoked.
When using local memory based profiling the ARM architectural HAL
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.