Copyright: TMS320C55x DSP/BIOS Tutorial (SPRH128I) Copyright © 2006, Texas Instruments Incorporated   Monday, July 24, 2006 11:39:13


DSP = 55x


Delivery format = HTMLHelp = “format“ = HTMLHelp or WinHelp

General Platform = 5000 = “platform” = 6000, 5000, 2000

Specific = 55 = “target”  = 62, 54, 55, 28


Threading

DSP/BIOS - Threads

Many real-time DSP applications must perform a number of seemingly unrelated functions at the same time, often in response to external events such as the availability of data or the presence of a control signal. Both the functions performed and when they are performed are important.

These functions are called threads. Different systems define threads either narrowly or broadly. Within DSP/BIOS, the term is defined broadly to include any independent stream of instructions executed by the DSP. A thread is a single point of control that may contain a subroutine, a macro, or a function call.

Multi-threaded programs run on a single processor by allowing higher-priority threads to preempt lower-priority threads and by allowing various types of interaction between threads, including blocking, communication, and synchronization.

DSP/BIOS supports several types of program threads with different priorities. Each thread type has different execution and preemption characteristics. The thread types (from highest to lowest priority) are:

•	Hardware interrupts (HWI): includes CLK functions

•	Software interrupts (SWI): includes PRD functions

•	Tasks (TSK)

•	Background thread (IDL)

See Also

For additional information about threads, see the following:

•	Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format

•	The DSP/BIOS online help

•	Various DSP/BIOS application notes available on the Texas Instruments website, including:

◊	Audio Example Using DSP/BIOS (SPRA598)

◊	Understanding the Functional Enhancements of DSP/BIOS and Their Utilization in Real-Time DSP Applications (SPRA648)

◊	Programming and Debugging Tips for DSP/BIOS (SPRA640)

◊	DSP/BIOS Technical Overview (SPRA646)

◊	DSP/BIOS by Degrees: Using DSP/BIOS in an Existing Application (SPRA591)

◊	Understanding Basic DSP/BIOS Features (SPRA653)


Choosing Thread Types

DSP/BIOS - Threads

DSP/BIOS supports several types of program threads. Each thread type has different execution and preemption characteristics. The thread types (from highest to lowest priority) are:

•	Hardware interrupts (HWI): includes clock (CLK) functions

•	Software interrupts (SWI): includes periodic (PRD) functions

•	Tasks (TSK)

•	Background thread (IDL)

This lesson helps you decide which type of thread to use for a particular situation. You can use the thread comparison table or answer the thread selection questions.

Instead of printing this section, it is best to use this section online so that you can find the recommended thread type for your needs by following the links next to your answers.


About Threads and Multi-Threading

DSP/BIOS - Threads

Many real-time DSP applications must perform a number of functions at the same time, often in response to external events such as the availability of data or the presence of a control signal. Both the functions performed and when they are performed are important. These functions are called threads.

Some operating systems define threads narrowly; others define them broadly. Within DSP/BIOS, the term is defined broadly to include any independent stream of instructions executed by the DSP. A thread is a single point of control that may execute a subroutine, a macro, or a function call.

Traditional programs run from a main function, use a single polling loop, or process events one at a time in the same sequence they occur.

Multi-threaded programs run on a single processor by allowing higher-priority threads to preempt lower-priority threads and by allowing various types of interaction between threads, including blocking, communication, and synchronization.


 Thread Type Characteristics

DSP/BIOS - Threads

	HWI	SWI	TSK	IDL
This table compares the characteristics of the DSP/BIOS thread types:

Priority	highest	2nd highest	2nd lowest	lowest
Number of priority levels	DSP- dependent	15 PRD functions run at priority of PRD_swi SWI object. TSK scheduler runs at lowest SWI priority.	16 (including 1  for the idle loop)	1
Can block (suspend) own execution	no, runs to completion except for preemption	no, runs to completion except for preemption	yes	should not; would prevent PC from getting target information
Execution states	inactive,  ready,  running	inactive,  ready,  running	ready,  running, blocked, terminated	ready,  running
Preemption by thread type disabled by	HWI_disable, HWI_enter (with certain masks)	SWI_disable	TSK_disable	never preempts
Posted or made ready to run by	interrupt occurs	SWI_post, SWI_andn, SWI_dec, SWI_inc, SWI_or, PRD_tick	TSK_create	main() exits and no other thread is currently running
Stack used	system stack (1 per program)	system stack (1 per program)	task stack  (1 per task)	task stack used by  default
Context saved when preempted by other thread	customizable	certain registers saved to system stack	entire context saved to task stack	not applicable
Context saved when blocked	not applicable	not applicable	saves the C register set (see the compiler manual)	not applicable
Share data with thread via	streams, queues, pipes, global variables	streams, queues, pipes, global variables	streams, queues, pipes, locks, mailboxes, global variables	streams, queues, pipes, global variables
Synchronize with thread via	not applicable	SWI mailbox	semaphores, mailboxes	not applicable
Function hooks	no	no	yes: create, delete, exit, task switch, ready	no
Static creation	included in default configuration template	yes	yes	yes
Dynamic creation	yes	yes	yes	no
Dynamically change priority	no*	yes: SWI_raisepri, SWI_restorepri	yes: TSK_setpri	no
Implicit logging	none	post and completion events	ready, start, block, resume, and termination events	none
Implicit statistics	monitored values	execution time	execution time	none
* When a HWI function calls HWI_enter, it can pass a bitmask that indicates which interrupts to enable while the HWI function runs. An enabled interrupt can preempt the HWI function even if the enabled interrupt has a lower priority than the current interrupt.


Priority

The various thread types have a general priority order. Hardware interrupts have the highest priority. Idle functions have the lowest priority. Unless preemption has been disabled in some way, threads with higher priority can preempt threads with lower priority.


Number of priority levels

Within a thread type category, threads have priorities relative to other threads of that type. Unless preemption has been disabled in some way, threads with higher priority can preempt threads with lower priority.


Can block own execution
Can block (suspend) own execution

Task threads can suspend their execution while waiting for a resource to become available. This is called “blocking.” They can also sleep for some amount of time or yield the processor to other threads of the same priority. This is not true of other thread types; unless they are preempted, other threads run to completion.


Execution states

Various thread types have different sets of possible execution states.


Preemption disabled by
Preemption by thread type disabled by

A thread can temporarily disable preemption by threads of a particular type by calling the functions listed in this row.


Posted or made ready to run by

This row lists the events and functions that can cause a thread of a particular type to run.


Stack used

This row lists the stack used by each thread type. The system stack is shared by all threads except for task threads. Each task object has its own task stack.


Context saved when preempted by other thread

This row provides an overview of what registers are saved when a thread of this type is preempted. For details, see the “DSP/BIOS User’s Guide.”


Context saved when blocked

This row provides an overview of what registers are saved when a tasks suspends its activity.


Share data with thread via

This row lists data structures that can be used to share data that needs to be accessed by multiple threads of this type. If one or both threads may need to temporarily protect the data from access by another thread, some data structure other than a global variable should be used.


Synchronize with thread via

This row lists data structures that can be used to coordinate the activities of multiple threads. For example, these data structures might be used if a thread can run only if other threads have made data available to it.


Function hooks

This row indicates the types of functions that can be applied globally to all task threads. For example, you might want all tasks to call a function that saves or restores external hardware registers when a task switch occurs.


Static creation

This row indicates whether objects of this type can be created with the DSP/BIOS Configuration Tool.


Dynamic creation

This row indicates whether objects of this type can be created in real-time through calls to the XXX_create function, where XXX is the code for the object module.


Dynamic priority change
Dynamically change priority

This row indicates whether a function call can change the priority of a particular thread in real-time.


Implicit logging

This row indicates which events are logged automatically by DSP/BIOS for this thread type. If an event is logged, it is shown on the Execution Graph.


Implicit statistics

This row indicates what statistics are accumulated automatically by DSP/BIOS for this thread type. If statistics are gathered, they are shown in the Statistics View.


Thread Selection Questions

DSP/BIOS - Threads

Click the answer to this question for a particular thread in your application. Continue answering questions until you reach the topic that recommends a thread type.

Does the thread perform critical processing and take little time to execute compared to other threads in your application?

  Yes.

  Some of it. Part of the thread performs critical processing. Other parts take longer to execute and are less time-dependent.

  Not as critical or as fast as other threads. The thread has a real-time deadline, but the function takes long enough to execute that other threads need to be able to preempt it.

  No. The thread performs non-critical background processing, such as collecting and communicating instrumentation data.


What Triggers the Thread?  (critical processing)
What Triggers the Thread?

DSP/BIOS - Threads

If the thread performs critical processing and takes little time to execute compared to other threads, it is best to perform the thread within a hardware interrupt or a clock function. Clock functions are run in the context of a hardware interrupt.

Is the need to perform the thread triggered by a clock interrupt, or is it triggered by some other hardware interrupt?

  Clock interrupt.

  Other hardware interrupt.

  Neither. The thread is triggered by some condition that occurs within the application program, not by an external interrupt.

Back to the first question


Choosing Between Software Interrupts and Tasks

DSP/BIOS - Threads

Is your answer Yes to any of the following questions?

•	Does the function need to be able to suspend execution while waiting for resources?

•	Does the function have complex interdependencies with other threads or complex data sharing requirements?

•	Would you like the function to have its own stack (instead of sharing the system stack)?

•	Do you want to use the LCK, MBX, or SEM modules with this thread?

•	Do you want to cause a function to run whenever any thread of this type is created, deleted, exited, made ready to run, or becomes the current thread? (Task threads support global function hooks for these events. For example, you might want all tasks to call a function that saves or restores external hardware registers when a task switch occurs.)

•	Does this thread need a lower priority than another thread for which you answered Yes to a question above? (If you answer Yes to one of these questions for a particular thread, you should use a task for that thread. Any threads with a lower priority than that thread must be task threads or idle functions.)

  Yes to at least one of these questions.

  No to all of these questions.

Back to the first question


What Triggers the Thread? (non-critical processing)
What Triggers the Thread?

DSP/BIOS - Threads

Is the need to perform the thread triggered by a time interval expiration, or is it triggered by some other program condition?

  Periodic interval based on time or program activity.

  Other program condition.

  Hardware interrupt.

Back to the first question


Split Processing Between Two Threads

DSP/BIOS - Threads

If a thread is triggered by a hardware interrupt, use a hardware interrupt (HWI) to handle that interrupt. The HWI function should perform any critical processing that must not be preempted (except possibly by another hardware interrupt). However, the function should use a software interrupt (SWI) or task (TSK) thread to handle non-critical processing. This helps to minimize interrupt latency.

Separate the code performed within the HWI function and the code to be performed by a lower-priority thread. The lower-priority thread should perform processing with the following characteristics:

•	Processing has a longer real-time deadline than the critical portions of the thread

•	Processing takes a relatively long time to perform

•	Processing may be preempted by other threads

Next, answer the questions to determine what type of thread you should use for the lower-priority portion of the processing.

Back to the first question


Use a Hardware Interrupt Thread

DSP/BIOS - Threads

Your answers suggest that you should use a hardware interrupt (HWI) thread.

HWI threads are triggered in response to external asynchronous events that occur in the DSP environment. An HWI function (also called an interrupt service routine or ISR) is executed after a hardware interrupt is triggered in order to perform critical processing that is subject to a hard deadline.

HWI functions are the threads with the highest priority in a DSP/BIOS application. HWIs should be used for application tasks that may need to run at frequencies approaching 200 kHz and that need to be completed within deadlines of 2 to 100 microseconds. HWI threads provide very low interrupt latency.

Hardware interrupts can be written in assembly language or a combination of both assembly language and C. HWI functions are usually written in assembly language for efficiency.

All hardware interrupts run to completion. If an HWI is posted multiple times before its function has a chance to run, the function runs only one time. For this reason, you should minimize the amount of code performed by an HWI function. If the HWI_enter call is used to enable certain interrupts within the function, those interrupts can preempt the function, even if they have lower priority.

Note that if an HWI function calls any of the PIP APIs (PIP_alloc, PIP_free, PIP_get, PIP_put) the pipe's notifyWriter or notifyReader functions run as part of the HWI context.

See Also

For a detailed description of hardware interrupt threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Use a Software Interrupt Thread

DSP/BIOS - Threads

Your answers suggest that you should use a software interrupt (SWI) thread.

SWI threads are patterned after hardware interrupts. While the function for an HWI object is triggered by a hardware interrupt, software interrupts are triggered by calling SWI functions, such as SWI_post, from the program.

SWI threads are suitable for handling application tasks that occur at slower rates or are subject to less severe real-time deadlines than those of hardware interrupts. SWIs allow HWIs to defer less critical processing to a lower-priority thread, minimizing the time the CPU spends inside a hardware interrupt, where other interrupts may be disabled.

Like HWI threads, SWI threads always run to completion. All input needed by a software interrupt’s function should be ready when the program posts the SWI. The SWI object’s mailbox structure provides a way to determine when resources are available. A SWI thread cannot wait for resource availability while other threads run.

Software interrupts have priorities that are higher than tasks but lower than hardware interrupts.

SWIs are more memory efficient than tasks because all SWI threads use the system stack. Tasks provide additional capabilities, such as blocking and yielding, that SWI threads do not support.

The SWI module should not be confused with the “SWI” instruction that exists on many processors. The DSP/BIOS SWI module is independent from any processor-specific software interrupt features.

See Also

For a detailed description of software interrupt threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Use a Task Thread

DSP/BIOS - Threads

Your answers suggest that you should use a task (TSK) thread.

Tasks differ from software interrupts in the following ways:

•	Tasks can be suspended during execution until necessary resources are available. Software interrupts cannot.

•	DSP/BIOS provides a number of structures that can be use for inter-task communication and synchronization. These structures include semaphores, message mailboxes, and locks. These structures cannot be used for communication and synchronization of software interrupts.

•	Each task has its own stack. Software interrupts share the system stack.

•	System-specific functions may be called whenever a task is created, deleted, exits, or switches thread context. These functions may be used to extend a task’s context beyond the basic processor register set.

•	Tasks have priorities lower than software interrupts and higher than the background idle thread. There are a total of 16 priority levels available for tasks. User tasks can have priorities from 1 to 15. The lowest priority level (0) is reserved for running the DSP/BIOS idle loop.

See Also

For a detailed description of task threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Use an Idle Thread

DSP/BIOS - Threads

Your answers suggest that you should use an idle (IDL) thread.

The idle loop (IDL) executes at the lowest priority in a DSP/BIOS application. After the program's main function returns, a DSP/BIOS application calls the startup routine for each DSP/BIOS module included in the application. After the startup routines run, the program falls into the idle loop. The idle loop is a continuous loop that calls all functions for the IDL objects. Each function must wait for all others to finish executing before it is called again. The idle loop runs continuously except when it is preempted by higher-priority threads. Only functions that do not have hard deadlines should be executed in the idle loop.

Idle loop functions are often used to poll non-real-time devices that do not (or cannot) generate interrupts, to monitor system status, or to perform other background activities.

Communication between the target and the DSP/BIOS analysis tools is performed within the background idle loop. This ensures that the DSP/BIOS analysis tools do not interfere with the program's tasks. If the target CPU is too busy to perform background tasks, the DSP/BIOS analysis tools stop receiving information from the target until the CPU is available.

See Also

For a detailed description of idle threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Use a Clock Function Thread

DSP/BIOS - Threads

Your answers suggest that you should use a clock function (CLK) thread.

DSPs typically have one or more on-chip timers which generate a hardware interrupt at periodic intervals. DSP/BIOS normally uses one of the available on-chip timers as the source for its own system clock. Using the on-chip timer hardware present on most TMS320 DSPs, the CLK module supports time resolutions close to the single instruction cycle.

Clock functions are triggered at the rate of the on-chip timer interrupt. By default, these functions are triggered by the HWI_TINT hardware interrupt and are performed as HWI functions.

In some cases, you may want to consider using a periodic function instead of a clock function.

See Also

For a detailed description of clock function threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Use a Periodic Function Thread

DSP/BIOS - Threads

Your answers suggest that you should use a periodic function (PRD) thread.

Periodic functions run based on a multiple of the system clock. The system clock is a 32-bit counter that ticks every time PRD_tick is called. By default, this system clock is driven by the on-chip timer, but you can configure it to be driven by some other event, such as I/O availability.

Periodic functions run at the priority of the PRD_swi SWI object. By default, PRD_swi has a low priority. You can use the Configuration Tool to raise the priority of periodic functions.

All PRD functions run at the same SWI priority, so one PRD function cannot preempt another. To allow all PRD functions to meet their real-time deadlines, PRD functions should perform minimal processing and post lower-priority software interrupts for any lengthy processing. This ensures that the PRD_swi software interrupt can preempt any lengthy processing when the next system tick occurs.

If the period for more than one periodic functions elapses on the same system tick, the periodic functions run in the order in which they were created.

You may need to use a task thread instead of a periodic function if the thread must be able to suspend execution.

See Also

For a detailed description of periodic function threads, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.

Back to the first question


Hints for Arranging Windows

DSP/BIOS

When testing an application, you may want to open many Code Composer Studio windows and DSP/BIOS analysis tools. Here are hints for organizing the windows:

•	Maximize window. It is often useful to maximize the Code Composer Studio main window to fill your entire screen. If you do this, you can press Alt + Tab to move between Code Composer Studio and this help system.

•	Undock windows by default. DSP/BIOS analysis tools normally open as docked windows; that is, they are attached to a window border. To change this behavior, choose the DSP/BIOS→RTA Control Panel menu item. Right-click on the RTA Control Panel window and choose Property Page from the shortcut menu. In the RTA Options tab, change the Initial RTA Window State to one of these settings:

◊	Docked. Analysis tool windows have no title bar. They are shown as panes in the Code Composer Studio window.

◊	Floating in Main Window. Analysis tool windows have their own title bar and can be moved around within the Code Composer Studio work area.

◊	Floating Outside Main Window. Analysis tool windows have their own title bar and can be moved anywhere on the screen.

•	Undock individual windows. You can use the following commands to detach any areas that are docked to the bottom of the window:

◊	Right-click on a window area and enable Float in Main Window. You can then move the window anywhere within the main Code Composer Studio window.

◊	Right-click on a window area and disable Allow Docking. You can then move the window anywhere on the screen.

◊	Drag the border of the window anywhere within the main Code Composer Studio window. When Allow Docking is enabled, the window attaches to any edge you drag it to or can become a separate window if you drag it away from the edge.

•	Arrange windows. Windows that float in the main window can be quickly arranged with the following commands:

◊	Window→Cascade. Overlaps windows from upper-left to lower-right. Makes all windows large.

◊	Window→Tile Horizontally. Arranges and resizes windows to fill the main Code Composer Studio window. Makes all windows wide and the same size.

◊	Window→Tile Vertically. Arranges and resizes windows to fill the main Code Composer Studio window. Makes all windows tall and the same size.

•	Save workspace settings. Choose File→Workspace→Save Workspace As after you have an arrangement you like. During a later session, you can use File→Workspace→Load Workspace to reopen and arrange all the windows.


Starting Code Composer Studio

To start Code Composer Studio, do one of the following:

•	Double-click the CCStudio icon on your desktop

•	From the Windows Start menu, choose Programs→Texas Instruments→Code Composer Studio→Code Composer Studio.

Note:	Code Composer Studio Setup

If you see an error message when you try to start Code Composer Studio, use Code Composer Setup to define your target configuration. See the Code Composer Setup online help for details.

If Code Composer Studio displays an error message that says it cannot initialize the target DSP or cannot load a program onto the target, choose the Debug→Reset DSP or GEL→Configuration→CPU_Reset menu item. If this does not correct the problem, close Code Composer Studio and run the appropriate command for your target under Programs→Texas Instruments→Code Composer Studio→Hardware Resets. Then restart Code Composer Studio.


Creating DSP/BIOS Objects

To create a new object, right-click the manager for that object type and choose Insert from the shortcut menu.

For example, to create an SWI object, right-click the SWI manager and choose Insert SWI from the shortcut menu.


Renaming DSP/BIOS Objects

To rename an object, do one of the following:

•	Right-click the object and choose Rename from the shortcut menu. Type the new object name

•	Click once on the object name and then click a second time (a slow double-click). Type the new object name.


Setting Properties for DSP/BIOS Managers and Objects

To set properties, right-click a manager or object and choose Properties from the shortcut menu. Change any properties and click OK.


Reloading a workspace file

To reload the workspace, choose File→Workspace→Load Workspace.

Saving and reloading the workspace is especially useful when you use several windows to debug a program and have resized and arranged the windows.


If You Changed the Default Installation Location

If you installed DSP/BIOS in a folder other than c:\BIOS-5-20, use that location wherever this tutorial mentions the c:\BIOS-5-20 location.


Selecting the Correct Template

In the New window, do the following:

1.	If you have installed support for more than one DSP family, select the tab for your DSP family.

2.	Select your DSP target platform. When you highlight a platform, a brief description is shown to the right. Each item supports a different target board or simulator.

3.	Click OK to open a new configuration of this type.


Freeing Profiler Resources Before Using RTDX

The DSP/BIOS analysis tools use RTDX for host/target communication. On physical DSP targets you cannot use both profiling and RTDX at the same time. Code Composer Studio displays an error message that says a resource conflict occurred if you attempt to use RTDX and profiling at the same time:

After using profiling, follow these steps when you want to use DSP/BIOS tools:

1.	Go to the Profiler menu and uncheck Enable Clock.

2.	If the Profile window is open, click the Turn Session Off toolbar button. Then, close the Profile window.

If you are using a simulator, you can use profiling and RTDX at the same time.


Disabling RTDX Before Using Profiling

The DSP/BIOS run-time analysis tools use RTDX for host/target communication. On physical DSP targets you cannot use both profiling and RTDX at the same time. Code Composer Studio displays an error message that says a resource conflict occurred if you attempt to use RTDX and profiling at the same time:

Before using profiling, follow these steps if you have used RTDX or any DSP/BIOS run-time analysis tools during this session:

1.	Close all tools that uses RTDX, such as the Message Log and other DSP/BIOS run-time analysis tools.

2.	Choose Tools→RTDX→Configuration Control to open the RTDX Configuration Control window.

3.	Remove the checkmark from the Enable RTDX checkbox. Then close the RTDX Configuration Control window.


DSP/BIOS Modules in the Configuration Tool
DSP/BIOS Modules

The DSP/BIOS Configuration Tool groups the DSP/BIOS modules by area of functionality as follows:

In addition to the modules shown in the configuration window, DSP/BIOS includes the following modules, which provide API functions but do not have objects or properties:

•	ATM. Atomic functions

•	C55. Target-specific functions

•	TRC. Trace manager functions


MADU

Minimum Addressable Data Unit. The smallest unit of data storage that can be read or written by the CPU. For the C6000, this is an 8-bit byte. For the C5000, this is a 16-bit word.


threads

Some operating systems define threads narrowly; others define them broadly.

Within DSP/BIOS, the term "threads" is defined broadly to include any independent stream of instructions executed by the DSP. A thread is a single point of control that may execute a subroutine, a macro, or a function call.


task

A DSP/BIOS thread type. Tasks are prioritized functions that preempt the DSP/BIOS idle loop and are preempted by hardware and software interrupts. They are created and managed by the TSK module.

Tasks are the only type of DSP/BIOS thread that can block. Tasks differ from software interrupts in that a task can block its execution until some requirement is met that allows it to continue executing. In addition, tasks can yield the processor to other tasks with the same priority level.


hardware interrupt

A DSP/BIOS thread type. An interrupt is triggered by a physical connection to the target by an on-chip peripheral or an external device. Hardware interrupts are also called Interrupt Service Routines (ISRs). Hardware interrupts can also be simulated when using the simulator. Use the HWI module to configure functions to run in response to individual interrupts.


software interrupt

A DSP/BIOS thread type. Software interrupts are prioritized functions that preempt DSP/BIOS tasks and are preempted by hardware interrupts and software interrupts of higher priority. They are created and managed by the SWI module.


idle function

A DSP/BIOS thread type. Idle functions are called repeatedly when DSP/BIOS determines that no other (non-idle) thread is ready to run. Idle functions can be added and configures using the IDL manager.


critical processing

Processing that must be performed in time to meet a real-time deadline and that should not be preempted.


interrupt latency

The maximum time between the triggering of an interrupt and the execution of the first instruction in the interrupt’s function.


External references to DSP/BIOS objects

The programcfg.h file contains external declarations for objects created with the DSP/BIOS Configuration Tool. For example:

extern LOG_Obj trace;

near

For more details, see the section on “Referencing Static DSP/BIOS Objects Created with the Configuration Tool” of the DSP/BIOS User’s Guide (SPRU432) and the Programming and Debugging Tips for DSP/BIOS (SPRA640) application note.


Setting the RTDX Mode

For most targets, the configuration template contains the correct RTDX Mode setting. However, if the RTDX Mode is incorrect, you will see the following message when you load a program:

RTDX target application does not match emulation protocol. Loaded program was created with an RTDX library which does not match the target device.

To correct this problem, follow these steps in the configuration file for your project:

1.	Click the + sign next to the Input/Output category to display its list of modules.

2.	Right-click on RTDX – Real-Time Data Exchange Settings and choose Properties from the shortcut menu.

3.	Change the RTDX Mode property.

4.	Click OK.

RTDX support is not available for certain DSP targets. As a result, DSP/BIOS real-time analysis is supported in stop-mode only on these targets. To see RTA data, choose Debug→Halt to pause the target. Analysis data can only be transferred from the target to the host PC only when you reach a breakpoint or halt the program.


Finding the Folder for Your Target

When you see a reference to a target folder, choose the folder that matches the EVM, DSK, or simulator you are using. If your target is not listed, choose the folder for the simulator that matches your DSP platform.

For example, if an instruction tells you to use the c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\target folder, you might use one of the following folders:

c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\dsk5510
c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\evm5509A
c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\sim5510
c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\h3omap1710
c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\h4omap2420
c:\BIOS-5-20\packages\ti\bios\examples\basic\clk\osk5912
and so on...

RTDX support is not available for certain DSP targets. As a result, DSP/BIOS real-time analysis is supported in stop-mode only on these targets. To see RTA data, choose Debug→Halt to pause the target. Analysis data can only be transferred from the target to the host PC only when you reach a breakpoint or halt the program.


LOG Module

The LOG module manages event logs. A program can write to a log explicitly with the LOG_printf API. In addition, certain DSP/BIOS events are automatically logged to the system log.


CLK Module

The CLK module provides methods for gathering timing information and for invoking functions periodically. The CLK module provides real-time clocks with functions to access low-resolution and high-resolution times. These times can be used to measure the passage of time in conjunction with statistics (STS) objects, as well as to add timestamp messages in event logs.


TSK Module

The TSK module manages task threads. Task threads run at lower priority than HWI and SWI threads. They can block while waiting for a resource to become available.


SWI Module

The SWI module manages software interrupt threads. These threads run at lower priority than HWI threads and higher priority than TSK threads. They cannot block while waiting for a resource. Instead, SWIs run to completion and can then be reposted by another thread when the resource again becomes available.


HWI Module

The HWI module manages hardware interrupt threads. These are functions that run at high-priority in response to an interrupt. They are also called ISRs.


GBL Module

The GBL module manages global settings used by other modules. For example, the board name and DSP speed. The GBL module does not manage any objects.


IDL Module

The IDL module manages idle function, which run when no other threads (HWIs, SWIs, and TSKs) are ready to run. Idle threads typically are used to communicate instrumentation information with the host PC and to perform other low-priority functions.


SEM Module

The SEM module manages semaphores. These are used for task synchronization and mutual exclusion. A task pends on a semaphore to reserve a resource; it posts a semaphore to release the resource. Semaphores can be counting semaphores or binary semaphores.


STS Module

The STS module manages statistics objects. These objects keep track of the count, total, maximum, and average for a particular statistic. DSP/BIOS maintains some statistics on its objects automatically. Your program can update other statistics explicitly.


TRC Module

The TRC module manages a set of trace control bits which control the real-time capture of program information through DSP/BIOS logs and statistics accumulators.


MEM Module

The MEM module provides a set of functions used to allocate, free, and manage storage from one or more disjointed segments of memory.


QUE Module

The QUE module manages queue objects that are used as queue data structures.


MSGQ Module

The MSGQ module allows for the structured sending and receiving of variable length messages. This module can be used for homogeneous or heterogeneous multi-processor messaging. The MSGQ module with a substantially similar API is implemented in DSP/BIOS Link for certain TI general-purpose processors (GPPs), particularly those used in OMAP devices.


POOL Module

The POOL module describes standard interface functions that allocators must provide. Allocator interface functions are called internally by the MSGQ module and not by user applications. A simple static allocator, called STATICPOOL, is provided with DSP/BIOS. Other allocators can be implemented by following the standard interface.


LCK Module

The LCK module manipulates lock objects. Each lock implicitly corresponds to a shared global resource, and is used to arbitrate access to this resource among several competing tasks.


MBX Module

The MBX module manipulates mailbox objects. Mailboxes can hold up to a specified number of messages. A program can pend on a message to reserve it and post a message to make it available again.


PRD Module

The PRD module manages objects that schedule periodic execution of program functions. The period can be driven by the CLK module or by calls to PRD_tick whenever a specific event occurs.


SYS Module

The SYS module provides general-purpose functions that provide basic system services, such as halting program execution and printing formatted text. In general, each SYS function is patterned after a similar function normally found in the standard C library.


Delivery format = HTMLHelp = “format“ = HTMLHelp or WinHelp

General Platform = 5000 = “platform” = 6000, 5000, 2000

Specific = 55 = “target”  = 62, 54, 55, 28

Version 5.31

version_dir = 5_31


About the DSP/BIOS Tutorial

DSP/BIOS

The DSP/BIOS tutorial introduces the basics of DSP/BIOS.

Prerequisites for the DSP/BIOS Tutorial

This tutorial is for use with DSP/BIOS version 5.31.

Before using the DSP/BIOS tutorial, you should have done the following:

•	Installed Code Composer Studio. Except where otherwise noted, this tutorial can be used with any supported board or a simulator. (This tutorial assumes you are using DSP/BIOS with Code Composer Studio v3.1 or higher. If you are using an earlier version, see the SetupGuide.html file provided in your DSP/BIOS installation.)

•	Learned how to use Code Composer Studio to create, build, run, and test a program. For example, you should be able to set breakpoints and step through a program. To learn the basics of using Code Composer Studio, see the section of this tutorial on the Code Composer Studio environment.

Using the DSP/BIOS Tutorial

If you are new to DSP/BIOS, perform the lessons in the order provided. This gives you a basic overview of using DSP/BIOS.

•	What is DSP/BIOS?

•	Lesson 1: Creating a Simple DSP/BIOS Application

•	Lesson 2: Scheduling Multiple Tasks

•	Lesson 3: Debugging Program Behavior

•	Lesson 4: Monitoring Interrupts and Latency

•	Learning More About DSP/BIOS

If you have used DSP/BIOS, but want to learn about a particular module, see the list of modules used in this tutorial.

Click the [image] button at the beginning of a lesson if you want to print all the topics in a lesson. The DSP/BIOS tutorial is not available as a PDF file and cannot be ordered as a printed document.


What is DSP/BIOS?

DSP/BIOS

DSP/BIOS is a scalable real-time kernel designed for DSP applications that require real-time scheduling and synchronization, host-to-target communication, along with real-time instrumentation. DSP/BIOS provides preemptive multi-threading, hardware abstraction, and real-time analysis.

DSP/BIOS is packaged as a set of modules that can be linked into an application; applications include only those functions of the DSP/BIOS that are referenced (directly or indirectly) by the application. In addition, you can optimize code size and speed by disabling DSP/BIOS features not used in a program.

You can use DSP/BIOS to instrument any application to be probed, traced, and monitored in real-time. Programs that take advantage of the multi-threading capabilities of DSP/BIOS are implicitly instrumented.

DSP/BIOS is included with Code Composer Studio, requires no runtime license fees, and is fully supported by Texas Instruments. DSP/BIOS is a key component of TI's eXpressDSP technology.

This tutorial is for use with DSP/BIOS version 5.31.

DSP/BIOS includes the following components:

•	DSP/BIOS Configuration Tool. This visual tool allows you to configure DSP/BIOS objects to be used by your program. For example, you can configure memory, thread priorities, and interrupt handlers. When you add objects or set properties in this tool, the corresponding Tconf commands are shown in the right pane. That is, it acts as a macro recorder for Tconf. Later, you can optionally use a text editor to modify the Tconf script generated by the DSP/BIOS Configuration Tool.

•	DSP/BIOS Tconf. This scripting language statically configures DSP/BIOS objects. It uses JavaScript with a specialized object model. Scripts written using Tconf generate files to be compiled as part of your project.

•	DSP/BIOS API. Your C and assembly language programs can call over 150 DSP/BIOS API functions from over 25 modules.

•	DSP/BIOS Real-time Analysis (RTA) Tools. These windows allow you to view program activity in real-time. For example, the Execution Graph shows a diagram of thread activity.


Learning More About DSP/BIOS

DSP/BIOS

You can learn more about DSP/BIOS from the following sources:

•	Setup Guide: See the SetupGuide.html file in your DSP/BIOS installation for details about integrating this version of DSP/BIOS with different versions of CCStudio.

•	DSP/BIOS Help: To open, choose Help→Contents and move to the “DSP/BIOS 5.31” book.

•	DSP/BIOS Manuals: Check the installation or the Texas Instuments web site for the appropriate version of the following manuals:

◊	DSP/BIOS User's Guide: SPRU423

◊	C5000 DSP/BIOS API Reference: SPRU404

◊	DSP/BIOS Textual Configuration User's Guide: SPRU007

•	Release Notes and Readme Files: See the release_notes.html file in the top-level DSP/BIOS folder for a list of new features and known issues for all the components of DSP/BIOS. See the readme files in various folders of the installation for details about a particular set of files.

•	DSP/BIOS Examples: See the examples installed with DSP/BIOS for sample code and configurations.

•	Application Notes: Check the Texas Instuments web site for all the application notes related to DSP/BIOS. The web site also provides feature lists, FAQs, training links, and technical support links.

•	Reference Frameworks: A number of frameworks are provided as a starting point for applications using DSP/BIOS. See the Texas Instuments web site for links to information about Reference Frameworks.

•	Driver Developer’s Kit: Download this kit (SPRC118) and the DDK User’s Guide (SPRU616) from the Texas Instuments web site.


Why Use DSP/BIOS Tconf?

DSP/BIOS

DSP/BIOS Tconf configures DSP/BIOS objects. The DSP/BIOS Configuration Tool acts as a graphical macro recorder for creating your initial Tconf configuration.

Since Tconf is a scripting language, you can modify scripts created by the DSP/BIOS Configuration Tool as needed. Some examples of ways you may want to modify Tconf scripts include:

•	Change the platform used in a configuration simply by changing the line that includes the platform file. Previously, creating a matching configuration for another platform was a tedious process.

•	Create multiple objects of a particular type within a loop. For example, you might want to test how many task threads can be used without impacting performance. By creating task objects in a loop controlled by a variable, you can easily modify the number of tasks configured.

•	Create configuration scripts that accept command line arguments to control how DSP/BIOS should be configured.

•	Document your configuration by adding comments to the script.

•	Generate additional files of any type needed in your development environment based on objects in the configuration.

In previous versions of Code Composer Studio, Tconf was an optional methodology for generating DSP/BIOS configurations. Tconf now takes on a more central role within DSP/BIOS. In DSP/BIOS version 5.31, the DSP/BIOS Configuration Tool now makes Tconf scripts visible. The CDB files formerly used by the DSP/BIOS Configuration Tool still exist, but they are no longer considered "source files." Instead, they are generated from Tconf scripts.


Modules Used in the DSP/BIOS Tutorial

DSP/BIOS

The lessons in this tutorial show how to use the following DSP/BIOS modules:

Lesson 1: CLK, GBL, LOG, TSK

Lesson 2: CLK, LOG, SEM, TSK

Lesson 3: CLK, LOG, PRD, STS, SWI, TRC

Lesson 4: HWI, LOG, MEM, QUE, TRC, TSK


Lesson 1: Creating a Simple DSP/BIOS Application

DSP/BIOS - L1

This lesson shows how to create a program that uses DSP/BIOS. The steps include creating a configuration, adding files to a project, and understanding DSP/BIOS API calls to a program.

The “clk” example used in this lesson is a simple program in which a task thread wakes up and prints a message after a defined period.

This lesson consists of the following steps:

Step 1: Creating the Project

Step 2: Creating a DSP/BIOS Configuration

Step 3: Extending the Configuration

Step 4: Saving the Configuration

Step 5: Examining the Clk Source Code

Step 6: Building the Project

Step 7: Testing the Program

Things to Try


Lesson 1, Step 1: Creating the Project
Step 1: Creating the Project

DSP/BIOS - L1

You begin this lesson by creating a project with Code Composer Studio and adding a C source code file to the project.

1.	Copy the c:\bios_5_31\packages\ti\bios\examples\basic\clk folder and all its subfolders to a working directory. (For example, create c:\biosprojects\clk.)

2.	Examine the files in the copied clk folder. You will notice that there are separate subfolders for a number of DSP platforms.

3.	If Code Composer Studio is not already running, start Code Composer Studio.

4.	Choose the Project→New menu item.  In the Project Creation dialog, fill in the fields as follows, and then click Finish.

◊	Project Name: myclk

◊	Location: Browse for the appropriate target subfolder of the clk folder you created. For example, you might use c:\biosprojects\clk\dsk5510 for a 'C55x target.

◊	Project Type: Executable (.out)

◊	Target: Select your DSP target platform.

5.	Choose the Project→Add Files to Project menu item. Move up one folder level (to clk) and choose the clk.c file. Then, click Open.

6.	Choose Project→Save to save your changes to the project. It is a good idea to save changes to a project before building or running a program.


Lesson 1, Step 2: Creating a Configuration
Step 2: Creating a DSP/BIOS Configuration

DSP/BIOS - L1

In order to use the DSP/BIOS API, a program must have a DSP/BIOS configuration. The configuration defines objects and object properties for use by the application.

In this step, you create a configuration for the program. The configuration defines LOG and TSK objects used by the program and sets the CLK tick rate.

1.	Choose File→New→DSP/BIOS Configuration from the CCStudio menus.

2.	In the New DSP/BIOS Configuration window, select the platform file for your DSP target.

3.	In the Enable DSP/BIOS Features area, make sure all the features are checked. A later lesson uses an example for which you can disable the TSK and Dynamic Memory features. Then click OK to open the Configuration window.

◊	The left pane of the window lists categories of modules. You can see a list of modules by clicking the + symbol next to the category. You can see a module's objects by clicking the + symbol to the left of a module manager.

◊	The middle pane shows properties of the selected manager or object.

◊	The right pane shows the Tconf code that corresponds to the current configuration. This area acts like a macro recorder. When you perform an action in the left pane, the corresponding Tconf commands are added to the right pane.

4.	Click the + sign next to the Instrumentation category to display its list of modules. These modules are used to gather run-time information about program execution.

5.	Right-click on the LOG – Event Log Manager and choose Insert LOG from the shortcut menu.

6.	In the dialog that asks for the name of the object, type trace as the name and click OK. Notice that the new LOG object is listed in the left pane and a Tconf statement that creates the object is added by the macro recorder in the right pane.

7.	Right-click on the trace LOG object. From the shortcut menu, select Properties. In the Properties dialog for this object, change the buflen property to 128 words. Click OK, and notice that a Tconf statement that sets the bufLen property of this object is added to the script on the right.

8.	For this exercise, we have enabled dynamic memory, and we therefore need to specify where to allocate space for the heap. To do this, begin by clicking the + sign next to the Memory Section Manager category to display all the memory sections. The list of memory sections depends on your target.

9.	Right-click on any memory section and select Properties. In the Properties dialog, check “create a heap in this memory”. Then click OK.

10.	Right-click on MEM – Memory Section Manager, and select Properties. In the Properties dialog, find the “Segment for DSP/BIOS Objects” and “Segment for malloc()/free()” drop-down menus. In both menus, select the memory section in which you created a heap in the previous step. Click OK to close the dialog.

11.	For C55x configurations, click the + sign next to the System category. Then, right-click on the Global Settings item and select Properties from the shortcut menu.

12.	Select LARGE as the memory model. DSP/BIOS provides libraries for the Large and Huge C55x memory models only.


Lesson 1, Step 3: Extending the Configuration
Step 3: Extending the Configuration

DSP/BIOS - L1

Tasks are a type of thread commonly used in DSP/BIOS. They can "block" to stop their execution until some event occurs or some resource is available. In this lesson, our task will go to sleep for a few seconds and then wake up to print a message.

In this step, you create a task object. You also set the clock rate that helps determine how long your task will “sleep.”

1.	Click the + sign next to the Scheduling category to display its list of modules. These modules are used to schedule the various types of threads DSP/BIOS provides.

2.	Right-click on the TSK – Task Manager and choose Insert TSK from the shortcut menu.

3.	In the dialog that asks for the name of the object, type myTask as the name and click OK.

4.	Right-click on the myTask object. From the shortcut menu, select Properties.

5.	In the Function tab, set the Task function to _taskFxn and the “Task function argument 0” to 10. (The underscore before the function name identifies this as a C function. If you omitted the underscore, Tconf would assume this was an assembly function.)

6.	Click OK. Notice that more statements are added to the Tconf script to set these properties.


Lesson 1, Step 4: Saving the Configuration
Step 4: Saving the Configuration

DSP/BIOS - L1

When you save a configuration with the DSP/BIOS Configuration Tool, the file contains the Tconf script shown in the right-pane. Later, when you build your project, the Tconf script is run to generate other files that are compiled and linked with your application.

1.	Choose File→Save. Move to your platform folder within the clk folder (for example,
c:\biosprojects\clk\dsk5510) and save this configuration with a filename of clk.tcf. Click Yes to overwrite the existing file.

When you save the file, the Tconf script is run to generate the following files:

◊	clkcfg.cmd. Linker command file.

◊	clkcfg.h. Includes DSP/BIOS module header files and declares external variables for objects created in the configuration file.

◊	clkcfg.s55. Assembly language source file for DSP/BIOS settings.

◊	clkcfg.h55. Assembly language header file included by clkcfg.s55.

◊	clkcfg_c.c. Code for various structures and settings.

◊	clk.cdb. In previous versions, this was the configuration source file. Now, it is a file generated by the Tconf script.

2.	Choose Project→Add Files to Project from the CCStudio menus. In the "Files of type" field, select Configuration File (*.tcf). Then choose the clk.tcf file you saved (in the target folder) and click Open.

3.	Expand the Project View to see that the clk.tcf file has been added to the DSP/BIOS Config item and the files clkcfg.s55 and clkcfg_c.c have been added to the Generated Files item. The generated files will be created when you build your project.


Lesson 1, Step 3: Examining the Clk Source Code
Step 5: Examining the Clk Source Code

DSP/BIOS - L1

Next, let’s examine the clk.c source code and see how it interacts with the configuration you just created. The steps that gather and calculate CPU rate information are used to convert the task function argument to an appropriate value for the task to sleep.

1.	Open the clk.c file from the Source Files folder in the Project View.

2.	The first few lines include the header files for DSP/BIOS modules used in this file. In DSP/BIOS programs, always include the std.h file first, then include the module header files. This example uses the LOG, CLK, TSK, and GBL modules, so those header files are included.

3.	The main() function simply prints a message to a log using the LOG_printf API. This API is very efficient. To reduce execution time, LOG_printf just passes raw data to the host PC; formatting is done on the PC.

4.	After the main() function is complete, the program doesn’t end. Instead, it turns control over to DSP/BIOS scheduling to run hardware interrupts (HWI), software interrupts (SWI), tasks (TSK), and idle functions (IDL) as needed.

5.	In this case, the program has one task thread to run, the myTask object you configured to run taskFxn() and to pass an argument of 10.

6.	Within the taskFxn, the Arg type argument is first cast as an Int using the ArgToInt() utility macro, which is provided with DSP/BIOS.

7.	Notice that DSP/BIOS programs use capitalized datatypes. These help make DSP/BIOS programs easily portable between platforms. They are defined in std.h and described in the DSP/BIOS API Reference.

8.	Next, the LOG_printf function prints the starting time in “ticks” reported by TSK_time. By default, the TSK ticks are driven by the CLK module, and have a rate equal to the low-resolution CLK rate.

9.	The GBL_getFrequency API returns freq as the current frequency of the DSP CPU as an integer number of KHz.

10.	The CLK_cpuCyclesPerLtime API returns cycles as the multiplier needed to convert low-resolution time to CPU cycles. This is the number of CPU cycles per low-resolution tick.

11.	The following computations calculate the time the task will sleep.

    milliSecsPerIntr = cycles/(Float)freq;

    timeout = value/milliSecsPerIntr;

◊	For example, on the ‘C5510 simulator, the default frequency of the CPU in KHz is 140000. The multiplier to convert to low-resolution time is also 140000. As a result, milliSecsPerIntr is 1 and the timeout is 10. This calculation would change if you changed the default Microseconds/Int property of the CLK manager.

12.	The TSK_sleep function causes the task to suspend its execution for 10 ticks. In a real-world program, lower-priority threads could run while this higher priority task was asleep. DSP/BIOS lets you configure the priorities of task threads (and software interrupt threads).

13.	When the task awakens, it prints another message that shows the current time according to TSK_time.

14.	After the task function runs once, it stops. Since there are no other threads to run, only the low-priority DSP/BIOS idle thread runs. The DSP/BIOS idle thread manages communication between the DSP and the PC. In real-world programs, task threads often loop continuously and sleep or block as needed to wait for other resources.


Lesson 1: clk.c Example Source Code
clk Example Source Code

DSP/BIOS - L1

The initial source code for the clk example looks like the following. In this example, a task goes to sleep for a number of ticks and prints the time after it wakes up. The DSP/BIOS API calls are shown in bold.

/*   clk.c  */

#include <std.h>

#include <log.h>

#include <clk.h>

#include <tsk.h>

#include <gbl.h>

#include "clkcfg.h"

/* ======== main ======== */

Void main()

{

    LOG_printf(&trace, "clk example started.\n");

}

Void taskFxn(Arg value_arg)

{

    Int value = ArgToInt(value_arg);

    LgUns freq;

    Float timeout, milliSecsPerIntr, cycles;

    LOG_printf(&trace, "The time in task is: %d ticks", (Int)TSK_time());

    /* get frequency of platform */

    freq = GBL_getFrequency();

    /* get cpu cycles per ltime interrupt */

    cycles = CLK_cpuCyclesPerLtime();

    /* computer milliseconds per ltime interrupt */

    milliSecsPerIntr = cycles/(Float)freq;

    /* compute timeout */

    timeout = value/milliSecsPerIntr;

    LOG_printf(&trace, "task going to sleep for %d ticks... ", (Int)timeout);

    TSK_sleep((LgUns)timeout);

    LOG_printf(&trace, "...awake! Time is: %d ticks", (Int)TSK_time());

}


Lesson 1, Step 6: Building the Project
Step 6: Building the Project

DSP/BIOS - L1

Now you are ready to build the project. Follow these steps:

1.	Choose Project→Save to save your changes to the project. It is a good idea to save changes to a project before building or running a program.

2.	Choose Project→Add Files to Project. Select Linker Command File (*.cmd) in the Files of type box. Select the clkcfg.cmd file from the folder that contains your clk.tcf file, and click Open.

3.	Choose Project→Build Options. Move to the Compiler tab and select the Preprocessor category.

4.	In the Include Search Path (-I) field, type the full path for the project directory that contains the clkcfg.h file. For example, you might use c:\biosprojects\clk\dsk5510. Press Tab to see this option added to the compiler command line.

◊	You need to add your project directory to the compiler’s include path because the files generated from the configuration are in a subdirectory below the clk.c file.

5.	Move to the Linker tab and select the Libraries category.

6.	In the Include Search Path (-I) field, type the full path for the directory that contains the rtdx.lib library. For example, you might use c:\CCStudion_v3.3\bios_5_30\packages\ti\rtdx\lib\c5500. Press Tab to see this option added to the compiler command line.

7.	Also in the Compiler tab, select the Advanced category. In the Memory Model field, choose Large (-ml). Click OK in the Build Options dialog.

8.	Choose Project→Build.


Lesson 1, Step 5: Testing the Program
Step 7: Testing the Program

DSP/BIOS - L1

Now you can test the program. Since the program simply writes to a DSP/BIOS log, testing is simple.

1.	In CCStudio, choose File→Load Program. In the Debug subfolder, select the program you just built, myclk.out, and click Open.

2.	Choose DSP/BIOS→Message Log. A Message Log area appears at the bottom of the Code Composer Studio window.

3.	If it is not already selected, select trace in the Log Name list.

4.	Choose Debug→Run or press F5. The messages appear in the Message Log area. The message may take a second to appear in the Message Log area because data stored in the log on the target is sent to the host PC only once per second by default.

◊	If the Message Log does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only. To see the data, choose Debug→Halt to pause the target. Stop-mode updates are required on platforms for which RTDX support is not available. Analysis data can be transferred from the target to the host PC only when you reach a breakpoint or halt the program.

5.	Choose Debug→Halt or press Shift F5 to stop the program. After the main function returns, your program is in the DSP/BIOS idle loop, waiting for an event to occur. See Things to Try to learn more about the idle loop.


Lesson 1: Things to Try
 Things to Try

DSP/BIOS - L1

To explore Code Composer Studio, try the following:

•	In the configuration, create a second task that runs _taskFxn. Give it a different argument value, such as 25. Rebuild and rerun the program to examine the results.

•	Now, add an infinite while loop within the taskFxn function. Put the beginning of the while loop just before the LOG_printf call that prints the “task going to sleep” message. Put the end of the while loop after the LOG_printf call that prints the “…awake” message. Rebuild and rerun the program to examine the results.

Many tasks in DSP programs run in continuous while loops such as this. You must be sure that the tasks either sleep for some period of time or wait for resource availability to allow other threads to run.

•	Load clk.out and put a breakpoint on a line that calls LOG_printf in main(). Use Debug→Breakpoints to add a breakpoint at IDL_F_loop. (Type IDL_F_loop in the Location box and click Add.)

Run the program. After the startup process and main are completed, a DSP/BIOS application drops into a background thread called the idle loop. This loop is managed by the IDL module and continues until you halt the program. The idle loop runs with interrupts enabled and can be preempted at any point by any hardware interrupt, software interrupt, or task triggered to handle the application’s real-time processing.


Lesson 2: Scheduling Multiple Tasks

DSP/BIOS - L2

This lesson shows how to use semaphores along with tasks to prevent multiple threads from accessing a shared resource. Such coordination is often important in DSP programs that need to share resources such as peripherals and memory.

The “mutex” example used in this lesson makes use of the SEM module of DSP/BIOS, in addition to the modules you used in the previous lesson. You’ll also learn how to set task priorities and more about the CLK module APIs.

This lesson consists of the following steps:

Step 1: Creating the Project

Step 2: Creating the Configuration

Step 3: Adding Tasks and a Semaphore

Step 4: Saving the Configuration

Step 5: Examining the Mutex Source Code

Step 6: Building the Project

Step 7: Testing the Program

Things to Try


Lesson 2, Step 1: Creating the Project
Step 1: Creating the Project

DSP/BIOS - L2

You begin this lesson by creating a project with Code Composer Studio and adding a C source code file to the project.

1.	Copy the c:\bios_5_31\packages\ti\bios\examples\basic\mutex folder to a working directory. (For example, c:\biosprojects\mutex.)

2.	If Code Composer Studio is not already running, start Code Composer Studio.

If another project was already open in Code Composer Studio, right-click on that project’s .pjt file in the Project View and choose Close from the shortcut menu.

3.	Choose the Project→New menu item.  In the Project Creation dialog, fill in the fields as follows, and then click Finish.

◊	Project Name: mymutex

◊	Location: Browse for the appropriate target subfolder of the mutex folder you created. For example, you might use c:\biosprojects\mutex\dsk5510 for a 'C55x target.

◊	Project Type: Executable (.out)

◊	Target: Select your DSP target platform.

4.	Choose the Project→Add Files to Project menu item. Move up one folder level (to mutex) and choose the mutex.c file. Then, click Open.

5.	Choose Project→Save to save your changes to the project. It is a good idea to save changes to a project before building or running a program.


Lesson 2, Step 2: Creating the Configuration
Step 2: Creating the Configuration

DSP/BIOS - L2

In this step, you create the initial configuration for the program and configure the logs it will use.

1.	Choose File→New→DSP/BIOS Configuration from the CCStudio menus.

2.	In the New DSP/BIOS Configuration window, select the platform file for your DSP target.

3.	In the Enable DSP/BIOS Features area, make sure all the features are checked.

4.	Click OK to open the Configuration window.

Setting Up Logs

5.	Insert a LOG object by right-clicking on the LOG – Event Log Manager (in the Instrumentation category) and choosing Insert LOG. Name the object trace and click OK.

6.	Right-click on the trace LOG object, and select Properties from the shortcut menu. Change the buflen to 256 words and click OK.

7.	Change the LOG_system object’s buflen to 512 words and click OK. This makes the system log long enough to observe interactions between the mutually excluded threads in this example.

Setting Up a Heap

8.	For this exercise, we have enabled dynamic memory, and we therefore need to specify where to allocate space for the heap. To do this, begin by clicking the + sign next to the Memory Section Manager category to display all the memory sections. The list of memory sections depends on your target.

9.	Right-click on any memory section and select Properties. In the Properties dialog, check “create a heap in this memory”. Then click OK.

10.	Right-click on MEM – Memory Section Manager, and select Properties. In the Properties dialog, find the “Segment for DSP/BIOS Objects” and “Segment for malloc()/free()” drop-down menus. In both menus, select the memory section in which you created a heap in the previous step. Click OK to close the dialog.

9.	For C55x configurations, click the + sign next to the System category. Then, right-click on the Global Settings item and select Properties from the shortcut menu.

10.	Select LARGE as the memory model. DSP/BIOS provides libraries for the Large and Huge C55x memory models only.


Lesson 2, Step 3: Adding Tasks and a Semaphore
Step 3: Adding Tasks and a Semaphore

DSP/BIOS - L2

In this step, you configure the tasks and semaphore to be used by the program.

Setting Up Tasks

1.	Insert a TSK object by right-clicking on the TSK – Task Manager (in the Scheduling category) and choosing Insert TSK. Name the object task1 and click OK.

2.	Open the Properties dialog for the task1 object. In the Function tab, set the Task function to _mutex1 and click OK. (Recall that the underscore identifies this as a C function.)

3.	Insert another TSK object and name the object task2.

4.	Open the Properties dialog for the task2 object. In the Function tab, set the Task function to _mutex2 and click OK.

5.	Select the TSK – Task Manager and notice that the middle pane shows a list of priorities and tasks within them.

◊	Priorities determine which tasks preempt others when they become ready to run. The highest priority a task can have is 15. The lowest priority level is 1, except for the “idle task.”

6.	Drag task2 to Priority 2. This gives task2 a higher priority than task1.

Setting Up a Semaphore

7.	Insert a SEM object by right-clicking on the SEM – Semaphore Manager (in the Synchronization category) and choosing Insert SEM. Name the object sem and click OK.

◊	A semaphore is used for signaling other tasks. DSP/BIOS tasks can “block” (stop executing) while waiting for a semaphore to become available.

8.	Open the Properties dialog for the sem object. Set the Initial semaphore count to 1 and click OK.

◊	A non-zero semaphore count indicates that the semaphore is available to be held. When a task “pends” on an available semaphore, it decrements the count. If the count becomes zero, other tasks have to wait until the semaphore is “posted”.


Lesson 2, Step 4: Saving the Configuration
Step 4: Saving the Configuration

DSP/BIOS - L2

In this step, you save the configuration for the program.

1.	Choose File→Save. Move to your platform folder within the mutex folder (for example,
c:\biosprojects\mutex\dsk5510) and save this configuration with a filename of mutex.tcf. Click Yes to overwrite the existing file.

2.	Choose Project→Add Files to Project from the CCStudio menus. In the "Files of type" field, select Configuration File (*.tcf). Then choose the mutex.tcf file you saved and click Open.

3.	Expand the Project View to see that the mutex.tcf file has been added to the DSP/BIOS Config item and the files mutexcfg.s55 and mutexcfg_c.c have been added to the Generated Files item. The generated files will be created when you build your project.


Lesson 2: Mutex Source Code

DSP/BIOS - L2

The source code used in this lesson is as follows.

/* ======== mutex.c ======== */

#include <std.h>

#include <clk.h>

#include <log.h>

#include <tsk.h>

#include <sem.h>

#include "mutexcfg.h"

Void    mutex1();

Void    mutex2();

LgUns   tsk1count = 0;

LgUns   tsk2count = 0;

LgUns   maincount = 0;

/* ======== main ======== */

Void main()

{

     /* Does nothing */

}

/* ======== mutex1 ======== */

Void mutex1()

{

    LgUns tempvar;

    LgUns time;

    for (;;) {

        LOG_printf(&trace, "\n  Running mutex1 function");

        if (SEM_count(&sem) == 0) {

            LOG_printf(&trace, "Sem blocked in mutex1");

        }

        SEM_pend(&sem, SYS_FOREVER);

        tempvar = maincount;

        time = CLK_getltime();

        /* wait for two system ticks to pass */

        while (CLK_getltime() <= (time + 1)) {

             ;

        }

        tsk1count++;

        maincount = ++tempvar;

        /* combine hex values to display LgUns */

        LOG_printf(&trace, "mutex1: loop 0x%04x%04x;",

                  (Int)(tsk1count >> 16), (Int)(tsk1count & 0xffff));

        LOG_printf(&trace, "              total count = 0x%04x%04x",

                  (Int)(maincount >> 16), (Int)(maincount & 0xffff));

        SEM_post(&sem);

        TSK_sleep(1);

    }

}

/* ======== mutex2 ======== */

Void mutex2()

{

    LgUns tempvar;

    for (;;) {

        LOG_printf(&trace, "\n  Running mutex2 function");

        if (SEM_count(&sem) == 0) {

            LOG_printf(&trace, "Sem blocked in mutex2");

        }

        SEM_pend(&sem, SYS_FOREVER);

        tempvar = maincount;

        tsk2count++;

        maincount = ++tempvar;

        /* combine hex values to display LgUns */

        LOG_printf(&trace, "mutex2: loop 0x%04x%04x;",

                  (Int)(tsk2count >> 16), (Int)(tsk2count & 0xffff));

        LOG_printf(&trace, "              total count = 0x%04x%04x",

                  (Int)(maincount >> 16), (Int)(maincount & 0xffff));

        SEM_post(&sem);

        TSK_sleep(1);

    }

}


Lesson 2, Step 5: Examining the Mutex Source Code
Step 5: Examining the Mutex Source Code

DSP/BIOS - L2

Next, let’s examine the mutex.c source code and see how it interacts with the configuration you just created.

1.	Open the mutex.c file from the Source Files folder in the Project View.

2.	The first few lines include the header files for DSP/BIOS modules used in this file. This example uses the LOG, CLK, TSK, and SEM modules, so those header files are included. The module-specific header files can be included in any order, so long as std.h is first.

3.	In the global variable definitions, maincount is a resource shared by both tasks.

4.	After the main() function (which does nothing) is complete, the program turns control over to DSP/BIOS scheduling.

5.	In this case, the program has two task threads to run: task1 and task2. Since task2 has a higher priority, it runs mutex2 first. The two mutex functions call SEM_pend and SEM_post to make use of the semaphore count owned by the semaphore.

◊	SEM_pend checks to see if the semaphore is available (count is non-zero). If so, it decrements the count and returns TRUE. Since the initial count was 1, the semaphore is now unavailable (count = 0).

◊	SEM_post increments the count. If the count was 0, the semaphore becomes available and any task waiting for the semaphore can run if it has a higher priority than the currently running task.

6.	Within an infinite loop, the mutex1 (lower-priority) function does the following:

◊	Prints a message if the semaphore count is zero.

◊	Pends on the semaphore. This prevents the mutex2 function from accessing the maincount variable during execution of the following protected block of code.

◊	Sets a local variable equal to maincount.

◊	Spins in a while loop for 2 system clock ticks, obtained through CLK_getltime. While mutex1 is in the loop, the mutex2 function wakes up and preempts mutex1 (because it has a higher priority). When mutex2 pends on the semaphore, it must block and wait for the semaphore to be posted.

◊	Increments the tsk1count and tempvar variables, and sets maincount to tempvar.

◊	Prints messages that show the tsk1count and maincount in hex form.

◊	Posts the semaphore to permit access to the shared data.

◊	Sleeps for 1 system clock tick. This is to allow the idle thread to run so that real-time analysis data can be transferred from the DSP to the host PC.

7.	Within an infinite loop, the mutex2 (higher-priority) function does the following:

◊	Prints a message if the semaphore count is zero.

◊	Pends on the semaphore. This prevents the mutex1 function from accessing the maincount variable during execution of the following protected block of code.

◊	Sets a local variable equal to maincount.

◊	Increments the tsk2count and tempvar variables, and sets maincount to tempvar.

◊	Prints messages that show the tsk2count and maincount in hex form.

◊	Posts the semaphore to permit access to the shared data.

◊	Sleeps for 1 system clock tick. This is to allow the idle thread to run so that real-time analysis data can be transferred from the DSP to the host PC.


Printing a LgUns with LOG_printf
Printing a LgUns Value with LOG_printf

The maincount variable uses the LgUns datatype so that it doesn’t overflow too quickly. However, LOG_printf does not handle LgUns values. It handles integers. To print a LgUns value, you can use bitwise shifting and ANDing to break a 32-bit number into its 16-bit counterparts as follows:

LOG_printf(&trace, "total count = 0x%04x%04x",

         (Int)(maincount >> 16),

         (Int)(maincount & 0xffff));

(Int)(maincount >> 16) is the upper 16 bits of maincount shifted into the 16-bits of an Int. (Int)(maincount & 0xffff) is the lower 16 bits of maincount.

The 0x%04x%04x format string used by LOG_printf causes a literal string of “0x” to precede the value to indicate that it is a hex value. Then, each %04x tells LOG_printf to display the value as hex, padding to 4 characters with leading zeros.


More About Semaphores

A semaphore is a data structure used for inter-task communication and to synchronize thread execution and access to shared data structures. Semaphores are often used to coordinate access to a shared resource among a set of competing tasks. They can also be used to exclude mutual execution of protected code sections.

SEM objects in DSP/BIOS are counting semaphores. Counting semaphores keep an internal count of the number of corresponding resources available.

To post a semaphore, a task calls SEM_post. This increments the count of the semaphore.

To pend on a semaphore, a task calls SEM_pend. If the semaphore count is greater than 0, SEM_pend simply decrements the count and returns. If the semaphore count is 0, that task waits for another thread to call SEM_post to post the semaphore. While the task is waiting, other threads can run. The timeout parameter to SEM_pend allows the task to wait until a timeout, to wait indefinitely (SYS_FOREVER), or to not wait at all (0).

In general, the semaphore count keeps track of the number of resources available. At the beginning of the semtest program, no resources are available because no writer task has put messages on the msgQueue. So, the initial semaphore count is zero. The reader task first pends on the semaphore. Since the initial count is 0, it blocks and allows a writer task to run. A writer task put a message on the msgQueue and posts the semaphore. This allows the reader task to resume running and to get the message from msgQueue.

If multiple tasks are pending on the same semaphore, the first task to pend on the semaphore is given access to the semaphore when another thread posts the semaphore. This task is not necessarily the highest-priority task.


Lesson 2, Step 6: Building the Project
Step 6: Building the Project

DSP/BIOS - L2

Now you are ready to build the project. Follow these steps:

1.	Choose Project→Save to save your changes to the project. It is a good idea to save changes to a project before building or running a program.

2.	Choose Project→Add Files to Project. Select Linker Command File (*.cmd) in the Files of type box. Select the mutexcfg.cmd file from the folder that contains your mutex.tcf file, and click Open.

3.	Choose Project→Build Options. Move to the Compiler tab and select the Preprocessor category.

4.	In the Include Search Path (-I) field, type the full path for the project directory that contains the mutexcfg.h file. For example, you might use c:\biosprojects\mutex\dsk5510. Press Tab to see this option added to the compiler command line.

◊	You need to add your project directory to the compiler’s include path because the files generated from the configuration are in a subdirectory below the mutex.c file.

5.	Move to the Linker tab and select the Libraries category.

6.	In the Include Search Path (-I) field, type the full path for the directory that contains the rtdx.lib library. For example, you might use c:\CCStudion_v3.3\bios_5_30\packages\ti\rtdx\lib\c5500. Press Tab to see this option added to the compiler command line.

7.	Also in the Compiler tab, select the Advanced category. In the Memory Model field, choose Large (-ml). Click OK in the Build Options dialog.

8.	Choose Project→Build.


Lesson 2, Step 7: Testing the Program
Step 7: Testing the Program

DSP/BIOS - L2

Now you can test the program. Since the program simply writes to a DSP/BIOS log, testing is simple.

1.	In CCStudio, choose File→Load Program. In the Debug subfolder, select the program you just built, mymutex.out, and click Open.

2.	Choose Debug→Go Main. The program runs to the main() function and then halts.

3.	Choose DSP/BIOS→Message Log. Make sure the trace log is selected in the Log Name box.

4.	Choose DSP/BIOS→Execution Graph.

5.	Choose DSP/BIOS→RTA Control Panel. You see a window with checkboxes for the instrumentation types provided by DSP/BIOS. By default, all of the instrumentation types are enabled.

6.	Remove the checkmark from the Enable SWI logging box. Verify that the Enable CLK logging, Enable TSK logging, and Global host enable boxes are checked. You may need to scroll down to see the Global host enable box.

◊	Omitting the Enable SWI logging checkmark simplifies the Execution Graph in this example. This is because portions of the SEM_post function are internally performed within the KNL_swi thread. However, such switching between task threads and the KNL_swi thread is not important to understanding mutual exclusion with semaphores.

7.	Arrange and resize the windows as needed to make them easy to see on your screen.

8.	Choose Debug→Run or click the [image] (Run) toolbar button.

9.	Compare the messages in the Message Log to your expectations. Look at the Execution Graph to examine the sequence in which the events occur.

◊	If the Message Log and Execution Graph do not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

10.	To better understand the sequence of events, choose DSP/BIOS→Kernel/Object View. Set breakpoints in both mutex functions on the lines that call SEM_pend, SEM_post, TSK_sleep, and the lines that set maincount = ++tempvar. Arrange the windows on your screen so that you can see the mutex.c file, trace log, and Kernel/Object View windows. Rerun the program. At each breakpoint, highlight TSK in the Kernel/Object View to see which task is running. Also highlight SEM and look at the semaphore count.

Because the mutex1 function waits in a while loop for two system clock ticks and the mutex2 function wakes up after one system clock tick, the mutex2 function blocks on the semaphore while waiting for the mutex1 function to post the semaphore. The reverse never happens since the protected processing block in the mutex2 function takes much less than one system clock tick to run. Also, because the mutex1 function waits for two system clock ticks, the mutex2 function runs twice as often as the mutex1 function.


Lesson 2: Understanding Execution Graph Results
Understanding Execution Graph Results

DSP/BIOS – L2

To get information on a specific box in the Execution Graph , choose DSP/BIOS→Message Log. Select Execution Graph Details in the Log Name list. You can double-click on any box in the Execution Graph to highlight the description of that event in the Execution Graph Details.

The Execution Graph for the mutex example looks like the following. The numbers on the graph refer to the descriptions below.

#	Actions Performed	Messages Printed (count values increase each cycle)
1	Since it has higher-priority, mutexTsk2 runs first. The initial semaphore count is 1, so mutexTsk2 continues executing when it pends on the semaphore. MutexTsk2 prints the lines shown to the right. It then posts the semaphore and sleeps for one system clock tick.	Running mutex2 function mutex2: loop 0x00000001;          total count = 0x00000001
2	While mutexTsk2 is sleeping, mutexTsk1 begins executing. The semaphore count is 1 again, so mutexTsk1 continues executing when it pends on the semaphore.	Running mutex1 function
3	While mutexTsk1 is spinning in a while loop for 2 system clock ticks, mutexTsk2 wakes up. Since it has higher priority, it preempts mutexTsk1. The semaphore count is now 0, so mutexTsk2 prints the lines shown to the right and blocks when it pends on the semaphore.	Running mutex2 function Sem blocked in mutex2
4	MutexTsk1 resumes running. When the second clock tick occurs, mutexTsk1 updates variables. MutexTsk1 prints the lines shown to the right and posts the semaphore.	mutex1: loop 0x00000001;          total count = 0x00000002
5	MutexTsk2 resumes running because it has higher priority and was waiting for the semaphore to be posted. MutexTsk2 prints the lines shown to the right. It then posts the semaphore and sleeps for one system clock tick.	mutex2: loop 0x00000002;          total count = 0x00000003
6	MutexTsk1 resumes running and sleeps for one system clock tick.
7	The program drops into the idle loop. When the two tasks wake up at the next system clock tick, this cycle repeats.


Lesson 2: Things to Try
Things to Try

DSP/BIOS - L2

To further explore DSP/BIOS, try the following:

•	In this example, you used a semaphore to mutually exclude tasks from running during critical processing. Can you use semaphores to mutually exclude a task thread and a hardware interrupt thread? What about a task and a software interrupt? Answer

•	Comment out all four calls to SEM_pend and SEM_post. How does the behavior of the program change? Look at the values displayed in the log for the loop counts and the total count. Answer

•	Uncomment the calls to SEM_pend and SEM_post, and comment out the two calls to TSK_sleep. How does the behavior of the program change? Run the program for a few seconds. Then halt the program to view the log information. Answer

•	Open the configuration and experiment with changes to the task priorities. Save the configuration and rebuild the program. How does this affect the order of the messages printed in the log? Answer

•	Why might you use a different initial semaphore count? Answer

•	How long does the mutex1 function actually wait in the following while loop? By how much can this length of time vary? Answer

    time = CLK_getltime();

    while (CLK_getltime() <= (time + 1)) {

      ;

    }

•	Choose DSP/BIOS→Kernel/Object View. Rerun the program and look at the SEM tab. What information does this tab provide? Answer

See Also

To learn more about semaphores, read Chapter 4 of the DSP/BIOS User’s Guide, which is provided in PDF format. For reference information, see the DSP/BIOS online help.


Using semaphores with other thread types

When you use semaphores for mutual exclusion, each thread must call both SEM_pend and SEM_post. However, SEM_pend cannot be called by an HWI or SWI function with a timeout other than zero. That is, HWIs and SWIs cannot yield or pend execution. Therefore, you cannot implement mutual exclusion between hardware or software interrupts and tasks with semaphores.

Instead, the higher priorities of hardware and software interrupts prevent tasks from interfering with critical processing. If a task needs to temporarily prevent hardware and software interrupts from executing, it should perform critical processing between calls to HWI_disable and HWI_enable or SWI_disable and SWI_enable.


What happens if you omit the semaphore?

If this program does not use semaphores, the maincount value is about half of the correct value. This is because the value is changed by the mutex2 function when it wakes up and the mutex1 function is running the while loop. However, the mutex1 function is still in the process of changing this value.


What happens if it doesn’t sleep?

Because the program never sleeps, the DSP/BIOS idle loop never has a chance to run and the trace log and Execution Graph are not updated until you halt the program. In addition, the mutex2 function has higher priority and never permits the mutex1 function to run.

If you comment out only the call to TSK_sleep in the mutex1 function, both functions run an equal number of times and the semaphore does block mutex2 correctly. The program still does not have time to run the DSP/BIOS idle loop, so log information is visible only after you halt the program.


What if you change the task priorities?

If both tasks have the same priority or if task1 has a higher priority than task2, both functions loop an equal number of times and neither task blocks on the semaphore.

If both tasks have the same priority, they yield to each other only when TSK_sleep is called, and neither function can preempt the other. If you allow task1 to yield by calling TSK_yield before the while loop, mutex2 does block on the semaphore but both functions loop an equal number of times.

If task1 has a higher priority than task2, then task2 cannot preempt task1 when it wakes up during the while loop in task1.


Why use a different initial semaphore count?

In general, the semaphore count keeps track of the number of resources available.

If the shared resource is not  available at the start of the program, the initial count should be zero. If a resource can be shared by a few tasks, the initial count could be 2 or 3, for example.

The SEM_pend and SEM_post functions treat semaphores as counting semaphores. If you are using a strictly binary semaphore, you can alternately use the SEM_pendBinary and SEM_postBinary functions.


How long does mutex1 spin in the loop?

The default properties of the CLK manager cause the system clock to tick every millisecond. Calls to CLK_getltime return a whole number of ticks. Therefore, the variation in when the call is actually performed can be up to 1 millisecond. As a result, this while loop waits for 1 to 2 milliseconds. However, since the TSK_sleep function is also based on the system clock, the while loop always waits long enough for the mutex2 function to wake up.

For example, if time = CLK_getltime(); occurs anywhere between clock ticks 3 and 4, the value of time is 3. When clock tick 5 occurs, CLK_getltime() <= (time + 1) and the while loop ends.


This tab provides information about semaphore objects in the program, including the current semaphore count and the number of tasks pending on the semaphore. If the number of tasks pending is non-zero, you may click on the number to see a list of tasks that are pending.

For more information about the fields on this tab, press F1 from within the tab.


Lesson 3: Debugging Program Behavior

DSP/BIOS - L3

This lesson shows how a DSP/BIOS program can use software interrupts (SWIs) to schedule and perform processing in response to hardware interrupts. You view performance information for debugging purposes. You also use more features of DSP/BIOS, including the Statistics View, software interrupts (SWI), and instrumentation via the STS (statistics) and TRC (trace) modules.

The example used in this lesson performs some simple signal processing. It multiplies each amplitude value in the input buffer by the gain and puts the resulting values into the output buffer. It also uses a load routine written in assembly to consume instruction cycles based on the processingLoad value passed to the routine.

This lesson consists of the following steps:

Step 1: Opening the Project

Step 2: Examining the Volume Source Code

Step 3: Understanding the Configuration

Step 4: Viewing Thread Execution with the Execution Graph

Step 5: Changing and Viewing the Load

Step 6: Analyzing Thread Statistics

Step 7: Adding Explicit STS Instrumentation

Step 8: Viewing Explicit Instrumentation

Step 9: Modifying the Configuration

Step 10: Editing the Source Code

Step 11: Changing the Processing Load

Step 12: Modifying Software Interrupt Prioritiesthe Configuration

Things to Try


Lesson 3, Step 1: Opening the Project
Step 1: Opening the Project

DSP/BIOS - L3

You begin this lesson by opening a project with Code Composer Studio and examining the source code files and libraries used in that project.

1.	Copy the c:\bios_5_31\packages\ti\bios\examples\advanced\volume folder to a working directory. (For example, create c:\biosprojects\volume.)

2.	If Code Composer Studio is not already running, start Code Composer Studio.

If another project is already open in Code Composer Studio, right-click on that project’s .pjt file in the Project View and choose Close from the shortcut menu.

3.	Choose Project→Open. Select the volume.pjt file in the subfolder for your DSP platform and click Open.

4.	Expand the Project View by clicking the + signs in the project tree. Notice that a DSP/BIOS configuration has already been created for this example.


Lesson 3, Step 2: Examining the Volume Source Code
Step 2: Examining the Volume Source Code

DSP/BIOS - L3

This example shows how an application using DSP/BIOS can schedule real-time behavior and manage data transfer triggered by external events. Rather than having the main() function loop forever handling tasks in a set sequence, data I/O in the real application is likely to happen as a result of periodic external interrupts. A simple way to simulate a periodic external interrupt in the example is to use the timer interrupt from the on-chip timer.

Let’s examine the volume.c source code and see how it works.

Declarations

1.	Open the volume.c file from the Source Files folder in the Project View. Notice the following portions of the code.

2.	The code uses #include to reference three DSP/BIOS header files: std.h, log.h, and swi.h. The std.h file must be included before other DSP/BIOS header files. You used the LOG module in the previous lessons.

3.	The code also #includes the volumecfg.h header file, which will be generated when you run the configuration file as part of the project build. This file contains external declarations for DSP/BIOS objects defined in the configuration file.

4.	In the declarations section, several constants are used: BUFSIZE, MINGAIN, and BASELOAD. These are set in volume.h.

main() Function

1.	The main function simply returns after calling LOG_printf to display a message. When the main function returns, the program drops into the DSP/BIOS idle loop. At this point, the DSP/BIOS scheduler manages thread execution.

processing() Function

1.	The processing function is called by a software interrupt called processing_SWI, which yields to all hardware interrupts. In previous lessons, you used DSP/BIOS “task” (TSK) threads. This lesson uses “software interrupt” (SWI) threads. The main differences between SWIs and TSKs is that SWIs cannot suspend their execution to wait for a resource. While they can be preempted by a higher-priority thread, they otherwise run to completion and are then reposted as needed. In contrast, TSKs typically run in a loop and suspend their execution as needed.

Instead of using a software interrupt (SWI), a hardware interrupt (HWI) thread could alternately perform the signal processing. However, signal processing may require a large number of cycles, possibly more than the time until the next interrupt. Such processing would prevent the interrupt from being handled. Since SWIs can be preempted by HWIs, they provide better ways to schedule all but the shortest interrupt processing.

2.	The processing function first transfers data bitwise from the input to the output buffer. During the transfer, it scales the input data using the gain.

3.	The processing function then calls the load assembly function with the processingLoad as an argument. This causes the SWI function to consume additional processing cycles.

dataIO() Function

1.	The dataIO function calls SWI_dec, which decrements a counter associated with a software interrupt object. When the counter reaches 0, the software interrupt schedules its function for execution and resets the counter.

The dataIO function simulates hardware-based data I/O. A typical program accumulates data in a buffer until it has enough data to process. In this example, the dataIO function is performed 10 times for each time the processing function is performed. The counter decremented by SWI_dec controls this.


Lesson 3: Volume Code Version 1

DSP/BIOS - L3

The source code for the Lesson 3 example is as follows.

/*      volume.c     */

#include <std.h>

#include <log.h>

#include <swi.h>

#include "volumecfg.h"

#include "volume.h"

/* Global declarations */

Int inp_buffer[BUFSIZE];          /* processing data buffers */

Int out_buffer[BUFSIZE];

Int gain = MINGAIN;               /* volume control variable */

Uns processingLoad = BASELOAD; /* processing load value */

/* Functions */

extern Void load(Uns loadValue);

Int processing(Int *input, Int *output);

Void dataIO(Void);

/* ======== main ======== */

Void main()

{

    LOG_printf(&trace,"volume example started\n");

    /* fall into DSP/BIOS idle loop */

    return;

}

/* ======== processing ======== *

 * FUNCTION:     Called from processing_SWI to apply signal

 *               processing transform to input signal.

 * PARAMETERS:   Address of input and output buffers.

 * RETURN VALUE: TRUE.    */

Int processing(Int *input, Int *output)

{

    Int size = BUFSIZE;

    while(size--){

        *output++ = *input++ * gain;

    }

    /* additional processing load */

    load(processingLoad);

    return(TRUE);

}

/* ======== dataIO ======== *

 * FUNCTION:     Called from timer ISR to fake a periodic

 *               hardware interrupt that reads in the input

 *               signal and outputs the processed signal.

 * PARAMETERS:   none

 * RETURN VALUE: none     */

Void dataIO()

{

    /* do data I/O */

    /* post processing_SWI software interrupt */

    SWI_dec(&processing_SWI);

}


Lesson 3, Step 3: Understanding the Configuration
Step 3: Understanding the Configuration

DSP/BIOS - L3

1.	In the Project View, double-click on the volume.tcf file (in the DSP/BIOS Config folder) to open it.

2.	Notice that the script in the right pane looks a bit different from the scripts you have created in the previous lessons. The TCF file includes other files using the utils.importFile() method.

◊	The utils.loadPlatform() method was also used in your earlier scripts to specify the platform. This is the standard way of performing the platform-specific portion of the DSP/BIOS configuration.

◊	For most platforms, there is a line that uses utils.importFile() to add more platform-specific configuration. These files perform optional configuration steps that can be handy. The files are stored in c:\bios_5_31\packages\ti\bios\examples\common, and you may want to reuse or modify the script for your platform in your own applications.

◊	The volume.tcf file imports the volume.tci configuration script. This TCI file contains the platform-independent portion of the configuration, and is reused by the TCF files for all platforms.

◊	The volume.tcf file ends by calling the prog.gen() method if no errors have occurred in processing the previous statements. This method generates files used to build the project that are based on the configuration. A TCF file must contain this method in order to be opened by the DSP/BIOS Configuration Tool. Any changes you make in the DSP/BIOS Configuration Tool are added right before the call to prog.gen() and any error checking associated with prog.gen().

The volume.tci configuration script imported by volume.tcf is shown here:

var trace = LOG.create("trace");

LOG_system.bufLen = 512;

var dataIO_CLK = CLK.create("dataIO_CLK");

dataIO_CLK["fxn"] = prog.extern("dataIO");

var processing_SWI = SWI.create("processing_SWI");

processing_SWI["fxn"] = prog.extern("processing");

processing_SWI.mailbox = 10;

processing_SWI["arg0"] = prog.extern("inp_buffer");

processing_SWI["arg1"] = prog.extern("out_buffer");

3.	Click the + sign next to the Instrumentation and Scheduling categories to display their lists of modules.

4.	Click the + signs next to the CLK, LOG, and SWI managers. Notice that even though you don’t see the script statements that create the trace, dataIO_CLK, and processing_SWI objects, the tool still knows about them because it has read in the volume.tci file.

Setting LOG Object Properties

5.	Highlight the LOG object called trace. You see the properties for this log in the right half of the window. The volume.c program calls LOG_printf to write volume example started to this log.

6.	Change the buflen property to 32 words and click OK.

Notice that the following line is added to the Tconf script in the right pane. This command configures the length of the LOG buffer.

    bios.LOG.instance("trace").buflen = 32;

◊	This line is added to the volume.tcf file after the line that imports volume.tci. If you wanted to move such platform-independent statements to volume.tci, you could do so using a text editor after you have saved and closed the configuration with the DSP/BIOS Configuration Tool.

Examining CLK Object Properties

7.	Highlight the CLK object called dataIO_CLK. Notice that the center pane shows that the function called when this CLK object is activated is _dataIO. This is the dataIO function in volume.c.

Note:	Underscores and C Function Names

Recall that the underscore before the function name identifies this as a C function. This C function name is prefixed by an underscore because running the configuration script generates assembly language files. The underscore prefix is the convention for accessing C functions from assembly.

8.	What causes the dataIO_CLK object to run its function? To find out, right-click on the CLK - Clock Manager. From the shortcut menu, select Properties. You see the Clock Manager Properties dialog.

Notice that the Microseconds/Int property is close to 1000 and the Enable CLK Manager property is enabled. This means the clock functions, including dataIO_CLK, run once a millisecond.

Notice that there is a check mark in the Enable CLK Manager box. When this box is checked, the timer interrupt drives the CLK manager. This box is gray and cannot be changed because there are CLK objects that need to use the CLK manager. If you wanted to disable the CLK manager, you would first need to delete all the CLK objects.

9.	Click Cancel to close the Clock Manager Properties dialog without making any changes.

Examining HWI and SWI Object Properties

10.	Expand the list of HWI objects and right-click on the HWI_TINT hardware interrupt object. From the shortcut menu, select Properties.

This object runs a function called CLK_F_isr when the on-chip timer causes an interrupt. The CLK object functions run from the context of this CLK_F_isr hardware interrupt function. Therefore, they run to completion without yielding and have higher priority than any software interrupts. (The CLK_F_isr saves the register context, so the CLK functions do not need to save and restore context as would be required normally within a hardware interrupt function.)

Click Cancel to close the Properties dialog without making any changes.

11.	Expand the list of SWI objects and open the Properties dialog for the processing_SWI software interrupt object.

◊	function. When this software interrupt is activated, the processing function runs. This function is shown in Step 2: Examining the Source Code.

◊	mailbox. The mailbox value can control when a software interrupt runs. Several API calls affect the value of the mailbox and can post the software interrupt depending on the resulting value.  When a software interrupt is posted, it runs when it is the highest priority software or hardware interrupt thread that has been posted.

◊	arg0, arg1. The inp_buffer and out_buffer addresses are passed to the processing function.

12.	Click Cancel to close the Properties dialog without making any changes.

13.	What causes this SWI object to run its function? In volume.c, the dataIO function calls SWI_dec, which decrements the mailbox value and posts the software interrupt if the new mailbox value is 0. Since the initial mailbox value is 10, this SWI object runs its function every tenth time the dataIO_CLK object runs the dataIO function, or once every 10 milliseconds.

Saving and Rebuilding

14.	Choose File→Close. You are asked whether you want to save your changes to volume.tcf. Click Yes.


Lesson 3, Step 4: Viewing Thread Execution with the Execution Graph
Step 4: Viewing Thread Execution with the Execution Graph

DSP/BIOS - L3

You could test this program by putting a Probe Point within the processing function and viewing graphs of input and output data. (To see how,  go to the Data Visualization lesson in the Code Composer Studio IDE module of the tutorial.)

However, in order to focus on the real-time aspects of DSP/BIOS, let us assume you have already tested the signal processing algorithm. At this stage of development, your focus should be on making sure threads can meet their real-time deadlines.

1.	Choose Project→Build or click the [image] (Incremental Build) toolbar button.

2.	Choose File→Load Program. In the Debug subfolder of the target-specific folder, select the program you just built, volume.out, and click Open.

3.	Choose Debug→Go Main. The program runs to the first statement in the main function.

4.	Choose DSP/BIOS→RTA Control Panel. You see a window with checkboxes for the instrumentation types provided by DSP/BIOS. By default, all of the instrumentation types are enabled.

5.	Verify that the Enable SWI logging and Enable CLK logging boxes are checked. Scroll down and verify that the Global host enable box is checked.

6.	Choose DSP/BIOS→Execution Graph. Resize the Execution Graph window as needed.

7.	Right-click on the RTA Control Panel and choose Property Page from the shortcut menu.

8.	Verify that the Refresh Rate for Message Log/Execution Graph is 1 second and click OK.

9.	Choose Debug→Run or click the [image] (Run) toolbar button.

The Execution Graph should look similar to this:

◊	If the Execution Graph does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

10.	The marks in the Time row show each time the Clock Manager ran the CLK functions. Count the marks between times the processing_SWI object was running. There should be 10 marks. This indicates that the processing_SWI object ran its function every tenth time the dataIO_CLK object ran its function. This is as expected because the mailbox value that is decremented by the dataIO function starts at 10.


Lesson 3, Step 5: Changing and Viewing the Load
Step 5: Changing and Viewing the Load

DSP/BIOS - L3

Using the Execution Graph, you saw that the program meets its real-time deadlines. However, the signal processing functions in a typical program must perform more complex and cycle-consuming work than multiplying a value and copying it to another buffer. You can simulate such complex threads by increasing the cycles consumed by the load function.

Note:	Load Values in This Example

The load values suggested in this tutorial are for a 'C549 running at 100 MHz. If your DSP is running at a different (usually higher) speed, you need to multiply the load values by (Your_MHz / 100). If you do not know what MHz you are running at, open volume.tcf and look at the Global Settings property called DSP Speed in MHz (CLKOUT). The Global Settings manager is in the System category of the configuration.

1.	Choose DSP/BIOS→CPU Load Graph. A blank CPU Load Graph appears.

◊	Certain simulators do not support use of the CPU Load Graph. If the bottom of the CPU Load Graph says CPU N/A, you may perform the following steps but will not see the CPU load values.

2.	Right-click on the RTA Control Panel and choose Property Page from the shortcut menu.

3.	Change the Refresh Rate for Statistics View/CPU Load Graph to 0.5 seconds and click OK.

◊	The Statistics View and the CPU Load transfer small amounts of data from the target to the host, so you can set these windows to update frequently without causing a large effect on program execution. The Message Log and Execution Graph transfer the number of words specified for the buflen property of the corresponding LOG object in the configuration. Since more data is transferred, you may want to make these windows update less frequently.

4.	Notice that the CPU load is currently very low.

◊	If the CPU Load Graph does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

5.	Choose File→Load GEL. In the Load GEL File dialog, select the volume.gel file in the volume folder and click Open.

6.	Choose GEL→Application Control→Load.

7.	Type 50 as the new load and click Execute. (Scale the value as described in the Note above if your MHz is higher than 100.) The CPU load increases to about 9%.

8.	Right-click on the Execution Graph and choose Clear from the shortcut menu. The program should still meet its real-time deadline—that is, there should still be 10 time marks between each execution of the processing_SWI function.

9.	Using the GEL control, change the load to 200 and click Execute.

10.	Right-click on the Execution Graph and choose Clear from the shortcut menu. One or two of the time marks occur while the processing_SWI function is executing. Does this mean the program is missing its real-time deadline? No, it shows that the program is functioning correctly. The hardware interrupt that runs the CLK object functions can interrupt the software interrupt processing, and the software interrupt still completes its work before it needs to run again.

11.	Using the GEL control, change the load to 900 and click Execute. The CPU load increases to about 95% and the Execution Graph and CPU load are updated less frequently.

12.	Right-click on the Execution Graph and choose Clear from the shortcut menu. The program still meets its real-time deadline because processing_SWI completes before 10 time marks have occurred.

13.	Using the GEL control, change the load to 1200 and click Execute. The CPU Load Graph and the Execution Graph stop updating as frequently and may not update at all. This is because Execution Graph data is sent to the host within an idle thread, which has the lowest execution priority within the program. Because the higher-priority threads are using all the processing time, there is not enough time for the host control updates to be performed. The program is now missing its real-time deadline.

14.	Choose Debug→Halt. This stops the program and updates the Execution Graph. You can see that processing_SWI runs far too long. You may see squares in the Assertions row. These squares indicate that the Code Composer Studio detected that the application missed a real-time deadline.

15.	Using the GEL control, change the load to 10 and click Execute.

16.	Choose Debug→Run. The CPU load and Execution Graph begin updating again.

Note:	Modifying the Load Using RTDX

Using the Load GEL control temporarily halts the target. If you are analyzing a real-time system and do not want to affect system performance, you may want to look into modifing the load using a Real-Time Data Exchange (RTDX) application.


Lesson 3, Step 6: Analyzing Thread Statistics
Step 6: Analyzing Thread Statistics

DSP/BIOS - L3

You can use other DSP/BIOS controls to examine the load on the DSP and the processing statistics for the processing_SWI object.

1.	Choose DSP/BIOS→Statistics View. A Statistics View area appears.

2.	In the RTA Control Panel, verify that the Enable SWI accumulators box is checked.

3.	If you have halted the program, click the [image] (Run) toolbar button.

◊	If the Statistics View does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

4.	You see statistics for various objects. Resize the window so that you can see all the columns.

5.	Notice the Max value for processing_SWI in the Statistics View. SWI statistics are shown in units of instruction cycles by default. The actual statistics are different from the figure shown below on different DSP platforms. However, notice that the Max value does not change while the load value is constant.

6.	Right-click on the Statistics View area and choose Property Page from the shortcut menu. In the Units tab, select Milliseconds as the unit for the processing_SWI object. In order to meet its real-time deadline, processing_SWI must complete in less than 10 milliseconds. (If processing_SWI is not shown in the Units tab, make sure processing_SWI is selected in the General tab.)

7.	Using the GEL control, increase the load and click Execute. Notice the change in the Max value for the number of instructions performed from the beginning to the end of processing_SWI increases.

8.	Experiment with different load values. If you decrease the load, right-click on the Statistics View and select Clear from the shortcut menu. This resets the fields to their lowest possible values, allowing you to see the current value in the Max field.

9.	Click the [image] (Halt) toolbar button and close all the DSP/BIOS and GEL controls you have opened.


Lesson 3, Step 7: Adding Explicit STS Instrumentation
Step 7: Adding Explicit STS Instrumentation

DSP/BIOS - L3

In the previous section, you used the Statistics View to see the number of instructions performed during a software interrupt’s execution. DSP/BIOS programs configured to enable real-time analysis support such statistics automatically. This is called implicit instrumentation. You can also use API calls to gather other statistics. This is called explicit instrumentation.

1.	In the Project View, double-click on the volume.tcf file (in the DSP/BIOS Config folder) to open it.

2.	Click the + sign next to the Instrumentation category to display its list of modules.

3.	Right-click on the STS manager and choose Insert STS from the shortcut menu. Type processingLoad_STS as the name for the STS object and click OK. The default properties for this object are all correct.

4.	Choose File→Close. You are asked whether you want to save your changes to volume.tcf. Click Yes.

5.	In the Project View, double-click on the volume.c program to open it for editing. Make the following changes to the program. See the modified source code listing to confirm that you have made the correct changes.

◊	Add the following lines below the line that includes the swi.h file:

#include <clk.h>

#include <sts.h>

#include <trc.h>

◊	Add the following lines within the processing function before the call to the load function:

/* enable instrumentation only if TRC_USER0 is set */

if (TRC_query(TRC_USER0) == 0) {

    STS_set(&processingLoad_STS, CLK_gethtime());

}

◊	Add the following lines within the processing function after the call to the load function:

if (TRC_query(TRC_USER0) == 0) {

    STS_delta(&processingLoad_STS, CLK_gethtime());

}

The calls to TRC_query with the TRC_USER0 constant cause the program to perform the STS_set and STS_delta calls only if you enable the USER0 trace in the RTA control panel (or programmatically by calling TRC_enable).

6.	Choose File→Save to save your changes to volume.c.


Lesson 3: Volume Code Version 2
Volume Code Version 2

DSP/BIOS – L3

After modifying the source code in Step 7, the source code for Lesson 3 should look like the following. Changes are shown in blue.

/*      volume.c     */

#include <std.h>

#include <log.h>

#include <swi.h>

#include <clk.h>

#include <sts.h>

#include <trc.h>

#include "volumecfg.h"

#include "volume.h"

/* Global declarations */

Int inp_buffer[BUFSIZE];          /* processing data buffers */

Int out_buffer[BUFSIZE];

Int gain = MINGAIN;               /* volume control variable */

Uns processingLoad = BASELOAD; /* processing load value */

/* Functions */

extern Void load(Uns loadValue);

Int processing(Int *input, Int *output);

Void dataIO(Void);

/* ======== main ======== */

Void main()

{

    LOG_printf(&trace,"volume example started\n");

    /* fall into DSP/BIOS idle loop */

    return;

}

/* ======== processing ======== *

 * FUNCTION:     Called from processing_SWI to apply signal

 *               processing transform to input signal.

 * PARAMETERS:   Address of input and output buffers.

 * RETURN VALUE: TRUE.    */

Int processing(Int *input, Int *output)

{

    Int size = BUFSIZE;

    while(size--){

        *output++ = *input++ * gain;

    }

    /* enable instrumentation only if TRC_USER0 is set */

    if (TRC_query(TRC_USER0) == 0) {

        STS_set(&processingLoad_STS, CLK_gethtime());

    }

    /* additional processing load */

    load(processingLoad);

    if (TRC_query(TRC_USER0) == 0) {

        STS_delta(&processingLoad_STS, CLK_gethtime());

    }

    return(TRUE);

}

/* ======== dataIO ======== *

 * FUNCTION:     Called from timer ISR to fake a periodic

 *               hardware interrupt that reads in the input

 *               signal and outputs the processed signal.

 * PARAMETERS:   none

 * RETURN VALUE: none     */

Void dataIO()

{

    /* do data I/O */

    /* post processing_SWI software interrupt */

    SWI_dec(&processing_SWI);

}


Lesson 3, Step 8: Viewing Explicit Instrumentation
Step 8: Viewing Explicit Instrumentation

DSP/BIOS - L3

To view information provided by the explicit instrumentation calls you added, you use the Statistics View.

Note:	DSPs Running at Speeds Other Than 100 MIPS

The values in this section are for a 'C549 running at 100 MHz. If your DSP is running at a different speed, multiply processingLoad_STS values by (Your_MHz / 100). To find your MHz value, look at the Global Settings property called DSP Speed in MHz (CLKOUT) in the configuration. The Global Settings manager is in the System category.

1.	Choose Project→Build or click the [image] (Incremental Build) toolbar button.

2.	Choose File→Load Program. In the Debug subfolder, select the program you just rebuilt, volume.out, and click Open.

3.	Choose DSP/BIOS→RTA Control Panel. Verify that the Enable SWI accumulators, Enable USER0 trace, and Global host enable boxes are checked. Enabling USER0 tracing causes the calls to TRC_query(TRC_USER0) to return 0.

4.	Choose DSP/BIOS→Statistics View.

5.	Right-click on the Statistics View area and choose Property Page from the shortcut menu. In the General tab, hold down the Ctrl key and deselect all objects except the processing_SWI and processingLoad_STS objects. Only the processing_SWI and processingLoad_STS should be highlighted when you are finished with this step.

6.	Go to the Units tab and select the processingLoad_STS object and type “inc” (for increment) as the unit label. Since the calls to STS_set and STS_delta use the CLK_gethtime function, the statistics are reported in timer increments.

7.	In the Units tab, select the processing_SWI object and select Instructions as the unit.

8.	Click OK. You see the statistics fields for the two objects you selected. Resize the window so that you can see all the columns.

9.	Choose Debug→Run or click the [image] (Run) toolbar button.

◊	If the Statistics View does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

10.	Multiply the Max value for processingLoad_STS by 2.

SWI statistics are measured in instructions. Because you used the CLK_gethtime function to benchmark the processing load, processingLoad_STS statistics are measured in on-chip timer counter increments. This counter is incremented at the following rate, where CLKOUT is the DSP clock speed in MIPS (see the Global Settings Property dialog) and TDDR is the value of the timer divide-down register (see the CLK Manager Property dialog):

CLKOUT / (TDDR + 1)

Therefore, to convert the processingLoad_STS units to instructions, multiply by TDDR + 1. For example, if the DSP runs at 100 MIPS and the TDDR is 1, the on-chip timer counter is incremented at a rate of 50 MHz. In this example, you would multiply the processingLoad_STS value by 2.

11.	Subtract the resulting processingLoad_STS Max value in instructions from the processing_SWI Max value. The result varies depending on your DSP platform and development target. These instructions are performed within the processing function, but not between the calls to STS_set and STS_delta, as shown below.

For example, suppose TDDR is 1, the load is 10, the processingLoad_STS Max is 5085, and the processing_SWI Max is 12962. To calculate the instruction cycles performed within the processing function but outside the calls to STS_set and STS_delta, the equation would be:

12962 - (5085 * 2) = 2792

12.	Choose GEL→Application Control→Load. (If you have closed and restarted Code Composer Studio, you must reload the GEL file.)

13.	Change the Load and click Execute.

14.	Notice that while both Max values increase, the difference between the two Max values (after you multiply the processingLoad_STS Max by TDDR + 1) stays the same.

15.	Remove the check mark from the enable USER0 trace box in the RTA Control Panel.

16.	Right-click on the Statistics View and choose Clear from the shortcut menu.

17.	Notice that no values are updated for processingLoad_STS. This is because disabling the USER0 trace causes the following statement in the program to be false:

if (TRC_query(TRC_USER0) == 0)

As a result, the calls to STS_set and STS_delta are not performed.


Lesson 3, Step 9: Modifying the Configuration
Step 9: Modifying the Configuration

DSP/BIOS - L3

Next, you can add a periodic function object (PRD) to the configuration file. PRD threads are a special case of SWI; they are posted at a periodic rate that may be controlled by the CLK module or by program activity such as data availability.

1.	In the Project View, double-click on the volume.tcf file to open it.

2.	Click the + sign next to the Scheduling category\ to display its list of modules.

3.	Right-click on the PRD manager and choose Insert PRD from the shortcut menu. Type loadchange_PRD as the name for this object, and click OK..

4.	Right-click on the loadchange_PRD object and choose Properties from the shortcut menu.

5.	Set the following properties for the loadchange_PRD object and click OK.

◊	Change the period to 2. By default, the PRD manager uses the CLK manager to drive PRD execution. The default properties for the CLK class make a clock interrupt trigger a PRD tick each millisecond. So, this PRD object runs its function every 2 milliseconds.

◊	Change the function to _loadchange. This PRD object executes the loadchange C function each time the period you chose elapses. (Recall that you need to use an underscore prefix for C functions in the configuration file.) You look at this function in the next section.

6.	Notice that a SWI object called PRD_swi was created automatically when you added the loadchange_PRD object. This software interrupt executes periodic functions in real time. Therefore, all PRD functions are called within the context of a software interrupt and can yield to hardware interrupts. In contrast, CLK functions run in the context of a hardware interrupt. (The KNL_swi object runs a function that runs the TSK manager.)

7.	Click the + sign next to the CLK manager. Notice that the CLK object called PRD_clock runs a function called PRD_F_tick. This function causes the DSP/BIOS system clock to tick (by calling the PRD_tick API function) and the PRD_swi software interrupt to be posted if any PRD functions need to run. PRD_swi runs the functions for all the PRD objects whose period has elapsed.

8.	Right click on the PRD manager, and choose Properties from the shortcut menu. The PRD manager has a property called Use CLK Manager to drive PRD. Make sure this box is checked for this example.

In your own projects, if you remove the check mark from this box, the PRD_clock object would be deleted automatically. Your program could then call PRD_tick from some other event, such as a hardware interrupt, to drive periodic functions.

9.	Recall that the processing_SWI object has a mailbox value of 10. That mailbox value is decremented by the dataIO_CLK object, which runs the dataIO C function every millisecond. As a result, the processing_SWI runs its function every 10 milliseconds. In contrast, the loadchange_PRD object should run its function every 2 milliseconds.

10.	Choose File→Close. You are asked whether you want to save your changes to volume.tcf. Click Yes.


Lesson 3, Step 10: Editing the Source Code
Step 10: Editing the Source Code

DSP/BIOS - L3

Double-click on the volume.c file in the Project View to see the source code in the Code Composer Studio window.

Make the following edits to this file. See the modified source code for a complete code listing.

1.	Add the following global declaration after the declaration of processingLoad:

Uns newLoad = BASELOAD;

2.	Add the following to the function declarations:

Void loadchange(Void);

3.	Add the following function to be called by the PRD object you created. This is where the processing load is controlled.

/* ======== loadchange ======== */

Void loadchange()

{

    if (newLoad != processingLoad) {

        if ((newLoad <= MINCONTROL) || (newLoad > MAXCONTROL)) {

            LOG_printf(&trace, "Load value out of range");

        }

        else {

            processingLoad = newLoad;

            LOG_printf(&trace, "New load value = %d", processingLoad);

        }

    }

}

This function checks for a change to the newLoad variable which you will set using the CCStudion Watch Window. If you have changed the value, it modifies the processingLoad value.


Lesson 3: Volume Code Version 3
Lesson 3: Volume Code Version 1

DSP/BIOS - L3

The modified source code for the Lesson 3 example is as follows. Changes are shown in blue.

/*      volume.c     */

#include <std.h>

#include <log.h>

#include <swi.h>

#include <clk.h>

#include <sts.h>

#include <trc.h>

#include "volumecfg.h"

#include "volume.h"

/* Global declarations */

Int inp_buffer[BUFSIZE];       /* processing data buffers */

Int out_buffer[BUFSIZE];

Int gain = MINGAIN;             /* volume control variable */

Uns processingLoad = BASELOAD;  /* processing routine load value */

Uns newLoad = BASELOAD;         /* new load value */

/* Functions */

extern Void load(Uns loadValue);

Int processing(Int *input, Int *output);

Void dataIO(Void);

Void loadchange(Void);

/* ======== main ======== */

Void main()

{

    LOG_printf(&trace,"volume example started\n");

    /* fall into DSP/BIOS idle loop */

    return;

}

/* ======== processing ======== */

Int processing(Int *input, Int *output)

{

    Int size = BUFSIZE;

    while(size--){

        *output++ = *input++ * gain;

    }

    /* enable performance instrumentation only if TRC_USER0 is set */

    if (TRC_query(TRC_USER0) == 0) {

        STS_set(&processingLoad_STS, CLK_gethtime());

    }

    /* additional processing load */

    load(processingLoad);

    if (TRC_query(TRC_USER0) == 0) {

        STS_delta(&processingLoad_STS, CLK_gethtime());

    }

    return(TRUE);

}

/* ======== dataIO ======== */

Void dataIO()

{

    /* do data I/O */

    /* post processing_SWI software interrupt */

    SWI_dec(&processing_SWI);

}

/*  ======== loadchange ======== */

Void loadchange()

{

    if (newLoad != processingLoad) {

        if ((newLoad <= MINCONTROL) || (newLoad > MAXCONTROL)) {

            LOG_printf(&trace, "Load value out of range");

        }

        else {

            processingLoad = newLoad;

            LOG_printf(&trace, "New load value = %d", processingLoad);

        }

    }

}


Lesson 3, Step 11: Changing the Processing Load
Step 11: Changing the Processing Load

DSP/BIOS - L3

You could test this program by putting a Probe Point within the processing function and viewing graphs of input and output data. (To see how,  go to the Data Visualization lesson in the Code Composer Studio IDE module of the tutorial.)

However, in order to focus on the real-time aspects of DSP/BIOS, let us assume that you have already tested the signal processing algorithm. At this stage of development, your focus should be on making sure threads can meet their real-time deadlines.

In this example, we’ll use the CCStudio Watch Window to set the newLoad value. Since the Watch Window halts the target, it isn’t an ideal solution. If you need to modify your own application’s values without halting the target, you can use RTDX.

1.	Choose Project→Build.

2.	Choose File→Load Program. In the Debug subfolder, select the program you just rebuilt, volume.out, and click Open.

3.	Choose DSP/BIOS→RTA Control Panel.

◊	Verify that the Enable SWI logging, Enable PRD logging, Enable CLK logging, Enable SWI accumulators, and Enable PRD accumulators boxes are checked. Scroll down and verify that the Global host enable box is checked.

4.	Choose DSP/BIOS→Execution Graph. Resize the Execution Graph window as needed.

5.	Choose DSP/BIOS→Statistics View.

6.	Right-click on the Statistics View area and choose Property Page from the shortcut menu. In the General tab, highlight the objects shown here.

7.	In the Units tab of the Statistics View Properties window, select the loadchange_PRD object and type “ticks” as the unit label. Click OK.

8.	Resize the Statistics View window and its columns so that you can see the Count, Max, and Average columns.

9.	Right-click on the RTA Control Panel and choose Property Page from the shortcut menu.

10.	Set the Refresh Rate for Message Log/Execution Graph to 1 second and the Refresh Rate for Statistics View/CPU Load Graph to 0.5 seconds. Then click OK.

11.	Choose View→Watch Window.

12.	In the Watch 1 tab of the Watch Window, click in the left column and type newLoad at the insertion point. Then press Enter.

13.	Choose Debug→Run or click the [image] (Run) toolbar button.

Notice that processing_SWI occurs once every 10 time ticks (and PRD ticks). PRD_swi runs once every 2 PRD ticks. The loadchange_PRD runs within the context of PRD_swi. These are the expected execution frequencies.

PRD statistics are measured in PRD ticks. SWI statistics are measured in instruction cycles. The Max field for loadchange_PRD shows that there is less than a full PRD tick between the time this function needs to start running and its completion. (The statistics for the other objects vary depending on your DSP platform.)

14.	Click in the Value column of the Watch Window and gradually increase the processing load.

15.	Repeat the previous step until you see the Max and Average values for loadchange_PRD increase and red squares appear in the Assertions row of the Execution Graph. Assertions indicate that a thread is not meeting its real-time deadline. (Again, the actual statistics vary depending on your DSP platform.)

◊	If the Statistics View does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

What is happening? The Max value for loadchange_PRD increases when you increase the load beyond a certain point. With the increased load, the processing_SWI takes so long to run that the loadchange_PRD cannot begin running until long past its real-time deadline.

When you increase the load so much that the low-priority idle loop is no longer executed, the host stops receiving real-time analysis data and the DSP/BIOS analysis tools stop updating. This is called “starving the idle loop.” Halting the target updates the analysis tools with the queued data.


Lesson 3, Step 12: Modifying Software Interrupt Priorities
Step 12: Modifying Software Interrupt Priorities

DSP/BIOS - L3

To understand why the program is not meeting its real-time deadline, you need to examine the priorities of the software interrupt threads.

1.	Select Debug→Halt to halt the target.

2.	In the Project View, double-click on the volume.tcf file to open it.

3.	Click the + sign next to the Scheduling category to display its list of modules.

4.	Highlight the SWI manager. Notice the SWI object priorities shown in the center pane of the window. (The KNL_swi object runs a function that runs the TSK manager. This object must always have the lowest SWI priority. Tasks are not used in this lesson.)

Because the PRD_swi and processing_SWI objects both have the same priority level, the PRD_swi cannot preempt the processing_SWI while it is running.

The processing_SWI needs to run once every 10 milliseconds and PRD_swi needs to run every 2 milliseconds. When the load is high, processing_SWI takes longer than 2 milliseconds to run and prevents PRD_swi from meeting its real-time deadline.

5.	To correct this problem, use your mouse to select and drag PRD_swi to a higher priority level, such as Priority 2.

6.	Select File→Close and click Yes to save your changes to volume.tcf.

7.	Choose Project→Build or click the [image] (Incremental Build) toolbar button.

8.	Select File→Reload Program.

9.	Select Debug→Run to run the example again.

10.	Notice that you can now increase the load without causing PRD_swi to miss its real-time deadline.

Note:	Starving Idle Loop

It is still possible to starve the idle loop by increasing the processing load to its maximum value. When you increase the load so much that the CPU no longer has time to run the low-priority idle loop, the host stops receiving real-time analysis data and the DSP/BIOS analysis tools stop updating.


Lesson 3: Things to Try
Things to Try

DSP/BIOS - L3

To further explore DSP/BIOS, try the following:

•	Change the Host Operation property of the processingLoad_STS object in the configuration to A * x and the A property to 2. This Host Operation multiplies the statistics by 2. Rebuild the program and notice how the values in the Statistics View change.

•	Modify volume.c by using the CLK_getltime function instead of the CLK_gethtime function. Rebuild the program and notice how the values in the Statistics View change. The CLK_getltime function gets a low-resolution time that corresponds to the timer marks you saw in the Execution Graph. You must increase the load significantly to change the Statistics View values when using CLK_getltime.

•	Use a text editor to open the volume.tcf file and the volume.tci file. Move the statements added to volume.tci by the DSP/BIOS Configuration Tool to the end of volume.tci. Experiment with editing some Tconf statements and opening the TCF file in the DSP/BIOS Configuration Tool to see the effects of your changes.

•	Add the following lines to volume.tci. Then rebuild and run the application. This disables dynamic memory allocation and the TSK managers. The resulting executable has a smaller code footprint on the DSP.

    bios.disableMemoryHeaps(prog);

    bios.disableTskManager(prog);


Lesson 4: Monitoring Interrupts

DSP/BIOS – L4

This example shows how to measure interrupt latency, which is the time between the triggering of a hardware interrupt and the time the first instruction in the hardware interrupt function is executed. The maximum interrupt latency can be an important consideration in designing your application.

The interrupt used in this example is the timer interrupt, which is used by DSP/BIOS to drive the CLK module times. DSP/BIOS provides HWI objects for each on-chip interrupt. By configuring the monitor properties of the HWI object used in this example, you can examine latency in real-time. The interrupt occurs while a simple task function (TSK) or the background thread (IDL) is running.

This lesson consists of the following steps:

Step 1: Opening and Examining the Project

Step 2: Examining the Configuration

Step 3: Examining the Latency Source Code

Step 4: Testing the Program

Step 5: Modifying the Program

Things to Try


Lesson 4, Step 1: Opening and Examining the Project
Step 1: Opening and Examining the Project

DSP/BIOS – L4

You begin this lesson by opening the latency project with Code Composer Studio and examining the source code files used in the project.

1.	Copy the c:\bios_5_31\packages\ti\bios\examples\advanced\latency folder to a working directory. (For example, create c:\biosprojects\latency.)

2.	If Code Composer Studio is not already running, start Code Composer Studio.

If another project was already open in Code Composer Studio, right-click on that project’s .pjt file in the Project View and choose Close from the shortcut menu.

3.	Choose Project→Open. Select the latency.pjt file the subfolder for your DSP platform and click Open.

4.	Expand the Project View by clicking the + signs in the project tree. The files used in this project include:

◊	latency.tcf. This is the Tconf file containing the DSP/BIOS configuration script. It imports the latency.tci script, along with several platform-specific scripts.

◊	latency.c. This is the source code for the main program. You examine the source code in the next step.

◊	latencycfg.s55. This assembly file is created when you run the Tconf script and contains DSP/BIOS configuration settings.

◊	latencycfg_c.c. This file is created when you run the Tconf script.

◊	latencycfg.h55. This header file is created when you run the Tconf script and is included by volumecfg.s55.

◊	latencycfg.cmd. This linker command file is created when you run the Tconf script.


Lesson 4, Step 2: Examining the Configuration
Step 2: Examining the Configuration

DSP/BIOS – L4

We’ll begin by examining the objects that already exist in the configuration.

1.	In the Project View, double-click on the latency.tcf file (in the DSP/BIOS Config folder) to open it.

2.	Again, notice that the script in the right pane includes a number of other files. For example:

utils.loadPlatform("ti.platforms.dsk5510");

utils.importFile("dsk5510_common.tci");

utils.importFile("latency.tci");

utils.importFile("latency_dsk5510_custom.tci");

◊	The utils.loadPlatform() method loads the basic platform-specific configuration.

◊	The dsk5510_common.tci file contains platform-specific configuration used by all the examples.

◊	The latency.tci file contains platform-independent configuration. In this case, it configures the CLK module, LOG objects, and TSK and QUE objects used for the task that the HWI will need to interrupt.

◊	The latency_dsk5510_custom.tci file contains platform-specific configuration statements for the latency example only. In this case, it configures the HWI interrupt used by the timer, which is different on different platforms.

3.	Click the + sign next to the Scheduling category to display its list of modules.

4.	Open the Properties dialog for the CLK – Clock Manager, and note which interrupt is shown as the CPU Interrupt for the clock manager. (This field is grayed out because you cannot change it here.) Then, click Cancel.

5.	Click the + sign to open the list of HWI objects. Then, right-click on the HWI object that matches the CPU Interrupt for the clock manager and choose Properties.

6.	Notice that the monitor property is set to Data Value. The addr property is set to the address of the timer counter register. In addition, the type and operation properties are set to values appropriate for the values your platform stores in its timer counter register.

7.	Click Cancel when you have finished examining the properties of this HWI object.

8.	Click the + sign to open the list of TSK objects, and select the “task” object. Notice that it runs the _taskFxn function (which is taskFxn without the underscore in the C code). Also notice its argument, which tells the function how many times to perform a loop.

◊	The other TSK object, TSK_idle, runs the idle loop, which manages communication between the target DSP and the host PC. This happens at the lowest priority. In this example, the idle loop can only run when the “task” task is sleeping and the hardware interrupt is not running.

9.	Click the + sign next to the Synchronization category. Then,  open the list of QUE objects. You’ll see an object called “dummyQue”. When we examine the source code for the task function, you’ll see how this QUE object is used.

10.	If you look under the Instrumentation category, you’ll see the same LOG_system and trace logs that were used in the previous lessons.

11.	Choose File→Close. You are asked whether you want to save your changes to latency.tcf. Click No.


Lesson 4, Step 3: Examining the Latency Source Code
Step 3: Examining the Latency Source Code

DSP/BIOS – L4

This example shows how an application using DSP/BIOS can monitor hardware interrupts. Let’s examine the latency.c source code and see how it works.

Declarations

1.	Open the latency.c file from the Source Files folder in the Project View. Notice the following portions of the code.

2.	The code uses #include to reference a number of DSP/BIOS header files: std.h, que.h, log.h, mem.h, trc.h, and tsk.h.

3.	The code also #includes the volumecfg.h header file, which will be generated when you run the configuration file as part of the project build. This file contains external declarations for DSP/BIOS objects defined in the configuration file.

4.	In the declarations section, several constants are set. SEG0 is a heap identifier set in the configuration to reference a heap from which dynamic memory can be allocated.

main() Function

5.	The main function prints a message to the trace log. It also enables several types of tracing using the TRC_enable API. These trace constants correspond to “Enable HWI accumulators”, “Global target enable”, and “Global host enable” in the RTA Control Panel. When main() is complete, the program drops into the DSP/BIOS idle loop. At this point, the DSP/BIOS scheduler manages thread execution.

taskFxn() Function

6.	The taskFxn function is made ready to run when the program hands control to the DSP/BIOS scheduler. Task functions run once, but typically they contain a loop that runs forever or until the application mode changes. This function contains a for (;;) loop to accomplish that.

7.	The MEM_alloc function dynamically allocates memory for an element to be placed on the QUE data structure. It allocates the memory from the SEG0 heap, and the size is determined by the QUE_Elem datatype, which is defined in que.h. (The MEM_free function should also be performed if this memory were no longer needed at a later point in your program’s execution.)

8.	Within a for loop, a second for statement loops for the number of times passed to this function as the argument in the configuration. By default, this is 1000.

9.	For each loop, the queue element is put on the queue and removed from the queue. Since this is a simplified example, nothing is done with the data. QUE_get and QUE_put run atomically, which means they are never interrupted.

10.	After the loop has run 1000 times, the task sleeps for 2 clock intervals (typically 2 milliseconds). The task then returns to the beginning of the outer for loop.


Lesson 4: Latency Source Code
Latency Source Code

DSP/BIOS – L4

The source code for the latency example is as follows.

/*      latency.c     */

#include <std.h>

#include <que.h>

#include <log.h>

#include <mem.h>

#include <trc.h>

#include <tsk.h>

#include "latencycfg.h"

extern Int SEG0;

#define LOOPS 10000

#define SLEEP 2

/*  ======== main ======== */

Void main(Arg Loops_arg)

{

    LOG_printf(&trace, "latency example started.\n");

    TRC_enable(TRC_STSHWI | TRC_GBLTARG | TRC_GBLHOST);

}

/*  ======== taskFxn ===== */

Void taskFxn(Arg loops_arg)

{

    Void *elem;

    Int i;

    Int loops = ArgToInt (loops_arg);

    elem = (Void *)MEM_alloc(SEG0, sizeof(QUE_Elem), 0);

    for ( ; ; ) {

        /* Do some atomic operations for a while. */

        for (i = 0; i < loops; i++) {

            QUE_put(&dummyQue, elem);

            elem = QUE_get(&dummyQue);

        }

        TSK_sleep(SLEEP);

    }

}


Lesson 4, Step 4: Testing the Program
Step 4: Testing the Program

DSP/BIOS – L4

Now we are ready to test the program and find the hardware latency value.

1.	Choose Project→Build or click the [image] (Incremental Build) toolbar button.

2.	Choose File→Load Program. In the Debug subfolder of the target-specific folder, select the program you just built, latency.out, and click Open.

3.	Choose Debug→Go Main. The program runs to the first statement in the main function.

4.	Choose DSP/BIOS→Statistics View. You see an area with statistics for the task and idle functions and for an STS (statistics) object that is automatically created when the a HWI is configured for monitoring. This automatically created STS object is called HWI_<int>_STS, where <int> is the CPU interrupt used by the CLK module.

5.	Choose Debug→Run. The program runs to the first statement in the main function.

◊	If the Statistics View does not update automatically while the program is running, your platform likely supports DSP/BIOS real-time analysis in stop-mode only because RTDX support is not available. To see the data, choose Debug→Halt to pause the target.

6.	Notice the Max and Average values for the STS that monitors the interrupt. These values vary by platform, but are typically quite small.

7.	Choose Debug→Halt. The program runs to the first statement in the main function.


Lesson 4, Step 5: Modifying the Program
Step 5: Modifying the Program

DSP/BIOS – L4

You’ve found that the latency for both the IDL loop and simple DSP/BIOS APIs in task threads is small. What about software interrupt (SWI) threads? To check the latency of a SWI thread, we can add a SWI and check to see if the maximum latency changes.

1.	In the Project View, double-click on the latency.tcf file (in the DSP/BIOS Config folder) to open it.

2.	Click the + sign to open the Scheduling category, then open the list of SWI objects.

3.	Right-click on the SWI – Software Interrupt Manager and choose Insert SWI.

4.	Name the object “SWItest”.

5.	Open the properties dialog for SWItest and set the following properties. Then click OK.

◊	function: _swiFxn

◊	mailbox: 2

6.	Choose File→Close. You are asked whether you want to save your changes to latency.tcf. Click Yes.

7.	Open the latency.c file from the Source Files folder in the Project View.

8.	Make the additions to the source code shown in blue below:

◊	Add swi.h to the list of include files:

#include <swi.h>

◊	Add the following function at the end of the file. This is the function that will be called by the SWI object.

Int swiFxn(Void)

{

    Int i;

    for (i = 0; i < LOOPS; i++) {

        QUE_put(&dummyQue, elem);

        elem = QUE_get(&dummyQue);

    }

    return(TRUE);

}

◊	Add the following SWI_dec call just before the line in taskFxn that calls TSK_sleep. This causes the SWI function to run every second time the task function runs because the mailbox value you set for the SWI function was 2.

SWI_dec(&SWItest);   /* post processing_SWI software interrupt */

TSK_sleep(SLEEP);

◊	Move the following line from taskFxn to the global definitions before main(). This is necessary because the swiFxn uses elem in its QUE_put and QUE_get calls. However, SWI functions are not permitted to call MEM_alloc because it can block while waiting for a memory resource, and SWIs are not permitted to block. The DSP/BIOS online help contains a “Function Callability Table” that identifies such restrictions on what APIs can be called from what contexts.

Void *elem;

9.	Choose File→Save or press Ctrl+S to save the latency.c file.

10.	Choose Project→Build or click the [image] (Incremental Build) toolbar button.

11.	Choose File→Reload Program.

12.	Choose DSP/BIOS→Statistics View.

13.	Choose Debug→Run.

14.	Notice the Max and Average values for the STS that monitors the interrupt. These values are likely quite similar to the values shown when only the task and idle threads were interrupted. This shows that using a software interrupt does not dramatically increase interrupt latency.

15.	Choose Debug→Halt.

16.	In the taskFxn function of latency.c, make the additions to the source code shown in blue below:

HWI_disable();

for (i = 0; i < loops; i++) {

    QUE_put(&dummyQue, elem);

    elem = QUE_get(&dummyQue);

}

HWI_enable();

◊	The HWI_disable API prevents hardware interrupts from occurring until the HWI_enable API is called.

17.	Choose File→Save or press Ctrl+S to save the latency.c file.

18.	Rebuild and reload the program. Then, run it and compare the new Max and Average values for the STS that monitors the interrupt. This time, the difference will be significant. Remember that when you disable interrupts, you should be sure to do it for a short time only.

19.	Choose Debug→Halt.


Lesson 4: Things to Try
Things to Try

DSP/BIOS – L4

To further explore DSP/BIOS, try the following:

•	View the Execution Graph as the program runs to see the order of events.

•	Add a CLK function that uses LOG_printf to display a status value. Set this status to various values in the TSK function so that you can see where processing was interrupted when the CLK function ran.

•	Choose DSP/BIOS→Kernel/Object View. Examine the states of the objects used in this application while at a breakpoint.