wiki:ppc_prog_overview/xilkernel

Xilkernel Support and Programming

The Xilkernel Real-Time operating system is simple, robust and light-weight. The PowerPC version takes up only 16 to 32 kilobytes depending on features implemented. Xilkernel supplies the programmer with extra abstracted advanced functionality:

  • Threading
  • Mutex Locks
  • Semaphore Locks
  • Shared Memory
  • Dynamic Memory
  • Message Queues
  • More abstracted interrupt support

This extra functionality is extremely useful for allowing non-linear execution of functions as well as controlled resource sharing – very useful tools for developing experimental network protocols and MAC layers.

Configuration

Xilkernel can be chosen and configured under “Software Platform Settings” in XPS. It’s important to set any features the user wishes to utilize to “true,” under the “OS and Libraries tab.” It’s also better to configure Xilkernel to support more than what is needed for development, and then to cut away at unnecessary items after development is finished. Detailed information on the configuration parameters can be found in the “OS and Libraries Document Collection,” located in “$EDK\doc\oslib_rm.pdf.”

The kernel starts by calling the xilkernel_main( ) function. It is typically placed in the “main ( )” function, and all code after the xilkernel_main( ) call will be ignored/never reached. When running the kernel, it’s also important to include the library “xmk.h” as the first one listed at the top of all C-files running on the system.

Threading

A thread, in xilkernel, is simply a function that is not necessarily processed linearly. If you are a beginner programmer, you may be used to the idea of a linear program, where at any given time, you are sitting in one function call. This is not necessarily the case with threads. Threads are coded like functions, but can work "in parallel" -- not truely in parallel, however, unless you have multiple processors. In a single processor, parts of the function/thread are processed in a scheduled fashion, very quickly -- giving the the impression that they are being processed in parallel in realtime. Aside from being simple functions, threads can do a lot of cool things, like interact with each other and pass data back and forth. By reading the rest of this document and checking out the reference designs, you should be able to pick up (relatively easily), how to do all of these things.

Threading is a required component of Xilkernel – at least one thread is required to spawn from the system at kernel start. The main threads are simply functions of type void* and do not take any inputs. The launch threads are defined in SPS under the “OS and Libraries Tab,” under the “config_pthread_support” header, and finally in the “static_pthread_table.” A priority level may be given to the thread if priority-scheduling is used in the system – otherwise it will be ignored.

There are two scheduling modes in xilkernel: SCHED_PRIO and SCHED_RR, which are also chosen in SPS. SCHED_PRIO is priority based scheduling, in which lower priority threads will always yield to higher priority threads (designated by lower priority numbers), until the higher priority thread finishes or pthread-joining is used. If two threads have the same priority, they will be processed concurrently in round-robin based scheduling. Thus, SCHED_RR is round-robin-based scheduling and is a more simplified form of SCHED_PRIO, in which all threads must be processed at close-to the same time. The human perception of this is that the threads are running in parallel.

The pthread support also provides some more advanced functionality such as thread-joining. If a thread is joined with another thread, the caller will pause and wait until the called thread finishes its task. The threading API may be found in “$EDK\doc\oslib_rm.pdf.” Threading reference designs may be found here (SCHED_PRIO) and here (SCHED_RR). The following are some of the common or more important functions involved in threading:

  • pthread_create( ) – this function generates and starts a thread running, given a thread identifier name, an attribute structure, a function name (the process itself), and arguments for the function if any are needed.
  • pthread_exit( ) – this function terminates the calling thread
  • pthread_join( ) – take in another thread ID. The calling thread will freeze its execution until the called thread finishes.
  • pthread_attr_init( ) – takes in an empty attribute structure an initializes it for use in pthread_create( ).
  • pthread_setschedparam( ) – can be used to give a thread a priority level

Mutex

Mutex is short for “mutual exclusion lock.” These are a very simple form of semaphore. In xilkernel, a thread may create a mutex (in either a locked or unlocked state). If the calling thread puts it in a locked state, that thread will have the lock. Otherwise, the first thread to call the locking function will get it. Any time a thread in the system attempts to lock an already-locked mutex, the thread will pause it’s processing until the lock becomes available. This is a very simple tool for controlling access to a resource (such memory or a GPIO peripheral). The mutex API may be found in “$EDK\doc\oslib_rm.pdf.” A mutex reference design for WARP may be found here.The following are some of the common or more important functions involved in mutex use:

  • pthread_mutex_init( ) – this function generates a controllable mutex given a mutex ID and mutex attribute structure.
  • pthread_mutex_lock( ) – this function will give the calling thread a lock on the mutex if it is available. Otherwise, the thread will suspend execution until the mutex is freed by the other process.
  • pthread_mutex_unlock( ) – causes the calling thread to unlock the mutex. The function will fail if the calling thread was not the one that initially locked it.
  • pthread_mutexattr_init( ) – initializes an empty mutex attribute structure for use in pthread_mutex_init( ).
  • pthread_mutexattr_settype( ) – allows the mutex to be defined as type PTHREAD_MUTEX_DEFAULT or PTHREAD_MUTEX_RECURSIVE (integer-type flags). If the mutex type is set to recursive, a thread locked on the mutex can perform extra multiple locks or unlocks and keep track of them. A thread is available for locking if the lock-count becomes zero.

Semaphores

Semaphores are very similar to mutecies, but provide extra functionality. Semaphores can be given string names; they contain “counting” information, which can be used to tell how many threads it is blocking; they also provide more advanced waiting (locking) functions such as timed-waiting. The semaphore functions are also utilized in higher-level memory management such as xilkernel’s message queues. The semaphore API may be found in “$EDK\doc\oslib_rm.pdf.” A semaphore reference design for WARP may be found here. The following are some of the common or more important functions involved in semaphore use:

  • sem_init( ) – this function generates an unnamed semaphore given a pre-defined semaphore ID. The function also allows the semaphore to be placed in a locked state (a count value greater than or equal to 1) or an unlocked state (a count value of 0).
  • sem_getvalue( ) – this function will return the lock count of the desired semaphore given its ID.
  • sem_wait( ) – given a semaphore ID, the calling process will suspend (or rather not return from sem_wait) until it can get a lock on the semaphore.
  • sem_post( ) – given a semaphore ID, the function unlocks the semaphore

Shared Memory

The shared memory functionality is still under development by Xilinx. In its current state, it is a weaker version of a locking mechanism as it does not provide any thread blocking/freezing features (there is no waiting functionality). Instead, the shared memory locking mechanism is just a counter for the number of threads that are attached to the shared memory. The convenience of shared memory, is that it can be used to allocate a buffer of custom size, and return the buffer’s base address upon a successful attach call. The shared memory API may be found in “$EDK\doc\oslib_rm.pdf.” A shared memory reference design me be found here. Note: The detach function in the documentation is listed as “shm_dt,” but should be “shmdt.” The following are some of the common or more important functions involved in using shared memory:

  • shmget( ) – given a request for a size of memory space, the function will return an integer ID for the shared memory region upon success.
  • shmctl( ) – this function can be used to copy the shared memory’s data/attribute structure to an empty one, or to destroy the shared memory region and free it.
  • shmat( ) – this function attaches the shared memory region to a process or thread. If the call is successful, the start address of the shared memory segment will be returned. It will also increment shm_nattach (a counter) located in the shared memory’s data structure.
  • shmdt( ) – detaches the shared memory region from a process and decreases the shm_nattach value. The function does not destroy the memory region and allows it to be attached again.

Dynamic Memory

Xilkernel’s dynamic memory functions are used for allocating buffers of custom sizes and widths. The buffers are made from a preset memory table located in the “OS and Libraries” section of “Software Platform Settings.” This functionality is useful for creating a buffer repository for memory structures of custom size – such as a packet. The dynamic memory allocation functions provide no resource-locking mechanisms, however. Dynamic memory API may be found in “$EDK\doc\oslib_rm.pdf.” A basic reference design may be found here. It is also implemented in the ACKMAC Research Application. The following are some of the common or more important functions involved in working with dynamic memory:

  • bufcreate( ) – upon a successful call, the function will create a buffer ID given a requested depth (number of blocks) and width (size of blocks).
  • bufdestroy( ) – destroys the buffer ID, but not the reserved memory pool
  • bufmalloc( ) – allocate memory from a predefined memory pool (from “software platform settings”) to be associated with the buffer ID.
  • buffree ( ) – frees the memory that was allocated to a buffer ID

Message Queues

Message Queues are an extremely powerful feature of xilkernel. They are a very easy and safe means of transferring data back and forth between multiple processes. Messaging is safe because the functionality uses semaphores internally. It’s easy because messages can take in custom structs. Only four functions are needed to setup, send and receive messages between threads. There is also support for having multiple messages in the system. Message Queue API may be found in “$EDK\doc\oslib_rm.pdf.” A message queue demo for WARP may be found here. The following are some of the common or more important functions involved in working with message queues:

  • msgget( ) – returns an ID number for a message queue given an unused/empty key value.
  • msgctl( ) – can be used to either copy the message queue’s data structure into a buffer, or destroy the message queue, given a message queue ID number.
  • msgsnd( ) – places a message on the queue
  • msgrcv ( ) – takes a message off the queue and places it in a buffer. The user can define the function to return immediately or suspend execution until a message exists if no messages are present at the time of the calling.

Setting Up Interrupts in C (Xilkernel OS)

Getting interrupts to run in xilkernel is similar to getting them running on the standalone OS, but xilkernel abstracts away function calls to the interrupt controller. First the peripherals must be tied to the interrupt controller in hardware properly. Once this has been accomplished, the following steps are needed to have an interrupts implemented in the software:

  1. In “Software Platform Settings” under “Interrupt Handlers,” give the desired interrupt a handler name. The interrupt controller does not require a handler unless the user would like a special function call for any interrupt that occurs
  2. In the main code, initialize the interrupting peripheral with it’s API
  3. Initialize the interrupting peripheral’s interrupt with it’s API and start it running if required (such as a timer)
  4. Use the xilkernel function: enable_interrupt( ) – which takes in the interrupt ID of the peripheral, found in “xparameters.h.” For the enable function call, it is of type “int_id_t.”
  5. Have a function prepared with the handler name

A basic timer interrupt demo for xilkernel may be found here. The xilkernel API documentation for xilkernel interrupts may be found in “$EDK\doc\oslib_rm.pdf.”

PREV: EDK Programming Essentials
| HOME | NEXT: Alternative Operating Systems
Last modified 8 years ago Last modified on Mar 14, 2007 1:31:07 AM