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.