Anatomy of a Small Open-Source Kernel for DSPs

Julian provides the technical and historical background of dsp_k, with a particular focus on the kernel.
Task Control

A second kernel personality service extends the functional services with a family of task functions. These add to the BSP so as to provide you with a means of detailed application task control. At runtime, all tasks are in some state, and there is a set of corresponding state functions provided, which you can see in dsp_Kt.c. In addition to a state, tasks have attributes, and there are task functions to retrieve or set these. Furthermore, resource-oriented functions attach system resources to tasks.

A runtime dsp_K task notionally exists on some kernel queue. Those tasks ready to be run live on the ready queue, others pending some event reside on the pending queue, and those that have terminated are found on the null queue. While some kernels do indeed support queue structures, dsp_K uses a state variable such that one attribute of a task is its state (because this requires less memory). The list of possible states is an enumeration found in dsp_K.h (and near the top of Listing 3) but includes DSP_K_STATE_READY and DSP_K_STATE_PEND, for example. For each state a task can reside in, there exists a corresponding task function like DSP_K_TASK_READY. The purpose of the state functions is to provide your application tasks with a way to set their own or others' states and then to call its own scheduler function.

Where the state functions are used to set values, a family of attribute functions are used to get values. You can see the task attributes in the t__DSP_K_tcb structure within dsp_K.h and they include errno, parent and group (see also Listing 3). It follows, for example, that the attribute function DSP_K_TASK_PPID retrieves the parent task number for the calling task. The DSP_K_TASK_ERRNO function differs a little from the other attribute functions in that it both sets and returns an errno value for the task and is often used in library code like this:

return(DSP_K_TASK_ERRNO( -EIO ))

By resource functions I mean those that temporarily assign system resources to a task. For example, you can use DSP_K_TASK_TIMER to attach an available timer. Once the timer expires or is canceled, it is returned to the pool of available timers and available for use by another task. These resource functions might be used to support higher layer library functions.

Similar to the UNIX function with which you are probably familiar, you use the library function fork() to spawn a child task. It is supported in dsp_K through the BSP task resource function DSP_K_TASK_CREATE. When compiled, your application is configured with a fixed number of tasks, or more specifically, a number of task slots. Although dsp_K applications are statically configured, it does not follow that all task slots have to be initialized with a function, and some may be left empty. Furthermore, after time some tasks may exit and so release their slot resource. The task create function searches for a free slot, and if one is found initializes it with a new task (entry point) to run.

After the task control block structure is initialized with a task, the dsp_K version of fork goes on to copy the parent context and stack, adjusting pointers onto its own stack so that it will run independently. Once copying is finished, the parent scheduler is called, and it may be that the child runs before the parent or vice versa.

So, to summarize, we have seen that the BSP comprises a set of functional and personality services that together provide a platform to build upon in constructing an application. Through highly scalable system configuration settings and selected layers of support software, such as POSIX-like libraries, we are able to build a highly tailored application.


Together with your application code, the kernel is statically configured at buildtime. Therefore, exactly which hardware target resources are required to run the combined program are known. Configuration will fix the number of tasks and the tick priority among other settings. The BSP includes a file, dsp_Ku.h, that undefines/defines all possible settings; this was influenced by the configAll.h VxWorks header file. A key issue with DSPs especially is that there is limited resource availability, and you need to allocate those required at buildtime. By way of example, all but example4 (heap memory management) in the dsp_K generic sources subdirectory are built to run on chip in a few kilobytes of memory.

You tailor each application by inclusion of a custom configuration file called dsp_Kcfg.h; Listing 5 shows an example. You explicitly set only those required buildtime variables to allocate the resources needed by your application. You do not need to set unwanted buildtime variables because the dsp_Ku.h file takes care of them for you. Your custom configuration file is included at buildtime by the BSP, in addition to any API and higher-layer libraries used, so that the combined binary includes only those elements needed to run your application. For example, if you omit the DSP_K_CONTEXT_MULTIPLIER variable then the context switch will be made smaller by not saving and restoring the SHARC multiplier registers. You can even disable core BSP settings, like the tick rate, through DSP_K_KERNEL_TICKS, thus reducing an application to a cooperative multitasking one, showing the full configurability and scalability of dsp_K.

Listing 5. Custom Configuration File

The BSP provides the programmer with a layer 1 programming interface. When configuring your dsp_K-based application, you arrange layers of software on top of it to extend the programmer interface. This is done simply by including the required software libraries in your project settings so they get compiled and linked in, and by setting the required configuration options in dsp_Kcfg.h. Layer 2 API libraries extend the kernel personality, providing functions such as intertask message passing. Next up, the device driver layer 3 enables your applications with access to hardware and protected software elements like signals. Topmost at layer 4 are libraries that provide you with a familiar POSIX-like programmer interface; these are our EL/IX implementations.

You may gain a deeper insight into dsp_K-application configuration and layering through the generic examples and documents provided with the distribution.