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 BX_EQSIZE | ( | len | ) | (3*(len)) |
A macro to work out how many ints are needed for the event queue.
| len | The length of the event queue |
| 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 eq_wmark eq_buff 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 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 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 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 init_func process.init_stack 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 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.| init | Pointer to a BX_INIT structure that contains the information about the kernel's parameters. |
init_func parameter./* * 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
/* * 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( .. ); ... } }
1.6.3