Initialisation functions
[Pre-emptive, soft real-time scheduler]

Data Structures

struct  BX_INIT
 The kernel init parameter structure. More...

Defines

#define BX_EQSIZE(len)   (3*(len))
 A macro to work out how many ints are needed for the event queue.

Typedefs

typedef void(* BX_ISR_FUNC )(void)
 Prototype for ISR functions.
typedef void(* BX_DISR_FUNC )(void *, void *)
 Prototype for delayed/soft ISR functions.
typedef void(* BX_PROC_FUNC )(void *)
 Prototype for process entry point functions.

Functions

void BX_Init (const BX_INIT *init)
 Initialise the kernel.

Define Documentation

#define BX_EQSIZE ( len   )     (3*(len))

A macro to work out how many ints are needed for the event queue.

Parameters:
len The length of the event queue
Returns:
The number of ints to allocate

Function Documentation

void BX_Init ( const BX_INIT init  ) 

Initialise the kernel.

This function must be called when the processor is in IRQ mode. Both IRQ and FIQ should be disabled. The IRQ stack must be valid and will be used for interrupts in the future.

The kernel is initialised and parametrised by a structure. The following fields are defined:

  • eq_length
    is the length of the event queue. To keep the interrupt latency as low as possible, when the kernel is doing something that can take some time, such as evaluating the priority inheritance chain, it does it while the interrupt is enabled. Therefore, if an interrupt occurs and calls a kernel function, to keep the system in a consistent state and to keep the kernel calls ordered, the kernel call from the interrupt does not actually perform its operation but places it into the event queue. When the kernel finishes the time-consuming procedure, it will the run through the event queue and execute the operations queued on it. This structure field is the length of the event queue in number of events it can hold. The longer the queue, the less likely that the kernel will need to hold off interrupts. The event queue must be at least three times as long as the maximum number of kernel calls any single interrupt handler can call. It is suggested that you use an event queue that is at least 20 events long, longer if you have interrupt routines that call several kernel functions before returning.
  • eq_wmark
    is the watermark level for the event queue. When the kernel is queueing events (due to it running some time consuming operation), interrupts deposit their kernel calls in the queue. In order to avoid the queue to overflow, when the number of events in the queue reaches the watermark, the kernel disables furter interrupts. When, then due to processing the events in the queue, the queue length drops below the watermark, it enables them again. The watermark level should be such that it leaves at least twice the maximum number of kernel calls a single interrupt routine can cause. The watermark should not be higher than the length of the event queue length minus 5 and should not be less than 5. In general setting it to about 2/3 of the event queue length is a good policy.
  • eq_buff
    is a pointer to an int array. The size of the array must be 3 times the length of the queue (a single queue entry occupies 3 32-bit words). The array must be in RAM, of course.
  • irq_handler
    is a function pointer, the function having the prototype of void func( void ); This function is your interrupt handler. Since interrupt handling is dependent on what micrcocontroller you use (they have different interrupt controllers), it is up to you to write your interrupt handler. This function is a normal C function. Do not give it an interrupt attribute. The interrupt prologue and epilogue is done by the kernel. The function will run with the processor being in interrupt mode (therefore using the interrupt stack). It should be noted that since this function is just a normal C function, it can be either a THUMB function or an ARM function.
  • idle_state
    is a function pointer, with the same signature as irq_handler. Whenever the kernel has nothing to do, i.e. all processes are sleeping, it will call this function. This function is called with IRQ disabled, running in the kernel's internal context. This function must return as soon as an interrupt becomes pending (even if it is not yet accepted by the CPU core). The best use of this function is to put the core into sleep mode to save power. If you don't want to do anything while the kernel is idle, you can give a NULL pointer here. Note that if you establish an idle_state handler and that function does not return, the system will stop. In addition, any attempt to call any kernel function from the idle handler will crash the system.
  • init_func
    is a function pointer. This will be the first process that the kernel starts. In fact, the call to BX_Init() will never return and control will be passed to the init_func function. When the function enters, it will have the highest priority and it will have its protection level set to BX_PROT_FIQD, that is, neither IRQs nor FIQs are accepted. Consequently, this function is protected from everything, it can safely start other processes, initialise resources and so on. Then it can drop its priority and lower its protection level and become a normal process. As all processes, the function will run in system mode (0x1f), that is, user mode with privileges.
  • init_desc
    is pointer to a BX_PROC structure (in RAM). That will be the process descriptor for the init_func process.
  • init_stack
    is a word aligned pointer to the bottom of the stack allocated for the init_proc process. It is defined as an unsigned int pointer, to force the word alignment. If you want to allocate 1KB stack for the init process, then you would declare an unsigned int array of 256 elements and set this structure field to the value of array_name + 256, that is just behind the last element of the array.
  • kern_stack
    is a word aligned pointer to the bottom of the stack allocated for the kernel itself. It is defined the same way as init_stack. This will be the satck that is used while the kernel performs some internal work or when it is executing a delayed or user initiated (soft) interrupt routine (i.e. it's working on the event queue). It is suggested that you allocate at least 1KB of kernel stack.
Parameters:
init Pointer to a BX_INIT structure that contains the information about the kernel's parameters.
Attention:
This function does not return. It will pass the control to the function denoted by the init_func parameter.
Example boot code
This is the assembly code that contains your vector table.
/*
*   External functions defined by the library
*/

    .globl      BX_IRQ_Entry        // IRQ handler

/*
*   External functions defined by you
*/

    .globl      fatal_error         // Error (UND and ABT) exception
    .globl      fast_irq            // Fast IRQ handler
    .globl      supervisor          // Supervisor instruction handler
    .globl      sys_boot            // C routine to boot the system

/*
*   Exception vector table in FLASH
*   This table is in its own section, which must be
*   placed at 0 by the linker. We don't have to align,
*   because we *know* that it is aligned (being at 0).
*/

    .section    .vectors,"ax",%progbits
    .code       32                  // Indicate that it's ARM code
    b       reset                   // Reset
    b       fatal_error             // Undefined instruction (UND)
    b       supervisor              // Supervisor call (SVI)
    b       fatal_error             // Instruction prefetch abort (ABT)
    b       fatal_error             // Data access abort (ABT)
    .word   0                       // Unused by the ARM7TDMI core
    b       BX_IRQ_Entry            // Interrupt (IRQ)
    b       fast_irq                // Fast interrupt (FIQ)

/*
*   Reset code. It could go into the code segment or can
*   reside in the .vectors segment. We choose the latter.
*/

reset:
    msr     cpsr,#0xd7              // Set ABT mode
    ldr     sp,err_stk              // Set the ABT stack
    msr     cpsr,#0xdb              // Set UND mode
    ldr     sp,err_stk              // Set the UND stack
    msr     cpsr,#0xd3              // Set SVI mode
    ldr     sp,svi_stk              // Set the SVI stack
    msr     cpsr,#0xd1              // Set FIQ mode
    ldr     sp,fiq_stk              // Set the FIQ stack
    msr     cpsr,#0xd2              // Set IRQ mode
    ldr     sp,irq_stk              // Set the IRQ stack
    ldr     r0,sysinit              // Get boot code address
    bx      r0                      // Start it (in IRQ mode)

/*
*   Addresses of various objects
*/

irq_stk:    .word   istack
err_stk:    .word   estack
fiq_stk:    .word   fstack
svi_stk:    .word   sstack
sysinit:    .word   sys_boot

/*
*   Stacks for everything except system mode.
*   Of course if you do not use fast interrupts and supervisor mode
*   and if an abort or undefined instruction is a fatal error, from
*   which system reset is the way out, then you don't need that many
*   stacks and you can set them all to point to the interrupt stack.
*
*   Note that we put the stacks into their own segment, not into the
*   bss. That's because we will clear the bss to 0 and it would be
*   most unhealthy to zero the stack we're currently running on...
*/

    .section    .stacks,"aw",%nobits
    .align  2
    .space  1024                    // 1 KB interrupt stack
istack:
    .space  512                     // 1/2 KB fatal error handling stack
estack:
    .space  512                     // 1/2 KB fast interrupt stack
fstack:
    .space  512                     // 1/2 KB supervisor stack
sstack:

    .end

This is the C file that contains your system boot code and your first process (thread).
/*
*   Prototypes for the functions we use
*/

        void system_boot( void ) __attribute__ ((noreturn));
static  void system_init( void ) __attribute__ ((noreturn));
static  void interrupt( void );
static  void idle( void );
static  void boot_hardware( void );

/*
*   Definition of various parameters
*/

#define EVENTQ_LEN  30      // Event queue is 30 events long
#define EVENTQ_WM   20      // Event watermark is 20 events
#define MAIN_STACK  256     // Init process stack is 1KB (256 words)
#define KERN_STACK  256     // The kernel's stack is 1KB

/*
*   This will be the process descriptor for the main process. We make it
*   public, so others can send events to this process
*/

BX_PROC main_proc;

/*
*   Local data
*/

static int events[ EVENTQ_LEN * 3 ];    // The kernel's event queue
static int main_stack[ INIT_STACK ];    // The stack for the main process
static int kern_stack[ KERN_STACK ];    // The stack for the kernel itself

/*
*   These are defined by the linker, they are the start and end of the
*   various segments. We assume that the initialised data segment's
*   initailisers are placed in the FLASH just after the code.
*/

extern char __end_code[];               // End of the code segment
extern char __beg_data[];               // Start of the data segment
extern char __end_data[];               // End of the data segment
extern char __beg_bss[];                // Start of the bss segment
extern char __end_bss[];                // End of the bss segment

/*
*   This is the init structure. We initialise it using gcc's named field
*   extension, this makes it safer. We define it const, so gcc will put
*   it into FLASH.
*/

static const BX_INIT init = {

    .evtq_size  = EVENTQ_LEN,               // Length of the event queue
    .evtq_wmrk  = EVENTQ_WM,                // Interrupt watermark
    .evtq_buff  = events,                   // Memory for the event queue
    .uirq_func  = interrupt,                // Interrupt handler function
    .idle_func  = idle,                     // Idle state handler
    .init_desc  = &main_proc,               // Init process descriptor
    .init_func  = main,                     // Init process function
    .init_stack = main_stack + MAIN_STACK   // Note: BOTTOM of stack!
    .kern_stack = kern_stack + KERN_STACK   // Note: BOTTOM of stack!
};

/*
*   This is the function that is called after reset.
*   It is entered in IRQ mode, with IRQ and FIQ both disabled.
*   The only thing that has been set up is a few stacks, nothing else.
*   This function will never return, it will pass control to the first
*   thread instead.
*/

void sys_boot( void )
{
    // Load the initialised data (copy it from FLASH to RAM)

    memcpy( __beg_data, __end_code, __end_data - __beg_data );

    // Clear the BSS Note that the interrupt stack is NOT in the
    // BSS segment, thus this is safe to do.

    memset( __beg_bss, 0, __end_bss - __beg_bss );

    // Boot the processor hardware, such as clock PLL, power management etc.

    boot_hardware();    // This function is omitted from the example

    // Now we're ready to start the system. BX_Init() will never return.

    BX_Init( &init );
}

/*
*   This is the idle function.
*   It must *NOT* call any kernel functions. It can enable interrupts, but
*   it should not, unless it is really necessary. It is called with IRQ
*   disabled and supposed to sit here until an interrupt becomes pending.
*   If your microcontroller does not have a way to stop the core while
*   waiting for interrupts, you can leave this function empty. We now
*   assume that the microcontroller has a HW register that will stop
*   the core immediately and the core will resume operation if an interrupt
*   becomes pending (even though the core does not accept interrupts at the
*   moment).
*/

static void idle( void )
{
    // Send the CPU core to sleep. The instruction will stop
    // the core and it will wake up when an interrupt becomes
    // active.

    SOME_MCU_REG = VALUE_THAT_TURNS_THE_CORE_OFF;
}

/*
*   This is the handler for IRQs
*   Note that the function does not and must not have an interrupt attribute,
*   it is a normal C function. It can even be a THUMB function, if you enable
*   interworking.
*/

static void interrupt( void )
{
void (*isr)( void );

    // Get the address of the ISR function from the interrupt controller

    isr = SOME_MCU_REGISTER;

    // Call the ISR

    (*isr)();

    // Do whatever we have to to make the interrupt controller happy

    SOME_MCU_REGISTER = VALUE_TO_ACKNOWLEDGE_INTERRUPTS;
}

/*
*   This function is the entry point of the first thread.
*   It will be started with IRQ and FIQ disabled and with the highest priority.
*   It runs in system state (like all threads) using its own stack, main_stack
*   and its process descriptor is main_proc.
*/

static void main( void )
{
    // Start all subprocesses, init resources etc. Note that all
    // interrupts are disabled, nothing can interfere with us.
    // The threads you start will just sit there, ready to run
    // but this function will retain control.

    start_all_subsystems();

    // Now drop our priority to the level we should be on, let's
    // say it is 10. Note that even if we've started processes
    // with higher priority, we'll still be running because we
    // are in protected mode.

    BX_ProcNice( 10 );

    // Drop our protection level to normal. That will enable all
    // interrupts and, if there is any process with a priority
    // greater than ours, will cause a context switch.
    // From now on we're just a normal process.

    BX_ProcProtDrop( BX_PROT_NORM );

    // As any normal process, we should not return, just sit in a
    // loop (probably processing events).

    for (;;) {

        signal = BX_SignalWait( .. );
        ...
    }
}

Generated on Mon Aug 16 09:50:08 2010 by  doxygen 1.6.3