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
- PowerPC Kernel Configuration Parameters
- MIPS Kernel Configuration Parameters
- RH32 Kernel Configuration Parameters
- M68000 Family Kernel Configuration Parameters
- i386 Kernel Configuration Parameters
Kernel Configuration FilesThe 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 ParametersPolicy/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:
---------------------------------------------------------------
INCLUDE: /aloha/products/base/ada/board_common.ss/ \ power.rx_ppc.mvme1603.ada95.4.0.0.rel/Board_Common.sw RUNTIMES: /aloha/products/releases/apex_embedded.4.0/lib/power.rx_ppc.2.4.1/k rn LINKER_DESCRIPTION_FILE: <view>/Link.des
---------------------------------------------------------------
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:
- Startup_Table Structure
- Processor Exception Interface Procedures
- Interrupt_Vector_Table Structure
- Configuration_Table Structure
- Initialization Routines
- Elaboration Callout Routine
- Kernel Trap Handlers and Support Routines
- Default Interrupt Service Routine
- Interrupt Control Package
- Pending_Overflow_Callout Routine
- Timer Support Package
- OS Support Package
- V_Stack_Support Package
- Main Startup Routine
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:
-------- #c1: Startup Table Structure ----------
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:
V_Level1_Program V_Level1_Normal V_Level1_Break V_Level1_External
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.
procedure V_Level1_Normal; procedure V_Level1_Break; procedure V_Level1_ExternalL; procedure V_Level1_Program;
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:
type interrupt_vector_table_t is array(natural range <>) of address;
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:
procedure handler( trap_type : integer; context : v_cpu_conf.a_context_t);
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.
heap_stack_bottom = 16#80000# heap_stack_top = 16#FFFFF# krn_stack_size = 16#1000# intr_stack_size = 16#2000# MAIN_TASK_STACK_Size = 16#D000#
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:
- Round-to-nearest mode
- Invalid handler enabled (causes Numeric_Error)
- Zero_divide handler enabled (causes Numeric_Error)
- Overflow handler enabled (causes Numeric_Error)
- Underflow handler disabled
- Inexact handler disabled.
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:
- You have no stack.
- Interrupts are disabled.
- Trap Table is not yet initialized.
- r1 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.
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:
- Execution of a delay statement
- Execution of a task creation
- Evaluation of a "new" allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation of a protected object
- Execution of a protected operation
- Execution of an entry call
- Execution of an abort statement
- Raise of an exception unless handled locally
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:
type time_t is record High : Integer; Low : Unsigned_Types.Unsigned_Integer; end record;
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:
- Supervisor mode
- Big_Endian byte order
- Concurrent operation allowed
- Carry bit clear
- SFU1 (FPU enabled)
- SFU2-7 disabled
- Misaligned accesses cause an exception
- Interrupts disabled
- Shadow registers enabled
- 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 ParametersPolicy/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:
---------------------------------------------------------------------- INCLUDE: /aloha/products/base/ada/board_common.ss/ \ mips.rx_mips1b.idt381.ada95.4.0.0.rel/Board_Common.sw RUNTIMES: /aloha/products/releases/apex_embedded.4.0/lib/mips.rx_mips1b.2.4.1/krn LINKER_DESCRIPTION_FILE: <view>/Link.des
----------------------------------------------------------------------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:
- Startup_Table Structure
- Interrupt_Vector_Table Structure
- Configuration_Table Structure
- Miscellaneous Variables
- Immediate/Hardware Initialization Routines
- V_Elaboration_Callout Routine
- V_Decode_Exception Routine
- V_Decode_Interrupt Routine
- V_Rfe Routine (MIPS I Family only)
- V_Eret Routine (MIPS II/III/IV Family only)
- V_Restore_Ef Routine
- V_Untouchable Routine
- V_Gen_Except Routine
- V_Utlb_Except Routine
- V_Default_Isr Routine
- V_Pending_Overflow_Callout Routine
- V_Timer_Support Package
- V_Os_Support Package
- V_Alloc_Support Package
- V_Stack_Support Package
- V_Start_Program Routine
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:
----------- #c1: Startup_Table Structure -----------
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 a user-written interrupt service routine (ISR).
- The address of an interrupt service routine provided by the kernel through the package, V_Krn_Conf_I.
- The address of the default interrupt service routine, V_Default_Isr.
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:
type interrupt_vector_table_t is array(natural range <>) of address;
Apex provides the interrupt handlers for exception types for the MIPS I Family:
4,5 - address_error 6,7 - bus_error 12,38 - int_overflow 35 - ntaken_inst 36 - taken_inst 39 - div_zero 40 - range_check 41 - stack_check 49 - fp_save_init 50 - fp_restore 65 - fp_unusable 69 - fp_exception.
Apex provides the interrupt handlers for exception types for the MIPS II/III/IV Family:
4,5 - address_error 6,7 - bus_error 12,38 - int_overflow 15 - fp_exception 35 - ntaken_inst 36 - taken_inst 39 - div_zero 40 - range_check 41 - stack_check 49 - fp_save_init 50 - fp_restore 65 - fp_unusable
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:
procedure handler( vector_id : integer; ef : krn_cpu_defs.a_ef_t);
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:
- Memory Management
- Processor Status Configuration Parameters
- Interrupt Configuration Parameters
- Floating Point Coprocessor Parameters
- Time Slice Parameters
- Idle Processing Parameters
- Miscellaneous Configuration Parameters
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:
configuration_table.heap_stack_bottom := program_end;
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:
- Evaluation of a new allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation of a protected object
- Execution of a protected operation
- Execution of a task creation
- Execution of an entry call
- Execution of an abort statement
- Raise of an exception unless handled locally
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)
- KUc=0, IEc=0
- all registers are restored except for: k0, k1, t0, a0, sp and return pc
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)
- KSU=0, ERL=?, EXL=?, IE=? (may be entered in any of the above combinations)
- all registers are restored except for: t0, a0, sp and return pc
Restores these last registers from the exception frame then does an eret instruction.
V_Restore_Ef Routine
- For the MIPS I Family, KUc=0, IEc=? (may be entered with interrupts enabled or disabled)
- For the MIPS II/III/IV Family, KSU=0, ERL=0, EXL=0, IE=? (may be entered with interrupts enabled or disabled)
V_Untouchable Routine
- For the MIPS I Family, KUc=0, IEc=0
- For the MIPS II/III/IV Family, KSU=0, ERL=0, EXL=0, IE=0
- all registers are saved except for: k0, k1
- exception frame is at bottom of stack
V_Gen_Except Routine
- For the MIPS I Family, KUc=0, IEc=0 (interrupted KUc/IUc shifted into KUp/IEp)
- For the MIPS II/III/IV Family, KSU=0, ERL=0, EXL=1, IE=? (may be entered with interrupts enabled or disabled)
- No registers are saved
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
- For the MIPS II/III/IV Family, KSU=0, ERL=0, EXL=1, IE=?
- For the MIPS I, KUc=0, IEc=0 (interrupted KUc/IUc shifted into KUp/IEp)
- No registers are saved
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:
type time_t is record High : Integer; Low : Unsigned_Types.Unsigned_Integer; end record;
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 ParametersPolicy/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:
---------------------------------------------------------------------- INCLUDE: /aloha/products/base/ada/board_common.ss/ \ rh32.rx_p3.rh32pdu.ada95.4.0.0.rel/Board_Common.sw RUNTIMES: /aloha/products/releases/apex_embedded.4.0/lib/rh32.rx_p3.2.4.1/tdm LINKER_DESCRIPTION_FILE: <view>/Link.des ----------------------------------------------------------------------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
- Startup_Table Structure
- Interrupt_Vector_Table Structure
- Configuration_Table Structure
- Memory Management
- Interrupt Configuration Parameters
- Floating point Coprocessor Parameters
- Time Slice Parameters
- Idle Processing Parameters
- Miscellaneous Configuration Parameters
- Miscellaneous Variables
- V_Immediate_Initialization and V_Hardware_Initialization Routines
- V_Elaboration_Callout Routine
- V_Rfs Routine
- V_Restore_Ef Routine
- V_Untouchable Routine
- V_Gen_Exception Routine
- V_Surprise Routines
- V_Default_Isr Routine
- V_Pending_Overflow_Callout Routine
- V_Timer_Support Package
- V_Os_Support Package
- V_Alloc_Support Package
- V_Stack_Support Package
- V_Start_Program Routine
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:
------------- #c1: Startup_Table Structure -------------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 a user-written interrupt service routine (ISR).
- The address of an interrupt service routine provided by the kernel through the package, V_Krn_Conf_I.
- The address of the default interrupt service routine, V_Default_Isr.
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:
------------------------ from v_krn_conf.1.ada type Interrupt_Vector_Table_t is array(natural range <>) of address;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:
- address_error, - bus_error, - int_overflow, - div_zero, - range_check, - stack_check, and - fp_exception.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:
procedure handler( vector_id : integer; ef : krn_cpu_defs.a_ef_t);
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.
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:
- Memory Management
- Processor Status Configuration Parameters
- Interrupt Configuration Parameters
- Floating point Coprocessor Parameters
- Time Slice Parameters
- Idle Processing Parameters
- Miscellaneous Configuration Parameters
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:
configuration_table.heap_stack_bottom := program_end;
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.
Heap_Stack_Bottom = 16#0008_0000# Heap_Stack_Top = 16#000F_FFFF# Krn_Stack_Size = 16#0_1000# Intr_Stack_Size = 16#0_2000# Krn_Exception_Stack_Size = 16#0_1000# Main_Task_Stack_Size = 16#0_D000# (from v_usr_conf.2.ada) Exception_Stack_Size = 16#0_1000# (from v_usr_conf_b.a)
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:
- Evaluation of a new allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation of a protected object
- Execution of a protected operation
- Execution of a task creation
- Execution of an entry call
- Execution of an abort statement
- Raise of an exception unless handled locally
This routine must not reference any entities which require elaboration, since it is called before any elaboration takes place.
V_Elaboration_Callout Routine
procedure v_elaboration_callout(elaboration_entry: address);
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
- kernel mode
- all registers are restored except for: k0, k1, t0, a0, sp and return pc
Uses k0 register for doing the return. Since k1 was not saved or used, it is not restored.
procedure V_RFS(ef: krn_cpu_defs.a_ef_t);
V_Restore_Ef Routine
- kernel mode
- interrupts disabled
procedure V_Restore_Ef(ef: krn_cpu_defs.a_ef_t);
V_Untouchable Routine
Upon entry: kernel mode, interrupts disabled
- all registers are saved except for: k0, k1
- exception frame is at bottom of stack
procedure V_Untouchable(ef: krn_cpu_defs.a_ef_t);
V_Gen_Exception Routine
procedure V_Gen_Exception;
- Cur_Su=0, Cur_Ie=0 (interrupted Cur_Su/Ie shifted into Old_Su/Ie)
- k0 - cpu status register
- k1 - cpu history register
- t0 - cpu detail register
- t2 - vector ID (index into IVT)
- s1 - pending interrupt bits (interrupts only)
- sp moved down for Ef_Size
- r1, v0, v1, a0-a3, t0-t3, s0, s1, and ra, already saved in exception frame.
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
procedure V_Exc_Surprise; procedure V_Int_Surprise; procedure V_Trap_Surprise;
- Cur_Su=0, Cur_Ie=0 (interrupted Cur_Su/Ie shifted into Old_Su/Ie)
- No registers have been saved
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:
type time_t is record High : Integer; Low : Unsigned_Types.Unsigned_Integer; end record;
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 ParametersPolicy/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:
---------------------------------------------------------------------- INCLUDE: /aloha/products/base/ada/board_common.ss/ \ m68k.rx_mc68040.mvme167.ada83.2.4.wrk/Board_Common.sw RUNTIMES: /aloha/products/releases/apex_embedded.4.0/lib/m68k.rx_mc68040.2.4.1/krn LINKER_DESCRIPTION_FILE: <view>/Link.des ----------------------------------------------------------------------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
- Startup_Table Structure
- Interrupt_Vector_Table Structure
- Configuration_Table Structure
- V_Hardware_Initialization Routine
- V_Elaboration_Callout Routine
- V_Default_Isr Routine
- V_Privilege_Violation_Handler Routine
- V_Pending_Overflow_Callout Routine
- V_Timer_Support Package
- V_Os_Support Package
- V_Alloc_Support Package
- V_Stack_Support Package
- V_Start_Program Routine
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:
------------- #c1: Startup_Table structure -------------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 address of a user-written interrupt service routine (ISR).
- The address of an interrupt service routine provided by the kernel through the package V_Krn_Conf_I.
- The address of the default interrupt service routine, V_Default_Isr.
- The constant Untouchable_Vector declared in the kernel interface package, V_Krn_Conf_I.
The type declaration for the Interrupt_Vector_Table is an array of addresses:
---------------- from v_krn_conf.1.ada type interrupt_vector_table_t is array(natural <>) of address;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.
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:
- Memory Management
- Interrupt Configuration Parameters
- Floating Point Coprocessor Parameter
- Time Slice Parameters
- Idle Processing Parameters
- Miscellaneous Configuration Parameters
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:
configuration_table.heap_stack_bottom := program_end;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.
Heap_Stack_Bottom = 16#6_0000# Heap_Stack_Top = 16#F_FFFF# Krn_Stack_Size = 16#0_1000# Intr_Stack_Size = 16#0_1000# Krn_Exception_Stack_Size = 16#0_1000# Task_Supervisor_Task_Size = 16#0_1000# Main_Task_Stack_Size = 16#0_B000# (from v_usr_conf_b.a) Exception_Stack_Size = 16#0_1000# (from v_usr_conf_b.a) Supervisor_Tasks_Enabled = False
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.
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:
- Evaluation of a new allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation or use of protected objects
- Execution of a task creation
- Execution of an entry call
- Execution of an abort statement
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:
- Evaluation of a "new" allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation of a protected object
- Execution of a protected operation
- Execution of a task creation
- Execution of an entry call
- Execution of an abort statement
- Raise of an exception unless handled locally
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.
procedure V_Elaboration_Callout(elaboration_entry: address);
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:
type time_t is record High : Integer; Low : Unsigned_Types.Unsigned_Integer; end record;
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 ParametersPolicy/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:
--------------------------------------------------------------------- INCLUDE: /aloha/products/base/ada/board_common.ss/ \ i386.rx_i386.board.ada95.4.0.0.rel/Board_Common.sw RUNTIMES: /aloha/products/releases/apex_embedded.4.0/lib/i386.rx_i386.2.4.1/krn LINKER_DESCRIPTION_FILE: <view>/Link.des ----------------------------------------------------------------------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
- Startup_Table Structure
- Interrupt_Vector_Table and Null_Handler_Table Structures
- Memory_Map_Table Structure
- Configuration_Table Structure
- Miscellaneous Startup Variables
- GDT and TSS Parameters
- V_Memory_Initialization and V_Hardware_Initialization Routines
- V_Elaboration_Callout Routine
- V_Default_Isr and V_Default_Unknown_Isr Routines
- Get_Intr_Priority and Set_Intr_Priority Routines
- V_Pending_Overflow_Callout Routine
- V_Timer_Support Package
- OS Support Package
- V_Page_Support Package
- V_Alloc_Support Package
- V_Stack_Support Package
- V_Start_Program Routine
- Package V_Cpu_Init and Initial Program Execution
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:
------------- #c1: Startup_Table Structure -------------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:
- The address of a user-written interrupt service routine (ISR).
- The address of an interrupt service routine provided by the kernel through the package V_Krn_Conf_I.
- The constant Untouchable_Vector declared in the kernel interface package, V_Krn_Conf_I.
- The constant Default_Vector declared in the kernel interface package V_Krn_Conf_I
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.
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:
Push Intr_Number jmp base(Unhandled_Interrupt_Address'Ref)
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:
- Linear address maps directly to its physical address
- User bit is set
- Write bit is set if Memory_Map_Table's Memory_Acc field is either Read_Write or Write_Only
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
- Memory Management Parameters
- Processor Status Parameters
- Interrupt Configuration Parameters
- Monitor Configuration Parameters
- Floating Point Coprocessor Configuration Parameters
- Time Slice Configuration Parameters
- Idle Processing Configuration ParametersI
- Miscellaneous Configuration Parameters
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:
- Evaluation of a New allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Creation of a protected object
- Execution of a protected operation
- Execution of a task creation
- Execution of an entry call
- Execution of an abort statement
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 : integer;
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 : integer;
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:
- Evaluation of a new allocator
- Evaluation of an instantiation of unchecked deallocation
- Execution of a delay statement
- Execution of a task creation
- Creation of a protected object
- Execution of a protected operation
- Execution of an entry call
- Execution of an abort statement
- Raise of an exception unless handled locally
- Instantiation of a library level shared generic
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.
procedure V_Elaboration_Callout(elaboration_entry: address);
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:
type time_t is record High : Integer; Low : Unsigned_Types.Unsigned_Integer; end record;
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. |