Before using, you must initialise the kernel. More...
Functions | |
| void | BZ_Init (const BZ_INIT *init) |
| Initialises the kernel. | |
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:
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 0 for mesg_num.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 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 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.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 ) { ... } ... } }
| 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.
| init | Pointer to a BZ_INIT structure that contains the kernel configuration. |
1.6.3