# 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:

- idle
  This is a pointer to a function that takes no parameter and returns nothing. The kernel will call this function whenever there are no threads to run. This function should suspend the processor until an external event happens (that is, interrupt request). This function is called with interrupts disabled. It can enable the interrupts if it wants. The function executes in the context of a user thread, therefore it must limit its stack usage to the minimum. This function must not call any kernel functions; if it does, then the system will crash. If you have no means to stop the CPU until an interrupt happens, it is safe to return immediately without doing anything; the system will work fine but it will burn CPU cycles and thus power while idling.

- irqs
  This is a pointer to a void function taking no parameter. This function will be called by the kernel's interrupt entry routine. It can be either an ARM or THUMB function. The function will execute in interrupt context (i.e. it uses the interrupt satck) and is entered with interrupts disabled. It must not enable interrupts or call kernel functions for which it is stated that they should not be called from interrupst; if it does, the system will crash. Note that you must not use the interrupt attribute on this function, it is a normal C function. The interrupt prologue/epilogue will be taken care of by the kernel.

- proc_tab and proc_num
  A pointer to the process table, an array of BZ_PROC structures and the number of array elements. The process table belongs to the kernel and you should never touch it. A process table entry is 12 bytes long. The table must be as large as many processes you intend to start. The process table must reside in RAM, of course. The max number of processes is 255.

- port_tab and port_num
  A pointer to the message port table, an array of BZ_PORT structures the number of array elements. The message port table holds the message ports used by the messaging subsystem. They must reside in RAM and belong to the kernel, you must never touch them. The table must be as large as many message ports you intend to use. If you do not use messaging, then set `port_tab` to NULL and `port_num` to 0. A message port is 4 bytes long. The max number of ports is 255.

- mesg_tab and mesg_num
  A pointer to an array of BZ_MESG structures and the number of array elements. This will be the message pool for the message subsystem. Thenumber of messages in the pool is the limit of messages waiting on message ports, not delivered yet. The number is limited 255. A message is 8 bytes. If you do not want to use the message subsystem then specify NULL for `mesg_tab` and 0 for `mesg_num`.

- init A pointer to a function taking no arguments and returning nothing. This will be the first user process to start. In fact, BZ_Init() does not return, rather, it switches the processor into system state and jumps to the function specified by `init`. The function will be started with interrupts disabled, you have to explicitely enable them later. It can be either an ARM or a THUMB function.

- init_stk
  A pointer to the stack for the `init` process. As with the BZ_ProcSpawn() function, this is the actual stackpointer value. Since the ARM uses decrement before push, if you declare the stack as an array of 32-bit integers, then it should point to the element just beyond the array. That is, if you declare your stack as `int stack[ STACKSIZE ];` then you should set `init_stk` to `stack + STACKSIZE`. In theory, this pointer should be 8-byte aligned but in reality it is sufficient if it is 4-byte aligned.

- init_pid
  The process ID of the `init` process. Every thread in the system has an ID between 0 and `proc_num-1`, which is basically the index into the process table. If you choose priority scheduling, then the higher the process ID, the more important the process. If you use round-robin scheduling, then the actual value of the PID does not matter. This parameter is the PID for the first process to be started, that is, the slot in the process table that this process will use.

- schedule
  If this parameter is 0, then the kernel will use round-robin scheduling. If it is non-zero, then priority scheduling will be used. The kernel will still be co-operative, so a process is only suspended if it explicitely asks for it. If round-robin scheduling is used, then if you have N processes (PIDs going from 0 to N-1) and process k is suspended, then the kernel will check if k-1, k-2 .. 1, 0, N-1, N-2 ... is runnable and start the first one it finds. If priority scheduling is selected, then the testing order is always N-1, N-2, .. 0 regardless of the PID of the process giving up the CPU.

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.