HAL Port — Implementation Details
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
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
containing additional I/O functionality, but that header will be
cyg/hal/hal_io.h so there
is no need to include it directly.
Additionally, the architecture HAL provides
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
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
vectors.S. The handler will save the
CPU state and call
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
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
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
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 architectural HAL will generate the linker script for eCos
applications. This involves the architectural
src/arm.ld and a
memory layout file, typically provided by the platform HAL. It is
.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
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
can be configured to direct the diagnostic output support used to the
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
PLATFORM_SETUP_CPU macro to initialize the
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
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.
|2023-08-15||eCosPro Non-Commercial Public License|