Initialisation functions
[Cooperative scheduler]

Before using, you must initialise the kernel. More...

Functions

void BZ_Init (const BZ_INIT *init)
 Initialises the kernel.

Detailed Description

Before using, you must initialise the kernel.

The BZ_Init() function does that. It must be called from an interrupt context and a configuration structure is passed to the function. Based on the structure the function initialises the kernel and then switches from interrupt state to system state and starts the first thread. From that thread you can start all the other threads.

The initialisation structure contains all the parameters and pointers to the verious memory resurces that the kernel uses. The following structure fields are defined:

The following example illustrates the initialisation of the kernel, both the assembly that gets from reset to the C code and the C code until the first user thread is started:

/*
*   External functions defined by the library
*/

    .globl      BZ_IrqEntry         // IRQ handler entry point

/*
*   External functions defined by you
*/

    .globl      FatalError          // A fatal error occured
    .globl      SystemEntry         // 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).
*   Apart from reset and normal interrupt, we consider everything,
*   including FIQ as fatal error.
*/

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

/*
*   Reset code. It could go into the code segment or can
*   reside in the .vectors segment. We choose the latter.
*   Since in case of a fatal error we can't really do much,
*   we do not use a separate stack, we simply use the interrupt
*   stack.
*/

reset:
    ldr     r0,irq_stk              // Get the address of the stack
    msr     cpsr,#0xd7              // Set ABT mode
    mov     sp,r0                   // Set the ABT stack
    msr     cpsr,#0xdb              // Set UND mode
    mov     sp,r0                   // Set the UND stack
    msr     cpsr,#0xd3              // Set SVI mode
    mov     sp,r0                   // Set the SVI stack
    msr     cpsr,#0xd1              // Set FIQ mode
    mov     sp,r0                   // Set the FIQ stack
    msr     cpsr,#0xd2              // Set IRQ mode
    mov     sp,r0                   // 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
sysinit:    .word   SystemEntry

/*
*   Stack for everything except system mode.
*
*   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    .stack,"aw",%nobits
    .align  3                       // Align to 8-byte boundary
    .space  512                     // 1/2 KB interrupt stack
istack:                             // Defined *after* the stackspace!

    .end

#define NUM_PROC    5       // The number of threads in the system
#define NUM_PORT    2       // We have two threads that use message ports
#define NUM_MESG    20      // We buffer up to 20 messages
#define INIT_STK    128     // The init process will have 512 bytes of stack
#define INIT_PID    0       // PID for the init thread


static BZ_PROC  procs[ NUM_PROC ];  // Memory reserved for threads (60 bytes)
static BZ_PORT  ports[ NUM_PORT ];  // Memory reserved for ports (8 bytes)
static BZ_MESG  mesgs[ NUM_MESG ];  // Memory reserved for messages (160 bytes)
static int      stack[ INIT_STK ];  // Stack for the first thread

/*
*   Local prototypes
*/

static void Interrupt( void );
static void Idle( void );
static void Init( void ) __attribute__((noreturn));

void SystemEntry( void ) __attribute__((noreturn));

/*
*   The configuration structure for the kernel
*/

static const BZ_INIT kernel_config = {

    .idle       = Idle,                 // Idle function
    .irqs       = Interrupt,            // Interrupt handler function
    .init       = Init,                 // Entry point of the first thread
    .init_stk   = stack + INIT_STK,     // Stack for the first thread
    .init_pid   = INIT_PID,             // Process ID for the first thread
    .proc_tab   = procs,                // Process table
    .proc_num   = NUM_PROC,             // Size of the process table
    .port_tab   = ports,                // Message ports
    .port_num   = NUM_PORT,             // Number of ports
    .mesg_tab   = mesgs,                // Message buffer pool
    .mesg_num   = NUM_MESG,             // Size of the pool
    .schedule   = 1                     // We want priority scheduling
};

/*
*   This is where reset ends up after setting up the the
*   interrupt state and interrupt stack. At entry we're in
*   interrupt state and interrupts are disabled. We're using
*   the interrupt stack. This function never returns.
*/


void SystemEntry( void )
{
    // Clear the .bss and initialise .data

    ...

    // Set up basic hardware, such as CPU clock, power, debug port, etc.

    ...

    // Start the kernel. This call will not return, execution will continue
    // with entering Init() in system state, interrupts disabled.

    BZ_Init( &kernel_config );
}

/*
*   This is the idle function.
*   It is entered with interrupts disabled. It can either return without
*   doing anything or it can stop the CPU until an interrupt becomes pending,
*   in which case it will return immediately.
*   If you use an LPC2xxx processor, you can stop the CPU using a register
*   in the system control block. The processor will resume execution when
*   an interrupt becomes pending, even if the CPU itself is in disabled
*   interrupt state.
*/

static void Idle( void )
{
    REGISTER_IN_SCB = STOP_THE_CPU_CLOCK;
}

/*
*   This is the interrupt handler entry point.
*   It is a normal C function, you MUST NOT declare it with and interrupt
*   attribute. The actual IRQ slot in the vector table should jump to the
*   BZ_IrqEntry function (provided by the kernel) and that will, after
*   the necessary prologue, call this function.
*   This function, in turn, has to call the actual interrupt handler for
*   whatever interrupt is pending. On the LPC2xxx chips the vectored
*   interrupt controller has a register that contains the function pointer
*   for the next highest priority interrupt; writing 0 to that register
*   acknowledges that interrupt and loads the register with the next pending
*   interrupt's (if any) service routine address. It also has a register
*   indicating whether there are any pending interrupts at all.
*/

static void Interrupt( void )
{
    while ( VIC_INDICATES_PENDING_IRQ ) {

        (*VIC_REGISTER_CONTAINIG_THE_ISR_POINTER)();
        VIC_REGISTER_CONTAINIG_THE_ISR_POINTER = 0;
    }
}

/*
*   This is the first thread to run in the system.
*   It enters with interrupts disabled.
*/

static void Init( void )
{
    // Boot the hardware - we can't enable interrupts before
    // the hardware is ready.

    InitSomeHardware();
    ...
    InitSomeOtherHardware();

    // Spawn all threads. They will not start to run,
    // merely registered as runnables.

    BZ_ProcSpawn( some_process ... );
    ...
    BZ_ProcSpawn( last_process ... );

    // The system is ready, enable the interrupts and enter the
    // event loop - we're just a thread, like any other. This
    // also means thate we MUST NOT return.

    BZ_IrqDisable( 0 );

    for ( ;; ) {

        events = BZ_EventWait( event_list );

        if ( events & SOME_EVENT ) {

            ...
        }

        ...
    }
}


Function Documentation

void BZ_Init ( const BZ_INIT init  ) 

Initialises the kernel.

This function should be called from an interrupt context. It never returns to the caller, rather, it starts the first user process in the system. The kernel configuration is passed to this function by a structure, which can be located in a read-only location.

Parameters:
init Pointer to a BZ_INIT structure that contains the kernel configuration.
Note:
The function does not check the validity of the parameters. If you specify, for example, a PID for the first process that is out of the range of the process table, the system will crash. The kernel does some sanity checks when it's running, but it assumes that you do initialise it properly.
Attention:
This function must be called from an interrupt context, with interrupts disabled. It never returns but starts the first user process.
Generated on Tue Jul 13 16:51:44 2010 by  doxygen 1.6.3