Architectural HAL files

hal/ARCH/arch/v1_1/include/basetype.h

This file defines the properties of the base architecture that are used to compile the portable parts of the kernel. It is included automatically by cyg/infra/cyg_type.h. The following definitions may be included.

Byte order

CYG_BYTEORDER

This defines the byte order of the target and must be set to either CYG_LSBFIRST or CYG_MSBFIRST.

Label translation

CYG_LABEL_NAME(name)

This is a wrapper used in some C and C++ files which specify labels defined in assembly code or the linker script. It need only be defined if the default implementation in cyg/kernel/ktypes.h, which passes the name argument unaltered, is inadequate. The most usual alternative definition of this macro prepends an underscore to the label name. This depends on the labeling convention of the tool set.

Base types

	      cyg_halint8
	      cyg_halint16
	      cyg_halint32
	      cyg_halint64
	      cyg_halcount8
	      cyg_halcount16
	      cyg_halcount32
	      cyg_halcount64
	      cyg_halbool
	      

These macros define the C base types that should be used to define variables of the given size. They only need to be defined if the default types specified in cyg/infra/cyg_type.h cannot be used. Note that these are only the base types, they will be composed with signed and unsigned to form full type specifications.

Atomic types

	      cyg_halatomic
	      CYG_ATOMIC

These types are guaranteed to be read or written in a single uninterruptible operation. It is architecture defined what size this type is, but it will be at least a byte.

hal/ARCH/arch/v1_1/include/hal_arch.h

This file contains definitions that are related to the basic architecture of the CPU.

Register save format


typedef struct HAL_SavedRegisters
{
/* architecture-dependent list of registers to be saved */    
} HAL_SavedRegisters;

This structure describes the layout of a saved machine state on the stack. Such states are saved during thread context switches, interrupts and exceptions. Different quantities of state may be saved during each of these, but usually a thread context state is a subset of the interrupt state which is itself a subset of an exception state. Where these states are significantly different, this structure should contain a union of the three states.

Thread context initialization

	    HAL_THREAD_INIT_CONTEXT( sp, arg, entry, id )

This macro initializes a thread's context so that it may be switched to by HAL_THREAD_SWITCH_CONTEXT(). The arguments are:

sp

A location containing the current value of the thread's stack pointer. This should be a variable or a structure field. The SP value will be read out of here and an adjusted value written back.

arg

A value that is passed as the first argument to the entry point function.

entry

The address of an entry point function. This will be called according the C calling conventions, and the value of arg will be passed as the first argument.

id

A thread id value. This is only used for debugging purposes, it is ORed into the initialization pattern for unused registers and may be used to help identify the thread from its register dump. The least significant 16 bits of this value should be zero to allow space for a register identifier.

Thread context switching

HAL_THREAD_SWITCH_CONTEXT( from, to )

This macro implements the thread switch code. The arguments are:

from

A pointer to a location where the stack pointer of the current thread will be stored.

to

A pointer to a location from where the stack pointer of the next thread will be read.

The state of the current thread is saved onto its stack, using the current value of the stack pointer, and the address of the saved state placed in *from. The value in *to is then read and the state of the new thread is loaded from it.

Note that interrupts are not disabled during this process, any interrupts that occur will be delivered onto the stack to which the current value of the CPU stack pointer points. Hence the stack pointer should never be invalid, or loaded with a value that might cause the saved state to become corrupted by an interrupt.

Bit indexing


HAL_LSBIT_INDEX( mask, index )
HAL_MSBIT_INDEX( mask, index )

These macros place in index the bit index of the least(most) significant bit in mask. Some architectures have instruction level support for one or other of these operations. If no architectural support is available, then these macros may call C functions to do the job.

Idle thread activity

HAL_IDLE_THREAD_ACTION( count )

It may be necessary under some circumstances for the HAL to execute code in the kernel idle thread's loop. An example might be to execute a processor halt instruction. This macro provides a portable way of doing this. The argument is a copy of the idle thread's loop counter, and may be used to trigger actions at longer intervals than every loop.

Reorder barrier

HAL_REORDER_BARRIER()

When optimizing the compiler can reorder code. In some parts of multi-threaded systems, where the order of actions is vital, this can sometimes cause problems. This macro may be inserted into places where reordering should not happen and prevents code being migrated across it by the compiler optimizer. It should be placed between statements that must be executed in the order written in the code.

Breakpoint support

HAL_BREAKPOINT( label )
HAL_BREAKINST
HAL_BREAKINST_SIZE

These macros provide support for breakpoints.

HAL_BREAKPOINT() executes a breakpoint instruction. The label is defined at the breakpoint instruction so that exception code can detect which breakpoint was executed.

HAL_BREAKINST contains the breakpoint instruction code as an integer value. HAL_BREAKINST_SIZE is the size of that breakpoint instruction in bytes. Together these may be used to place a breakpoint in any code.

GDB support


HAL_THREAD_GET_SAVED_REGISTERS( sp, regs )
HAL_GET_GDB_REGISTERS( regval, regs )
HAL_SET_GDB_REGISTERS( regs, regval )  

These macros provide support for interfacing GDB to the HAL.

HAL_THREAD_GET_SAVED_REGISTERS() extracts a pointer to a HAL_SavedRegisters structure from a stack pointer value. The stack pointer passed in should be the value saved by the thread context macros. The macro will assign a pointer to the HAL_SavedRegisters structure to the variable passed as the second argument.

HAL_GET_GDB_REGISTERS() translates a register state as saved by the HAL and into a register dump in the format expected by GDB. It takes a pointer to a HAL_SavedRegisters structure in the regs argument and a pointer to the memory to contain the GDB register dump in the regval argument.

HAL_SET_GDB_REGISTERS() translates a GDB format register dump into a the format expected by the HAL. It takes a pointer to the memory containing the GDB register dump in the regval argument and a pointer to a HAL_SavedRegisters structure in the regs argument.

Setjmp and longjmp support

HAL_JMP_BUF_SIZE
hal_jmp_buf[HAL_JMP_BUF_SIZE]
hal_setjmp( hal_jmp_buf env )
hal_longjmp( hal_jmp_buf env, int val )

These functions provide support for the C setjmp() and longjmp() functions. Refer to the C library for further information.

hal/ARCH/arch/v1_1/include/hal_intr.h

This file contains definitions related to interrupt handling.

Vector numbers

CYG_VECTOR_XXX
CYG_VSR_MIN
CYG_VSR_MAX
CYG_ISR_MIN
CYG_ISR_MAX
CYG_EXCEPTION_MIN
CYG_EXCEPTION_MAX
CYG_ISR_COUNT
CYG_VSR_COUNT
CYG_EXCEPTION_COUNT 

All possible interrupt and exception vectors should be specified here, together with maximum and minimum values for range checking.

There are two ranges of numbers, those for the vector service routines and those for the interrupt service routines. The relationship between these two ranges is undefined, and no equivalence should be assumed if vectors from the two ranges coincide.

The VSR vectors correspond to the set of exception vectors that can be delivered by the CPU architecture, many of these will be internal exception traps. The ISR vectors correspond to the set of external interrupts that can be delivered and are usually determined by extra decoding of an interrupt controller by the interrupt VSR.

Where a CPU supports synchronous exceptions, the range of such exceptions allowed are defined by CYG_EXCEPTION_MIN and CYG_EXCEPTION_MAX. The actual exception numbers will normally correspond to the VSR exception range. In future other exceptions generated by the system software (such as stack overflow) may be added.

CYG_ISR_COUNT, CYG_VSR_COUNT and CYG_EXCEPTION_COUNT define the number of ISRs, VSRs and EXCEPTIONs repectively for the purposes of defining arrays etc. There might be a translation from the supplied vector numbers into array offsets. Hence CYG_XXX_COUNT may not simply be CYG_XXX_MAX - CYG_XXX_MIN or CYG_XXX_MAX+1.

Interrupt state control

HAL_DISABLE_INTERRUPTS( old )
HAL_RESTORE_INTERRUPTS( old )
HAL_ENABLE_INTERRUPTS()

These macros provide control over the state of the CPU's interrupt mask mechanism. They should normally manipulate a CPU status register to enable and disable interrupt delivery. They should not access an interrupt controller.

HAL_DISABLE_INTERRUPTS()disables the delivery of interrupts and stores the original state of the interrupt mask in the variable passed in the old argument.

HAL_RESTORE_INTERRUPTS()restores the state of the interrupt mask to that recorded in old.

HAL_ENABLE_INTERRUPTS()simply enables interrupts regardless of the current state of the mask.

It is at the HAL implementer's discretion exactly which interrupts are masked by this mechanism. Where a CPU has more than one interrupt type that may be masked separately (e.g. the ARM's IRQ and FIQ) only those that can raise DSRs need to be masked here. A separate architecture specific mechanism may then be used to control the other interrupt types.

ISR and VSR management

HAL_INTERRUPT_ATTACH( vector, isr, data, object )
HAL_INTERRUPT_DETACH( vector, isr )
HAL_VSR_SET( vector, vsr, poldvsr )
HAL_VSR_GET( vector, pvsr )

These macros manage the attachment of interrupt and vector service routines to interrupt and exception vectors respectively.

HAL_INTERRUPT_ATTACH() attaches the ISR, data pointer and object pointer to the given vector. When an interrupt occurs on this vector the ISR is called using the C calling convention and the vector number and data pointer are passed to it as the first and second arguments respectively.

HAL_INTERRUPT_DETACH() detaches the ISR from the vector.

HAL_VSR_SET() replaces the VSR attached to the vector with the replacement supplied in vsr. The old VSR is returned in the location pointed to by pvsr.

HAL_VSR_GET() assigns a copy of the VSR to the location pointed to by pvsr.

Interrupt controller management

HAL_INTERRUPT_MASK( vector )
HAL_INTERRUPT_UNMASK( vector )
HAL_INTERRUPT_ACKNOWLEDGE( vector )
HAL_INTERRUPT_CONFIGURE( vector, level, up )
HAL_INTERRUPT_SET_LEVEL( vector, level )

These macros exert control over any prioritized interrupt controller that is present. If no priority controller exists, then these macros should be empty.

HAL_INTERRUPT_MASK() causes the interrupt associated with the given vector to be blocked.

HAL_INTERRUPT_UNMASK() causes the interrupt associated with the given vector to be unblocked.

HAL_INTERRUPT_ACKNOWLEDGE() acknowledges the current interrupt from the given vector. This is usually executed from the ISR for this vector when it is prepared to allow further interrupts. Most interrupt controllers need some form of acknowledge action before the next interrupt is allowed through. Executing this macro may cause another interrupt to be delivered. Whether this interrupts the current code depends on the state of the CPU interrupt mask.

HAL_INTERRUPT_CONFIGURE() provides control over how an interrupt signal is detected. The arguments are:

vector

The interrupt to be configured.

level

Set to true if the interrupt is detected by level, and false if it is edge triggered.

up

If the interrupt is set to level detect, then if this is true it is detected by a high signal level, and if false by a low signal level. If the interrupt is set to edge triggered, then if this is true it is triggered by a rising edge and if false by a falling edge.

HAL_INTERRUPT_SET_LEVEL() provides control over the hardware priority of the interrupt. The arguments are:

vector

The interrupt whose level is to be set.

level

The priority level to which the interrupt is to set. In some architectures the set interrupt level is also used as an interrupt enable/disable. Hence this function and HAL_INTERRUPT_MASK() and HAL_INTERRUPT_UNMASK() may interfere with each other.

Clock control

HAL_CLOCK_INITIALIZE( period )
HAL_CLOCK_RESET( vector, period )
HAL_CLOCK_READ( pvalue )

These macros provide control over a clock or timer device that may be used by the kernel to provide timeout, delay and scheduling services. The clock is assumed to be implemented by some form of counter that is incremented or decremented by some external source and which raises an interrupt when it reaches zero.

HAL_CLOCK_INITIALIZE()initializes the clock device to interrupt at the given period. The period is essentially the value used to initialize the clock counter and must be calculated from the clock frequency and the desired interrupt rate.

HAL_CLOCK_RESET() re-initializes the clock to provoke the next interrupt. This macro is only really necessary when the clock device needs to be reset in some way after each interrupt.

HAL_CLOCK_READ()reads the current value of the clock counter and puts the value in the location pointed to by pvalue. The value stored will always be the number of clock ``ticks'' since the last interrupt, and hence ranges between zero and the initial period value.

hal/ARCH/arch/v1_1/include/hal_io.h

This file contains definitions for supporting access to device control registers in an architecture neutral fashion.

Register address

HAL_IO_REGISTER

This type is used to store the address of an I/O register. It will normally be a memory address, an integer port address or an offset into an I/O space. More complex architectures may need to code an address space plus offset pair into a single word, or may represent it as a structure.

Values of variables and constants of this type will usually be supplied by configuration mechanisms.

Register read

HAL_READ_XXX( register, value )
HAL_READ_XXX_VECTOR( register, buffer, count, stride )

These macros support the reading of I/O registers in various sizes. The XXX component of the name may be UINT8, UINT16, UINT32.

HAL_READ_XXX() reads the appropriately sized value from the register and stores it in the variable passed as the second argument.

HAL_READ_XXX_VECTOR() reads count values of the appropriate size into buffer. The stride controls how the pointer advances through the register space. A stride of zero will read the same register repeatedly, and a stride of one will read adjacent registers of the given size. Greater strides will step by larger amounts, to allow for sparsely mapped registers for example.

Register write

HAL_WRITE_XXX( register, value )
HAL_WRITE_XXX_VECTOR( register, buffer, count, stride )

These macros support the writing of I/O registers in various sizes. The XXX component of the name may be UINT8, UINT16, UINT32.

HAL_WRITE_XXX()writes the appropriately sized value from the variable passed as the second argument stored it in the register.

HAL_WRITE_XXX_VECTOR() writes count values of the appropriate size from buffer. The stride controls how the pointer advances through the register space. A stride of zero will write the same register repeatedly, and a stride of one will write adjacent registers of the given size. Greater strides will step by larger amounts, to allow for sparsely mapped registers for example.

hal/ARCH/arch/v1_1/include/hal_cache.h

This file contains definitions for supporting control of the caches on the CPU.

There are versions of the macros defined here for both the Data and Instruction caches. these are distinguished by the use of either DCACHE or ICACHE in the macro names. In the following descriptions, XCACHE is also used to stand for either of these. Where there are issues specific to a particular cache, this will be explained in the text.

Cache dimensions


HAL_XCACHE_SIZE
HAL_XCACHE_LINE_SIZE
HAL_XCACHE_WAYS
HAL_XCACHE_SETS

These macros define the size and dimensions of the Instruction and Data caches.

HAL_XCACHE_SIZE

gives the total size of the cache in bytes.

HAL_XCACHE_LINE_SIZE

gives the cache line size in bytes.

HAL_XCACHE_WAYS

gives the number of ways in each set and defines its level of associativity. This would be 1 for a direct mapped cache.

HAL_XCACHE_SETS

gives the number of sets in the cache, and is derived from the previous values.

Global cache control


HAL_XCACHE_ENABLE()
HAL_XCACHE_DISABLE()
HAL_XCACHE_INVALIDATE_ALL()
HAL_XCACHE_SYNC()
HAL_XCACHE_BURST_SIZE( size )
HAL_DCACHE_WRITE_MODE( mode )
HAL_XCACHE_LOCK( base, size )
HAL_XCACHE_UNLOCK( base, size )

These macros affect the state of the entire cache, or a large part of it.

HAL_XCACHE_ENABLE() and HAL_XCACHE_DISABLE()

enable and disable the cache.

HAL_XCACHE_INVALIDATE_ALL()

causes the entire contents of the cache to be invalidated.

HAL_XCACHE_SYNC()

causes the contents of the cache to be brought into synchronization with the contents of memory. In some implementations this may be equivalent to HAL_XCACHE_INVALIDATE_ALL().

HAL_XCACHE_BURST_SIZE()

allows the size of cache to/from memory bursts to be controlled. This macro will only be defined if this functionality is available.

HAL_DCACHE_WRITE_MODE()

controls the way in which data cache lines are written back to memory. There will be definitions for the possible modes. Typical definitions are HAL_DCACHE_WRITEBACK_MODE and HAL_DCACHE_WRITETHRU_MODE. This macro will only be defined if this functionality is available.

HAL_XCACHE_LOCK()

causes data to be locked into the cache. The base and size arguments define the memory region that will be locked into the cache. It is architecture dependent whether more than one locked region is allowed at any one time, and whether this operation causes the cache to cease acting as a cache for addresses outside the region during the duration of the lock. This macro will only be defined if this functionality is available.

HAL_XCACHE_UNLOCK()

cancels the locking of the memory region given. This should normally correspond to a region supplied in a matching lock call. This macro will only be defined if this functionality is available.

Cache line control


HAL_DCACHE_ALLOCATE( base , size )
HAL_DCACHE_FLUSH( base , size )
HAL_XCACHE_INVALIDATE( base , size )
HAL_DCACHE_STORE( base , size )
HAL_DCACHE_READ_HINT( base , size )
HAL_DCACHE_WRITE_HINT( base , size )
HAL_DCACHE_ZERO( base , size )

All of these macros apply a cache operation to all cache lines that match the memory address region defined by the base and size arguments. These macros will only be defined if the described functionality is available. Also, it is not guaranteed that the cache function will only be applied to just the described regions, in some architectures it may be applied to the whole cache.

HAL_DCACHE_ALLOCATE()

allocates lines in the cache for the given region without reading their contents from memory, hence the contents of the lines is undefined. This is useful for preallocating lines which are to be completely overwritten, for example in a block copy operation.

HAL_DCACHE_FLUSH()

invalidates all cache lines in the region after writing any dirty lines to memory.

HAL_XCACHE_INVALIDATE()

invalidates all cache lines in the region. Any dirty lines are invalidated without being written to memory.

HAL_DCACHE_STORE()

writes all dirty lines in the region to memory, but does not invalidate any lines.

HAL_DCACHE_READ_HINT()

hints to the cache that the region is going to be read from in the near future. This may cause the region to be speculatively read into the cache.

HAL_DCACHE_WRITE_HINT()

hints to the cache that the region is going to be written to in the near future. This may have the identical behavior to HAL_DCACHE_READ_HINT().

HAL_DCACHE_ZERO()

allocates and zeroes lines in the cache for the given region without reading memory. This is useful if a large area of memory is to be cleared.

hal/ARCH/arch/v1_1/src/vectors.S

This file contains code to deal with exception and interrupt vectors. Since the reset entry point is usually implemented as one of these it also deals with system startup.

The exact implementation of this code is under the control of the HAL implementer. So long as it interacts correctly with the macros defined in hal_intr.h it may take any form. However, all current implementation follow the same pattern, and there should be a very good reason to break with this. The rest of this section describes how the standard HAL implementation operates.

This file usually contains the following sections of code:

HAL startup

Execution normally begins at the reset vector with the machine in a minimal startup state.

The following is a list of the jobs that need to be done in approximately the order in which they should be accomplished. Many of these will not be needed in some configurations.

Vectors and VSRs

The CPU delivers all exceptions whether synchronous or interrupts to a set of vectors. Depending on the architecture, these may be implemented in a number of different ways. Examples of existing mechanisms are:

PowerPC

Exceptions are vectored to locations 256 bytes apart starting at either zero or 0xFFF00000. There are 16 such vectors defined by the architecture and extra vectors may be defined by specific implementations.

MIPS

All exceptions are vectored to a single address and software is responsible for reading the exception code from a CPU register to discover its true source.

MN10300

External interrupts are vectored to an address stored in one of seven interrupt vector registers. These only supply the lower 16 bits of the address, the upper 16 bits are fixed to 0x4000XXXX. Hence the service routine is constrained to the 64k range starting at 0x40000000.

Pentium

Exceptions are delivered via an Interrupt Descriptor Table (IDT) which is essentially an indirection table indexed by exception type. The IDT may be placed anywhere in memory. In PC hardware the interrupt controller can be programmed to deliver the external interrupts to a block of 16 vectors at any offset in the IDT.

680X0

Exceptions are delivered via an indirection table described by a CPU base register (for X > 0). External interrupts are either delivered via a set of level-specific vectors defined by the architecture, or a vector number may be supplied by the device in which case another entry in the table may be used.

The model adopted by the HAL is that VSRs should be easily replaceable with a pointer to an alternative routine. Of the above architectures, only the Pentium and 680X0 allow this directly in the hardware. In the other three, extra software is required. The code attached directly to the vector is a short trampoline that indirects via a HAL supplied VSR table to the true VSR. In the PowerPC and MN10300 the table offset is implicit in the vector routine called, for the MIPS the code reads the cause register and indirects through the appropriate table entry.

Default exception handling

Most synchronous exception vectors will point to a default exception VSR which is responsible for handling all exceptions in a generic manner.

Since most exceptions handled by this VSR are errors (or breakpoints when a program is being debugged), its default behavior should be to save the entire machine state, disable interrupts, and invoke the debugger's entry point, passing it a pointer to the saved state.

If the debugger returns then the saved state is restored and the interrupted code resumed. Since the debugger may adjust the saved state while it runs a little care must be taken to restore the state correctly.

Default interrupt handling

Most external interrupt vectors will point to a default interrupt VSR which decode the actual interrupt being delivered and invokes the appropriate ISR.

The default interrupt VSR has a number of responsibilities if it is going to interact with the Kernel cleanly and allow interrupts to cause thread preemption.

To support this VSR an ISR vector table is needed. For each valid vector three pointers need to be stored: the ISR, its data pointer and an interrupt object pointer needed by the kernel. It is implementation defined whether these are stored in a single table of triples, or in three separate tables.

The VSR should follow the following approximate plan:

The detailed order of these steps may vary slightly depending on the architecture, in particular where interrupts are enabled and disabled.

hal/ARCH/arch/v1_1/src/hal_misc.c

This file contains any miscellaneous functions that are reference by the HAL. Typical functions that might go here are C implementations of the least- and most- significant bit index routines, constructor calling functions such as cyg_hal_invoke_constructors() and support routines for the exception and interrupt vector handling.

hal/ARCH/PLATFORM/v1_1/include/hal_diag.h

During early development it is useful to have the ability to output messages to some default destination. This may be a memory buffer, a simulator supported output channel, a ROM emulator virtual UART or a serial line. This file defines set of macros that provide simple, polled output for this purpose.

HAL_DIAG_INIT() performs any initialization required on the device being used to generate diagnostic output. This may include setting baud rate, and stop, parity and character bits.

HAL_DIAG_WRITE_CHAR(c) writes the character supplied to the diagnostic output device.

These macros may either implement the required functionality directly, or may call functions elsewhere in the HAL to do it. In the latter case these should be in the file hal/ARCH/PLATFORM/v1_1/src/hal_diag.c.

hal/ARCH/PLATFORM/v1_1/src/PLATFORM.ld

This is the platform specific linker script file. It is responsible for locating the code, data and other sections in memory.

This file is passed through the C preprocessor before being passed on to the linker, so it may have conditionals for different configurations.

hal/ARCH/PLATFORM/v1_1/src/PLATFORM.S

This is a platform specific assembly code file. Its main purpose is to contain any platform specific startup code called from vectors.S.

hal/ARCH/PLATFORM/v1_1/src/context.S

If present, this is an assembly code file that contains the code to support thread contexts. The routines to switch between various contexts, as well as initialize a thread context may be present in this file.

hal/ARCH/PLATFORM/v1_1/src/hal_diag.c

If present, this file contains the implementation of the HAL diagnostic support routines.