Name
HAL Port — Implementation Details
Description
This documentation explains how the eCos HAL specification has been mapped onto the MIPS hardware and should be read in conjunction with the manuals for the processor in use. 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
and cyg/hal/hal_io.h
. These header
files export the functionality provided by all the MIPS 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 conventional bootstrap mechanism involves the CPU starting
execution at 0xBFC00000. Normally ROM or flash will be mapped
here and a ROM startup RedBoot or application will be linked to
start at this address. Some variants have an on-board boot ROM
that runs at this address, and RedBoot or applications must be
placed elsewhere in memory. In either case, execution must
normally start at the reset_vector
location
in vectors.S
.
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 may also set up the exception trampolines in low memory, initializing CP0 registers, the memory controller, interrupt controller caches, timers, MMU and FPU, mostly by invoking variant or platform HAL defined macros. Depending on the variant and platform, some of these things are initialized in assembly code during startup, others may be initialized in later C code.
In addition to the setup it does itself, the initialization code
calls out to the variant and platform HALs to perform their own
initialization. Full
initialization is handled by
hal_variant_init
and
hal_platform_init
. The former should handle
any further initialization of the CPU variant and on-chip
devices. The platform initialization routine will complete any
initialization needed for devices external to the microprocessor.
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/hal_misc.c
.
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 vector table is either constructed at runtime or is part of the initialized data of the executable. For ROM, ROMRAM and JTAG startup all entries are initialized. For RAM startup only the interrupt VSR table entry is (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 to a shared trampoline
routine, other_vector
which reads the Cause
register, isolates the ExcCode field and uses it to index the VSR
table and jump to the routine at the given offset. VSR table
entries usually point to
either __default_exception_vsr
or __default_interrupt_vsr
, which are
responsible for delivering the exception or interrupt to the
kernel.
The exception VSR, __default_exception_vsr
,
saves the CPU state on the thread stack, optionally switches to
the interrupt stack and
calls cyg_hal_exception_handler()
to pass
the exception on. Depending on the configuration, this routine
then partly decodes the exception and passes it on for FPU
emulation or exception handling, limited memory access errors,
GDB stub exception handling or application level handling. When
it finally returns the VSR jumps to code common with the
interrupt VSR to restore the interrupted state and resume
execution.
The interrupt VSR, __default_interrupt_vsr
,
saves the CPU state in the same way as the exception VSR,
increments the scheduler lock and switches to the interrupt
stack. It then calls two variant or platform supplied
macros, hal_intc_decode
and hal_intc_translate
to query the interrupt
controller for an interrupt number and then translate the
interrupt number into an interrupt table offset. This offset is
used to fetch an ISR from the interrupt handler table, and a data
pointer from the interrupt data table, and the ISR is called to
handle the interrupt. When the ISR returns, the stack pointer is
switched back to the thread stack
and interrupt_end()
called. This may result
in a thread context switch and the current thread may not resume
for some time. When it does, the interrupted CPU state is
restored from the thread stack and execution resumed from where
it was interrupted.
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 IE bit. Similarly there are
default implementations of the interrupt controller macros
HAL_INTERRUPT_MASK
,
HAL_INTERRUPT_UNMASK
and HAL_INTERRUPT_ACKNOWLEDGE
macros that
manipulate the Status register IM bits.
HAL_INTERRUPT_SET_LEVEL
and
HAL_INTERRUPT_CONFIGURE
are no-ops at the
architectural level. If a variant or platform contains an
external interrupt controller, then it should redefine these
macros to manipulate it.
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.
The MIPS architecture HAL has the option of either using thread
stacks for all exception and interrupt processing or implementing
a separate interrupt stack. The default is to use an interrupt
stack, since not doing so would require significantly larger
per-thread stacks. This can be changed with the configuration
option
CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK
.
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
. The context structure is
defined as a single structure used for all purposes: thread
context, exceptions and interrupts. However, not all fields will
be stored in all cases.
Bit Indexing
The architectural HAL provides inline assembler implementations of
HAL_LSBIT_INDEX
and
HAL_MSBIT_INDEX
which use algorithmic methods to
extract a bit index in constant time. Variant HALs for later versions
of the architecture can replace these with macros that use inline
assembly to use CLZ or other instructions.
Idle Thread Processing
The architecture HAL provides a default
HAL_IDLE_THREAD_ACTION
implementation that
simply spins. Variant and platform HALs can provide a replacement
if required.
Clock Support
The architectural HAL provides a default implementation of the
various system clock macros such as
HAL_CLOCK_INITIALIZE
. These macros use the
architecture defined CP0 Count and Compare registers to implement
the eCos system clock. The variant or platform HAL needs to
define CYGNUM_HAL_RTC_PERIOD
in terms of the
frequency supplied to the Count register.
HAL I/O
The MIPS 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 provides standard macros for dealing with both data and instruction caches. These macros make use of the CACHE instruction to affect cache contents. The architecture HAL does not provide support for enabling and disabling the caches, since there is no common mechanism for doing this; these must be implemented by the variant HAL.
Linker Scripts
The architecture HAL does not provide the main linker script,
this must be supplied by the variant HAL and the makefile rules
to generate the final target.ld
must be
included in the variant's CDL file.
Diagnostic Support
The architectural HAL does not implement diagnostic support. Instead this is left to the variant or platform HAL, depending on whether suitable peripherals are available on-chip or off-chip.
SMP Support
The MIPS architectural HAL does not provide any SMP support.
Debug Support
The architectural HAL provides basic support for gdb stubs using
the debug monitor exceptions. Breakpoints may be 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 MIPS 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.
Debug support depends on the exact CPU model. Older parts, pre MIPS32R2, use the BREAK instruction for breakpoints and rely on instruction analysis to plant a breakpoint for single step for both MIPS32 and MIPS16 instruction sets. CPUs with debug mode use the SDBBP instructions for breakpoints and the Debug register SSt bit to implement single step for both MIPS32 and microMIPS instruction sets.
HAL_DELAY_US() Macro
cyg/hal/hal_intr.h
provides a simple implementation of the
HAL_DELAY_US
macro based around reading the
system timer. The timer must therefore be initialized before this
macro is used, from either the variant or platform HAL
initialization routines.
Profiling Support
The MIPS 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.
2024-03-18 | eCosPro Non-Commercial Public License |