TOC PREV NEXT INDEX DOC LIST MASTER INDEX



Kernel Configuration Parameters

The Apex Runtime System (RTS) is a multi-tasking runtime system that supports embedded real-time systems and is composed of a kernel and a user library. Rational Software Corporation's Apex Runtime System delivers all the functions you expect from a classical runtime system: multi-tasking, memory management, semaphores, mailboxes, interrupt support, etc. This chapter describes the kernel and provides instructions for configuring the kernel.

The kernel manages the resources of your target system and supplies thread scheduling support for tasking; tasking resides in the user library.

This section contains only the target-processor specific configuration information for users who wish to customize their configuration or create a new configuration. If you are using one of the board support packages supplied by Rational, the configuration instructions in the "Getting Started" chapter of Programming for Rational Exec are sufficient for your needs.

This chapter contains the following sections:


Kernel Configuration Files

The kernel configuration files contain the parameters that enable you to customize your kernel configuration.

These files consist of kernel parameters to control memory layout (stack and heaps), trap table configuration (if applicable), interrupt vector layout, idle processing, time slicing, floating point coprocessor hardware (if applicable), supervisor tasks, etc.

The following sections provides details about each of the configuration parameters. The configuration parameters are highly dependent on your target processor. A separate discussion is provided for each of the supported Apex targets.


PowerPC Kernel Configuration Parameters

Policy/Switches file for the krn_conf.ss subsystem.

The context switches for the views in the krn_conf.ss subsystem have the following additional values:

The Board_Common.sw file contains all of the switches that are common to the BSP. The RUNTIMES switch is used to identify the archive libraries that are required to support the kernel as opposed to a user program.

The linker description file is located locally in the krn_conf.ss view.

Configuration Components

The components of the kernel configuration package are found in the files v_krn_conf.1.ada and v_krn_conf.2.ada:

Each of these components is discussed in detail below. To find any one of these components in a source file, search for the title string of the desired component. In the source file, the code for each component begins with a line similar to the following:

Startup_Table Structure

The declaration for the constant Startup_Table is declared in the body of this package. This aggregate is used to provide parameters to the startup routine. The declaration of this structure's type is in the file v_krn_conf_i_1.ada.

The parameters and their interpretations are as follows:

Startup_Stack_Base

Startup_Stack_Base specifies the start address for the stack used during program initialization. The startup stack grows from this address towards low memory.

All stacks, including the kernel's, is allocated from the heap/stack area. The kernel's stack is allocated from the top of this heap/stack area. After the kernel is booted (by V_Boot), the stack is changed to the kernel's stack. Therefore, the location of the startup stack should also be at the top of the heap/stack area.

Startup_Stack_Base is the base address of the stack that the kernel uses during initialization. This is the initial value of the stack pointer, register r31, used by the kernel. The address you supply should point to the byte after the highest addressed byte of the stack. The stack grows toward low memory and the value in the stack pointer is always decremented before it is used.

We recommend that the value of Startup_Stack_Base be the address of the byte just after the last byte of RAM in the system. For example, the default value, 0x0080_0000, is be for a system with eight megabytes of RAM, that is, RAM in the address range from 0 to 0x7F_FFFF#. We also recommend that the kernel's heap/stack area be placed at this point, so that the memory used by initial stack can be reused.

Processor Exception Interface Procedures

When a processor exceptions occurs the srr0 and srr1 registers are initialized with the state of the processor at the time of the exception and the processor begins execution at an address (exception vector) predetermined for each exception. The code at these addresses can be found in the file v_exception.1.ada and the location is specified by the linker options file v_krn_link.opt. These routines branch to one of the exception interface handlers within this package:

Most processor exceptions are mapped to V_Level1_Normal.

V_Level1_Normal and V_Level1_Break save the entire CPU context and then lookup the handler in the interrupt vector table and jump to the handler. The handler may then further decode the interrupt and chain to a different entry in the interrupt vector table by setting the value of context.next_id.

V_Level1_Program decodes the program exception. This includes sending breakpoints to TDM.

This handler is then called as a normal Ada procedure. User application ISRs are installed in Interrupt_Vector_Table and not the CPU's exception table.

Interrupt_Vector_Table Structure

This configuration parameter contains the Interrupt_Vector_Table structure:

Exception handlers are called after all of the state has been saved away in the context (Except for system calls). Since there is only one external interrupt on the PowerPC, the interrupt vector table allows for interrupts that start after the fixed portion of the interrupt vector table. These must be defined in the Io_Conf package in the user configuration. The first level interrupt is vectored to V_Decode_External which can in turn vector to board specific interrupts.

The type declaration for the Interrupt_Vector_Table is an array of addresses:

Each address is the address of the interrupt service routine (ISR) associated with the interrupt number which is its table index.

When using TDM, the layout of this table must be exactly the same as the layout of TDM's interrupt vector table.

Handlers installed in the interrupt vector table have the following subprogram interface:

where:

trap_type
Number of the trap
context
Saved CPU context (trap number, psr, sxip, snip, sfip, general registers) at the point of the trap.

Interrupt handlers linked in the kernel program may optionally be implemented by instantiating the V_Krn_Conf_I.Interrupt_Handler generic.

The variable Interrupt_Vector_Table is declared in this package, and should be modified to specify the initial set of interrupt handlers for your system.

Kernel resident handlers may also be attached and detached dynamically using the Replace_Vector() procedure. Handlers resident in the user program are attached and detached dynamically with Rational Exec services and/or Ada task interrupt entries.

Interrupt_Vector_Table entries for interrupts which are unused, unexpected or having dynamically installed handlers should be initialized to Untouchable_Trap_Handler so that TDM traps these interrupts.

Configuration_Table Structure

The Configuration_Table structure is the primary mechanism by which you tailor the kernel to your target system.

The constant Configuration_Table in the body of this package should be modified to describe your runtime environment. This record is passed to the kernel during its startup to control runtime initialization. The declaration of this record's type is in the package V_Krn_Conf_I.

Memory Management

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks.

Some memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Following the memory management parameter descriptions, Figure 14, "Example Heap/Stack Layout (PowerPC)," on page 123 presents an example layout for the heap/stack area.

For more information about memory management, see "Memory Management" in the Rational Apex Runtime Guide.

Heap_Stack_Bottom and Heap_Stack_Top

Heap_Stack_Bottom and Heap_Stack_Top define the area of memory from which heaps and stacks are allocated. Heap_Stack_Bottom is the low address, usually set to the first RAM address following the end of TDM, the kernel and user programs. If Heap_Stack_Bottom is set to the constant End_Of_User_Program this value is computed dynamically by reading the user program's group tables. Heap_Stack_Top is the highest byte that can be written to.

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The next few memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Krn_Stack_Size

Krn_Stack_Size is the size of the kernel's stack. Currently only used when V_Start_Program elaborates the kernel packages. Normally the same size as, and overlaps, the startup stack and the interrupt stack. The kernel stack is allocated from the heap/stack area.

Intr_Stack_Size

Intr_Stack_Size is the size of the interrupt handlers' stack. This is the stack that interrupt handlers use. The stack size should include 1K bytes for exception handling. All interrupt service routines use this same stack, so you must allow space for nested interrupts if they are possible. The interrupt trap wrapper, V_Interrupt_Trap_Handler switches to the interrupt handlers' stack.

The default board configuration allows the kernel stack to be zero so that elaboration of the kernel occurs on the interrupt stack.

Krn_Exception_Stack_Size

Krn_Exception_Stack_Size is the size of the area set aside below the bottom of the interrupt handler's stack for exception unwinding. It is also the exception stack size for tasks created in the kernel program. Kernel tasks consist of the idle task and tasks created by V_Krn_Aug.Task_Create.

Zero_Stacks_Enabled

At startup all the memory to be used for task stacks is zeroed. However, if tasks are dynamically terminated and recreated, the stack area for subsequently created tasks is no longer zeroed. This can lead to erroneous stack usage information displayed by the debugger's lt use command.

Zero_Stacks_Enabled is set to True to guarantee that at task create its stack area is zeroed. Since it takes extra time to zero the stack area, Zero_Stacks_Enabled is normally only set to True when used in conjunction with lt use for dynamically created tasks.

Example HEAP/STACK Layout

If we have the following memory parameter settings, Figure 13 is accurate.

Figure 13 Example Heap/Stack Layout (PowerPC)

Processor Status Parameters

The processor status parameters are all related to the Processor State Register (PSR).

Supervisor_Tasks_Enabled

When set to True, all tasks including the main program execute in supervisor state. Supervisor_Tasks_Enabled is not currently used. We do not support programs which run in user state.

Interrupt Configuration Parameters

The following parameters deal with the PowerPC trap table and the kernel's interrupt vector table.

Interrupt_Vector_Base

Interrupt_Vector_Base indicates the address of the starting location of the table Interrupt_Vector_Table.

Interrupt_Vector_Size

Interrupt_Vector_Size indicates the number of interrupt vectors in the kernel's interrupt vector table. The default is Interrupt_Vector_Table'Length.

Interrupt_Vector_Size tells the kernel how many vectors should be in the table. This is used during initialization and as a bounds check on Interrupt_Isr_Attach.

Because the kernel supports pseudo interrupts, Interrupt_Vector_Size may be larger than 511.

Enable_Intr_Status

Interrupt mask when all interrupts are enabled

Disable_Intr_Status

Interrupt mask when all interrupts are disabled

User_Intr_Status

Interrupt mask when the user program is started at completion of kernel startup. Normally set to the same value is Enable_Intr_Status to allow all interrupts to be enabled.

Floating Point Control Register Parameter

The following parameter deals with the Floating Point Unit.

Floating_Point_Control

Floating_Point_Control is a structure which specifies the initial value for the FPCR register of the FPU.

The default value for this structure is as follows:

Time Slice Parameters

Time_Slicing_Enabled

This parameter is set to True to enable time slicing.

This parameter is ignored by the no-tasking kernel.

Time_Slice_Interval

Task's initial time slice value. An individual task's time slice can be changed at runtime from the user program by using functions provided with Rational Exec.

The default timeslice interval is 1.0 seconds.

This parameter is ignored by the no-tasking kernel.

Time_Slice_Priority

If time slicing is enabled, then, it is only applicable to tasks whose priority is less than or equal to this threshold priority.

This parameter is ignored by the no-tasking kernel.

Scheduling Configuration Parameters

Idle Processing Configuration Parameters

Idle_Stack_Size

Idle_Stack_Size specifies the size in bytes of the idle task's stack. This configuration parameter is equivalent to Idle Task's Storage_Size for an application defined task.

Users should adjust this parameter depending on the operations the idle task performs, and the number of interrupts which the host processor may be processing.

The default Idle_Stack_Size is 16#2000# bytes.

This parameter is ignored by the no-tasking kernel.

Miscellaneous Configuration Parameters

Task_Storage_Size

Task_Storage_Size specifies the size in bytes of the area allocated in the task control block for user storage. Rational Exec services manage this area in the task control block.

Note: This area is used by the Rational implementation of Ada.Task_Attributes (See LRM C.7.2) to store pointers to task attributes. If this package is used, Number_Of_Attributes objects of size Address'Size will be allocated in this area in the task control block. Number_Of_Attributes is defined in Ada.Attributes_Storage in the Systems_Programming.ss subsystem. It is currently four, and currently non configurable.

Initialization Routines

V_Immediate_Initialization carries out initialization which must occur right away after reset. An example is board control registers which come up in unknown states.

If your stack needs some preparation, such as mapping in its pages, do that here. This routine is called before any RAM is accessed.

Things are in a precarious state at this point:

V_Hardware_Initialization performs any operations necessary to initialize the target system prior to kernel initialization.

V_Hardware_Initialization performs any operations necessary to initialize the hardware prior to kernel initialization.

This routine is called before the kernel is initialized. It should not make any calls to kernel services or use any language features that call kernel services implicitly. In particular, the following language features should not be used:

This routine must not reference any entities which require elaboration, since it is called before any elaboration takes place.

Elaboration Callout Routine

V_Elaboration_Callout is the default elaboration callout routine. This procedure is executed prior to elaborating each entry in the elaboration table during program elaboration.

The parameter Elaboration_Entry provides the address of the entry point for the elaboration code for the unit that is about to be elaborated.

The default body of this procedure is null.

Kernel Trap Handlers and Support Routines

The following routines are the hardware level exception handlers which are installed in the trap table.

V_Trap_Handler

V_Trap_Handler handles all hardware exceptions for the kernel. It simply saves the context and calls the associated ISR from the interrupt vector table.

V_Decode_External

Exception type 5, external interrupt, is mapped to the PowerPC Interrupt_Vector_Table and is initialized with the address of V_Decode _External. See also "Interrupt_Vector_Table" in "PowerPC Processor-Specific Runtime Issues" in the Embedded Runtime Topics chapter of Programming for Rational Exec.

Default Interrupt Service Routine

V_Default_Isr is the default interrupt service routine (ISR). The kernel attaches this ISR to any interrupt vector whose handler is detached by a call to V_Krn_Conf_I.Replace_Vector with a null handler address. Also, if the user program calls V_Interrupts.Detach_Isr, this default ISR is attached.

V_Default_Isr is called by one of the trap handlers. The default action for unexpected interrupts is to call TDM to post an unexpected signal. TDM signals the host debugger about the unexpected trap.

The address of this routine should be assigned to the parameter User_Default_Isr in the configuration table, and to each unused interrupt vector in the interrupt vector table described above.

Interrupt Control Package

V_Ext_Intr_Support defines the routines for accessing the board's interrupt controller.

Interrupts_Disable disables all interrupts, returning the interrupt status in effect when it was called. This should use processor level masking if possible.

Interrupts_Restore restores interrupts disabled by Interrupts_Disable.

Interrupts_Get_Status gets the current interrupt status, which determines which interrupts are currently enabled. This may not be the same as the status manipulated by Interrupts_Disable/Restore; for example, it may get the status of an external interrupt controller instead of that of the processor.

Interrupts_Set_Status sets the interrupt status accessed by Interrupts_Get_Status.

Interrupts_Priority_Disable_Status maps an Ada interrupt priority to an interrupt status that can be used by Interrupts_Set_Status. This is used to implement interrupt level priority ceiling locking in Ada protected objects (see LRM D.3). As required by Ada semantics, higher priorities must disable at least the same interrupts as lower priorities and may disable additional interrupts. In the extreme, Interrupts_Set_Status (Interrupts_Priority_Disable (Interrupt_Priority'Last)) should disable all interrupts that can be disabled.

Pending_Overflow_Callout Routine

This routine is called by the kernel when the pending service ring has overflowed. If this routine is called, you need to increase the configuration parameter Pending_Count.

Timer Support Package

The V_Timer_Support package spec is the interface to the timer functions that are required by the kernel. You must supply a body for this package that implements the functions for your timer hardware.

The type V_I_Types.Time_T is used to represent time. This is a signed 64-bit integer count of time units, the length of which is defined by the constant Real_Time.Time_Unit in the board_common.ss subsystem. Since not all of the platforms on which Apex is implemented provide 64-bit integer types, Time_T is implemented using two 32-bit integers:

Time_T is the underlying implementation of both Ada.Calendar.Time and Ada.Real_Time.Time; as such it unifies the requirements of these types. Ada.Calendar.Time must be able to represent any local ("wall clock") time between the years 1901 and 2099 with a granularity no greater than 20 milliseconds. Ada.Real_Time.Time must cover the 50-year period from system start-up to within 20 microseconds. A 64-bit integer count provides for a range of +/- 292 years to nanosecond resolution, such that the required range can be provided for Ada.Calendar.Time relative to system startup anywhere between 1901 and 2099. Nanosecond resolution provides compatibility with the POSIX Real-Time Extensions (1003.1b-1993), which provides time as an integer count of seconds plus and integer count of nanoseconds.

This is also the representation of the Ada.Real_Time.Time_Span type for representing fine-grain duration. The only functional difference between Time and Time_Span is that an implicit start time or epoch is implied for Time values; a Time value represents a point in time as the signed duration between that time and the epoch. By default, Apex uses the POSIX (1003.1-1990) epoch: 0 hours, 0 minutes, 0.0 seconds, 1 January 1970, Universal Coordinated Time.

The value of Time_Unit is system-dependent, and can be configured to be a value convenient for implementation over your timer hardware. (See "Configuring Ada.Real_Time" in Using the Ada Runtime.)

The following operations must be provided by V_Timer_Support for the kernel:

Init resets the clock's current time and initializes the timer hardware. This procedure is called during kernel startup.

Reset_Time resets the clock's current time with the input parameter and cancels the active alarm which is rescheduled later.

Get_Current_Time reads and returns the clock's current time.

Schedule_Alarm schedules an alarm posted some time in the future. This procedure is called by the kernel when a future timing event must occur. The kernel passes in an absolute (not relative) time in the future when Post_Alarm should be called. The kernel computes this time by adding some amount onto the value returned by Get_Current_Time. Example events are the expiration of a delay or the end of the time slice. If Schedule_Alarm is called with an Alarm_Time later than the current Alarm_Time, the request is ignored. The kernel resubmits it sometime after the current alarm occurs. If a request is made to schedule an Alarm_Time sooner than the current Alarm_Time, this new Alarm_Time becomes the current Alarm_Time and the old Alarm_Time can be forgotten.

Post_Alarm takes no parameters. Process_Timer_Interrupt handles the timer interrupt by advancing the current time and checking if the time for the next alarm is reached. If it has, Process_Timer.Interrupt calls Post_Alarm to post the alarm to the kernel.

OS Support Package

V_Os_Support supplies functions to support the embedded kernel's emulation of OS exit and diagnostic output.

When V_Os_Support routines are called, the kernel is either exiting normally or it is printing out a diagnostic, prior to exiting with a fatal error. The V_Os_Support routines should not call any kernel functions.

Halt is called when the kernel program exits.

Put_Chr is called to output a character. Put_Str is called to output a string. The kernel only calls these procedures to output diagnostic information, that is, when something is wrong.

Post_Exception is called from interrupt service routines to signal unexpected external interrupts or hardware exceptions to TDM. Information about the exception is held in the context record.

V_Stack_Support Package

This file contains the kernel program's stack allocation callouts.

The default implementation simply allocates all task stacks from the kernel's heap area using the V_Alloc_Support callouts.

Main Startup Routine

V_Start_Program is the main startup routine of the kernel. It is jumped to from V_Start, which is the actual entry point of the kernel

Although the source code for V_Start_Program is provided as part of the kernel configuration package, it can normally be used as is. V_Start_Program is provided primarily for reference. Both TDM and the user program have similar routines.

1 . Initializes the MSR as follows:

2 . Initializes the shadow scoreboard register.

3 . Calls the V_Immediate_Initialization routine to initialize the board before referencing RAM.

4 . Initializes the startup stack.

5 . Copies the static data section from ROM to RAM if ROM is being used.

6 . Zeroes the kernel program's BSS.

7 . Hand-elaborates this configuration package's specification and body.

8 . Optionally, dynamically sets Heap_Stack_Bottom to the end of the user program.

9 . Zero's the heap/stack memory area.

10 . Calls the V_Hardware_Initialization routine to perform user-specified hardware initialization.

11 . Calls the V_Init_Trap_Table routine to initialize the trap table.

12 . Calls the V_Boot routine to initialize the kernel's tasking data structures and, upon return, switch from the startup stack to the kernel's stack.

13 . Calls V_Serial_Support.Init to initialize the serial port.

14 . Elaborates the kernel program's packages, calling the elaboration callout routine before each, if it is supplied.

15 . Calls V_Execute_Callout, if supplied.

16 . Calls the V_Execute routine which transfers control to the user program by its start address contained in the Link_Block.


MIPS Kernel Configuration Parameters

Policy/Switches file for the krn_conf.ss subsystem.

The context switches for the views in the krn_conf.ss subsystem have the following additional values:

The Board_Common.sw file contains all of the switches that are common to the BSP. The RUNTIMES switch is used to identify the archive libraries that are required to support the kernel as opposed to a user program.

The linker description file is located locally in the krn_conf.ss view.

Configuration Components

The components of the kernel configuration package are found in the files v_krn_conf.1.ada and v_krn_conf.2.ada:

Each of these components is discussed in detail below. To find any one of these components in the source files, v_krn_conf.1.ada and v_krn_conf.2.ada, search for the title string of the desired component. In the source file, the code for each component begins with a line similar to the following:

To configure the kernel program, modify the body of the package, v_krn_conf.2.ada.

Startup_Table Structure

The declaration for Startup_Table is in the file v_krn_conf.2.ada.

You must declare the Startup_Table as a constant, because the values in the table are used as soon as the kernel starts, prior to the elaboration of the kernel configuration package (see the V_Start_Program routine in v_krn_conf.2.ada). The value assigned to each field of the Startup_Table must be a literal, an address constant or a value returned by the address attribute ('Address).

Startup_Stack_Base is the base address of the stack that the kernel uses during initialization. This is the initial value of the stack pointer, register sp, used by the kernel. The address you supply should point to the byte after the highest addressed byte of the stack. The stack grows toward low memory and the value in the stack pointer is always decremented before it is used.

We recommend that the value of Startup_Stack_Base be the address of the byte just after the last byte of RAM in the system. For example, the default value, 16#8010_0000#, is for a system with one megabyte of RAM, RAM in the address range from 16#8000_0000# to 16#800F_FFFF#. We also recommend that the kernel's heap/stack area be placed at this point, so that the memory used by initial stack can be reused.

Startup_Stack_Size tells the kernel how many bytes of stack space you allow for the startup stack. The kernel uses this number to compute the stack limit; the kernel has stack limit checks enabled during startup. The kernel requires less than 4096 bytes of startup stack space.

Interrupt_Vector_Table Structure

The kernel program contains an array of interrupt handler addresses called the Interrupt Vector Table (IVT). Handlers in the IVT are dispatched by the kernel's generalized exception handler, V_Gen_Except. This exception handler provides the necessary wrapper code for exceptions, software traps and external interrupts.

This Interrupt_Vector_Table structure specifies the initial contents of each vector in the kernel's IVT. The Interrupt_Vector_Base field in the configuration table points to the base of the IVT.

Each table entry must contain one of these values:

The address of Interrupt_Vector_Table is passed to the kernel as a component of the Configuration_Table, described in Configuration_Table Structure.

There are six methods to put the address of an ISR into the interrupt vector table:

1 . As an initial value in the Interrupt_Vector_Table. This requires that the ISR be linked as part of the kernel program.

2 . Dynamically attach and detach kernel resident handlers using the Replace_Vector procedure in the package V_Krn_Conf_I.

3 . Use Ada interrupt entries as interrupt handlers.

4 . Use a protected procedure as an interrupt handler (See LRM C.3).

5 . Use the routine V_Interrupts.Attach_Isr from Rational Exec to dynamically install an ISR residing in the user program.

6 . You can write code that simply writes new values into the interrupt vector table. After all, it's just an array in memory. The Rational Exec service, V_Interrupts.Get_Ivt, can be called to get the starting address of this interrupt vector table.

The type declaration for the Interrupt_Vector_Table is an array of addresses:

Apex provides the interrupt handlers for exception types for the MIPS I Family:

Apex provides the interrupt handlers for exception types for the MIPS II/III/IV Family:

Initialize Interrupt_Vector_Table entries for interrupts which are unused, unexpected or that have dynamically installed handlers with the address of the unassigned interrupt handler, V_Krn_Conf.V_Default_Isr.

Handlers installed in the interrupt vector table have the following subprogram interface:

where:

Vector_Id
Vector_Id obtained from the cause register or a pseudo Vector_Id from V_Decode_Interrupt
Ef
the saved CPU context (c0_bad_vaddr, c0_status, c0_cause, c0_epc, r0..r31) at the point of the exception or interrupt. The saved context also contains the field, next_id. The ISR can update it with the vector ID of the next ISR to be dispatched to. It has been initialized to No_Vector_Id. If the next_id points to a Untouchable vector, then, the Next_Id is handled in TDM. Using the Next_Id field allows multiple devices in either the user, kernel or TDM programs to share the same interrupt.

Optionally, interrupt handlers linked in the kernel program may be implemented by instantiating the Interrupt_Handler generic. This generic is provided for compatibility with other Apex cross targets. The generic parameter is a subprogram with no parameters.

Configuration_Table Structure

The Configuration_Table is the primary mechanism by which you tailor the kernel to your target system. The parameters that make up the configuration table are partitioned into these categories:

Because Ada does not allow constants to be reassigned, the Heap_Stack_Bottom field must be changed to something besides V_Krn_Conf_I.End_Of_User_Program (the default) before the Configuration_Table may be changed to a constant. The following source line must be located and commented out before compilation:

This is the last source line in the procedure Auto_Config_Heap_Stack_Bottom.

Memory Management

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The user program maintains its own local heap area for doing Ada new allocations. By default, memory for the user program's local heap is obtained from the kernel's heap/stack area.

In addition to the stacks described here, every Ada task has its own stack, which is allocated from the heap/stack area when the task is created.

Some memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

For more information about memory management, see "Memory Management" in the Rational Apex Runtime Guide.

Heap_Stack_Bottom and Heap_Stack_Top

Heap_Stack_Bottom and Heap_Stack_Top define the area of memory from which heaps and stacks are allocated. Heap_Stack_Bottom is the low address, usually set to the first RAM address following the end of TDM, the kernel and user programs. If Heap_Stack_Bottom is set to the constant End_Of_User_Program this value is computed dynamically by reading the user program's group tables. Heap_Stack_Top is the highest addressed RAM location available for heaps and stacks. It is not the byte after the heap/stack area, it is the last byte of the area.

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The next few memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

The debugger's lt command has a use option for displaying maximum stack usage. Use this feedback to refine your initial guess for the different stack sizes.

Krn_Stack_Size

Krn_Stack_Size is the size of the kernel's stack. Currently only used when V_Krn_Conf.V_Start_Program elaborates the kernel packages. Normally the same size as and overlaps the startup stack.

Intr_Stack_Size

Intr_Stack_Size is the size of the interrupt handlers' stack. This is the stack that interrupt handlers use. The stack size excludes room for exception handling. All interrupt service routines use this same stack, so you must allow space for nested interrupts if they are possible. V_Krn_Conf.V_Level1_External switches to the interrupt handlers' stack.

Krn_Exception_Stack_Size

Krn_Exception_Stack_Size is the size of the area set aside below the bottom of the interrupt handler's stack for exception unwinding. It is also the exception stack size for tasks created in the kernel program. Kernel tasks consist of the idle task and tasks created by V_Krn_Aug.Task_Create.

Task_Supervisor_Stack_Size

Task_Supervisor_Stack_Size specifies how much supervisor stack space is needed for each task that executes in user state.

Task_Supervisor_Stack_Size is currently not used. We do not support programs which run in user state.

Zero_Stacks_Enabled

At startup all the memory to be used for task stacks is zeroed. However, if tasks are dynamically terminated and recreated, the stack area for subsequently created tasks is no longer zeroed. This can lead to erroneous stack usage information displayed by the debugger's lt use command.

Zero_Stacks_Enabled is set to True to guarantee that at task create its stack area is zeroed. Since it takes extra time to zero the stack area, Zero_Stacks_Enabled is normally only set to True when used in conjunction with lt use for dynamically created tasks.

Processor Status Configuration Parameters

Supervisor_Tasks_Enabled

Supervisor_Tasks_Enabled is not used. All tasks must run in kernel mode.

User_Int_Mask

User_Int_Mask is the setting of the c0_status register's IM field when the user program is started at completion of kernel startup. 16#FF# (all interrupt levels enabled) is the default.

Disable_Int_Mask

Disable_Int_Mask is the setting of the c0_status register's IM field when interrupts are disabled within the kernel

Note that the default depends on which interrupt is used by the FPC and bus error (they are enabled while in the kernel).

Interrupt Configuration Parameters

Interrupt_Vector_Base

Due to the MIPS architecture, a substantial amount of code is required to save and restore the CPU state for a synchronous or asynchronous exception. A generalized exception handler, V_Gen_Except, provides the necessary exception handler wrapper code. After saving the CPU context, it uses the contents of the c0_cause register to create an index to dispatch through the kernel's interrupt vector table (an array of handler addresses).

Interrupt_Vector_Base points to where this interrupt vector table is located in memory.

The interrupt vector table has Interrupt_Vector_Size entries. The size of an entry is 4 bytes.

Interrupt_Vector_Size

Interrupt_Vector_Size indicates the number of interrupt vectors in the kernel's interrupt vector table. This number should be equal to Interrupt_Vector_Table'Length.

Interrupt_Vector_Size tells the kernel how many vectors should be in the table. This is used during initialization and as a bounds check on V_Interrupts.Attach_Isr.

Because the kernel supports pseudo interrupts, Interrupt_Vector_Size may be arbitrarily large.

Floating Point Coprocessor Parameters

The following parameters deal with the MIPS Floating Point Coprocessor.

Hw_Flt_Enabled

For the MIPS I Family, Hw_Flt_Enabled is set to True if the board has a Floating Point Coprocessor (FPC). Set to False to inhibit the use of the FPC and cause all FPops to be simulated.

For the MIPS II/III/IV Family, Hw_Flt_Enabled must always be set to True. The MIPS II/III/IV kernel contains no floating point emulation routines.

Floating_Point_Control

Floating_Point_Control is a structure which specifies the initial value for the floating point control/status register (FCR31) of the MIPS target processor. This structure and its subcomponents are declared in the package V_I_Types found in standard.

Fields of the components and their values are as specified in the MIPS RISC Architecture Manual.

The default value for this structure is as follows:

1 . The condition bit is cleared.

2 . All the sticky bits are cleared.

3 . Traps invalid, zero-divide, and overflow are enabled.

4 . The default rounding mode is set to "to nearest".

5 . The Flush denormalized results to 0.0 bit (FS) is set. (MIPS II/III/IV Family only)

The default Interrupt_Vector_Table sets the software FP exception vector to Fp_Exception_Handler. This handler raises the Ada exception, Numeric_Error if one of these unmasked exceptions is signalled.

Time Slice Parameters

The following parameters deal with time slicing functions.

Time_Slicing_Enabled

This parameter is set to True to enable time slicing and false to prevent time slicing. This parameter is ignored by the no-tasking kernel.

This parameter is the initial setting of the kernel and can be changed dynamically by the subprogram V_Xtasking.Set_Time_Slicing_Enabled. The function V_Xtasking.Current_Time_Slicing_Enabled can read the current value.

Time_Slice_Interval

Each task in the system is started with the value of the Time_Slice_Interval as its time slice. This is the amount of time that the task runs before being pre-empted for any equal priority tasks that may be ready to run. An individual task's time slice interval can be changed during execution by calling V_Xtasking.Set_Time_Slice and read by V_Xtasking.Current_Time_Slice. Time_Slice_Interval is of type V_I_Types.Time_Span_T and has a default of 1.0 seconds. Values less than or equal to zero (<=0) disable time slicing.

This parameter is ignored by the no-tasking kernel.

Time_Slicing_Priority

Time_Slicing_Priority specifies the maximum task priority to which time slicing applies. This way, normal tasks can be time sliced but high priority tasks execute until they give up the processor (or until an even higher priority task becomes ready).

All tasks whose priority is less than or equal to Time_Slicing_Priority are time sliced.

This parameter is ignored by the no-tasking kernel.

Idle Processing Parameters

The following parameter deals with idle time functions.

Idle_Stack_Size

Idle_Stack_Size specifies the size in bytes of the idle task's stack. This configuration parameter is equivalent to the 'Storage_Size attribute for an application define task.

The default Idle_Stack_Size is 5,000 bytes.

This parameter is ignored by the no-tasking kernel.

Miscellaneous Configuration Parameters

Task_Storage_Size

Task_Storage_Size specifies the size in bytes of the area allocated in the task control block for user storage. The Rational Exec Services, Allocate_Task_Storage and Get_Task_Storage manage this area in the task control block.

Note: This area is used by the Rational implementation of Ada.Task_Attributes (See LRM C.7.2) to store pointers to task attributes. If this package is used, Number_Of_Attributes objects of size Address'Size will be allocated in this area in the task control block. Number_Of_Attributes is defined in Ada.Attributes_Storage in the Systems_Programming.ss subsystem. It is currently four, and currently non configurable.

Pending_Count

Pending_Count specifies the maximum number of kernel service requests from an ISR (or nested ISR) held pending until the outermost ISR completes. When the outermost ISR completes, the kernel processes the queue of pending requests. If the Pending_Count is exceeded, V_Krn_Conf.V_Pending_Overflow_Callout is called.

Miscellaneous Variables

This component contains miscellaneous variables.

Immediate/Hardware Initialization Routines

V_Immediate_Initialization carries out initialization which must occur right away after a CPU reset. An example is board control registers which come up in unknown states.

If your stack needs some preparation, such as mapping in its pages, do that here. This routine is called before any RAM is accessed.

Things are in a precarious state at this point:

1 . You have no stack

2 . Interrupts are disabled

3 . Register ra contains your return address. All other general purpose registers are considered dead by the calling routine and can be used freely.

V_Hardware_Initialization performs any operations necessary to initialize the target system prior to kernel initialization. This routine is called with interrupts enabled.

The V_Hardware_Initialization performs any operations necessary to initialize the hardware prior to kernel initialization.

This routine is called before the kernel is initialized. It should not make any calls to kernel services or use any Ada language features that call kernel services implicitly. In particular, the following language features should not be used:

This routine must not reference any entities which require elaboration, since it is called before any elaboration takes place.

V_Elaboration_Callout Routine

V_Elaboration_Callout is executed prior to elaborating each entry in the elaboration table during program elaboration.

Elaboration_Entry is the address of the entry point for the elaboration code for the unit that is about to be elaborated.

The default body of this procedure is null.

V_Decode_Exception Routine

For the MIPS I Family, upon entry and exit: KUc=0/IEc=0 (kernel mode with interrupts disabled)

For the MIPS II/III/IV Family, upon entry and exit: KSU=0/IE=0 (kernel mode with interrupts disabled)

Decode INT_MASK to find an exception mapped to an interrupt. Also look for TDM's Control-c serial interrupt.

V_Decode_Interrupt Routine

For the MIPS I Family, upon entry and exit: KUc=0/IEc=0 (kernel mode with interrupts disabled)

For the MIPS II/III/IV Family, upon entry and exit: KSU=0/IE=0 (kernel mode with interrupts disabled)

V_Rfe Routine (MIPS I Family only)

Upon entry:

Uses k0 register for doing the return. Since k1 was not saved or used it is not restored.

V_Eret Routine (MIPS II/III/IV Family only)

Upon entry:

Restores these last registers from the exception frame then does an eret instruction.

V_Restore_Ef Routine

Upon entry:

V_Untouchable Routine

Upon entry:

V_Gen_Except Routine

Upon entry:

In the MIPS architecture, virtually all exceptions, traps, and interrupts (referred to collectively as exceptions) flow through one vector. This is the routine which handles these events. It first saves the context in an exception frame and then does some preliminary decoding of the cause of the exception, to see if it can be handled without going through the interrupt vector table (IVT) table. For example, kernel services are not routed through the IVT. If it must be routed through the table, it calls V_Decode_Exception or V_Decode_Interrupts depending on the type of exception, to get the index into the IVT. It then calls the handler from the table given by the index.

V_Utlb_Except Routine

Upon entry:

For the MIPS I Family, this is the one exception to the rule that all exceptions are routed through one vector. This other vector is used to handle User Translation Lookaside Buffer (UTLB) misses. Currently, Apex does not use virtual memory, so under normal circumstances, this exception never occurs. However, if the user program or kernel attempts to execute code or access memory in kuseg or kseg2 erroneously, this exception may occur since the TLB is not initialized. The default action for this routine is to simply jump to the other exception vector and let it handle it. No information is lost because it is recorded in the c0_cause register.

For the MIPS II/III/IV Family, this vector is used to handle User Translation Lookaside Buffer (UTLB) misses. Currently, Apex does not use virtual memory, so under normal circumstances, this exception never occurs. However, if the user program or kernel attempts to execute code or access memory in kuseg, ksseg, or kseg3 erroneously, this exception may occur since the TLB is not initialized. The default action for this routine is to simply jump to the other exception vector and let it handle it. No information is lost because it is recorded in the c0_cause register.

V_Default_Isr Routine

V_Default_Isr is the default Interrupt Service Routine (ISR).

The purpose of this routine is to give you access to interrupts that are coming in that you aren't expecting, for which there is no handler.

The kernel attaches this ISR to any interrupt vector whose handler is detached by a call to Replace_Vector with a null handler address. Also, if the user program calls Detach_Isr, this default ISR is attached.

Assign the address of this routine to each unused interrupt vector described in Interrupt_Vector_Table Structure.

V_Pending_Overflow_Callout Routine

This routine is called by the kernel when the pending service ring has overflowed. If this routine is called, you need to increase the configuration parameter Pending_Count. This routine may be called if an ISR is not properly clearing the cause of the interrupt condition.

V_Timer_Support Package

The V_Timer_Support package spec is the interface to the timer functions that are required by the kernel. You must supply a body for this package that implements the functions for your timer hardware. The addresses of the procedures defined here should be passed to the kernel by the timer support callouts. The default configuration_table does this.

The type V_I_Types.Time_T is used to represent time. This is a signed 64-bit integer count of time units, the length of which is defined by the constant Real_Time.Time_Unit in the board_common.ss subsystem. Since not all of the platforms on which Apex is implemented provide 64-bit integer types, Time_T is implemented using two 32-bit integers:

Time_T is the underlying implementation of both Ada.Calendar.Time and Ada.Real_Time.Time; as such it unifies the requirements of these types. Ada.Calendar.Time must be able to represent any local ("wall clock") time between the years 1901 and 2099 with a granularity no greater than 20 milliseconds. Ada.Real_Time.Time must cover the 50-year period from system start-up to within 20 microseconds. A 64-bit integer count provides for a range of +/- 292 years to nanosecond resolution, such that the required range can be provided for Ada.Calendar.Time relative to system startup anywhere between 1901 and 2099. Nanosecond resolution provides compatibility with the POSIX Real-Time Extensions (1003.1b-1993), which provides time as an integer count of seconds plus and integer count of nanoseconds.

This is also the representation of the Ada.Real_Time.Time_Span type for representing fine-grain duration. The only functional difference between Time and Time_Span is that an implicit start time or epoch is implied for Time values; a Time value represents a point in time as the signed duration between that time and the epoch. By default, Apex uses the POSIX (1003.1-1990) epoch: 0 hours, 0 minutes, 0.0 seconds, 1 January 1970, Universal Coordinated Time.

The value of Time_Unit is system-dependent, and can be configured to be a value convenient for implementation over your timer hardware. (See "Configuring Ada.Real_Time" in Using the Ada Runtime).

The following operations must be provided by V_Timer_Support for the kernel:

Init resets the clock's current time and initializes the timer hardware. This procedure is called during kernel startup.

Set_Time resets the clock's current time with the input parameter and cancels the active alarm which is rescheduled later. Your implementation of Set_Time has the option to use the Timer_Support_Arg parameter. The user is provided with two procedures for setting the time: V_I_Time.Set_Time and Xcalendar.Set_Clock. Both of these procedures also have the Timer_Support_Arg parameter which is passed directly to Set_Time in V_Timer_Support. The Timer_Support_Arg parameter defaults to No_Addr.

Get_Current_Time reads and returns the clock's current time.

Schedule_Alarm schedules an alarm posted some time in the future. This procedure is called by the kernel when a future timing event must occur. The kernel passes in an absolute (not relative) time in the future when Post_Alarm should be called. The kernel computes this time by adding some amount onto the value returned by Get_Current_Time. Example events are the expiration of a delay or the end of the time slice. If Schedule_Alarm is called with an Alarm_Time later than the current Alarm_Time, the request should be ignored. The kernel resubmits it sometime after the current alarm occurs. If a request is made to schedule an Alarm_Time sooner than the current Alarm_Time, this new Alarm_Time becomes the current Alarm_Time and the old Alarm_Time can be forgotten.

Process_Timer_Interrupt should handle the timer interrupt by advancing the current time and checking if the time for the next alarm is reached. If it has, Process_Timer_Interrupt calls Post_Alarm to post the alarm to the kernel.

Timer_Handler is the timer hardware interrupt handler, created by using the kernel's generic interrupt wrapper. Insert the address of this routine into the Interrupt_Vector_Table in the appropriate spot for you timer hardware.

V_Os_Support Package

V_Os_Support supplies four subprograms to interface the kernel with the world. One is an exit routine that the kernel calls when all work is finished. The other routines enable the kernel to print diagnostic messages before it exits.

When V_Os_Support routines are called, the kernel is either exiting normally or it is printing out a diagnostic, prior to exiting with a fatal error. The V_Os_Support routines should not call any kernel functions.

Halt is called when the kernel program exits.

Put_Str is called to output a string of characters of a given length at the specified memory location. The overloaded Put procedures output a character or a string. The kernel only calls these procedures to output diagnostic information, that is, when something is wrong.

V_Alloc_Support Package

This package contains the kernel program's memory allocation callouts.

V_Stack_Support Package

V_Stack_Support contains the kernel program's stack allocation callouts.

The default implementation simply allocates all task stacks from the kernel's heap area using the V_Alloc_Support callouts.

V_Start_Program Routine

V_Start_Program is the main startup routine of the kernel. It is jumped to from V_Start, which is the actual entry point of the kernel.

Although the source code for V_Start_Program is provided as part of the kernel configuration package, it can normally be used as is. V_Start_Program is provided primarily for reference. Both TDM and the user program have similar routines provided for reference.

V_Start_Program performs the following steps.

1 . Initializes c0_status register, so that, system coprocessor (cp0) and FPC (cp1) are usable, interrupts disabled, and in kernel mode. Also turns off the two sw interrupts (SW0,SW1) in the c0_cause register.

2 . Calls the V_Immediate_Initialization routine to initialize the board before enabling interrupts

3 . Initializes the startup stack and enables interrupts.

4 . Copies the static data section from ROM to RAM if ROM is being used.

5 . Zeroes the kernel program's BSS.

6 . Calls the V_Hardware_Initialization routine to perform user-specified hardware initialization.

7 . Hand-elaborates this configuration package's specification and body.

8 . Optionally, dynamically sets Heap_Stack_Bottom to the end of the user program.

9 . Zeroes the heap/stack memory area.

10 . Calls the V_Boot routine to initialize the kernel's tasking data structures, upon return, switched from startup stack to the kernel's stack.

11 . Installs the general and UTLB miss exception vectors.

12 . Sets the INT_MASK in c0_status according to Configuration_Table's Disable_Int_Mask parameter.

13 . Elaborates the kernel program's packages.

14 . Calls the V_Execute routine which transfers control to the user program by its start address contained in the Link_Block.


RH32 Kernel Configuration Parameters

Policy/Switches file for the krn_conf.ss subsystem.

The context switches for the views in the krn_conf.ss subsystem have the following additional values:

The Board_Common.sw file contains all of the switches that are common to the BSP. The RUNTIMES switch is used to identify the archive libraries that are required to support the kernel as opposed to a user program.

The linker description file is located locally in the krn_conf.ss view.

Configuration Components

Each of these components is discussed in detail below. To find any one of these components in the source files, search for the title string of the desired component. In the source file, the code for each component begins with a line similar to the following:

To configure the kernel program, modify the body of the package, v_krn_conf.2.ada

Startup_Table Structure

The declaration for the Startup_Table is in the file v_krn_conf.2.ada.

You must declare the Startup_Table as a constant, because the values in the table are used as soon as the kernel starts, prior to the elaboration of the kernel configuration package (see the V_Start_Program routine in v_krn_conf.2.ada). The value assigned to each field of the Startup_Table must be a literal, an address constant or a value returned by the address attribute ('Address).

Startup_Stack_Base is the base address of the stack that the kernel uses during initialization. This is the initial value of the stack pointer, register s7, used by the kernel. The address you supply should point to the byte after the highest addressed byte of the stack. The stack grows toward low memory and the value in the stack pointer is always decremented before it is used.

We recommend that the value of Startup_Stack_Base be the address of the byte just after the last byte of RAM in the system. For example, the default value, 16#0010_0000#, is for a system with one megabyte of RAM, that is, RAM in the address range from 16#0000_0000# to 16#000F_FFFF#. We also recommend that the kernel's heap/stack area be placed at this point, so that the memory used by initial stack can be reused. See also Figure 15, "Example Heap/Stack Layout (RH32)," on page 160.

Startup_Stack_Size tells the kernel how many bytes of stack space you allow for the startup stack. The kernel uses this number to compute the stack limit; the kernel has stack limit checks enabled during startup. The kernel requires less than 4096 bytes of startup stack space.

Interrupt_Vector_Table Structure

The kernel program contains an array of interrupt handler addresses called the Interrupt Vector Table (IVT). Handlers in the IVT are dispatched by the kernel's generalized exception handler, V_Gen_Except. This exception handler provides the necessary wrapper code for exceptions, software traps and external interrupts.

This Interrupt_Vector_Table structure specifies the initial contents of each vector in the kernel's IVT. The Interrupt_Vector_Base field in the configuration table points to the base of the IVT.

Each table entry must contain one of these values:

The address of Interrupt_Vector_Table is passed to the kernel as a component of the Configuration_Table, described in Configuration_Table Structure.

There are six methods to put the address of an ISR into the interrupt vector table:

1 . As an initial value in the Interrupt_Vector_Table. This requires that the ISR be linked as part of the kernel program.

2 . Dynamically attach and detach kernel resident handlers using the Replace_Vector procedure in the package V_Krn_Conf_I.

3 . Use Ada interrupt entries as interrupt handlers.

4 . Use a protected procedure as an interrupt handler (See LRM C.3).

5 . Use the routine V_Interrupts.Attach_Isr from Rational Exec to install the ISR dynamically.

6 . You can write code that simply writes new values into the interrupt vector table. After all, it's just an array in memory.

When using V_Interrupts.Attach_Isr, it is best to install subprograms from your program as ISRs. It is simple and easy to communicate between an ISR and your program if the ISR is part of your program. It can reference shared data structures and call other routines.

However, if the ISR is linked with the kernel, the only method for communicating data between the ISR and your program is by shared memory at an agreed upon address.

The type declaration for the Interrupt_Vector_Table is an array of addresses:

Table 4 shows the default state of the kernel interrupt vector table, Interrupt_Vector_Table, in v_krn_conf.2.ada. Since the kernel supports pseudo interrupt vectors, the size of the Interrupt_Vector_Table may be arbitrarily large. Assign the address of Interrupt_Vector_Table to the field Interrupt_Vector_Image in the configuration table, which is discussed in Configuration_Table Structure.

Kernel resident handlers can be attached and detached dynamically using the Replace_Vector procedure in the package V_Krn_Conf_I. Handlers resident in the user program are attached and detached dynamically by Rational Exec services and Ada task interrupt entries.

Apex provides the interrupt handlers for these RH32 exceptions:

These handlers are declared in the package V_Krn_Conf_I.

Initialize Interrupt_Vector_Table entries for interrupts which are unused, unexpected or that have dynamically installed handlers with the address of the unassigned interrupt handler, V_Krn_Conf.V_Default_Isr.

Since trap types 3, 5, 6, 131 and 144 have their own trap handler and aren't dispatched to by the interrupt vector table, their Interrupt_Vector_Table entries are don't care (V_Default_Isr'Address is normally used).

Handlers installed in the interrupt vector table have the following subprogram interface:

where:

Vector_id
is the vector_id obtained from the detail register or a pseudo vector_id from V_Decode_Interrupt
Ef
is the saved CPU context (cpu_status, Detail, History, (r0..r31) at the point of the exception or interrupt

Optionally, interrupt handlers linked in the kernel program may be implemented by instantiating the Interrupt_Handler generic in v_krn_conf.1.ada. This generic is provided for compatibility with other targets. The generic's subprogram differs from the subprogram called by V_Gen_Except, in that, it is a procedure with no parameters.

Table 4 Default Kernel Interrupt Vector Table —— RH32 Family
Vector ID
Default Value
Description
000
krn_cpu_defs.untouchable_vector
Power On Surprise
001
krn_cpu_defs.untouchable_vector
Power Fail Surprise
002
krn_cpu_defs.untouchable_vector
Monitor Surprise
003
krn_cpu_defs.untouchable_vector
HW Error Surprise
004..015
krn_cpu_defs.untouchable_vector
Reserved
016
krn_cpu_defs.untouchable_vector
Register file hardware error
017
krn_cpu_defs.untouchable_vector
Privileged instruction violation
018
krn_cpu_defs.untouchable_vector
TLB miss, instruction
019
krn_cpu_defs.untouchable_vector
TLB miss, data
020
krn_cpu_defs.untouchable_vector
TLB violation, instruction
021
krn_cpu_defs.untouchable_vector
TLB violation, data
022
krn_cpu_defs.untouchable_vector
Illegal instruction
023
krn_cpu_defs.untouchable_vector
Hardware error
024
krn_cpu_defs.untouchable_vector
Not used
025
v_krn_conf_i.overflow_handler'address
Fixed point overflow
026
v_krn_conf_i.fp_exception_handler'address
Coprocessor exception
027
krn_cpu_defs.untouchable_vector
Unrecoverable hardware error
028
krn_cpu_defs.untouchable_vector
Double word address exception
029
krn_cpu_defs.untouchable_vector
Processor status hardware error
030
krn_cpu_defs.untouchable_vector
Cache error, instruction
031
krn_cpu_defs.untouchable_vector
Cache error, data
032
v_krn_conf_i.enter_from_user'address
Trap_Krn (kernel services)
033
krn_cpu_defs.untouchable_vector
Trap_Tdm (TDM services)
034..047
krn_cpu_defs.untouchable_vector
Reserved
048
krn_cpu_defs.untouchable_vector
Invalid FP operation
049
krn_cpu_defs.untouchable_vector
Divide by zero
050
krn_cpu_defs.untouchable_vector
Overflow
051
krn_cpu_defs.untouchable_vector
Underflow
052
krn_cpu_defs.untouchable_vector
Inexact exception
053
krn_cpu_defs.untouchable_vector
Tiny exception
054
krn_cpu_defs.untouchable_vector
Source operand exception
055
krn_cpu_defs.untouchable_vector
Register file error
056
krn_cpu_defs.untouchable_vector
Self-checking error
057
krn_cpu_defs.untouchable_vector
Bus error
058
krn_cpu_defs.untouchable_vector
Pipeline parity error
059
krn_cpu_defs.untouchable_vector
Illegal instruction
060..063
krn_cpu_defs.untouchable_vector
Reserved
064
v_krn_conf_i.overflow_handler'address
Trap_Overflow
065
v_krn_conf_i.div_zero_handler'address
Trap_Div_Zero
066
v_krn_conf_i.range_handler'address
Trap_Range
067
v_krn_conf_i.stack_overflow_handler'address
Trap_Stack_Overflow
068..095
krn_cpu_defs.untouchable_vector
Reserved
096
v_timer_support.timer_handler'address
INT_0 (timer B intr)
097
V_Default_Isr'Address
INT_1
098
V_Default_Isr'Address
INT_2
099
V_Default_Isr'Address
INT_3
100
V_Default_Isr'Address
INT_4
101
V_Default_Isr'Address
INT_5
102
V_Default_Isr'Address
INT_6
103
V_Default_Isr'Address
INT_7
0104..255

Users exceptions

Configuration_Table Structure

The Configuration_Table is the primary mechanism by which you tailor the kernel to your target system. The parameters that make up the configuration table are partitioned into these categories:

Note: Because Ada does not allow constants to be reassigned, the Heap_Stack_Bottom field must be changed to something besides V_Krn_Conf_I.End_Of_User_Program (the default) before the Configuration_Table may be changed to a constant. The following source line must be located and commented out before compilation:

This is the last source line in the procedure Auto_Config_Heap_Stack_Bottom.

Memory Management

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. Objects are allocated from a heap when the Ada new operator is used. Rational Exec provides two mechanisms for managing heaps.

In addition to the stacks described here, every Ada task has its own stack, which is allocated from the heap/stack area when the task is created.

Some memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Following the memory management parameter descriptions, Figure 14 presents an example layout for the heap/stack area

For more information about memory management, see "Memory Management" in the Rational Apex Runtime Guide.

Heap_Stack_Bottom and Heap_Stack_Top

Heap_Stack_Bottom and Heap_Stack_Top define the area of memory from which heaps and stacks are allocated. Heap_Stack_Bottom is the low address, usually set to the first RAM address following the end of TDM, the kernel and user programs. If Heap_Stack_Bottom is set to the constant V_Krn_Conf_I.End_Of_User_Program this value is computed dynamically by reading the user program's group tables. Heap_Stack_Top is the highest addressed RAM location available for heaps and stacks (that is, it is not the byte after the heap/stack area, it is the last byte of the area).

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The next few memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

The debugger's lt command has a use option for displaying maximum stack usage. Use this feedback to refine your initial guess for the different stack sizes.

Krn_Stack_Size

Krn_Stack_Size is the size of the kernel's stack. Currently only used when V_Krn_Conf.V_Start_Program elaborates the kernel packages. Normally the same size as and overlaps the startup stack. See also Startup_Table Structure.

Intr_Stack_Size

Intr_Stack_Size is the size of the interrupt handlers' stack. This is the stack that interrupt handlers use. The stack size excludes room for exception handling. All interrupt service routines use this same stack, so you must allow space for nested interrupts if they are possible. V_Gen_Exception switches to the interrupt handler's stack.

Krn_Exception_Stack_Size

Krn_Exception_Stack_Size is the space set aside below the bottom of the interrupt handler's stack for exception unwinding. This parameter is only applicable to tasks created in the kernel program.

Task_Supervisor_Stack_Size

Task_Supervisor_Stack_Size specifies how much supervisor stack space is needed for each task that executes in user state.

Task_Supervisor_Stack_Size is currently not used. We do not support programs which run in user state.

Zero_Stacks_Enabled

At startup all the memory to be used for task stacks is zeroed. However, if tasks are dynamically terminated and recreated, the stack area for subsequently created tasks is no longer zeroed. This can lead to erroneous stack usage information displayed by the debugger's lt use command.

Zero_Stacks_Enabled is set to True to guarantee that at task create its stack area is zeroed. Since it takes extra time to zero the stack area, Zero_Stacks_Enabled is normally only set to True when used in conjunction with lt use for dynamically created tasks.

Example Heap/Stack Layout

If we have the following memory parameter settings, Figure 14 is accurate.

Figure 14 Example Heap/Stack Layout (RH32)

Processor Status Configuration Parameters

Supervisor_Tasks_Enabled

Supervisor_Tasks_Enabled is not used. All tasks must run in kernel mode.

User_Int_Mask

User_Int_Mask is the setting of the c0_status register's IM field when the user program is started at completion of kernel startup. 16#FF# (all interrupt levels enabled) is the default.

Disable_Int_Mask

Disable_Int_Mask is the setting of the c0_status register's IM field when interrupts are disabled within the kernel

Note that the default depends on which interrupt is used by the FPP and bus error (they are enabled while in the kernel).

Interrupt Configuration Parameters

Interrupt_Vector_Base

Due to the RH32 architecture, a substantial amount of code is required to save and restore the CPU state for a synchronous or asynchronous exception. A generalized exception handler, V_Gen_Exception, provides the necessary exception handler wrapper code. After saving the CPU context, it uses the contents of the c0_cause register to create an index to dispatch through the kernel's interrupt vector table (an array of handler addresses).

Interrupt_Vector_Base points to where this interrupt vector table is located in memory.

The interrupt vector table has Interrupt_Vector_Size entries. The size of an entry is 4 bytes.

Interrupt_Vector_Size

Interrupt_Vector_Size indicates the number of interrupt vectors in the kernel's interrupt vector table. This number should be equal to Interrupt_Vector_Table'Length.

Interrupt_Vector_Size tells the kernel how many vectors should be in the table. This is used during initialization and as a bounds check on V_Interrupts.Attach_Isr.

Because the kernel supports pseudo interrupts, Interrupt_Vector_Size may be arbitrarily large.

Floating point Coprocessor Parameters

The following parameters deal with the Floating Point Accelerator.

Hw_Flt_Enabled

For the RH32 Family, processor Hw_Flt_Enabled is always set to True.

Floating_Point_Control

Floating_Point_Control is a structure which specifies the initial value for the floating point control register of the RH32 Floating Point processor. This structure and its subcomponents are declared in the package V_I_Types found in rational.ss.

Fields of the components and their values are as specified in the RH32 Architecture Manual.

The default value for this structure is as follows:

1 . The condition bit is cleared.

2 . Traps invalid, zero-divide, and overflow are enabled.

3 . The default rounding mode is set to "to nearest".

The default Interrupt_Vector_Table set the software FP exception vector to V_Krn_Conf_I.Fp_Exception_Handler. This handler raises the Ada exception, Numeric_Error if one of these unmasked exceptions is signalled.

Time Slice Parameters

The following parameters deal with time slicing functions.

Time_Slicing_Enabled

This parameter is set to True to enable time slicing and false to prevent time slicing. This parameter is ignored by the no-tasking kernel.

This parameter is the initial setting of the kernel and can be changed dynamically by the Rational Exec subprogram V_Xtasking.Set_Time_Slicing_Enabled. Function V_Xtasking.Current_Time_Slicing_Enabled can read the current value.

Time_Slice_Interval

Each task in the system is started with the value of the Time_Slice_Interval as its time slice. This is the amount of time that the task runs before being pre-empted for any equal priority tasks that may be ready to run. An individual task's time slice interval can be changed during execution by calling V_Xtasking.Set_Time_Slice and read by V_Xtasking.Current_Time_Slice. Time_Slice_Interval is of type V_I_Type.Time_Span_T. Values less than or equal to zero (<=0) disable time slicing. The default Time_Slice_Interval is 1.0 second.

This parameter is ignored by the no-tasking kernel.

Time_Slicing_Priority

Time_Slicing_Priority specifies the maximum task priority to which time slicing applies. This way, normal tasks can be time sliced but high priority tasks execute until they give up the processor (or until an even higher priority task becomes ready).

All tasks whose priority is less than or equal to Time_Slicing_Priority time sliced.

This parameter is ignored by the no-tasking kernel.

Idle Processing Parameters

The following parameter deals with idle time functions.

Idle_Stack_Size

Idle_Stack_Size specifies the size in bytes of the idle task's stack. This configuration parameter is equivalent to the 'Storage_Size attribute for an application define task.

The default Idle_Stack_Size is 5,000 bytes.

This parameter is ignored by the no-tasking kernel.

Miscellaneous Configuration Parameters

Task_Storage_Size

Task_Storage_Size specifies the size in bytes of the area allocated in the task control block for user storage. The Rational Exec Services, Allocate_Task_Storage and Get_Task_Storage manage this area in the task control block.

Note: This area is used by the Rational implementation of Ada.Task_Attributes (See LRM C.7.2) to store pointers to task attributes. If this package is used, Number_Of_Attributes objects of size Address'Size will be allocated in this area in the task control block. Number_Of_Attributes is defined in Ada.Attributes_Storage in the Systems_Programming.ss subsystem. It is currently four, and currently non configurable.

Pending_Count

Pending_Count specifies the maximum number of kernel service requests from an ISR (or nested ISR) held pending until the outermost ISR completes. When the outermost ISR completes, the kernel processes the queue of pending requests.

Miscellaneous Variables

This component contains miscellaneous variables.

V_Immediate_Initialization and V_Hardware_Initialization Routines

V_Immediate_Initialization carries out initialization which must occur right away after a CPU reset. An example is board control registers which come up in unknown states.

If your stack needs some preparation, such as mapping in it's pages, do that here. This routine is called before any RAM is accessed.

Things are in a precarious state at this point:

1 . You have no stack

2 . Interrupts are disabled

3 . Register ra contains your return address. All other general purpose registers are considered dead by the calling routine and can be used freely.

V_Hardware_Initialization performs any operations necessary to initialize the target system prior to kernel initialization. This routine is called with interrupts enabled. Refer to V_Start_Program() source in v_krn_conf.2.ada to see when it is called during kernel startup.

The V_Hardware_Initialization performs any operations necessary to initialize the hardware prior to kernel initialization.

This routine is called before the kernel is initialized. It should not make any calls to kernel services or use any Ada language features that call kernel services implicitly. In particular, the following language features should not be used:

This routine must not reference any entities which require elaboration, since it is called before any elaboration takes place.

V_Elaboration_Callout Routine

V_Elaboration_Callout is the default elaboration callout routine. This procedure is executed prior to elaborating each entry in the elaboration table during program elaboration.

Elaboration_Entry is the address of the entry point for the elaboration code for the unit that is about to be elaborated.

The default body of this procedure is null.

V_Rfs Routine

Upon entry:

Uses k0 register for doing the return. Since k1 was not saved or used, it is not restored.

V_Restore_Ef Routine

Upon entry:

V_Untouchable Routine

Upon entry: kernel mode, interrupts disabled

V_Gen_Exception Routine

Upon entry:

In the RH32 architecture, exceptions, traps, and interrupts are handled with different surprise handlers, which decode the exception or interrupt, and produce an index into the interrupt vector table (IVT). This routine, V_Gen_Exception, is then called to save the remaining context, and dispatch the exception or interrupt.

V_Surprise Routines

Upon entry:

V_Trap_Surprise handles trap instructions, if a kernel trap is detected, the trap is dispatched directly to the kernel. Otherwise, the V_Surprise routines decode the exception or interrupt, translate the event into an index into the interrupt vector table, and then call the V_Gen_Exception for common processing of all exceptions and interrupts.

V_Default_Isr Routine

V_Default_Isr is the default Interrupt Service Routine (ISR).

The purpose of this routine is to give you access to interrupts that are coming in that you aren't expecting, for which there is no handler.

The kernel attaches this ISR to any interrupt vector whose handler is detached by a call to V_Krn_Conf_I.Replace_Vector with a null handler address. Also, if the user program calls V_Interrupts.Detach_Isr, this default ISR is attached.

Assign the address of this routine to each unused interrupt vector in the Interrupt_Vector_Table described above.

V_Pending_Overflow_Callout Routine

This routine is called by the kernel when the pending service ring has overflowed. If this routine is called, you need to increase the configuration parameter Pending_Count.

V_Timer_Support Package

The V_Timer_Support package spec is the interface to the timer functions that are required by the kernel. You must supply a body for this package that implements the functions for your timer hardware.

The type V_I_Types.Time_T is used to represent time. This is a signed 64-bit integer count of time units, the length of which is defined by the constant Real_Time.Time_Unit in the board_common.ss subsystem. Since not all of the platforms on which Apex is implemented provide 64-bit integer types, Time_T is implemented using two 32-bit integers:

Time_T is the underlying implementation of both Ada.Calendar.Time and Ada.Real_Time.Time; as such it unifies the requirements of these types. Ada.Calendar.Time must be able to represent any local ("wall clock") time between the years 1901 and 2099 with a granularity no greater than 20 milliseconds. Ada.Real_Time.Time must cover the 50-year period from system start-up to within 20 microseconds. A 64-bit integer count provides for a range of +/- 292 years to nanosecond resolution, such that the required range can be provided for Ada.Calendar.Time relative to system startup anywhere between 1901 and 2099. Nanosecond resolution provides compatibility with the POSIX Real-Time Extensions (1003.1b-1993), which provides time as an integer count of seconds plus and integer count of nanoseconds.

This is also the representation of the Ada.Real_Time.Time_Span type for representing fine-grain duration. The only functional difference between Time and Time_Span is that an implicit start time or epoch is implied for Time values; a Time value represents a point in time as the signed duration between that time and the epoch. Apex uses the POSIX (1003.1-1990) epoch: 0 hours, 0 minutes, 0.0 seconds, 1 January 1970, Universal Coordinated Time.

The value of Time_Unit is system-dependent, and can be configured to be a value convenient for implementation over your timer hardware. (See "Configuring Ada.Real_Time" in Using the Ada Runtime).

The following operations must be provided by V_Timer_Support for the kernel:

Init resets the clock's current time and initializes the timer hardware. This procedure is called during kernel startup.

Set_Time resets the clock's current time with the input parameter and cancels the active alarm which is rescheduled later.

Get_Current_Time reads and returns the clock's current time.

Schedule_Alarm schedules an alarm posted some time in the future. This procedure is called by the kernel when a future timing event must occur. The kernel passes in an absolute (not relative) time in the future when Post_Alarm should be called. The kernel computes this time by adding some amount onto the value returned by Get_Current_Time. Example events are the expiration of a delay or the end of the time slice. If Schedule_Alarm is called with an Alarm_Time later than the current Alarm_Time, the request should be ignored. The kernel resubmits it sometime after the current alarm occurs. If a request is made to schedule an Alarm_Time sooner than the current Alarm_Time, this new Alarm_Time becomes the current Alarm_Time and the old Alarm_Time can be forgotten.

Process_Timer_Interrupt should handle the timer interrupt by advancing the current time and checking if the time for the next alarm is reached. If it has, Process_Timer_Interrupt calls Post_Alarm to post the alarm to the kernel.

Timer_Handler is the timer hardware interrupt handler, created by using the kernel's generic interrupt wrapper. Insert the address of this routine into the Interrupt_Vector_Table in the appropriate spot for you timer hardware.

V_Os_Support Package

The V_Os_Support package supplies subprograms to interface the kernel with the world. One is an exit routine that the kernel calls when all work is finished. The other routines enable the kernel to print diagnostic messages before it exits.

When V_Os_Support routines are called, the kernel is either exiting normally or it is printing out a diagnostic, prior to exiting with a fatal error. The V_Os_Support routines should not call any kernel functions.

Halt is called when the kernel program exits.

Put_Str is called to output a string of characters of a given length at the specified memory location. The overloaded PUT procedures output a character or a string. The kernel only calls these procedures to output diagnostic information, that is, when something is wrong.

V_Alloc_Support Package

This package contains the kernel program's memory allocation callouts.

V_Stack_Support Package

V_Stack_Support contains the kernel program's stack allocation callouts.

The default implementation simply allocates all task stacks from the kernel's heap area using the V_Alloc_Support callouts.

V_Start_Program Routine

V_Start_Program is the main startup routine of the kernel. It is jumped to from V_Start, which is the actual entry point of the kernel (see v_start.2.ada).

Although the source code for V_Start_Program is provided as part of the kernel configuration package, it can normally be used as is. V_Start_Program is provided primarily for reference. Both TDM and the user program have similar routines provided for reference.

V_Start_Program performs the following steps.

1 . Initializes c0_status register, so that, system coprocessor (cp0) and FPP (cp1) are usable, interrupts disabled, and in kernel mode. Also turns off the two sw interrupts (SW0,SW1) in the c0_cause register.

2 . Calls the V_Immediate_Initialization routine to initialize the board before enabling interrupts

3 . Initializes the startup stack and enables interrupts.

4 . Copies the static data section from ROM to RAM if ROM is being used.

5 . Zeroes the kernel program's BSS.

6 . Calls the V_Hardware_Initialization routine to perform user-specified hardware initialization.

7 . Hand-elaborates this configuration package's specification and body.

8 . Optionally, dynamically sets Heap_Stack_Bottom to the end of the user program.

9 . Zeroes the heap/stack memory area.

10 . Calls the V_Boot routine to initialize the kernel's tasking data structures, upon return, switched from startup stack to the kernel's stack.

11 . Installs the general and UTLB miss exception vectors.

12 . Sets the Int_Mask in c0_status according to Configuration_Table's Disable_Int_Mask parameter.

13 . Elaborates the kernel program's packages.

14 . Calls the V_Execute routine which transfers control to the user program by its start address contained in the Link_Block.


M68000 Family Kernel Configuration Parameters

Policy/Switches file for the krn_conf.ss subsystem.

The context switches for the views in the krn_conf.ss subsystem have the following additional values:

The Board_Common.sw file contains all of the switches that are common to the BSP. The RUNTIMES switch is used to identify the archive libraries that are required to support the kernel as opposed to a user program.

The linker description file is located locally in the krn_conf.ss view.

Configuration Components

The components of the kernel configuration package are

Each of these components is discussed in detail below. To find any one of these components in the source files search for the title string. In the source file, the code for each component begins with a line similar to the following:

To configure the kernel program, modify the body of the package, v_krn_conf.2.ada

Startup_Table Structure

The declaration for the Startup_Table is in the file v_krn_conf.2.ada and the type declaration is in v_krn_conf.1.ada.

You must declare the Startup_Table as a constant, because the values in the table are used as soon as the kernel starts, prior to the elaboration of the kernel configuration package (see the V_Start_Program routine in v_krn_conf.2.ada). The value assigned to each field of the Startup_Table must be a literal, an address constant or a value returned by the address attribute ('Address).

Startup_Stack_Base is the base address of the stack that the kernel uses during initialization. This is the initial value of the stack pointer, register ISP, used by the kernel. The address you supply should point to the byte after the highest addressed byte of the stack. The stack grows toward low memory and the value in the stack pointer is always decremented before it is used.

We recommend that the value of Startup_Stack_Base be the address of the byte just after the last byte of RAM in the system. For example, the default value, 16#0010_0000#, is for a system with one megabyte of RAM, that is, RAM in the address range from 0 to 16#0F_FFFF#. We recommend that the kernel heap/stack area be placed at this point, so that the memory used by initial stack can be reused.

Startup_Stack_Size tells the kernel how many bytes of stack space you allow for the startup stack. The kernel uses this number to compute the stack limit; the kernel has stack limit checks enabled during startup.

Interrupt_Vector_Table Structure

The Interrupt_Vector_Table defines an image of the initial contents of the processor's interrupt vector table. The Motorola M68000 Family documentation calls this table the "Exception Vector Table".

Each table entry must contain one of these values:

The type declaration for the Interrupt_Vector_Table is an array of addresses:

Table 5 shows the default state of the kernel interrupt vector table, Interrupt_Vector_Table, in v_krn_conf.2.ada. The default value assignments are the same for all M68000 processors but the exact description of the vector's purpose may vary. Consult the users manual for your processor for more information on exception vector assignments. The kernel interrupt vector table provides handlers for vectors 5-8, 32 (default trap for entry to the kernel), 50 and 52-54. Note that if the trap number for the kernel is changed, you must also change package V_Traps in the user library.

Notes indicated in the table follow.

Table 5 Default Kernel Interrupt Table
Vector
Default Value
Description
000
V_Krn_Conf_I.Untouchable_Vector
Reset: Initial Supervisor/Interrupt SP
001
V_Krn_Conf_I.Untouchable_Vector
Reset: Initial PC
002
V_Krn_Conf_I.Untouchable_Vector
Bus Error/Access Fault (Note 1)
003
V_Krn_Conf_I.Untouchable_Vector
Address Error (Note 1)
004
V_Krn_Conf_I.Untouchable_Vector
Illegal Instruction
005
V_Krn_Conf_I.zero_divide_handler
'Address

Integer Zero Divide (mapped to numeric error)
006
V_Krn_Conf_I.chk_inst_handler
'Address

CHK, CHK2 instructions (mapped to constraint error)
007
V_Krn_Conf_I.trapv_cc_instr_handler
'Address

ccTRAPcc, FPTRAPcc, TRAPcc, TRAPV mapped to numeric error
008
V_Privilege_Violation_Handler
'Address

Privilege Violation (Note 2)
009
V_Krn_Conf_I.Untouchable_Vector
Trace
010
V_Krn_Conf_I.Untouchable_Vector
Line 1010 emulator
011
V_Krn_Conf_I.Untouchable_Vector
Line 1111 emulator
012
V_Krn_Conf_I.Untouchable_Vector
Hardware breakpoint (CPU32)
013
V_Krn_Conf_I.Untouchable_Vector
Coprocessor Protocol Violation
014
V_Krn_Conf_I.Untouchable_Vector
Format Error
015
V_Krn_Conf_I.Untouchable_Vector
Uninitialize Interrupt
016..23
V_Krn_Conf_I.Untouchable_Vector
(Unassigned, Reserved)
024
V_Krn_Conf_I.Untouchable_Vector
Spurious Interrupt
025..31
V_Krn_Conf_I.Untouchable_Vector
Level 1-7 Interrupt Autovector
032
V_Krn_Conf_I.Enter_From_User
'Address

Trap 00: Default for entering kernel
033..46
V_Krn_Conf_I.Untouchable_Vector
Trap 01 through Trap 14
047
V_Krn_Conf_I.Untouchable_Vector
Trap 15: Default Vector for TDM
048
V_Krn_Conf_I.Untouchable_Vector
FP Branch/Set on Unordered Condition
049
V_Krn_Conf_I.Untouchable_Vector
FP Inexact result
050
V_Krn_Conf_I.Float_Error_Handler
'Address

FP Divide by Zero
051
V_Krn_Conf_I.Untouchable_Vector
FP Underflow
052
V_Krn_Conf_I.Float_Error_Handler
'Address

FP Operand Error
053
V_Krn_Conf_I.Float_Error_Handler
'Address

FP Overflow
054
V_Krn_Conf_I.Float_Error_Handler
'Address

FP Signaling NAN
055
V_Krn_Conf_I.Untouchable_Vector
FP Unimplemented Data Type
056
V_Krn_Conf_I.Untouchable_Vector
PMMU Configuration
057
V_Krn_Conf_I.Untouchable_Vector
PMMU Illegal Operation
058
V_Krn_Conf_I.Untouchable_Vector
PMMU Access Level Violation
059..63
V_Krn_Conf_I.Untouchable_Vector
(Unassigned, Reserved)
064..140
V_Krn_Conf_I.Untouchable_Vector
User-Defined Vectors (77)
141
V_Krn_Conf_I.Untouchable_Vector
Interrupt for clock
142..255
V_Krn_Conf_I.Untouchable_Vector
User Defined Vectors (114)

Note 1: Vector 2 can be mapped to the kernel's Bus_Error_Handler. Vector 3 can be mapped to the kernel's Address_Error_Handler. For either case, the kernel's handler raises an Ada Storage_Error exception in the user program.

Note 2: If a privilege violation occurs (vector 8), the interrupt handler V_Privilege_Violation_Handler first checks if the instruction attempted is either a move_w d0, sr, move_w sr, d0, movec_1 caer, d0, or movec.1 d0, caer. In these cases, V_Privilege_Violation_Handler executes the instruction and returns to the program. Otherwise, it jumps to V_Default_Isr. For additional information, see V_Default_Isr Routine.

The address of Interrupt_Vector_Table is passed to the kernel by the Interrupt Vector_Image field in the Configuration_Table. If this field is assigned the constant V_Krn_Conf_I.Untouchable_Table, the Interrupt_Vector_Table structure is ignored. Otherwise, during initialization, the kernel constructs a new processor interrupt vector table from the values in the Interrupt_Vector_Table. If an entry in the Interrupt_Vector_Table is assigned the value Untouchable_Vector, the kernel preserves the entry from the currently active processor interrupt vector table.

The base address of the new processor interrupt vector table is specified in the Configuration_Table's Vector_Base_Register field. See also Vector_Base_Register and Interrupt_Vector_Image in Interrupt Configuration Parameters

There are six methods to put the address of an ISR into the processor interrupt vector table:

1 . As an initial value in the Interrupt_Vector_Table. This requires that the ISR be linked as part of the kernel program.

2 . Dynamically attach and detach kernel resident handlers using the Replace_Vector procedure in the package V_Krn_Conf_I.

3 . Use a protected procedure as an interrupt handler (See LRM C.3).

4 . Use Ada interrupt entries as interrupt handlers.

5 . Use the routine V_Interrupts.Attach_Isr from Rational Exec to dynamically install an ISR residing in the user program.

6 . You can write code that writes new values into the interrupt vector table. After all, it's just an array in memory. The Rational Exec service, V_Interrupts.Get_Ivt, can be called to get the starting address of this interrupt vector table.

All interrupt handlers linked with the kernel must be implemented as kernel interrupt handler routines, following the rules required by the kernel. This can be done easily by using the generic V_Krn_Conf_I.Interrupt_Handler. See the file v_krn_conf.1.ada for more details.

The above problem is solved by having user defined pseudo interrupt vectors in addition to the hardware interrupt vectors. The handler attached to the hardware vector dispatches to one of these pseudo vectors. The size of the Interrupt_Vector_Table can be extended beyond 256 entries to store these pseudo vectors.

For M68000 Family, an example using pseudo interrupt vectors is provided in "TDM Configuration Parameters" Interrupt_Vector_Table Structure where it discusses the Interrupt_Vector_Table structure in V_Tdm_Conf.

See also "Writing Interrupt Handlers, ISR Generic Wrappers" in the Embedded Programming Guide.

Configuration_Table Structure

The Configuration_Table is the primary mechanism by which you tailor the kernel to your target system. The parameters that make up the configuration table are partitioned into these categories:

Note: Because Ada does not allow constants to be reassigned, the Heap_Stack_Bottom field must be changed to something besides V_Krn_Conf_I.End_Of_User_Program (the default) before the Configuration_Table may be changed to a constant. The following source line must be located and commented out before compilation:

This is the last source line in the procedure Auto_Config_Heap_Stack_Bottom.

Memory Management

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The user program maintains its own local heap area for doing Ada new allocations. By default, memory for the user program's local heap is obtained from the kernel's heap/stack area.

In addition to the stacks described here, every Ada task has its own stack, which is allocated from the heap/stack area when the task is created.

Some memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Following the memory management parameter descriptions, Figure 15 presents an example layout for the heap/stack area.

For more information about memory management, see "Memory Management" in the Rational Apex Runtime Guide.

Heap_Stack_Bottom and Heap_Stack_Top

Heap_Stack_Bottom and Heap_Stack_Top define the area of memory from which heaps and stacks are allocated. Heap_Stack_Bottom is the low address. It is usually set to the first RAM address following the end of TDM, the kernel and user programs. If Heap_Stack_Bottom is set to the constant V_Krn_Conf_I.End_Of_User_Program this value is computed dynamically by reading the user program's group tables. Heap_Stack_Top is the highest addressed RAM location available for heaps and stacks. It is not the byte after the heap/stack area, it is the last byte of the area.

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The next few memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

The debugger's lt command has a use option for displaying maximum stack usage. Use this feedback to refine your initial guess for the different stack sizes.

Krn_Stack_Size

Krn_Stack_Size is the size of the kernel's stack. Currently only used when V_Krn_Conf.V_Start_Program elaborates the kernel packages. Normally the same size as and overlaps the startup stack. For additional information on the kernel stack, see "M68000 Family Processor-Specific Runtime Issues" in Programming for Rational Exec.

Note: The kernel adds 64 bytes to this size to account for TDM.

Intr_Stack_Size

Intr_Stack_Size is the size of the interrupt handlers' stack. This is the stack that interrupt handlers use. The stack size excludes room for exception handling. All interrupt service routines use this same stack, so you must allow space for nested interrupts if they are possible. The kernel service V_Krn_Conf_I.Isr_Enter switches to the interrupt handlers' stack. This service is called directly by the generic V_Krn_Conf_I.Interrupt_Handler. The Rational Exec generic ISR wrapper, V_Interrupts.Isr calls Isr_Enter using the Debug_Block. For additional information on the interrupt stack, see "Writing Interrupt Handlers For M68000 Family, Interrupt Stack" in Programming for Rational Exec.

Krn_Exception_Stack_Size

Krn_Exception_Stack_Size is the size of the area set aside below the bottom of the interrupt handler's stack for exception unwinding. It is also the exception stack size for tasks created in the kernel program. Kernel tasks consist of the idle task and tasks created by V_Krn_Aug.Task_Create.

Task_Supervisor_Stack_Size

Task_Supervisor_Stack_Size specifies how much supervisor stack space is needed for each task.

Each task that normally executes in user mode (S bit of the Status Register = 0), including the main program, requires a separate stack for supervisor mode. A task in user mode executes on the user stack, pointed to by the USR register. If a hardware exception, interrupt or user trap occur, the processor switches to supervisor mode (S = 1), switches to the supervisor stack and pushes an exception frame onto the stack.

The supervisor stack must have sufficient room to accommodate the worst case usage. If you follow the guidelines for interrupt service routines and use the generic V_Krn_Conf_I.Interrupt_Handler or the generic V_Interrupts.ISR, the worst case supervisor stack usage for one level of interrupt is 52 bytes. This must be multiplied by the number of priority levels actually used by the hardware. For example, if hardware can generate priority level 1, 3 and 6 interrupts, you need 52 * 3 = 156 bytes of supervisor stack space for the worst case, interrupts nested three deep.

If the configuration parameter Supervisor_Tasks_Enabled is true, all tasks are executing in supervisor state. In this case, Task_Supervisor_Stack_Size parameter is ignored.

Zero_Stacks_Enabled

At startup all the memory to be used for task stacks is zeroed. However, if tasks are dynamically terminated and recreated, the stack area for subsequently created tasks is no longer zeroed. This can lead to erroneous stack usage information displayed by the debugger's lt use command.

Zero_Stacks_Enabled is set to True to guarantee that at task create its stack area is zeroed. Since it takes extra time to zero the stack area, Zero_Stacks_Enabled is normally only set to True when used in conjunction with lt use for dynamically created tasks.

Example Heap/Stack Layout

If we have the following memory parameter settings, Figure 15 is accurate.

Figure 15 Example Heap/Stack Layout (M68000 Family)

Processor Status Parameters

The processor status parameters are all related to the processor status register.

Supervisor_Tasks_Enabled

When set to True, all tasks including the main program execute in supervisor state.

Supervisor_Tasks_Enabled is not currently used. We do not support programs which run in user state.

Master_State_Enabled

When set to True the kernel and supervisor tasks use the master stack instead of the interrupt stack. This means that the M bit of the status register (SR) is set to 1 during non-interrupt processing.

This parameter is not applicable to the MC68000, MC68010 or the CPU32 processors since they have no M bits in Status Register. For those processors, the parameter is ignored.

For additional information about executing in Master State, see "SM = 01: User Tasks, Master State Not Enabled" in Programming for Rational Exec.

User_Status

User_Status is the value loaded into the SR register at the completion of kernel startup, just prior to transferring control to the user program. User_Status is the complete SR register contents.

The user program can be started in either supervisor or user state at any interrupt priority level. However, if Supervisor_Tasks_Enabled is True, the user program is forced to start in supervisor state (that is, with the S bit of the SR register set to 1). The M bit is set according to the Master_State_Enabled parameter.

Example values:

16#0000#
user
all interrupts enabled (default)
16#0700#
user
all interrupts disabled
16#2000#
supervisor
all interrupts enabled
16#2700#
supervisor
all interrupts disabled

Disable_Intr_Priority

This is the value that the kernel uses for the interrupt priority mask within the MC68000 Family status register (SR) when it wants to disable interrupts. Only bits 8..10 are used so there are only eight possible values:

16#0000#
all interrupts enabled
16#0100#

16#0200#

16#0300#

16#0400#

16#0500#

16#0600#

16#0700#
all interrupts disabled (default)

Note: Interrupts with priority 7 can never be disabled.

By setting the Disable_Intr_Priority mask to less than 16#0700#, it is possible to have higher priority interrupts come in and be serviced. The kernel sets the interrupt priority mask to Disable_Intr_Priority when it is entering a critical region. Therefore, any code which executes at an interrupt privilege level above this value may not invoke a kernel service, including those invoked implicitly by Ada programs. The following language features may generate calls to the kernel:

For more information, see "ISRs and M68000 Family Interrupt Levels" in Programming for Rational Exec.

Interrupt Configuration Parameters

The following parameters deal with the interrupt vector table.

Vector_Base_Register

The initial value of the Vector_Base_Register tells the kernel where you want the processor's interrupt vector table located in memory. During initialization, the kernel first builds the table at this address and then loads the VBR register with the address of this table.

The constant V_Krn_Conf_I.Untouchable_Vbr should be given as the initial Vector_Base_Register value if the VBR register must not be changed during kernel initialization. In this case, the kernel builds the vector table "in place", modifying the vectors in the currently active interrupt vector table (that is, the table pointed to by the current VBR register).

If the value given by the Vector_Base_Register parameter is different from the initial value of the VBR, the value of any vector specified as untouchable in the Interrupt_Vector_Table is copied from the currently active vector table to the new interrupt table.

The default Vector_Base_Register is Untouchable_Vbr. If you use TDM to debug, this is the most useful value during development. However, if you use an emulator and plan to reset your hardware directly to the kernel, set it to 0.

Not all M68000 Family processors have a VBR. In that case, the value of this parameter is ignored and the table is built "in place" at address 0.

Interrupt_Vector_Size

Interrupt_Vector_Size indicates the number of interrupt vectors in the interrupt vector table. This number should be equal to Interrupt_Vector_Table'Length.

Interrupt_Vector_Size tells the kernel how many vectors should be in the table. This is used during initialization and also as a bounds check on V_Interrupts.Attach_Isr.

Because the kernel supports pseudo interrupts, Interrupt_Vector_Size may be larger than 256.

Interrupt_Vector_Image

Interrupt_Vector_Image tells the kernel the starting address of the interrupt vector table image that you defined (Interrupt_Vector_Table). During initialization, the kernel copies the contents of the image into what becomes the actual interrupt vector table.

If the interrupt vector should not be changed at all by the kernel, for example if the interrupt vector table is in ROM, set Interrupt_Vector_Table to V_Krn_Conf_I.Untouchable_Table.

The default Interrupt_Vector_Image is Interrupt_Vector'Address.

Floating Point Coprocessor Parameter

The following parameter is applicable only to the MC68881/2 floating point coprocessor, the MC68040 and the MC68060. The MC68000, MC68010 and CPU32 cannot have a floating point coprocessor, so this parameter can be ignored for those processors.

Floating_Point_Control

Floating_Point_Control is a structure that specifies the initial value for the Floating Point Control Register (FPCR) register. This structure and its subcomponents are declared in package V_I_Types found in the rational.ss subsystem of the release.

Refer to your Motorola processor manuals for a complete description of the FPCR.

The default value for this structure sets the exceptions OPERR, OVFL and DZ to 1 so that if any of these conditions occur, a hardware exception is raised. All other exceptions are set to 0.

The default Interrupt_Vector_Table sets the initial contents of each of the enable vectors to V_Krn_Conf_I.Float_Error_Handler. This handler raises the Ada exception Numeric_Error if one of these exceptions is signaled.

In the mode control byte (modes component), the default rounding precision is "to nearest" and the default rounding mode is "extended."

Time Slice Parameters

The following parameters deal with time slicing functions.

Time_Slicing_Enabled

This parameter is set to True to enable time slicing and false to prevent time slicing. This parameter is ignored by the no-tasking kernel.

This parameter is the initial setting of the kernel and can be changed dynamically by the Rational Exec subprogram V_Xtasking.Set_Time_Slicing_Enabled. Function V_Xtasking.Current_Time_Slicing_Enabled can read the current value.

Time_Slice_Interval

Each task in the system is started with the value of the Time_Slice_Interval as its time slice. This is the amount of time that the task runs before being pre-empted for any equal priority tasks that may be ready to run. An individual task's time slice interval can be changed during execution by calling V_Xtasking.Set_Time_Slice and read by V_Xtasking.Current_Time_Slice. Time_Slice_Interval is of type V_I_Types.Time_Span_T. Values less than or equal to zero (<=0) disable time slicing.The default Time_Slice_Interval is 1.0 second.

This parameter is ignored by the no-tasking kernel.

Time_Slicing_Priority

Time_Slicing_Priority specifies the maximum task priority to which time slicing applies. This way, normal tasks can be time sliced but high priority tasks execute until they give up the processor (or until an even higher priority task becomes ready).

All tasks whose priority is less than or equal to Time_Slicing_Priority is time sliced.

This parameter is ignored by the no-tasking kernel.

Idle Processing Parameters

The following parameters deal with idle time functions.

Idle_Stack_Size

Idle_Stack_Size specifies the size in bytes of the idle task's stack. This configuration parameter is equivalent to the'Storage_Size attribute for an application define task.

The default Idle_Stack_Size is 5,000 bytes.

This parameter is ignored by the no-tasking kernel.

Miscellaneous Configuration Parameters

Task_Storage_Size

Task_Storage_Size specifies the size in BYTES of the area allocated in the task control block for user storage. The Rational Exec Services, Allocate_Task_Storage and Get_Task_Storage manage this area in the task control block.

Note: This area is used by the Rational implementation of Ada.Task_Attributes (See LRM C.7.2) to store pointers to task attributes. If this package is used, Number_Of_Attributes objects of size Address'Size will be allocated in this area in the task control block. Number_Of_Attributes is defined in Ada.Attributes_Storage in the Systems_Programming.ss subsystem. It is currently four, and currently non configurable.

Pending_Count

Pending_Count specifies the maximum number of kernel service requests from an ISR (or nested ISR) held pending until the outermost ISR completes. When the outermost ISR completes, the kernel processes the queue of pending requests. If the Pending_Count is exceeded, V_Krn_Conf.V_Pending_Overflow_Callout is called.

V_Hardware_Initialization Routine

The V_Hardware_Initialization performs any operations necessary to initialize the hardware prior to kernel initialization.

This routine is called before the kernel is initialized. It should not make any calls to kernel services or use any Ada language features that implicitly call kernel services. In particular, the following language features should not be used:

V_Hardware_Initialization cannot reference any object which requires elaboration, since it is called before any elaboration takes place. The call to V_Hardware_Initialization is in V_Start_Program at the bottom of file v_krn_conf.2.ada.

The default V_Hardware_Initialization enables the instruction cache on the MC68020, MC68030 and MC68040.

V_Elaboration_Callout Routine

V_Elaboration_Callout is executed prior to elaborating each entry in the elaboration table.

Elaboration_Entry is the address of the entry point for the elaboration code for the unit that is about to be elaborated.

The default body of this procedure is null.

V_Default_Isr Routine

V_Default_Isr is the default Interrupt Service Routine (ISR).

The purpose of this routine is to give you access to interrupts that are coming in that you aren't expecting, for which there is no handler.

The kernel attaches this ISR to any interrupt vector whose handler is detached by a call to V_Krn_Conf_I.Replace_Vector with a null handler address. If the user program calls V_Interrupts.Detach_Isr, this default ISR is attached.

The default action for unexpected interrupts is to format/print the vector number of the unexpected interrupt before halting. This printing is done using diagnostic output. For more information, see Interrupt_Vector_Table Structure and V_Os_Support Package.

V_Privilege_Violation_Handler Routine

V_Privilege_Violation_Handler is the default handler for the privilege violation exception (interrupt vector number 8).

The library routines linked into the user program execute the following two privileged instructions to get/disable/restore interrupts: move_w, d0, sr and move_w, sr, d0.

The default implementation of this handler detects and emulates the execution of these two privileged instructions. This allows interrupts to be disabled/reset from a task executing in user state.

The default implementation also emulates the following two privileged cache instructions: movec_l, d0, cacr and movec_l, cacr, d0. It jumps to V_Default_Isr for all other privileged instructions.

Assign the address of this routine to vector entry #8 in the interrupt vector table described previously.

This handler may be modified to emulate additional privileged instructions.

See also Interrupt_Vector_Table Structure.

V_Pending_Overflow_Callout Routine

This routine is called by the kernel when the pending service ring has overflowed, to indicate that you need to increase the configuration parameter Pending_Count.

V_Timer_Support Package

The V_Timer_Support package spec is the interface to the timer functions that are required by the kernel. You must supply a body for this package that implements the functions for your timer hardware.

The type V_I_Types.Time_T is used to represent time. This is a signed 64-bit integer count of time units, the length of which is defined by the constant Real_Time.Time_Unit in the board_common.ss subsystem. Since not all of the platforms on which Apex is implemented provide 64-bit integer types, Time_T is implemented using two 32-bit integers:

Time_T is the underlying implementation of both Ada.Calendar.Time and Ada.Real_Time.Time; as such it unifies the requirements of these types. Ada.Calendar.Time must be able to represent any local ("wall clock") time between the years 1901 and 2099 with a granularity no greater than 20 milliseconds. Ada.Real_Time.Time must cover the 50-year period from system start-up to within 20 microseconds. A 64-bit integer count provides for a range of +/- 292 years to nanosecond resolution, such that the required range can be provided for Ada.Calendar.Time relative to system startup anywhere between 1901 and 2099. Nanosecond resolution provides compatibility with the POSIX Real-Time Extensions (1003.1b-1993), which provides time as an integer count of seconds plus and integer count of nanoseconds.

This is also the representation of the Ada.Real_Time.Time_Span type for representing fine-grain duration. The only functional difference between Time and Time_Span is that an implicit start time or epoch is implied for Time values; a Time value represents a point in time as the signed duration between that time and the epoch. Apex uses the POSIX (1003.1-1990) epoch: 0 hours, 0 minutes, 0.0 seconds, 1 January 1970, Universal Coordinated Time.

The value of Time_Unit is system-dependent, and can be configured to be a value convenient for implementation over your timer hardware. (See "Configuring Ada.Real_Time" in Using the Ada Runtime).

The following operations must be provided by V_Timer_Support for the kernel:

Init resets the clock's current time and initializes the timer hardware. This procedure is called during kernel startup.

Set_Time resets the clock's current time with the input parameter and cancels the active alarm which is rescheduled later.

Get_Current_Time reads and returns the clock's current time.

Schedule_Alarm schedules an alarm posted some time in the future. This procedure is called by the kernel when a future timing event must occur. The kernel passes in an absolute (not relative) time in the future when Post_Alarm should be called. The kernel computes this time by adding some amount onto the value returned by Get_Current_Time. Example events are the expiration of a delay or the end of the time slice. If Schedule_Alarm is called with an Alarm_Time later than the current Alarm_Time, the request is ignored. The kernel resubmits it sometime after the current alarm occurs. If a request is made to schedule an Alarm_Time sooner than the current Alarm_Time, this new Alarm_Time becomes the current Alarm_Time and the old Alarm_Time can be forgotten.

Process_Timer_Interrupt handles the timer interrupt by advancing the current time and checking if the time for the next alarm is reached. If it has, Process_Timer_Interrupt calls Post_Alarm to post the alarm to the kernel.

V_Os_Support Package

The V_Os_Support package supplies subprograms to interface the kernel with the world. One is an exit routine that the kernel calls when all work is finished. The other routines enable the kernel to print diagnostic messages before it exits.

When V_Os_Support routines are called, the kernel is either exiting normally or it is printing out a diagnostic, prior to exiting with a fatal error. The V_Os_Support routines should not call any kernel functions.

Halt is called when the kernel program exits.

Put_Str is called to output a string of characters of a given length at the specified memory location. The overloaded Put procedures output a character or a string. The kernel only calls these procedures to output diagnostic information, that is, when something is wrong.

V_Alloc_Support Package

This package contains the kernel program's memory allocation callouts.

V_Stack_Support Package

V_Stack_Support contains the kernel program's stack allocation callouts.

The default implementation simply allocates all task stacks from the kernel's heap area using the V_Alloc_Support callouts.

V_Start_Program Routine

V_Start_Program is the main startup routine of the kernel. It is jumped to from V_START, which is the actual entry point of the kernel (see v_start.2.ada).

Although the source code for V_Start_Program is provided as part of the kernel configuration package, it can normally be used "as is". V_Start_Program is provided primarily for reference. Both TDM and the user program have similar routines provided for reference.

Here are the steps of V_Start_Program:

1 . Rudimentary hardware initialization

2 . Copies the static data section from ROM to RAM if ROM is being used

3 . Zeroes the kernel program's BSS area

4 . Calls the V_Hardware_Initialization routine to perform user-specified hardware initialization

5 . Optionally, dynamically sets Heap_Stack_Bottom to the end of the user program (see Auto_Config_Heap_Stack_Bottom for details)

6 . Zero's the heap/stack memory area

7 . Calls the V_Boot routine to initialize the exception vector table and the kernel's tasking data structures, upon return, switched from startup stack to the kernel's stack

8 . Elaborates the kernel program's packages

9 . Calls the V_Execute routine which transfers control to the user program by its start address contained in the Link_Block


i386 Kernel Configuration Parameters

Policy/Switches file for the krn_conf.ss subsystem.

The context switches for the views in the krn_conf.ss subsystem have the following additional values:

The Board_Common.sw file contains all of the switches that are common to the BSP. The RUNTIMES switch is used to identify the archive libraries that are required to support the kernel as opposed to a user program.

The linker description file is located locally in the krn_conf.ss view.

Configuration Components

The configuration components of the kernel's configuration package are

This list serves as a table of contents for the kernel configuration package section. In these files each component starts with an Ada comment line similar to this:

Use your editor to search for the title string.

Startup_Table Structure

The declaration for the Startup_Table is in the file v_krn_conf.2.ada. The type declaration is in v_krn_conf.1.ada.

You must declare the Startup_Table as a constant, because the values in the table are used as soon as the kernel starts, prior to the elaboration of the kernel configuration package). The value assigned to each field of the Startup_Table must be a literal, an address constant or a value returned by the address attribute ('Address).

Startup_Stack_Base is the base address of the stack that the kernel uses during initialization. This is the initial value of the stack pointer, register ESP, used by the kernel. The address you supply should point to the byte after the highest addressed byte of the stack. The stack grows toward low memory and the value in the stack pointer is always decremented before it is used.

We recommend that the value of Startup_Stack_Base be the address of the byte just after the last byte of RAM in the system. For example, the default value, 16#0010_0000#, is for a system with one megabyte of RAM, that is, RAM in the address range from 0 to 16#0F_FFFF#. We also recommend that the kernel's heap/stack area be placed at this point so that the memory used by the initial stack can be reused.

Startup_Stack_Size tells the kernel how many bytes of stack space you allow for the startup stack. The kernel uses this number to compute the stack limit; the kernel has stack limit checks enabled during startup. The kernel requires less than 4096 bytes of startup stack space.

Note: The startup stack area is used before and during execution of Memory_Initialization. The top of RAM may not be accessible until Memory_Initialization returns. This applies to the Intel iSBC 486/133 SE board, where the top of Real Mode RAM is 16#000D_FFFF# before Memory_Initialization puts the board into Protected Mode. After memory initialization, the top of RAM is 16#000F_FFFF# for a board with 1MB of RAM.

Interrupt_Vector_Table and Null_Handler_Table Structures

This section presents information about the i386 Family interrupt vector table and null handler table structures.

Interrupt_Vector_Table

The Interrupt_Vector_Table specifies the initial contents of each descriptor in the processor's Interrupt Descriptor Table (IDT).

Each table entry must contain one of these values:

During initialization, the kernel builds the IDT using the Interrupt_Vector_Table values. The IDT structure is an array of interrupt gate descriptors.

Note: An interrupt gate resets the Interrupt Flag (IF) before jumping to the interrupt handler procedure, which prevents other interrupts from interfering with the handler.

If a vector has Untouchable_Vector as its value, the kernel copies the entire descriptor record (all 8 bytes, not just the offset field) from the IDT pointed to by the Monitor_Idt_Base configuration parameter.

The default Interrupt_Vector_Table has Untouchable_Vector as the initial value for each of the vectors not used by the kernel (these are handled by TDM).

If a vector has Default_Vector as its value, the kernel places the address of its corresponding Null_Handler_Table entry into the descriptor's offset field. If memory is not set aside for the Null_Handler_Table (that is, the Null_Handler_Base configuration parameter is set to V_Krn_Conf_I.No_Null_Handler) the User_Default_Unknown_Isr parameter is stored in the descriptors offset field.

There are six acceptable methods for putting an ISR's address into the IDT:

1 . As an initial value in the Interrupt_Vector_Table. This requires that the ISR be linked as part of the kernel program.

2 . Dynamically attach and detach kernel resident handlers using the Replace_Vector procedure in the package V_Krn_Conf_I.

3 . Use Ada interrupt entries as interrupt handlers.

4 . Use a protected procedure as an interrupt handler (See LRM C.3).

5 . Use the routine V_Interrupts.Attach_Isr from Rational Exec to dynamically install an ISR residing in the user program.

6 . Define an interrupt entry in a task. The compiler generates ISR code that is installed by using Attach_Isr.

Use interrupt entries, Protected Procedure Interrupt Handlers, or V_Interrupts.Attach_Isr wherever possible and install task entries or subprograms from your user program as ISRs. It is simple and easy to communicate between an ISR and your program if the ISR is part of your program. It can reference shared data structures and call other routines.

However, if the ISR is linked with the kernel, the only method for communicating data between the ISR and your program is by shared memory at an agreed upon address.

The Interrupt_Vector_Table's type is an array of addresses.

Table 6 shows the default state of the kernel interrupt vector table, Interrupt_Vector_Table, in v_krn_conf.2.ada.

Assign the address of Interrupt_Vector_Table to the field Interrupt_Vector_Image in the configuration table, which is discussed in the next section.

Table 6 Default Kernel Interrupt Vector Table
Vector
Default Value
Description
000
V_Krn_Conf_I.Divide_Error_Handler
'Address

Divide error (note 1)
001
V_Krn_Conf_I.Untouchable.Vector
Debug exceptions
002
V_Krn_Conf_I.Untouchable.Vector
Nonmaskable interrupt
003
V_Krn_Conf_I.Untouchable.Vector
Breakpoint
004
V_Krn_Conf_I.Overflow_Hanlder 'Address
Overflow (note 1)
005
V_Krn_Conf_I.Bounds_CHECK_Hanlder 'Address
Bounds check (note 2)
006
V_Krn_Conf_I.Untouchable.Vector
Invalid opcode
007
V_Krn_Conf_I.Coprocessor_Hanlder` 'Address
Coprocessor not available
008
V_Krn_Conf_I.Untouchable.Vector
Double fault
009
V_Krn_Conf_I.Untouchable.Vector
Reserved
010
V_Krn_Conf_I.Untouchable.Vector
Invalid TSS
011
V_Krn_Conf_I.Untouchable.Vector
Segmemt not present
012
V_Krn_Conf_I.Untouchable.Vector
Stack exception
013
V_Krn_Conf_I.Untouchable.Vector
.General protection
014
V_Krn_Conf_I.Page_Fault_Handler
Page fault (note 3)
015
V_Krn_Conf_I.Untouchable.Vector
Reserved
016
V_Krn_Conf_I.Float_Error_Handler'Address
Coprocessor error (note 1)
017..31
V_Krn_Conf_I.Untouchable.Vector
Reserved
032
V_Krn_Conf_I.Enter_From_User'Address
Default kernel interrupt (note 4)
033
V_Krn_Conf_I.Untouchable.Vector
TDM default interrupt (note 5)
034..47
V_Krn_Conf_I.Untouchable.Vector
Available for external interrupts
048
V_Krn_Conf_I.Timer_Handler'Address
Board-specific timer interrupt
049..255
V_Krn_Conf_I.Untouchable.Vector
Available for external interrupts

All interrupt handlers installed directly into the Interrupt_Vector_Table and linked with the kernel must be implemented as kernel interrupt handler routines, following the rules required by the kernel. This can easily be done by using the generic V_Krn_Conf_I.Interrupt_Handler. See the file v_krn_conf.1.ada for more details.

The kernel provides interrupt handlers for vectors 0, 4, 5, 7, 14 and 16.

Note 1: Divide error (vector 0), overflow (vector 4) or coprocessor (vector 16) is mapped by the kernel into Numeric Error.

Note 2: Bounds check (vector 5) is mapped by the kernel into Constraint Error.

Page fault (vector 14) is mapped by the kernel int Storage_Error.

The kernel maps the hardware exceptions documented in the above notes 1-3 into Ada exceptions that are raised at the appropriate spot in the Ada program.

Note 4: Interrupt vector 32 is the default entry point to the kernel. This can be configured to be a different interrupt number in the user library.

Note 5: Interrupt vector 33 is the default entry point to TDM.

Note 6: If the interrupt number for either the kernel or TDM must be changed, you must also change the TDM Interrupt_Vector_Table and V_Traps in the user library.

For additional information, see Monitor_Idt_Base in Monitor Configuration Parameters.

Null_Handler_Table

The Null_Handler_Table is a table of 8-byte null-interrupt handlers. Each handler captures its own interrupt number. The Null_Handler_Table is set up during kernel initialization. This is required because the i386 Family does not include the interrupt number as part of the interrupt's stack frame. The following code is generated for each interrupt vector entry:

where Unhandled_Interrupt_Address is set to the configuration parameter User_Default_Isr.

The address of the corresponding Null_Handler_Table entry is placed in the IDT's offset field for each Default_Vector entry in the Interrupt_Vector_Table.

The size for Null_Handler_Code_T is 8 bytes.

The Null_Handler_Table's type is an array of Null_Handler_Code_T records.

Assign the address of Null_Handler_Table to the field Null_Handler_Base in the configuration table. The length of Null_Handler_Table must be identical to the length of the Interrupt_Vector_Table. If you don't want to capture the interrupt number for an unhandled interrupt, set Null_Handler_Base to V_Krn_Conf_I.No_Null_Handler and declare Null_Handler_Table as a null array.

Memory_Map_Table Structure

The Memory_Map_Table indicates the memory layout for your board. The default version of V_Page_Support.V_Init_Paging initializes the i386 Family page tables, making present only those pages that encompass the memory partitions defined in this table. Any reference to a page that is not present causes a page fault.

The body of package V_Krn_Conf contains the declaration for the Memory_Map_Table.

Note: The first page (16#0# .. 16#FFF#) isn't used, enabling any illegal reference to a null pointer to cause a page fault exception.

If you want your application to access more than just the first 1MB of memory on your board, you must change the default configuration.

V_Page_Support.Init_Paging uses the Memory_Map_Table to setup i386 Family paging, so for pages that are present, the following apply:

The body of package V_Tdm_Conf_B, the TDM configuration package, also has a Memory_Map_Table structure. However, TDM uses it as a software check and does not modify the i386 Family page tables.

Configuration_Table Structure

The Configuration_Table is the primary mechanism by which you tailor the kernel to your target system. The parameters that make up the configuration table are partitioned into these categories:

GDT and TSS Parameters

The GDT and TSS parameters relate to the location of the i386 Family GDT and TSS data structures.

Gdt_Base

Gdt_Base tells the kernel where in memory to place the Global Descriptor Table (GDT). The GDT is initialized with entries from the Gdt_Image. The Gdt_Limit is 199 (16#c7#) bytes, meaning that 200 (16#c8#) bytes are required.

The kernel's Gdt_Base can be identical to the TDM Gdt_Base. The default Gdt_Base is 16#2000# for both TDM and the kernel.

Note: The GDT cannot be located in the kernel program's BSS. When control is returned to TDM by a Control-c, breakpoint, etc., it continues to use the GDT that was loaded by the kernel. When the kernel is reloaded, the BSS (and the current GDT, if loaded there erroneously) is zeroed, which may cause TDM to crash.

Tss_Base

Tss_Base tells the kernel where in memory to place the Task State Segment and is initialized with entries from the Tss_Image. The Tss_Limit is 103 (16#67#), meaning that 104 (16#68#) bytes are required.

The kernel is configured to use only a single i386 Family task, so only a single TSS is required. The base field of the TSS descriptor in the GDT is set to Tss_Base.

The kernel's Tss_Base can be identical to the TDM Tss_Base. The default Tss_Base is 16#2100# for both TDM and the kernel.

Note: The TSS cannot be located in the kernel program's BSS. When control is returned to TDM by a Control-c, breakpoint, etc., it continues to use the TSS that was loaded by the kernel. When the kernel is reloaded, the BSS (and the current TSS, if loaded there erroneously) is zeroed.

Memory Management Parameters

The memory management parameters relate to setting up the kernel's heap/stack area and the i386 Family page tables.

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. Objects are allocated from a heap when the Ada new operator is used.

In addition to the stacks described here, every Ada task has its own stack, which is allocated from the heap/stack area when the task is created.

Some memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Figure 16 presents a layout for the heap/stack area.

As an option, you can enable page protection. The non-heap/stack parameters indicate the address and size of the Memory_Map_Table and indicate where to locate the i386 Family page directory and page tables.

For more information about memory management, see "Memory Management" in the Using the Ada Runtime.

Heap_Stack_Bottom and Heap_Stack_Top

Heap_Stack_Bottom and Heap_Stack_Top define the area of memory from which heaps and stacks are allocated. Heap_Stack_Bottom is the low address, usually set to the first RAM address following the end of TDM, the kernel and user programs. Heap_Stack_Top is the highest addressed RAM location available for heaps and stacks. It is not the byte after the heap/stack area, it is the last byte of the area.

The heap/stack area is where the kernel and user programs normally allocate all heaps and stacks. The next few memory management parameters give the sizes of stacks, which are then allocated from the heap/stack area.

Krn_Stack_Size

Krn_Stack_Size is the size of the kernel's stack. This stack is also used by interrupt handlers.

The stack size should include 1K bytes for exception handling. All interrupt service routines use this same stack, so you must allow space for the worst possible case of nested interrupts.

Note: The kernel is going to add 64 bytes to this size to account for TDM.

Intr_Stack_Size

Intr_Stack_Size is the size of the interrupt handlers' stack. This is the stack that interrupt handlers use. The stack size should include 1K bytes for exception handling. All interrupt service routines use this same stack, so you must allow space for nested interrupts if they are possible. The interrupt wrapper, Interrupt_Handler switches to the interrupt handlers' stack.

Krn_Exception_Stack_Size

Krn_Exception_Stack_Size is the size of the area set aside below the bottom of the interrupt handler's stack for exception unwinding. It is also the exception stack size for tasks created in the kernel program. Kernel tasks consist of the idle task and tasks created by V_Krn_Aug.Task_Create.

Task_Supervisor_Stack_Size

Task_Supervisor_Stack_Size specifies how much supervisor stack space is needed for each task that executes in user state.

Zero_Stacks_Enabled

At startup all the memory to be used for task stacks is zeroed. However, if tasks are dynamically terminated and recreated, the stack area for subsequently created tasks is no longer zeroed. This can lead to erroneous stack usage information displayed by the debugger's lt use command.

Zero_Stacks_Enabled is set to True to guarantee that at task create its stack area is zeroed. Since it takes extra time to zero the stack area, Zero_Stacks_Enabled is normally only set to True when used in conjunction with lt use for dynamically created tasks.

Figure 16 Example Heap_Stack Layout (i386) (4 Mbyte Machine)

Page_Protection_Enabled

Page_Protection_Enabled is set to True to enable the use of the i386 Family page protection capabilities. When True, the default version of V_Page_Support_Init_Paging, initializes paging such that only Memory_Map_Table's pages are present, with linear addresses mapped directly to physical addresses. Any reference to a not present page causes a page fault CPU exception (interrupt #14). Default Page_Protection_Enabled is True.

Memory_Map_Size

Memory_Map_Size indicates the number of memory partitions. The default Memory_Map_Size is Memory_Map_Table'Length.

Memory_Map_Image

Memory_Map_Image indicates the starting address of the Memory_Map_Table. Default Memory_Map_Image is Memory_Map_Table'Address

Page_Table_Array_Base

Page_Table_Array_Base is the starting address of the page directory. Page tables pointed to by the directory entries follow in memory. The size of each table entry is 4K bytes. The Page_Table_Array_Base must be aligned on a 4K byte boundary. The Page_Table_Array is initialized by V_Page_Support.Init_Paging according to the Memory_Map_Table. The Page Directory Base Register (PDBR), CR3, is loaded with Page_Table_Array_Base. The default Page_Table_Array_Base is Memory_Address(16#3000#).

Page_Table_Array_Length

Page_Table_Array_Length specifies the number of 4K byte entries, which includes one entry for the Page Directory and an entry for each 4M byte partition specified in the Memory_Map_Table. The minimum value for Page_Table_Array_Length is 2 (8K bytes), where the Memory_Map_Table specifies a single memory partition, which is less than or equal to 4M bytes and is aligned on a page boundary. The default Page_Table_Array_Length is 2.

Processor Status Parameters

Supervisor_Tasks_Enabled

When set to True, all tasks including the main program execute in supervisor state.

Supervisor_Tasks_Enabled is not currently used. We do not support programs which run in user mode.

Interrupt Configuration Parameters

The following parameters deal with the i386 Family interrupt vector table.

Interrupt_Vector_Size

Interrupt_Vector_Size indicates the number of interrupt vectors in the interrupt vector table. This number should be equal to Interrupt_Vector_Table'Length. This is used during initialization and as a bounds check on V_Interrupts.Attach_Isr.

Interrupt_Vector_Image

Interrupt_Vector_Image tells the kernel the starting address of the interrupt vector table image that you defined (Interrupt_Vector_Table). During initialization, the kernel copies the contents of the image into the offset field of the IDT's interrupt descriptors.

The default Interrupt_Vector_Image is Interrupt_Vector_Table'Address.

Idt_Base

Idt_Base tells the kernel where in memory to place the Interrupt Descriptor Table (IDT). The linear base address component of the Interrupt Descriptor Table Register (IDTR) is loaded with this value. The IDT limit is derived from the Interrupt_Vector_Size parameter (Limit := Interrupt_Vector_Size * 8 -1). Idt_Base requires Interrupt_Vector_Size * 8 bytes.

The IDT is initialized using the Interrupt_Vector_Table.

The value V_Krn_Conf_I.Untouchable_Idt may be given if the address of TDM's or monitor's IDT should be used. Regardless, the IDT limit is always reevaluated using Interrupt_Vector_Size.

The kernel's Idt_Base and TDM's Idt_Base can be identical. The default Idt_Base is 16#2200# for both TDM and the kernel.

The IDT cannot be located in the kernel program's BSS. When control is returned to TDM by a Control-c, breakpoint, etc., TDM continues to use the IDT loaded by the kernel. When the kernel is reloaded, the BSS (and current IDT if loaded there erroneously) is zeroed.

Null_Handler_Base

Null_Handler_Base indicates the starting location of the Null_Handler_Table, which is modified at run time to contain generated i386 Family code used for capturing the ID of an unexpected interrupt.

The value V_Krn_Conf_I.No_Null_Handler may be given if you elect not to capture the interrupt ID before processing an unexpected interrupt. Default for the Null_Handler_Base is Null_Handler_Table'Address.

User_Default_Isr

User_Default_Isr contains the address of the handler for unexpected interrupts. The run-time generated code in the Null_Handler_Table jumps to this handler after pushing the ID of the unexpected interrupt onto the stack.

The default User_Default_Isr is V_Default_Isr'Address.

User_Default_Unknown_Isr

User_Default_Unknown_Isr contains the address of a secondary handler for unexpected interrupts. This secondary handler is used when no Null_Handler_Tabexists (Null_Handler_Base = V_Krn_Conf_I.No_Null_Handler).

The default User_Default_Unknown_Isr is V_Default_Unknown_Isr'Address.

User_Intr_Flag

The Interrupt Flag (IF) in EFLAGS register is set to User_Intr_Flag when the user program is started at the completion of kernel startup. You can set User_Intr_Flag to a value of either 0 or 1. A value of 0 disables interrupts, while a value of 1 (the default) enables interrupts.

The I/O Privilege Level (IOPL) in EFLAGS is set to 3. This enables the user program, which executes at privilege level 3, to enable or disable interrupts (STI/CLI) or to execute any I/O-related instruction without privilege violation.

Intr_Priority_Enabled

Intr_Priority_Enabled is set to True to enable use of the board-specific interrupt priority controller-related callouts. If set to False, the kernel enables and disables all external interrupts by IF in the EFLAGS register.

Disable_Intr_Priority

Disable_Intr_Priority is the interrupt priority setting passed to Set_Intr_Priority when the kernel needs to disable interrupts.

You can set Disable_Intr_Priority to a value that enables higher priority interrupts to be serviced. The kernel calls Set_Intr_Priority by using Disable_Intr_Priority when it enters a critical region. Consequently, any code that executes at an interrupt priority above this value may not invoke any kernel service, including those invoked implicitly by Ada programs.

These language features may generate calls to the kernel:

Enable_Intr_Priority

Enable_Intr_Priority is the interrupt priority setting passed to Set_Intr_Priority when the kernel needs to enable all external interrupts.

Monitor Configuration Parameters

The parameters discussed in this section deal with the location and size of the GDT and IDT used by TDM or the board's monitor.

Tdm_Present

Set the Tdm_Present flag to True if the kernel program is being started from TDM. TDM may have initialized board-specific hardware that should not be reinstalled. For example, TDM may have reset the interrupt controller and enabled the serial I/O get interrupt. If the kernel resets the interrupt controller, the serial I/O get interrupt is no longer enabled. However, if TDM is not present, the kernel program must reset the interrupt controller.

Monitor_Gdt_Base

Monitor_Gdt_Base is the base address of the board monitor's GDT. Default Gdt_Base is V_Krn_Conf_I.NO_GDT (no monitor GDT).

Monitor_Gdt_Length

Monitor_Gdt_Length specifies the number of monitor GDT entries (starting with GDT(0)) copied into the kernel's GDT. The runtime-generated code imposes an upper limit of 20 on the length. The default Monitor_Gdt_Length is 0 (no monitor GDT entries.

Monitor_Idt_Base

Monitor_Idt_Base is the base address of the TDM or board monitor IDT. Early in the kernel initialization the linear base address component of the IDTR is set to this value. Later in the initialization, when the kernel builds and loads its IDT, Untouchable_Vector entries in the Interrupt_Vector_Table are copied from the TDM or board monitor IDT.

The value V_Krn_Conf_I.Untouchable_Idt may be specified for Monitor_Idt_Base if the current IDTR is used. (This is valid only if the kernel is started in i386 Family Protected Mode. Use Untouchable_Idt when TDM is present. This is the default.)

If neither TDM nor a board monitor are present, set Monitor_Idt_Base to V_Krn_Conf_I.No_Idt.

Monitor_Idt_Length

Monitor_Idt_Length is the length of TDM's (or the board monitor's) IDT. Early in the kernel initialization, the limit component of the IDTR register is set to this value, where Limit = Monitor_Idt_Length * 8 - 1.

If Monitor_Idt_Base is set to either V_Krn_Conf_I.Untouchable_Idt or V_Krn_Conf_I.No_Idt, Monitor_Idt_Length is ignored

Floating Point Coprocessor Configuration Parameters

The following parameters deal with the Intel 80287 and 80387 floating point coprocessors.

Hw_Flt_Enabled

Hw_Flt_Enabled is set to True if the board has either an 80287 or 80387 Math Coprocessor installed or the processor chip has built-in floating point. Hw_Flt_Enabled defaults to True.

Floating_Point_Control

Floating_Point_Control is a structure that specifies the initial value for the Control Word of the floating point coprocessor. This structure and its subcomponents are declared in package V_I_Types found in the release subsystem rational.ss.

The fields of the components and the initial values are as specified in the i386 Family Programmer's Reference Manuals.

The default values for this structure are:

The invalid operation (IM), zero divide (ZM) and overflow (OM) exceptions are unmasked (set to 0); all others are masked (set to 1).

In the control byte, the default precision control is "to nearest", the default rounding control is "significant 64 bits" and the default infinity control is "projective".

The default Interrupt_Vector_Table sets the content of the coprocessor vector to V_Krn_Conf_I.Float_Error_Handler. This handler raises the Ada exception Numeric_Error if one of the unmasked exceptions is signaled.

Time Slice Configuration Parameters

The following parameters deal with time slicing functions.

Time_Slicing_Enabled

This parameter is set to True to enable time slicing and False to prevent time slicing. This parameter is ignored by the no-tasking kernel.

This parameter is the initial setting of the kernel and can be changed dynamically by the Rational Exec subprogram V_Xtasking.Set_Time_Slicing_Enabled. The function V_Xtasking.Current_Time_Slicing_Enabled can read the current value.

Time_Slice_Interval

Each task in the system is started with the value of the Time_Slice_Interval as its time slice. This is the amount of time that the task runs before being pre-empted for any equal priority tasks that may be ready to run. An individual task's time slice interval can be changed during execution by calling V_Xtasking.Set_Time_Slice and read by V_Xtasking.Current_Time_Slice.

This parameter is ignored by the no-tasking kernel.

Time_Slicing_Priority

Time_Slicing_Priority specifies the maximum task priority to which time slicing applies. This way, normal tasks can be time sliced but high priority tasks execute until they give up the processor (or until an even higher priority task becomes ready).

All tasks whose priority is less than or equal to Time_Slicing_Priority are time sliced.

This parameter is ignored by the no-tasking kernel.

Idle Processing Configuration Parameters

The following parameters deal with idle task functions.

Idle_Stack_Size

Idle_Stack_Size specifies the size in bytes of the idle task's stack. This configuration parameter is equivalent to the 'Storage_Size attribute for an application define task.

The default Idle_Stack_Size is 5,000 bytes.

This parameter is ignored by the no-tasking kernel.

Miscellaneous Configuration Parameters

Task_Storage_Size

Task_Storage_Size specifies the size in BYTES of the area allocated in the task control block for user storage. The Rational Exec Services, Allocate_Task_Storage and Get_Task_Storage manage this area in the task control block.

Note: This area is used by the Rational implementation of Ada.Task_Attributes (See LRM C.7.2) to store pointers to task attributes. If this package is used, Number_Of_Attributes objects of size Address'Size will be allocated in this area in the task control block. Number_Of_Attributes is defined in Ada.Attributes_Storage in the Systems_Programming.ss subsystem. It is currently four, and currently non configurable.

Pending_Count

Pending_Count specifies the maximum number of kernel service requests from an ISR (or nested ISR) held pending until the outermost ISR completes. When the outermost ISR completes, the kernel processes the queue of pending requests.

Miscellaneous Startup Variables

This component contains miscellaneous variables used by the startup machine code for initializing the IDT, GDT and floating point coprocessor.

Do not modify these variables.

Gdt_Image and Tss_Image Structures

Tables for initializing the kernel's GDT and TSS are declared in v_krn_conf.2.ada. Under normal circumstances these structures require no modification.

V_Memory_Initialization and V_Hardware_Initialization Routines

V_Memory_Initialization performs any operations necessary to initialize the target memory prior to RAM being initialized from ROM. This routine is called before the kernel is initialized. It should not make calls to the kernel.

This routine must not reference any entities that require elaboration, since it is called before any elaboration takes place. The V_Hardware_Initialization routine performs any operations that are necessary to initialize the hardware prior to kernel initialization.

If TDM is not present, normally V_Hardware_Initialization initializes the board's interrupt controller. V_Hardware_Initialization for the Intel iSBC 486/133 single board computer is configured to initialize the Intel 8259 Programmable Interrupt Controller (PIC).

This routine is called before the kernel is initialized. It should not make any calls to kernel services or use any Ada language features that implicitly call kernel services. In particular, the following language features should not be used:

V_Hardware_Initialization cannot reference any object which requires elaboration, since it is called before any elaboration takes place. The call to V_Hardware_Initialization is in V_Start_Program at the bottom of file v_krn_conf.2.ada.

Assign the address of this routine to the parameter Hardware_Initialization in the startup table.

V_Elaboration_Callout Routine

V_Elaboration_Callout is the default elaboration callout routine. This procedure is executed prior to elaborating each entry in the elaboration table.

Elaboration_Entry is the address of the entry point for the elaboration code for the unit that is about to be elaborated.

V_Default_Isr and V_Default_Unknown_Isr Routines

V_Default_Isr is the default Interrupt Service Routine (ISR).

The purpose of this routine is to give you access to interrupts that are coming in that you aren't expecting, for which there is no handler.

If a subprogram linked into the kernel program calls V_Krn_Conf_I.Replace_Vector with a null handler address or if the user program calls V_Interrupts.Detach_Isr, this default ISR is attached.

The default action for unexpected interrupts is to format/print the vector number of the unexpected interrupt before halting. This printing is done using diagnostic output.

Assign the address of V_Default_Isr to the User_Default_Isr field in the configuration table. The runtime-generated code placed in the Null_Handler_Table jumps to this routine after it pushes the interrupt number on the stack.

V_Default_Unknown_Isr is a secondary default ISR and is used only when no Null_Handler_Table exists (Null_Handler_Base = V_Krn_Conf_I.No_Handler).

The default action for this procedure is to jump to V_Default_Isr, with V_Cpu_Conf.Unknown_Intr_Number (10#17#) pushed onto the stack. Assign the address of V_Default_Unknown_Isr to he User_Default_Unknown_Isr field in the Configuration_Table.

Get_Intr_Priority and Set_Intr_Priority Routines

If Intr_Priority_Enabled is True, Get_Intr_Priority is called to get the current external interrupt priority.

If Intr_Priority_Enabled is True then Set_Intr_Priority is called to set the external interrupt priority. The current priority is read before setting the new value.

If Intr_Priority_Enabled is False then the Interrupt-Enable Flag (IF) in the EFLAGS registers is used exclusively for getting and setting interrupts and these interrupt routines are never called. However, most i386-based boards use an interrupt controller such as the 8259, which supports prioritized interrupts. For such boards you have the option of implementing these interrupt priority routines or not. When these routines are implemented with Intr_Priority_Enabled set to True, the kernel calls Get_Intr_Priority to get the current interrupt status (instead of reading the IF setting) and calls Set_Intr_Priority to disable and restore interrupts (instead of clearing and restoring IF).

Besides being called to support the kernel's critical regions, these routines are called when interrupts must be disabled and restored in the user program.

V_Pending_Overflow_Callout Routine

This routine is called by the kernel when the pending service ring has overflowed. If this routine is called, you need to increase the configuration parameter Pending_Count.

V_Timer_Support Package

The V_Timer_Support package spec is the interface to the timer functions that are required by the kernel. You must supply a body for this package that implements the functions for your timer hardware.

The type V_I_Types.Time_T is used to represent time. This is a signed 64-bit integer count of time units, the length of which is defined by the constant Real_Time.Time_Unit in the board_common.ss subsystem. Since not all of the platforms on which Apex is implemented provide 64-bit integer types, Time_T is implemented using two 32-bit integers:

Time_T is the underlying implementation of both Ada.Calendar.Time and Ada.Real_Time.Time; as such it unifies the requirements of these types. Ada.Calendar.Time must be able to represent any local ("wall clock") time between the years 1901 and 2099 with a granularity no greater than 20 milliseconds. Ada.Real_Time.Time must cover the 50-year period from system start-up to within 20 microseconds. A 64-bit integer count provides for a range of +/- 292 years to nanosecond resolution, such that the required range can be provided for Ada.Calendar.Time relative to system startup anywhere between 1901 and 2099. Nanosecond resolution provides compatibility with the POSIX Real-Time Extensions (1003.1b-1993), which provides time as an integer count of seconds plus and integer count of nanoseconds.

This is also the representation of the Ada.Real_Time.Time_Span type for representing fine-grain duration. The only functional difference between Time and Time_Span is that an implicit start time or epoch is implied for Time values; a Time value represents a point in time as the signed duration between that time and the epoch. Apex uses the POSIX (1003.1-1990) epoch: 0 hours, 0 minutes, 0.0 seconds, 1 January 1970, Universal Coordinated Time.

The value of Time_Unit is system-dependent, and can be configured to be a value convenient for implementation over your timer hardware. (See "Configuring Ada.Real_Time" in Using the Ada Runtime).

The following operations must be provided by V_Timer_Support for the kernel:

Init resets the clock's current time and initializes the timer hardware. This procedure is called during kernel startup.

Set_Time resets the clock's current time with the input parameter and cancels the active alarm which is rescheduled later.

Get_Current_Time reads and returns the clock's current time.

Schedule_Alarm schedules an alarm posted some time in the future. This procedure is called by the kernel when a future timing event must occur. The kernel passes in an absolute (not relative) time in the future when Post_Alarm should be called. The kernel computes this time by adding some amount onto the value returned by Get_Current_Time. Example events are the expiration of a delay or the end of the time slice. If Schedule_Alarm is called with an Alarm_Time later than the current Alarm_Time, the request should be ignored. The kernel resubmits it sometime after the current alarm occurs. If a request is made to schedule an Alarm_Time sooner than the current Alarm_Time, this new Alarm_Time becomes the current Alarm_Time and the old Alarm_Time can be forgotten.

Process_Timer_Interrupt should handle the timer interrupt by advancing the current time and checking if the time for the next alarm is reached. If it has, Process_Timer_Interrupt calls Post_Alarm to post the alarm to the kernel.

OS Support Package

V_Os_Support supplies functions to support the embedded kernel's emulation of OS exit and diagnostic output.

When V_Os_Support routines are called, the kernel is either exiting normally or it is printing out a diagnostic, prior to exiting with a fatal error. The V_Os_Support routines should not call any kernel functions.

Halt is called when the kernel program exits.

Put_Chr is called to output a character. Put_Str is called to output a string. The kernel only calls these procedures to output diagnostic information, that is, when something is wrong.

V_Page_Support Package

The V_Page_Support package supplies the routine for creating the i386 Family page tables and enabling paging.

V_Init_Paging is called directly by V_Krn_Conf.Init_Program to initialize the i386 Family page tables and then enable paging.

The default version of this routine sets up the page tables so that linear addresses map directly to physical addresses. Only those memory pages encompassed by Memory_Map_Table's partitions are made present. Attempts to access a page that is not present result in a page fault.

Refer to V_Page_Support in v_krn_conf.2.ada for details on how the default V_Init_Paging uses the memory management configuration parameters to set up the i386 Family page tables.

This routine may be null to inhibit the use of the i386 Family CPU page capabilities and reduce the kernel's memory size.

V_Alloc_Support Package

V_Alloc_Support contains the kernel program's memory allocation callouts.

Init initializes memory to be used for kernel allocations. It is called with the bottom and size of the kernel's heap after the kernel has already allocated the kernel, interrupt and main task stacks from the top of the heap area. The Unaligned_Min_Size parameter has been set to the size of the kernel's task control block. It's only a hint at what the minimum size of an allocation should be and can be ignored.

Alloc_New is called when the kernel needs memory for a kernel data structure, such as a task control block, a task stack, or a request for memory from the user program. The PRG parameter indicates which program the request is being made for. PRG is set to v_krn_aug.program_kernel to indicate memory being requested for a kernel data structure or a stack for a kernel task.

Alloc_Free is called when the kernel frees memory previously allocated.

Chunk_Add is called to provide the memory allocation routines with another big chunk of memory. (May not be supported for all models. It's not supported by the default, Exact_Fit.All.)

V_Stack_Support Package

V_Stack_Support contains the kernel program's stack allocation callouts.

Stack_New is called to allocate a new stack.

Stack_Free frees a previously allocated stack.

The default implementation simply allocates all task stacks from the kernel's heap area using the above V_Alloc_Support callouts.

The PRG parameter passed to these routines indicates the task's program.

V_Start_Program Routine

V_Start_Program is the main startup routine of the kernel. It is jumped to after the CPU is put into i386 Family Protected Mode. (See Package V_Cpu_Init and Initial Program Execution for the protected mode entry code).

Although the source code for V_Start_Program is provided as part of the kernel configuration package, do not modify this routine. V_Start_Program is provided primarily for reference. Both TDM and the user program have similar routines provided for reference.

Here are the steps of V_Start_Program:

1 . Initializes the kernel's startup stack.

2 . Calls the V_Memory_Initialization routine to perform user-specified memory initialization.

3 . Copies the static data section from ROM to RAM, if ROM is being used.

4 . Hand-elaborates V_Krn_Conf's specification and body.

5 . Initializes the kernel's TSS, GDT and LDT. If present, loads TDM or monitor IDT.

6 . Transfers control to Init_Program, which does the following:

a . Calls the V_Hardware_Initialization routine to perform user-specified hardware initialization.

b . Calls the V_Page_Support.V_Init_Paging, which, if enabled, initializes the i386 Family page tables and enables paging.

c . If enabled, initializes the floating point coprocessor.

d . Zeroes the heap/stack memory area.

e . Calls the V_BOOT routine to initialize the IDT and the kernel's tasking data structures.

f . Elaborates the kernel program's packages.

g . Calls the V_Execute routine, which transfers control to the user program by its start address contained in the Link_Block.

Package V_Cpu_Init and Initial Program Execution

When an i386 Family processor is reset, it begins execution in real-address-mode. The "real-mode" execution provides emulation for the older (for example, 8086) processors in the family. In this mode, addresses are composed of a 20-bit segment base and a 16-bit offset. In addition the default operand size in real-mode is 16-bits.

The Apex compiler and runtime are designed for the more capable protected-mode (supported by the i386 and later processors). The default operand size is 32 bits and for memory organization Apex uses the "flat" addressing model (with 32-bit addresses).

Since Apex does not support real-mode execution, the processor must be switched to protected-mode before TDM or the kernel (or an application) is started. To effect this switch, a small amount of code, found in the package V_Cpu_Init (in the same tdm_conf.ss or krn_conf.ss view as TDM or the kernel) is placed at the beginning of TDM and the kernel.

The entry point routine for TDM and the kernel (V_Start) contains code which determines the current execution mode (real or protected) and then calls the appropriate routine in V_Cpu_Init to insure that the processor is in protected, 32-bit address/operand, mode before V_Start_Program is called (in V_Tdm_Conf for TDM and V_Krn_Conf for the kernel).


Rational Software Corporation 
http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2002, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX DOC LIST MASTER INDEX TECHNOTES APEX TIPS