BIOS Users Guide
BIOS Users Guide
BIOS Users Guide
x Users Guide
IMPORTANT NOTICE
Texas Instruments Incorporated and its subsidiaries (TI) reserve the right to make corrections, modifications, enhancements, improvements, and other changes to its products and services at any time and to discontinue any product or service without notice. Customers should obtain the latest relevant information before placing orders and should verify that such information is current and complete. All products are sold subject to TI's terms and conditions of sale supplied at the time of order acknowledgment. TI warrants performance of its hardware products to the specifications applicable at the time of sale in accordance with TI's standard warranty. Testing and other quality control techniques are used to the extent TI deems necessary to support this warranty. Except where mandated by government requirements, testing of all parameters of each product is not necessarily performed. TI assumes no liability for applications assistance or customer product design. Customers are responsible for their products and applications using TI components. To minimize the risks associated with customer products and applications, customers should provide adequate design and operating safeguards. TI does not warrant or represent that any license, either express or implied, is granted under any TI patent right, copyright, mask work right, or other TI intellectual property right relating to any combination, machine, or process in which TI products or services are used. Information published by TI regarding third-party products or services does not constitute a license from TI to use such products or services or a warranty or endorsement thereof. Use of such information may require a license from a third party under the patents or other intellectual property of the third party, or a license from TI under the patents or other intellectual property of TI. Reproduction of information in TI data books or data sheets is permissible only if reproduction is without alteration and is accompanied by all associated warranties, conditions, limitations, and notices. Reproduction of this information with alteration is an unfair and deceptive business practice. TI is not responsible or liable for such altered documentation. Information of third parties may be subject to additional restrictions. Resale of TI products or services with statements different from or beyond the parameters stated by TI for that product or service voids all express and any implied warranties for the associated TI product or service and is an unfair and deceptive business practice. TI is not responsible or liable for any such statements. TI products are not authorized for use in safety-critical applications (such as life support) where a failure of the TI product would reasonably be expected to cause severe personal injury or death, unless officers of the parties have executed an agreement specifically governing such use. Buyers represent that they have all necessary expertise in the safety and regulatory ramifications of their applications, and acknowledge and agree that they are solely responsible for all legal, regulatory and safety-related requirements concerning their products and any use of TI products in such safety-critical applications, notwithstanding any applicationsrelated information or support that may be provided by TI. Further, Buyers must fully indemnify TI and its representatives against any damages arising out of the use of TI products in such safety-critical applications. TI products are neither designed nor intended for use in military/aerospace applications or environments unless the TI products are specifically designated by TI as military-grade or "enhanced plastic." Only products designated by TI as military-grade meet military specifications. Buyers acknowledge and agree that any such use of TI products which TI has not designated as military-grade is solely at the Buyer's risk, and that they are solely responsible for compliance with all legal and regulatory requirements in connection with such use. TI products are neither designed nor intended for use in automotive applications or environments unless the specific TI products are designated by TI as compliant with ISO/TS 16949 requirements. Buyers acknowledge and agree that, if they use any nondesignated products in automotive applications, TI will not be responsible for any failure to meet such requirements. Following are URLs where you can obtain information on other Texas Instruments products and application solutions: Products Amplifiers Data Converters DLP Products DSP Clocks and Timers Interface Logic Power Mgmt Microcontrollers RFID RF/IF and ZigBee Solutions amplifier.ti.com dataconverter.ti.com www.dlp.com dsp.ti.com www.ti.com/clocks interface.ti.com logic.ti.com power.ti.com microcontroller.ti.com www.ti-rfid.com www.ti.com/lprf Applications Audio Automotive Broadband Digital Control Medical Military Optical Networking Security Telephony Video & Imaging Wireless www.ti.com/audio www.ti.com/automotive www.ti.com/broadband www.ti.com/digitalcontrol www.ti.com/medical www.ti.com/military www.ti.com/opticalnetwork www.ti.com/security www.ti.com/telephony www.ti.com/video www.ti.com/wireless
Mailing Address: Texas Instruments, Post Office Box 655303 Dallas, Texas 75265 Copyright 2011, Texas Instruments Incorporated
Preface
Notational Conventions
This document uses the following conventions: Program listings, program examples, and interactive displays are shown in a special typeface. Examples use a bold version of the special typeface for emphasis. Here is a sample program listing:
#include <xdc/runtime/System.h> int main(){ System_printf("Hello World!\n"); return (0); }
Square brackets ( [ and ] ) identify an optional parameter. If you use an optional parameter, you specify the information within the brackets. Unless the square brackets are in a bold typeface, do not enter the brackets themselves.
iii
Related Documentation
You can use the following books to supplement this reference guide: The C Programming Language (second edition), by Brian W. Kernighan and Dennis M. Ritchie, published by Prentice-Hall, Englewood Cliffs, New Jersey, 1988 Programming in C, Kochan, Steve G., Hayden Book Company Programming Embedded Systems in C and C++, by Michael Barr, Andy Oram (Editor), published by O'Reilly & Associates; ISBN: 1565923545, February 1999 Real-Time Systems, by Jane W. S. Liu, published by Prentice Hall; ISBN: 013099651, June 2000 Principles of Concurrent and Distributed Programming (Prentice Hall International Series in Computer Science), by M. Ben-Ari, published by Prentice Hall; ISBN: 013711821X, May 1990 American National Standard for Information Systems-Programming Language C X3.159-1989, American National Standards Institute (ANSI standard for C); (out of print)
iv
Trademarks
Trademarks
The Texas Instruments logo and Texas Instruments are registered trademarks of Texas Instruments. Trademarks of Texas Instruments include: TI, Code Composer, Code Composer Studio, DSP/BIOS, SPOX, TMS320, TMS320C54x, TMS320C55x, TMS320C62x, TMS320C64x, TMS320C67x, TMS320C28x, TMS320C5000, TMS320C6000 and TMS320C2000. Windows is a registered trademark of Microsoft Corporation. Linux is a registered trademark of Linus Torvalds. All other brand or product names are trademarks or registered trademarks of their respective companies or organizations.
Trademarks
vi
Contents
About SYS/BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-1 This chapter provides an overview of SYS/BIOS and describes its relationship to XDCtools. 1.1 What is SYS/BIOS? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-2 1.2 Whats New? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-3 1.3 How are SYS/BIOS, RTSC, and XDCtools Related? . . . . . . . . . . . . . . . . . . . . . . . . . . .1-4 1.4 How are SYS/BIOS and IPC related? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-8 1.5 SYS/BIOS Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-9 1.6 Using C++ with SYS/BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-10 1.7 For More Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-13 SYS/BIOS Configuration and Building . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2-1 This chapter describes how to configure and build SYS/BIOS applications. 2.1 Creating a SYS/BIOS Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2-2 2.2 Configuring SYS/BIOS Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2-6 2.3 Building SYS/BIOS Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2-19 Threading Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-1 This chapter describes the types of threads a SYS/BIOS program can use. 3.1 SYS/BIOS Startup Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-2 3.2 Overview of Threading Modules. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-4 3.3 Hardware Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-15 3.4 Software Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-25 3.5 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-46 3.6 The Idle Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-67 3.7 Example Using Hwi, Swi, and Task Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3-68 Synchronization Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-1 This chapter describes modules that can be used to synchronize access to shared resources. 4.1 Semaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-2 4.2 Event Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-8 4.3 Gates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-14 4.4 Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-18 Timing Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5-1 This chapter describes modules that can be used for timing purposes. 5.1 Overview of Timing Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5-2
vii
Contents
Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-1 This chapter describes issues related to memory use in SYS/BIOS. 6.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2 6.2 Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-4 6.3 Placing Sections into Memory Segments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-11 6.4 Sections and Memory Mapping for MSP430, Stellaris M3, and C28x . . . . . . . . . . . . 6-15 6.5 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-16 6.6 Cache Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-19 6.7 Dynamic Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-20 6.8 Heap Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Hardware Abstraction Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-1 This chapter describes modules that provide hardware abstractions. 7.1 Hardware Abstraction Layer APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2 7.2 HWI Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-3 7.3 Timer Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12 7.4 Cache Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-18 7.5 HAL Package Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-20 Instrumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-1 This chapter describes modules and other tools that can be used for instrumentation purposes. 8.1 Overview of Instrumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2 8.2 Load Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2 8.3 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4 8.4 Real-Time Analysis Tools in CCS v4.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-6 8.5 RTA Agent. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-17 8.6 Performance Optimization. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-21 SYS/BIOS Emulation on Windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A-1 This appendix describes SYS/BIOS emulation when using the Microsoft Windows operating system. Rebuilding SYS/BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B-1 This appendix describes how to rebuild the SYS/BIOS source code. Timing Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C-1 This appendix describes SYS/BIOS timing benchmark statistics. Size Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . D-1 This appendix describes SYS/BIOS size benchmark statistics. Minimizing the Application Footprint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E-1 This appendix describes how to minimize the size of a SYS/BIOS application.
B C D E
viii
Figures
Figures
Thread Priorities ..............................................................................................................3-9 Preemption Scenario .....................................................................................................3-12 Using Swi_inc() to Post a Swi........................................................................................3-32 Using Swi_andn() to Post a Swi ....................................................................................3-33 Using Swi_or() to Post a Swi. ........................................................................................3-34 Using Swi_dec() to Post a Swi ......................................................................................3-35 Execution Mode Variations ............................................................................................3-49 Trace Window Results from Example 4-4 .......................................................................4-7
ix
Tables
Tables
Packages and Modules Provides by SYS/BIOS ............................................................ 1-9 Comparison of Thread Characteristics ........................................................................... 3-7 Thread Preemption ........................................................................................................3-11 Hook Functions by Thread Type .................................................................................. 3-13 System Stack Use for Hwi Nesting by Target Family ................................................... 3-17 System Stack Use for Swi Nesting by Target Family ................................................... 3-28 Swi Object Function Differences .................................................................................. 3-30 Task Stack Use by Target Family ................................................................................. 3-51 Timeline for One-shot and Continuous Clocks ............................................................... 5-4 Heap Implementation Comparison ............................................................................... 6-24 Proxy to Delegate Mappings ........................................................................................ 7-20
Contents
Chapter 1
About SYS/BIOS
This chapter provides an overview of SYS/BIOS and describes its relationship to XDCtools. Topic
1.1 1.2 1.3 1.4 1.5 1.6 1.7
Page
What is SYS/BIOS? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-2 Whats New? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-3 How are SYS/BIOS, RTSC, and XDCtools Related? . . . . . . . . . . . . . 1-4 How are SYS/BIOS and IPC related? . . . . . . . . . . . . . . . . . . . . . . . . . 1-8 SYS/BIOS Packages. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-9 Using C++ with SYS/BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-10 For More Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-13
1-1
What is SYS/BIOS?
1-2
Whats New?
In addition, significant enhancements have been made in the areas that include the following: Up to 32 priority levels are available for both tasks and software interrupt (Swi) threads. A new timer module is provided that enables applications to configure and use timers directly rather than have time-driven events limited to using the system tick. All kernel objects may be created statically or dynamically. An additional heap manager, called HeapMultiBuf, enables fast, deterministic variable-sized memory allocation performance that does not degrade regardless of memory fragmentation. A more flexible memory manager supports the use of multiple, concurrent heaps and enables developers to easily add custom heaps. A new Event object enables tasks to pend on multiple events, including semaphores, mailboxes, message queues, and user-defined events. An additional Gate object supports priority inheritance. Hook functions are supported for hardware and software interrupt objects as well as tasks. An option is provided to build the operating system with parameter checking APIs that assert if invalid parameter values are passed to a system call.
About SYS/BIOS
1-3
A standardized method allows SYS/BIOS APIs to handle errors, based on an error block approach. This enables errors to be handled efficiently without requiring the application to catch return codes. In addition, you can easily have the application halted whenever a SYS/BIOS error occurs, because all errors now pass through a single handler. The system log and execution graph in the Real-Time Analysis (RTA) tools support both dynamically and statically-created tasks. More powerful logging functions include a timestamp, up to 6 words per log entry, and the ability for logging events to span more than one log if additional storage is required. Per-task CPU load is now supported in addition to total CPU load. Host-native execution is provided. This enables developers to create prototype SYS/BIOS applications using Windows developer tools such as Visual C++, without the need for a DSP board or simulator. See Appendix A for details.
The subsections that follow give an overview of these three aspects of using RTSC with SYS/BIOS.
1-4
1.3.1
See Section 1.5 for a partial list of the packages delivered by SYS/BIOS. You can picture the architecture of the tools used to create applications as shown in the following figure:
xdc.runtime Package
Other Other Other (3rd Party) (3rd Party) (3rd Party) Packages Packages Packages
XDC Tools
Texas Instruments compilers Microsoft compilers other compilers
This book describes the SYS/BIOS packages. The RTSC-pedia web site describes the components with the red background (XDCtools and the xdc.runtime package).
About SYS/BIOS
1-5
1.3.2
An application's configuration is expressed within one or more textual script (.cfg) files which are parsed by the RTSC configuration tool to generate corresponding C source code, C header, and linker command files that can be compiled/linked into the end application. The following diagram depicts a build flow for a typical SYS/BIOS application.
The configuration .cfg file uses simple JavaScript syntax to set properties and call methods provided by objects. The combination of JavaScript and the script objects provided by XDCtools is referred to as an XDCscript. Users can create and modify their applications' configuration files in two different ways: by writing the textual .cfg file directly, or by using the visual configuration tool (XGCONF) embedded in Code Composer Studio.
1-6
The following figure shows the XGCONF configuration tool in Code Composer Studio being used to configure a static SYS/BIOS Task instance. You can see this configuration for yourself in the "Static Example" SYS/BIOS project template in CCS. See the SYS/BIOS Getting Started Guide for instructions on building a SYS/BIOS project in Code Composer Studio.
The instance of a Task (named "task0") that was set up in the configuration tool will cause the application's textual configuration file to be generated with the proper syntax so that this static object is created. The corresponding XDCscript code that is created for task0 looks something like:
var Task = xdc.useModule('ti.sysbios.knl.Task'); Task.numPriorities = 16; Task.idleTaskStackSize = 1024; var tskParams = new Task.Params; tskParams.arg0 = 1; tskParams.arg1 = 2; tskParams.priority = 15; tskParams.stack = null; tskParams.stackSize = 1024; var task0 = Task.create('&task0Fxn', tskParams);
About SYS/BIOS
1-7
1.3.3
Modules System
Description Basic low-level "system" services. For example, character output, printf-like output, and exit handling. Allows functions defined by different modules to be run before main(). Creates/frees memory heaps statically or dynamically. Allows events to be logged by RTSC modules and the application, and then passes those events to a Log handler. Provides for configurable assertion diagnostics. Allows raising, checking, and handling errors defined by any modules. Provides time-stamping APIs that forward calls to a platformspecific time stamper (or one provided by CCS). Allows diagnostics to be enabled/disabled at either configuration- or run-time on a per-module basis. Protects against concurrent access to critical data structures.
Startup
Memory Log
Assert Error
Timestamp
Diags
Concurrency Support
Gate
1-8
SYS/BIOS Packages
ti.sysbios.family.* ti.sysbios.gates
ti.sysbios.interfaces ti.sysbios.knl
ti.sysbios.utils
About SYS/BIOS
1-9
1.6.1
Memory Management
The functions new and delete are the C++ operators for dynamic memory allocation and deallocation. For TI targets, these operators use malloc() and free(). SYS/BIOS provides reentrant versions of malloc() and free() that internally use the xdc.runtime.Memory module and (by default) the ti.sysbios.heaps.HeapMem module.
1.6.2
Name Mangling
The C++ compiler implements function overloading, operator overloading, and type-safe linking by encoding a function's signature in its link-level name. The process of encoding the signature into the linkname is referred to as name mangling. Name mangling could potentially interfere with a SYS/BIOS application since you use function names within the configuration to refer to functions declared in your C++ source files. To prevent name mangling and thus to make your functions recognizable within the configuration, it is necessary to declare your functions in an extern C block as shown in the following code fragment from the bigtime.cpp example:
/* * Extern "C" block to prevent name mangling * of functions called within the Configuration Tool */ extern "C" { /* Wrapper functions to call Clock::tick() */ void clockTask(Clock clock); void clockPrd(Clock clock); void clockIdle(void); } // end extern "C"
1-10
This extern C block allows you to refer to the functions within the configuration file. For example, if you have a Task object that should run clockTask() every time the Task runs, you could configure a Task as follows:
var task0Params = new Task.Params(); task0Params.instance.name = "task0"; task0Params.arg0 = $externPtr("cl3"); Program.global.task0 = Task.create("&clockTask", task0Params);
Notice that in the configuration example above, the arg0 parameter of the Task is set to $externPtr("cl3"). The C++ code to create a global clock object for this argument is as follows:
/* Global clock objects */ Clock cl3(3); /* task clock */
Functions declared within the extern C block are not subject to name mangling. Since function overloading is accomplished through name mangling, function overloading has limitations for functions that are called from the configuration. Only one version of an overloaded function can appear within the extern C block. The code in the following example would result in an error.
extern C { // Example causes ERROR Int addNums(Int x, Int y); Int addNums(Int x, Int y, Int z); // error, only one version // of addNums is allowed }
While you can use name overloading in your SYS/BIOS C++ applications, only one version of the overloaded function can be called from the configuration. Default parameters is a C++ feature that is not available for functions called from the configuration. C++ allows you to specify default values for formal parameters within the function declaration. However, a function called from the configuration must provide parameter values. If no values are specified, the actual parameter values are undefined.
1.6.3
About SYS/BIOS
1-11
A wrapper function for a class method is shown in the following code fragment from the bigtime.cpp example:
/* * ======== clockPrd ======== * Wrapper function for PRD objects calling * Clock::tick() */ void clockPrd(Clock clock) { clock.tick(); return; }
Any additional parameters that the class method requires can be passed to the wrapper function.
1.6.4
1-12
1.7.1
About SYS/BIOS
1-13
To see the SYS/BIOS API documentation, you must have included your SYS/BIOS installation directory path in the XDCPATH environment variable. Please refer to the XDCtools Getting Started Guide for details on changing your XDCPATH. Click "+" next to a repository to expand its list of packages. Click "+" next to a package name to see the list of modules it provides. You can further expand the tree to see a list of the functions provided by a module. Double-click on a package or module to see its reference information. The SYS/BIOS API documentation is under the "sysbios" package. To view API documentation on memory allocation, logs, timestamps, asserts, and system, expand the "xdc.runtime" package. The "bios" package contains only the compatibility modules for earlier versions of SYS/BIOS. Notice the following icons in this window: Busy displaying the requested page. Close all page tabs. For each topic you view, there is a tab across the top of the page area. You can use these to quickly return to other pages you have viewed. You can also use the arrows next to "Views" to move backward and forward in your history of page views. To close a page and remove its tab, click the X on the tab. The xs option '--xp' adds the SYS/BIOS 6.0 packages to the path searched for XDCtools packages. If you have added this directory to your XDCPATH environment variable definition as described in the SYS/BIOS 6 Getting Started Guide, you do not need to use the --xp command-line option.
1-14
Chapter 2
This chapter describes how to configure and build SYS/BIOS applications. Topic
2.1 2.2 2.3
Page
Creating a SYS/BIOS Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-2 Configuring SYS/BIOS Applications . . . . . . . . . . . . . . . . . . . . . . . . . . 2-6 Building SYS/BIOS Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-19
2-1
3) Select your platform type in the Project Type field. For example, you might select "C6000". Then, click Next. 4) Click Next on the "Additional Project Settings" page. (You may want to use referenced projects later, when you have multiple SYS/BIOS that share a common configuration file.) 5) In the Device Variant row of the "Project Settings" page, select a filter in the first field. This shortens the list of device variants in the second field. Then, select the actual device you are using. For example, you might select "Generic devices" in the filter field and "Generic C64x+ Device" in the second field. Depending on your device, you might also need to adjust the Device Endianness and Runtime Support Library settings.
2-2
6) Click Next. Important: Don't click Finish at this point if you want to create a project that can use SYS/BIOS. 7) On the "Project Templates" page, expand the list of SYS/BIOS templates and choose one. A description of the selected template is shown in the dialog. To get started with SYS/BIOS, you can choose one of the Generic Examples, such as the Log Example or Task Mutex Example. To create your own application project, you might choose the "Minimal" or "Typical" depending on how memory-limited your target is. (If you have other software that uses SYS/BIOS and XDCtools, such as IPC, you can choose a template for that component.) 8) Click Next.
2-3
9) On the "RTSC Configuration Settings" page, make sure the versions of XDCtools, SYS/BIOS, and any other components you want to use are selected. By default, the most recent versions are selected.
10) The Target setting is based on device settings you made on earlier pages, and should not need to be changed. 11) Click the drop-down arrow next to the Platform field. CCS scans the available packages for available platforms. Click on the list and choose the platform you want to use. 12) The Build-profile field determines which libraries the application will link with. We recommend that you use the "release" setting even when you are creating and debugging an application. 13) Click Finish to create a new project and add it to the C/C++ Projects list.
2-4
2.1.1
2.1.2
The configuration project can be referenced by the main project, which causes it to be automatically built when you build the main project. To create two projects with this reference already set up, select the SYS/BIOS > Typical (with separate config project) template when creating a new CCS project.
2-5
The Source tab shows that the code to create this Task would be as follows:
var Task = xdc.useModule('ti.sysbios.knl.Task'); var task0Params = new Task.Params(); task0Params.instance.name = "task0"; task0Params.arg0 = 1; task0Params.arg1 = 2; task0Params.priority = 15; task0Params.stackSize = 1024; Program.global.task0 = Task.create("&myTskFxn", task0Params);
2-6
2.2.1
Note: If the configuration is shown in a text editor instead of XGCONF, right-click on the .cfg file and choose Open With > XGCONF. You can open multiple configuration files at the same time. However, using XGCONF with several configuration files is resource intensive and may slow down your system.
2-7
2.2.2
2.2.3
2-8
2.2.4
1) Available Packages view lets you add modules to your configuration. By default, this view appears in the lower-left corner of the window. See Section 2.2.5. 2) Outline view shows modules used in your current configuration and lets you choose the module to display in the Properties view. By default, this view appears on the right side of the window. Two display modes are provided: a user configuration list and a configuration results tree. See Section 2.2.6. 3) Property view shows the property settings of the selected module or instance and lets you make changes. You can also use the Source tab in the Property view to modify the script directly. See Section 2.2.7. 4) Problems view appears if errors and warnings are detected in the configuration during validation. See Section 2.2.8.
2-9
2.2.5
2-10
Adding Modules and Instances to the Configuration To start using a module, right-click and choose Use <module>. For example, choosing Use Swi adds the ability to create and configure software interrupts to your application. You can also drag modules from the Available Products view to the Outline view to add them to the configuration. When you select a module in the Available Products view, you see the properties you can set for that module in the Property view (whether you are using it yet or not). When you add use of a module to the configuration, that module is shown in the Outline view. You can get help on a particular module by right-clicking on the module name and choosing Help from the pop-up menu. Adding a module to the configuration causes an xdc.useModule() statement to be added to the configuration script. Managing the Available Products List When you open a configuration with XGCONF, the RTSC repositories that your application is set to use in the CCS Build settings are scanned for modules and the results are listed here. You can add or remove products by right-clicking and choosing Package Path. (This opens the dialog you see by choosing Project > Properties from the CCS menus, then choosing the CCS Build category and the RTSC tab.) Check boxes next to versions of products you want to be able to use. If a product isnt listed, click the Add button to browse for a RTSC repository in the file system. When you click OK, the Available Products list is refreshed. You can open the Package Repository Path Browser by right-clicking and choosing Check Path. This tool lists all the repositories and packages on the RTSC package path, shows the effects of adding repositories or changing the order of locations in the path, and sorts the packages by various fields. If there is a problem with the display or you have changed something in the file system that should affect the modules shown, you can right-click and choose Refresh View.
2-11
2.2.6
As in the Available Products view, you can type filter text at the top of the Outline view to find modules and instances by name. To create an instance, rightclick on a module and choose New <module>. For example, New Semaphore. Notice that not all modules let you create instance objects. You see a dialog that lets you specify values you want to use for the properties of that instance. You can type or select values in the Value column.
2-12
If you want to delete an instance from the configuration, right-click on that instance in the Outline view, and select Delete <name> from the menu. When you select a module or instance in the Outline view, you see the properties you can set for that module in the Property view. You can get help on a particular module by right-clicking on the module name and choosing Help from the pop-up menu. Help on SYS/BIOS and XDCtools configuration is in the red (XDCscript) section of the online documentation. For each module, the configuration help follows the blue sections that document that modules C APIs. Some modules have a red ball next to them, while others have a blue ball. The blue ball indicates that this is a target module, which provides code or data that can be referenced at runtime on the embedded target. The red ball indicates that this is a meta-only module, which exists in the configuration but does not directly exist on the target. To stop using a module in the configuration, right-click on that module in the Outline view, and select Stop Using <module> from the menu. Deleting a module from the configuration removes the corresponding xdc.useModule() statement and any instances of the module from the configuration script. If deleting the module would result in an invalid script, an error message is displayed and the module is not deleted. This can happen if a different module or instance refers to it in the script, for example in an assignment to a proxy or a configuration parameter.
2.2.7
All the property sheets you have viewed are accessible from the tabs at the bottom of the Property view.
2-13
You can use the arrow buttons in the upper-right of the Property view to move through sheets you have already viewed. The Home icon returns you to the BIOS module System Overview. For numeric fields, you can right-click on a field and choose Set Radix to choose whether to display this field as a decimal or hex value. Point to a field with your mouse for brief information about a property. Rightclick on a field and choose Help to jump directly to the documentation for that property. Click the Help icon to get documentation for the current module. Help on SYS/BIOS and XDCtools configuration is in the red (XDCscript) section of the online documentation. For each module, the configuration help follows the blue sections that document that modules C APIs. System Overview Block Diagram The System Overview shows all of the core modules in SYS/BIOS as blocks. A green checkmark shows the modules that are currently used by your configuration. You can add other modules in the diagram to your configuration by right-clicking on the block and choosing Use. You can configure any module by clicking on it. To return to the System Overview from some other property sheet, select the BIOS module in the Outline View and click the System Overview button.
You can add object instances to the configuration by right-clicking on a module and choosing the New command.
2-14
Basic Property Sheet The Basic property sheet organizes properties into categories and provides brief descriptions of some properties. Checkboxes, selection fields, and text fields are provided depending on the type of values a property can have.
You can get to the Basic property sheet by clicking the Basic button in the Advanced property sheet. For some modules, this sheet is called Runtime instead of Basic or there may be multiple basic-level property sheets. In the Basic property sheet for optional modules, you can uncheck the Add <module> to my configuration box to stop using a module. If you arent using a module, you can add it to your configuration by checking the box. Some advanced properties arent shown in the Basic sheet. For these, see the Advanced sheet (page 216). You can fold up or reopen portions of the property sheet by clicking the arrow next to a section heading.
2-15
Advanced Properties Sheet The Advanced layout provides a tabular list of property names and lets you set values in the table.
To modify the value of a property, click on a row in the Value column and type or select a new value. When you type a value for a property, XGCONF checks to make sure the type of the value matches the type expected for the property. This is separate from the more extensive validation checks that are performed when you save a configuration. For many modules, the Advanced layout has both a Basic tab and an xdc.runtime tab. The Basic tab shows the same properties as the Basic view, but in tabular form. The xdc.runtime tab shows <module>.common$ properties inherited by the module. For example, these include properties related to the memory used by the module and any instances, the diagnostics settings for the module, and the Gate object used by this module if any. In addition, events, asserts, and errors that can occur for this module are listed. See the online documentation for xdc.runtime.Types for more about the common$ properties. Point to a field with your mouse for brief information about a property. Rightclick on a field and choose Help to jump directly to the documentation for that property. Click the Help icon to get documentation for the current module.
2-16
Source Editor The source editor lets you edit the configuration script using a text editor. Some advanced scripting features of XDCscript are available only by editing the script directly. For more information see links from the RTSC-pedia http://rtsc.eclipse.org/docs-tip/RTSC_Scripting_Primer page. You can use Ctrl+S to save any changes you make. You can right-click on the file and choose Undo Typing to undo the most recent graphical editing operation, or Revert File to return to the most recently saved version of this file. When you save the file, it is validated and the other panes are refreshed to reflect your changes. When you select a module or instance in the Outline view, the Source tab highlights all the lines of the configuration file that are related to that module or instance.
2.2.8
The Outline view shows an error icon next to any modules or instances for which problems were detected. If you select such a module, the Properties view shows a red X icon next to the properties that are incorrectly set. If you double-click on an item in the Problems view while the Source tab is displayed, the Source tab highlights the statement that caused the error or warning, and an error icon is shown in the left margin. Position indicators for any problems also appear to the right of the scrollbar.
2-17
Depending on the type of problem, the validation may only report the first item found in the configuration. For example, a syntax error such as an unknown variable in the script may prevent later problems, such as invalid values being assigned to a property, from being reported. Such problems can be detected after you fix the syntax error and re-validate the configuration. If there is not enough detail available to blame a specific configuration parameter, no problem icon will be shown in the Properties tab. A problem icon will still be shown on the module or instance in the Outline view. You can sort the problem list by clicking on the headings. You can filter the problem list to display fewer items by clicking the Filter icon to open the Filters dialog.
2.2.9
You can force the configuration to be validated by saving the configuration or by clicking the Refresh icon. Validation means that semantic checks are performed to make sure that, for example, objects that are referenced actually exist. These checks also make sure that values are within the valid ranges for a property. Some datatype checking is performed when you set a property in the Properties tab. If you are editing the configuration code directly in the Source tab, the configuration is validated only if you save the configuration. While a configuration is being validated (this takes a few seconds), you see the a progress icon in the lower-right corner of the window.
After the configuration has been validated, any errors that were detected in the configuration are shown in the Problems view. For example, if you delete the statement that creates an instance that is referenced by another configuration parameter, you will see an error. See Section 2.2.8 for more about finding and fixing errors.
2-18
After you build the project, look at the C/C++ Projects view. You can expand the Debug folder to see the files that were generated by the build process. For help with build errors, see the RTSC-pedia http://rtsc.eclipse.org/docs-tip/Trouble_Shooting. page at
Configuro can also be used to integrate RTSC configuration into other build systems. For more information, see the RTSC-pedia page at http://rtsc.eclipse.org/docs-tip/Consuming_Configurable_Content.
2-19
2.3.1
2.3.2
2-20
3) Use various tools provided with XDCtools and SYS/BIOS to debug the application: Runtime Object Viewer (ROV). See section 6.5.3, ROV for System Stacks and Task Stacks and the RTSC-pedia page on ROV at http://rtsc.eclipse.org/docs-tip/RTSC_Object_Viewer. Real-Time Analysis Tools (RTA). See section 8.4, Real-Time Analysis Tools in CCS v4.x. MultiCore System Analyzer (MCSA). See the http://processors.wiki.ti.com/index.php/Multicore_System_Analyzer wiki page and the Multicore System Analyzer Users Guide (SPRUH43) if you are using MCSA and UIA. Note that CCS v5.x is required in order to use MCSA. In addition, the Unified Instrumentation Architecture (UIA) libraries must be used instead of the ti.sysbios.rta.Agent module.
2.3.3
Note: The "whole_program" and "whole_program_debug" options for the RTSC Build-Profile have been deprecated, and are no longer recommended. The option that provides the most similar result is to set the BIOS.libType configuration parameter to BIOS.LibType_Custom. CCS Build Configuration. This setting in the CCS Build settings allows you to choose between and customize multiple build configurations. Each configuration can have the compiler and linker settings you choose. BIOS.libType configuration parameter. You can set this parameter in XGCONF or by editing the .cfg file in your project. This parameter lets you
2-21
select from two pre-compiled versions of the SYS/BIOS libraries or to have a custom version of the SYS/BIOS libraries compiled based on the needs of your application. See the table and discussion that follow for more information. The options for the BIOS.libType configuration parameter are as follows:
Compile Time Fast Fast Fast (slow first time) Slower Code Size Good Better Best -Run-Time Performance Good Better Best --
Instrumented. (default) This option links with pre-built SYS/BIOS (and IPC) libraries that have instrumentation available. All Asserts and Diags settings are checked. Your configuration file can enable or disable various Diags and logging related settings. However, note that the checks to see if Diags are enabled before outputting a Log event are always performed, which has an impact on performance even if you use the ALWAYS_ON or ALWAYS_OFF setting. The resulting code size when using this option may be too large to fit on some targets, such as C28x and MSP430. This option is easy to use and debug and provides a fast build time. Non-Instrumented. This option links with pre-built SYS/BIOS (and IPC) libraries that have instrumentation turned off. No Assert or Diag settings are checked, and logging information is not available at run-time. The checking for Asserts and Diags is compiled out of the libraries, so runtime performance and code size are optimized. Checking of Error_Blocks and handling errors in ways other logging an event are still supported. This option is easy to use and provides a fast build time. Custom. This option builds custom versions of the SYS/BIOS (and IPC) libraries that contain the modules and APIs that your application needs to access. If you have not used a particular module in your .cfg file or your C code (and it is not required internally by a SYS/BIOS module that is used), that module is not contained in the custom libraries compiled for your application. This option provides the best run-time performance and best code size given the needs of your application. Instrumentation is available to whatever extent your application configures it.
2-22
The first time you build a project with the custom libType, the build will be longer. The custom libraries are stored in the "src" directory of your project. Subsequent builds may be faster; libraries do not need to be rebuilt unless you change one of the few configuration parameters that affect the build settings, or you use an additional module that wasnt already used in the previous configuration. Note: If you disable SYS/BIOS Task or Swi scheduling, you must use the "custom" option in order to successfully link your application. The custom option uses program optimization that removes many initialized constants and small code fragments (often "glue" code) from the final executable image. Such classic optimizations as constant folding and function inlining are used, including across module boundaries. The custom build preserves enough debug information to make it still possible to step through the optimized code in CCS and locate global variables. Debug. This option is not recommended; it is intended for internal use by Texas Instruments developers.
If you use the custom option for the BIOS.libType, you can also set the BIOS.customCCOpts parameter to customize the C compiler command-line options used when compiling the SYS/BIOS libraries. If you want to change this parameter, it is important to first examine and understand the default command-line options used to compile the SYS/BIOS libraries for your target. You can see the default in XGCONF or by placing the following statement in your configuration script and building the project:
print("customCCOpts =", BIOS.customCCOpts);
You must be careful not to cause problems for the SYS/BIOS compilation when you modify this parameter. For example, the --program_level_compile option is required. (Some --define and --include_path options are used on the compiler command line but are not listed in the customCCOpts definition; these also cannot be removed.)
2-23
For example, to create a debuggable custom library, you can remove the -o3 option from the BIOS.customCCOpts definition by specifying it with the following string for a C64x+ target:
BIOS.customCCOpts = "-mv64p --abi=eabi -q -mi10 -mo -pdr -pden -pds=238 -pds=880 -pds1110 --embed_inline_assembly --program_level_compile -g";
2-24
Chapter 3
Threading Modules
This chapter describes the types of threads a SYS/BIOS program can use. Topic
3.1 3.2 3.3 3.4 3.5 3.6 3.7
Page
SYS/BIOS Startup Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-2 Overview of Threading Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-4 Hardware Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-15 Software Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-25 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-46 The Idle Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-67 Example Using Hwi, Swi, and Task Threads . . . . . . . . . . . . . . . . . . 3-68
3-1
The "after main()" startup sequence is governed by SYS/BIOS and is initiated by an explicit call to the BIOS_start() function at the end of the application's main() function. The SYS/BIOS startup sequence that run when BIOS_start() is called is as follows: 1) Startup Functions. Run the user-supplied "startup functions" (see BIOS.startupFxns). 2) Enable Hardware Interrupts. 3) Timer Startup. If the system supports Timers, then at this point all configured timers are initialized per their user-configuration. If a timer was configured to start "automatically", it is started here. 4) Enable Software Interrupts. If the system supports software interrupts (Swis) (see BIOS.swiEnabled), then the SYS/BIOS startup sequence enables Swis at this point.
3-2
5) Task Startup. If the system supports Tasks (see BIOS.taskEnabled), then task scheduling begins here. If there are no statically or dynamically created Tasks in the system, then execution proceeds directly to the idle loop.
The following configuration script excerpt installs a user-supplied startup function at every possible control point in the XDCtools and SYS/BIOS startup sequence. Configuration scripts have the filename extension ".cfg" and are written in the XDCscript language, which is used to configure RTSC modules.
/* get handle to xdc Reset module */ Reset = xdc.useModule('xdc.runtime.Reset'); /* install a "reset function" */ Reset.fxns[Reset.fxns.length++] = '&myReset'; /* get handle to xdc Startup module */ var Startup = xdc.useModule('xdc.runtime.Startup'); /* install a "first function" */ Startup.firstFxns[Startup.firstFxns.length++] = '&myFirst'; /* install a "last function" */ Startup.lastFxns[Startup.lastFxns.length++] = '&myLast'; /* get handle to BIOS module */ var BIOS = xdc.useModule('ti.sysbios.BIOS'); /* install a BIOS startup function */ BIOS.addUserStartupFunction('&myBiosStartup');
Threading Modules
3-3
These thread types are described briefly in the following section and discussed in more detail in the rest of this chapter.
3.2.1
Types of Threads
The four major types of threads in a SYS/BIOS program are: Hardware interrupt (Hwi) threads. Hwi threads (also called Interrupt Service Routines or ISRs) are the threads with the highest priority in a SYS/BIOS application. Hwi threads are used to perform time critical tasks that are subject to hard deadlines. They are triggered in response to external asynchronous events (interrupts) that occur in the real-time environment. Hwi threads always run to completion but can be preempted temporarily by Hwi threads triggered by other interrupts, if enabled. See Section 3.3, Hardware Interrupts, page 3-15, for details about hardware interrupts.
3-4
Software interrupt (Swi) threads. Patterned after hardware interrupts (Hwi), software interrupt threads provide additional priority levels between Hwi threads and Task threads. Unlike Hwis, which are triggered by hardware interrupts, Swis are triggered programmatically by calling certain Swi module APIs. Swis handle threads subject to time constraints that preclude them from being run as tasks, but whose deadlines are not as severe as those of hardware ISRs. Like Hwi's, Swi's threads always run to completion. Swis allow Hwis to defer less critical processing to a lower-priority thread, minimizing the time the CPU spends inside an interrupt service routine, where other Hwis can be disabled. Swis require only enough space to save the context for each Swi interrupt priority level, while Tasks use a separate stack for each thread. See Section 3.4, Software Interrupts, page 3-25, for details about Swis. Task (Task) threads. Task threads have higher priority than the background (Idle) thread and lower priority than software interrupts. Tasks differ from software interrupts in that they can wait (block) during execution until necessary resources are available. Tasks require a separate stack for each thread. SYS/BIOS provides a number of mechanisms that can be used for inter-task communication and synchronization. These include Semaphores, Events, Message queues, and Mailboxes. See Section 3.5, Tasks, page 3-46, for details about tasks. Idle Loop (Idle) thread. Idle threads execute at the lowest priority in a SYS/BIOS application and are executed one after another in a continuous loop (the Idle Loop). After main returns, a SYS/BIOS application calls the startup routine for each SYS/BIOS module and then falls into the Idle Loop. Each thread 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. See Section 3.6, The Idle Loop, page 3-67, for details about the background thread.
Another type of thread, a Clock thread, is run within the context of a Swi thread that is triggered by a Hwi thread invoked by a repetitive timer peripheral interrupt. See Section 5.2 for details.
Threading Modules
3-5
3.2.2
3-6
Timer. Timer threads are run within the context of a Hwi thread. As such, they inherit the priority of the corresponding Timer interrupt. They are invoked at the rate of the programmed Timer period. Timer threads should do the absolute minimum necessary to complete the task required. If more processing time is required, consider posting a Swi to do the work or posting a Semaphore for later processing by a task so that CPU time is efficiently managed.
3.2.3
Table 3-1.
Should not pend. Pending would disable all registered Idle threads. Ready, running
Execution states
Hwi_disable()
Swi_disable()
Program exit
Interrupt occurs
Task_create() and various task synchronization mechanisms (Event, Semaphore, Mailbox) Task stack (1 per task)
Stack used
Notes:
1) If you disable the Task Manager, Idle threads use the system stack.
Threading Modules
3-7
Table 3.1.
--Not applicable--
Saves the savedby-callee registers (see optimizing compiler users guide for your platform). Streams, lists, pipes, gates, mailboxes, message queues, global variables Semaphores, events, mailboxes Yes: register, create, ready, switch, exit, delete Yes Yes Yes
--Not applicable--
--Not applicable--
Swi trigger
-Not applicable--
Yes: register, create, begin, end, delete Yes Yes See Note 1
No
Yes No No
Interrupt event
None
Implicit statistics
Notes:
None
None
None
3-8
3.2.4
Thread Priorities
Within SYS/BIOS, hardware interrupts have the highest priority. The priorities among the set of Hwi objects are not maintained implicitly by SYS/BIOS. The Hwi priority only applies to the order in which multiple interrupts that are ready on a given CPU cycle are serviced by the CPU. Hardware interrupts are preempted by another interrupt unless interrupts are globally disabled or when specific interrupts are individually disabled.
Figure 3-1.
Thread Priorities
Hardware Interrupts (Hwi)
Timer Functions
Priority
Swis have lower priority than Hwis. There are up to 32 priority levels available for Swis (16 by default) The maximum number of priority levels is 16 for MSP430 and C28x. Swis can be preempted by a higher-priority Swi or any Hwi. Swis cannot block. Tasks have lower priority than Swis. There are up to 32 task priority levels (16 by default). The maximum number of priority levels is 16 for MSP430 and C28x. Tasks can be preempted by any higher-priority thread. Tasks can block while waiting for resource availability and lower-priority threads. The background Idle Loop is the thread with the lowest priority of all. It runs in a loop when the CPU is not busy running another thread. When tasks are enabled, the Idle Loop is implemented as the only task running at priority 0. When tasks are disabled, the Idle Loop is fallen into after the application's "main()" function is called.
Threading Modules
3-9
3.2.5
Both Hwis and Swis can interact with the SYS/BIOS task scheduler. When a task is blocked, it is often because the task is pending on a semaphore which is unavailable. Semaphores can be posted from Hwis and Swis as well as from other tasks. If a Hwi or Swi posts a semaphore to unblock a pending task, the processor switches to that task if that task has a higher priority than the currently running task (after the Hwi or Swi completes). When running either a Hwi or Swi, SYS/BIOS uses a dedicated system interrupt stack, called the system stack (sometimes called the ISR stack). Each task uses its own private stack. Therefore, if there are no Tasks in the system, all threads share the same system stack. For performance reasons, sometimes it is advantageous to place the system stack in precious fast memory. See Section 3.4.3 for information about system stack size and Section 3.5.3 for information about task stack size.
3-10
Table 3-2 shows what happens when one type of thread is running (top row) and another thread becomes ready to run (left column). The action shown is that of the newly posted (ready to run) thread.
Table 3-2.
Thread Preemption
Running Thread
Hwi Preempts if enabled* Waits for reenable Waits Waits Waits Waits
Swi Preempts
Task Preempts
Idle Preempts
Disabled Hwi
Enabled, higher-priority Swi Lower-priority Swi Enabled, higher-priority Task Low-priority Task
* On some targets, hardware interrupts can be individually enabled and disabled. This is not true on all targets. Also, some targets have controllers that support hardware interrupt prioritization, in which case a Hwi can only be preempted by a higher-priority Hwi. Note that Table 3-2 shows the results if the type of thread that is posted is enabled. If that thread type is disabled (for example, by Task_disable), a thread cannot run in any case until its thread type is reenabled. Figure 3-2 shows the execution graph for a scenario in which Swis and Hwis are enabled (the default), and a Hwi posts a Swi whose priority is higher than that of the Swi running when the interrupt occurs. Also, a second Hwi occurs while the first ISR is running and preempts the first ISR.
Threading Modules
3-11
Figure 3-2.
Preemption Scenario
Events Swi A finishes Swi B finishes Hwi 1 finishes Hwi 2 finishes Hwi 2 occurs Hwi 1 occurs Background posts Swi B Hwi 2 posts Swi A
Thread Priority Hardware interrupt 1 (Hwi 1) Increasing Priority Hardware interrupt 2 (Hwi 2) Software interrupt A (Swi A) Software interrupt B (Swi B) Background (Idle) Time
preempted
Swi A ready
Swi B preempted
background preempted
In Figure 3-2, the low-priority Swi is asynchronously preempted by the Hwis. The first Hwi posts a higher-priority Swi, which is executed after both Hwis finish executing. Here is sample pseudo-code for the example depicted in Figure 3-2:
backgroundThread() { Swi_post(Swi_B) /* priority = 5 */ } Hwi_1 () { . . . } Hwi_2 () { Swi_post(Swi_A) /* priority = 7 */ }
3-12
3.2.6
Hooks
Hwi, Swi, and Task threads optionally provide points in a thread's life cycle to insert user code for instrumentation, monitoring, or statistics gathering purposes. Each of these code points is called a "hook" and the user function provided for the hook is called a "hook function". The following hook functions can be set for the various thread types:
Hooks are declared as a set of hook functions called "hook sets". You do not need to define all hook functions within a set, only those that are required by the application. Hook functions can only be declared statically (in an XDCtools configuration script) so that they may be efficiently invoked when provided and result in no runtime overhead when a hook function is not provided. Except for the Register hook, all hook functions are invoked with a handle to the object associated with that thread as its argument (that is, a Hwi object, a Swi object, or a Task object). Other arguments are provided for some threadtype-specific hook functions. You can define as many hook sets as necessary for your application. When more than one hook set is defined, the individual hook functions within each set are invoked in hook ID order for a particular hook type. For example, during Task_create() the order that the Create hook within each Task hook set is invoked is the order in which the Task hook sets were originally defined. The argument to a thread's Register hook (which is invoked only once) is an index (the "hook ID") indicating the hook set's relative order in the hook function calling sequence. Each set of hook functions has a unique associated "hook context pointer". This general-purpose pointer can be used by itself to hold hook set specific information, or it can be initialized to point to a block of memory allocated by the Create hook function within a hook set if more space is required for a particular application.
Threading Modules
3-13
An individual hook function obtains the value of its associated context pointer through the following thread-type-specific APIs: Hwi_getHookContext(), Swi_getHookContext(), and Task_getHookContext(). Corresponding APIs for initializing the context pointers are also provided: Hwi_setHookContext(), Swi_setHookContext(), and Task_setHookContext(). Each of these APIs take the hook ID as an argument. The following diagram shows an application with three Hwi hook sets:
Hwi Hook Set [0] registerHookFunc0() createHookFunc0() beginHookFunc0() endHookFunc0() deleteHookFunc0() Hwi Hook Set [1] registerHookFunc1() createHookFunc1() beginHookFunc1() endHookFunc1() deleteHookFunc1() Hwi Hook Set [2] registerHookFunc2() createHookFunc2() beginHookFunc2() endHookFunc2() deleteHookFunc2()
The hook context pointers are accessed using Hwi_getHookContext() using the index provided to the three Register hook functions. Just prior to invoking your ISR functions, the Begin Hook functions are invoked in the following order: 1) beginHookFunc0(); 2) beginHookFunc1(); 3) beginHookFunc2(); Likewise, upon return from your ISR functions the End Hook functions are invoked in the following order: 1) endHookFunc0(); 2) endHookFunc1(); 3) endHookFunc2();
3-14
Hardware Interrupts
Threading Modules
3-15
Hardware Interrupts
3.3.1
Here, hwi0 is a handle to the created Hwi object, id is the interrupt number being defined, hwiFunc is the name of the function associated with the Hwi, and hwiParams is a structure that contains Hwi instance parameters (enable/restore masks, the Hwi function argument, etc). Here, hwiParams.arg is set to 5. If NULL is passed instead of a pointer to an actual Hwi_Params struct, a default set of parameters is used. The "eb" is an error block that you can use to handle errors that may occur during Hwi object creation. The corresponding static configuration Hwi object creation syntax is:
var Hwi = xdc.useModule('ti.sysbios.hal.Hwi'); var hwiParams = new Hwi.Params; hwiParams.arg = 5; Program.global.hwi0 = Hwi.create(id, '&hwiFunc', hwiParams);
Here, the "hwiParams = new Hwi.Params" statement does the equivalent of creating and initializing the hwiParams structure with default values. In the static configuration world, no error block (eb) is required for the "create" function. The "Program.global.hwi0" name becomes a a runtime-accessible handle (symbol name = "hwi0") to the statically-created Hwi object.
3-16
Hardware Interrupts
3.3.2
The following table shows the amount of system stack required to absorb the worst-case Hwi interrupt nesting. This first number is the amount of system stack space required for the first priority level on a target. The second number shows the amount of stack space required for each subsequent priority level used in the application.
Target Family M3 MSP430 MSP430X MSP430X_small C674 C64P C64T C28_float C28_large Arm9 A8F
Units 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 16-bit words 16-bit words 8-bit bytes 8-bit bytes
See Section 3.4.3 for information about system stack use by software interrupts and Section 3.5.3 for information about task stack size.
Threading Modules
3-17
Hardware Interrupts
3.3.3
Hwi Hooks
The Hwi module supports the following set of Hook functions: Register. A function called before any statically created Hwis are initialized at runtime. The register hook is called at boot time before main() and before interrupts are enabled. Create. A function called when a Hwi is created. This includes Hwis that are created statically and those created dynamically using Hwi_create(). Begin. A function called just prior to running a Hwi ISR function. End. A function called just after a Hwi ISR function finishes. Delete. A function called when a Hwi is deleted at runtime with Hwi_delete().
The following HookSet structure type definition encapsulates the hook functions supported by the Hwi module:
typedef struct Hwi_HookSet { Void (*registerFxn)(Int); /* Register Hook */ Void (*createFxn)(Handle, Error.Block *); /* Create Hook */ Void (*beginFxn)(Handle); /* Begin Hook */ Void (*endFxn)(Handle); /* End Hook */ Void (*deleteFxn)(Handle); /* Delete Hook */ };
Hwi Hook functions can only be configured statically. 3.3.3.1 Register Function The register function is provided to allow a hook set to store its corresponding hook ID. This ID can be passed to Hwi_setHookContext() and Hwi_getHookContext() to set or get hook-specific context. The Register function must be specified if the hook implementation needs to use Hwi_setHookContext() or Hwi_getHookContext(). The registerFxn hook function is called during system initialization before interrupts have been enabled. The Register function has the following signature:
Void registerFxn(Int id);
3.3.3.2 Create and Delete Functions The Create and Delete functions are called whenever a Hwi is created or deleted. The Create function is passed an Error_Block that is to be passed to Memory_alloc() for applications that require additional context storage space.
3-18
Hardware Interrupts
The createFxn and deleteFxn functions are called with interrupts enabled (unless called at boot time or from main()). These functions have the following signatures:
Void createFxn(Hwi_Handle hwi, Error_Block *eb); Void deleteFxn(Hwi_Handle hwi);
3.3.3.3 Begin and End Functions The Begin and End hook functions are called with interrupts globally disabled. As a result, any hook processing function contributes to overall system interrupt response latency. In order to minimize this impact, carefully consider the processing time spent in a Hwi beginFxn or endFxn hook function. The beginFxn is invoked just prior to calling the ISR function. The endFxn is invoked immediately after the return from the ISR function. These functions have the following signatures:
Void beginFxn(Hwi_Handle hwi); Void endFxn(Hwi_Handle hwi);
When more than one Hook Set is defined, the individual hook functions of a common type are invoked in hook ID order. 3.3.3.4 Hwi Hooks Example The following example application uses two Hwi hook sets. The Hwi associated with a statically-created Timer is used to exercise the Hwi hook functions. This example demonstrates how to read and write the Hook Context Pointer associated with each hook set. The XDCtools configuration script and program output are shown after the C code listing.
Threading Modules
3-19
Hardware Interrupts
extern Timer_Handle myTimer; volatile Bool myEnd2Flag = FALSE; Int myHookSetId1, myHookSetId2; Error_Block eb; Error_init(&eb); /* HookSet 1 functions */ /* ======== myRegister1 ======== * invoked during Hwi module startup before main() * for each HookSet */ Void myRegister1(Int hookSetId) { System_printf("myRegister1: assigned hookSet Id = %d\n", hookSetId); myHookSetId1 = hookSetId; } /* ======== myCreate1 ======== * invoked during Hwi module startup before main() * for statically created Hwis */ Void myCreate1(Hwi_Handle hwi, Error_Block *eb) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); /* pEnv should be 0 at this point. If not, there's a bug. */ System_printf("myCreate1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xdead1); }
3-20
Hardware Interrupts /* ======== myBegin1 ======== * invoked before Timer Hwi func */ Void myBegin1(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); System_printf("myBegin1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xbeef1); } /* ======== myEnd1 ======== * invoked after Timer Hwi func */ Void myEnd1(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId1); System_printf("myEnd1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId1, (Ptr)0xc0de1); } /* HookSet 2 functions */ /* ======== myRegister2 ======== * invoked during Hwi module startup before main * for each HookSet */ Void myRegister2(Int hookSetId) { System_printf("myRegister2: assigned hookSet Id = %d\n", hookSetId); myHookSetId2 = hookSetId; }
Threading Modules
3-21
Hardware Interrupts
/* ======== myCreate2 ======== * invoked during Hwi module startup before main * for statically created Hwis */ Void myCreate2(Hwi_Handle hwi, Error_Block *eb) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); /* pEnv should be 0 at this point. If not, there's a bug. */ System_printf("myCreate2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xdead2); } /* ======== myBegin2 ======== * invoked before Timer Hwi func */ Void myBegin2(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); System_printf("myBegin2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xbeef2); } /* ======== myEnd2 ======== * invoked after Timer Hwi func */ Void myEnd2(Hwi_Handle hwi) { Ptr pEnv; pEnv = Hwi_getHookContext(hwi, myHookSetId2); System_printf("myEnd2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Hwi_setHookContext(hwi, myHookSetId2, (Ptr)0xc0de2); myEnd2Flag = TRUE; } /* ======== myTimerFunc ======== * Timer interrupt handler */ Void myTimerFunc(UArg arg) { System_printf("Entering myTimerHwi\n"); }
3-22
Hardware Interrupts
/* ======== myTaskFunc ======== */ Void myTaskFunc(UArg arg0, UArg arg1) { System_printf("Entering myTask.\n"); Timer_start(myTimer); /* wait for timer interrupt and myEnd2 to complete */ while (!myEnd2Flag) { ; } System_printf("myTask exiting ...\n"); } /* ======== myIdleFunc ======== */ Void myIdleFunc() { System_printf("Entering myIdleFunc().\n"); System_exit(0); } /* ======== main ======== */ Int main(Int argc, Char* argv[]) { System_printf("Starting HwiHookExample...\n"); BIOS_start(); return (0); }
Threading Modules
3-23
Hardware Interrupts /* Create myTimer as source of Hwi */ var Timer = xdc.useModule('ti.sysbios.hal.Timer'); var timerParams = new Timer.Params(); timerParams.startMode = Timer.StartMode_USER; timerParams.runMode = Timer.RunMode_ONESHOT; timerParams.period = 1000; // 1ms Program.global.myTimer = Timer.create(Timer.ANY, "&myTimerFunc", timerParams); /* Define and add two Hwi HookSets * Notice, no deleteFxn is provided. */ var Hwi = xdc.useModule('ti.sysbios.hal.Hwi'); /* Hook Set 1 */ Hwi.addHookSet({ registerFxn: '&myRegister1', createFxn: '&myCreate1', beginFxn: '&myBegin1', endFxn: '&myEnd1', }); /* Hook Set 2 */ Hwi.addHookSet({ registerFxn: '&myRegister2', createFxn: '&myCreate2', beginFxn: '&myBegin2', endFxn: '&myEnd2', });
3-24
Software Interrupts
The Swi Manager controls the execution of all Swi functions. When the application calls one of the APIs above, the Swi Manager schedules the function corresponding to the specified Swi for execution. To handle Swi functions, the Swi Manager uses Swi objects. If a Swi is posted, it runs only after all pending Hwis have run. A Swi function in progress can be preempted at any time by a Hwi; the Hwi completes before the Swi function resumes. On the other hand, Swi functions always preempt tasks. All pending Swis run before even the highest priority task is allowed to run. In effect, a Swi is like a task with a priority higher than all ordinary tasks. Note: Two things to remember about Swi functions are: A Swi function runs to completion unless it is interrupted by a Hwi or preempted by a higher-priority Swi. Any hardware ISR that triggers or posts a Swi must have been invoked by the Hwi dispatcher (or by generated interrupt stubs on platforms for which the Hwi dispatcher is not provided, such as the MSP430). That is, the Swi must be triggered by a function called from a Hwi object.
Threading Modules
3-25
Software Interrupts
3.4.1
Here, swi0 is a handle to the created Swi object, swiFunc is the name of the function associated with the Swi, and swiParams is a structure of type Swi_Params that contains the Swi instance parameters (priority, arg0, arg1, etc). If NULL is passed instead of a pointer to an actual Swi_Params struct, a default set of parameters is used. "eb" is an error block you can use to handle errors that may occur during Swi object creation. Note: Swi_create() cannot be called from the context of a Hwi or another Swi thread. Applications that dynamically create Swi threads must do so from either the context of the main() function or a Task thread.
3-26
Software Interrupts
To create a Swi object in an XDCtools configuration file, use statements like these:
var Swi = xdc.useModule('ti.sysbios.knl.Swi'); var swiParams = new Swi.Params(); program.global.swi0 = Swi.create(swiParams);
3.4.2
3.4.3
Note: The Clock module creates and uses a Swi with the maximum Swi priority (that is, if there are 16 Swi priorities, the Clock Swi has priority 15).
Threading Modules
3-27
Software Interrupts
The following table shows the amount of system stack required to absorb the worst-case Swi interrupt nesting. This first number is the amount of system stack space required for the first priority level on a target. The second number shows the amount of stack space required for each subsequent priority level used in the application.
Target Family M3 MSP430 MSP430X MSP430X_small C674 C64P C64T C28_float C28_large Arm9 A8F
Units 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 8-bit bytes 16-bit words 16-bit words 8-bit bytes 8-bit bytes
See Section 3.3.2 for information about system stack use by Hwis and Section 3.5.3 for information about task stack size.
3.4.4
3-28
Software Interrupts
If Swis are enabled, the Swi Manager checks the priority of the posted Swi object against the priority of the thread that is currently running. If the thread currently running is the background Idle Loop, a Task, or a lower priority Swi, the Swi Manager removes the Swi from the list of posted Swi objects and switches the CPU control from the current thread to start execution of the posted Swi function. If the thread currently running is a Swi of the same or higher priority, the Swi Manager returns control to the current thread, and the posted Swi function runs after all other Swis of higher priority or the same priority that were previously posted finish execution. When multiple Swis of the same priority level have been posted, their respective Swi functions are executed in the order the Swis were posted. There are two important things to remember about Swi: When a Swi starts executing, it must run to completion without blocking. When called from within a hardware ISR, the code calling any Swi function that can trigger or post a Swi must be invoked by the Hwi dispatcher (or by generated interrupt stubs on platforms for which the Hwi dispatcher is not provided, such as the MSP430). That is, the Swi must be triggered by a function called from a Hwi object.
Swi functions can be preempted by threads of higher priority (such as a Hwi or a Swi of higher priority). However, Swi functions cannot block. You cannot suspend a Swi while it waits for somethinglike a deviceto be ready. If a Swi is posted multiple times before the Swi Manager has removed it from the posted Swi list, its Swi function executes only once, much like a Hwi is executed only once if the Hwi is triggered multiple times before the CPU clears the corresponding interrupt flag bit in the interrupt flag register. (See Section 3.4.5 for more information on how to handle Swis that are posted multiple times before they are scheduled for execution.) Applications should not make any assumptions about the order in which Swi functions of equal priority are called. However, a Swi function can safely post itself (or be posted by another interrupt). If more than one is pending, all Swi functions are called before any tasks run.
3.4.5
Threading Modules
3-29
Software Interrupts
Swi_post(), Swi_or(), and Swi_inc() post a Swi object unconditionally: Swi_post() does not modify the value of the Swi object trigger when it is used to post a Swi. Swi_or() sets the bits in the trigger determined by a mask that is passed as a parameter, and then posts the Swi. Swi_inc() increases the Swi's trigger value by one before posting the Swi object.
Swi_andn() and Swi_dec() post a Swi object only if the value of its trigger becomes 0: Swi_andn() clears the bits in the trigger determined by a mask passed as a parameter. Swi_dec() decreases the value of the trigger by one.
Table 3-6.
The Swi trigger allows you to have tighter control over the conditions that should cause a Swi function to be posted, or the number of times the Swi function should be executed once the Swi is posted and scheduled for execution. To access the value of its trigger, a Swi function can call Swi_getTrigger(). Swi_getTrigger() can be called only from the Swi objects function. The value returned by Swi_getTrigger() is the value of the trigger before the Swi object was removed from the posted Swi queue and the Swi function was scheduled for execution. When the Swi Manager removes a pending Swi object from the posted objects queue, its trigger is reset to its initial value. The initial value of the trigger should be set in the applications configuration script. If while the Swi function is executing, the Swi is posted again, its trigger is updated accordingly. However, this does not affect the value returned by Swi_getTrigger() while the Swi function executes. That is, the trigger value that Swi_getTrigger() returns is the latched trigger value when the Swi was removed from the list of pending Swis. The Swi's trigger however, is
3-30
Software Interrupts
immediately reset after the Swi is removed from the list of pending Swis and scheduled for execution. This gives the application the ability to keep updating the value of the Swi trigger if a new posting occurs, even if the Swi function has not finished its execution. For example, if a Swi object is posted multiple times before it is removed from the queue of posted Swis, the Swi Manager schedules its function to execute only once. However, if a Swi function must always run multiple times when the Swi object is posted multiple times, Swi_inc() should be used to post the Swi as shown in Figure 3-3. When a Swi has been posted using Swi_inc(), once the Swi Manager calls the corresponding Swi function for execution, the Swi function can access the Swi object trigger to know how many times it was posted before it was scheduled to run, and proceed to execute the same function as many times as the value of the trigger.
Threading Modules
3-31
Software Interrupts
Figure 3-3.
Program Configuration
Swi object mySwi, Function mySwiFxn
Trigger Value 0
Program Execution
-- Calls Swi_inc(&mySwi) -- mySwi is posted again before it is scheduled for execution -- Swi manager removes mySwi from posted Swi queue -- mySwiFxn is scheduled for execution
0 1
2 2
-- mySwiFxn is preempted by ISR that calls Swi_inc(&mySwi) -- mySwi is added to posted Swi queue
mySwiFxn() { . . . repetitions = SWI_getTrigger(); while (repetitions --) { 'run Swi function' } ... }
3-32
Software Interrupts
If more than one event must always happen for a given Swi to be triggered, Swi_andn() should be used to post the corresponding Swi object as shown in Figure 3-4. For example, if a Swi must wait for input data from two different devices before it can proceed, its trigger should have two set bits when the Swi object is configured. When both functions that provide input data have completed their tasks, they should both call Swi_andn() with complementary bitmasks that clear each of the bits set in the Swi trigger default value. Hence, the Swi is posted only when data from both processes is ready.
Figure 3-4.
Program Configuration
Swi object mySwi, Function mySwiFxn
Trigger Value
0 ... 1 1
Program Execution
0 ... 1 0
0 ... 0 0
-- Swi manager removes mySwi from posted Swi queue -- mySwiFxn is scheduled for execution
0 ... 1 1
0 ... 0 0
0 ... 1 1
0 ... 0 0
Threading Modules
3-33
Software Interrupts
In some situations the Swi function can call different functions depending on the event that posted it. In that case the program can use Swi_or() to post the Swi object unconditionally when an event happens. This is shown in Figure 3-5. The value of the bitmask used by Swi_or() encodes the event type that triggered the post operation, and can be used by the Swi function as a flag that identifies the event and serves to choose the function to execute.
Figure 3-5.
Program Configuration
Swi object mySwi, Function mySwiFxn
Trigger Value
0 ... 0 0
Program Execution
0 ... 0 1
-- mySwiFxn is executed
0 ... 0 0
0 ... 0 1
0 ... 1 0
-- mySwiFxn is executed
0 ... 0 0
0 ... 1 0
mySwiFxn() { ... eventType = Swi_getTrigger(); switch (eventType) { case '0x1': 'run alg 1' case '0x2': 'run alg 2' case '0x3': 'run alg 3' } ... }
3-34
Software Interrupts
If the program execution requires that multiple occurrences of the same event must take place before a Swi is posted, Swi_dec() should be used to post the Swi as shown in Figure 3-6. By configuring the Swi trigger to be equal to the number of occurrences of the event before the Swi should be posted and calling Swi_dec() every time the event occurs, the Swi is posted only after its trigger reaches 0; that is, after the event has occurred a number of times equal to the trigger value.
Figure 3-6.
Program Configuration
Swi object mySwi, Function mySwiFxn
Trigger Value 2
Program Execution
-- Swi manager removes mySwi from posted Swi queue -- mySwiFxn is scheduled for execution
3.4.6
Remember that a Swi function must complete before any blocked Task is allowed to run.
Threading Modules
3-35
Software Interrupts
3.4.7
where key is a value used by the Swi module to determine if Swi_disable() has been called more than once. This allows nesting of Swi_disable() / Swi_restore() calls, since only the outermost Swi_restore() call actually enables Swis. In other words, a task can disable and enable Swis without having to determine if Swi_disable() has already been called elsewhere. When Swis are disabled, a posted Swi function does not run at that time. The interrupt is latched in software and runs when Swis are enabled and it is the highest-priority thread that is ready to run. To delete a dynamically created Swi, use Swi_delete(). The memory associated with Swi is freed. Swi_delete() can only be called from the task level.
3.4.8
Swi Hooks
The Swi module supports the following set of Hook functions: Register. A function called before any statically created Swis are initialized at runtime. The register hook is called at boot time before main() and before interrupts are enabled. Create. A function called when a Swi is created. This includes Swis that are created statically and those created dynamically using Swi_create(). Ready. A function called when any Swi becomes ready to run. Begin. A function called just prior to running a Swi function. End. A function called just after returning from a Swi function.
3-36
Software Interrupts
The following Swi_HookSet structure type definition encapsulates the hook functions supported by the Swi module:
typedef struct Swi_HookSet { Void (*registerFxn)(Int); /* Register Hook */ Void (*createFxn)(Handle, Error.Block *); /* Create Hook */ Void (*readyFxn)(Handle); /* Ready Hook */ Void (*beginFxn)(Handle); /* Begin Hook */ Void (*endFxn)(Handle); /* End Hook */ Void (*deleteFxn)(Handle); /* Delete Hook */ };
Swi Hook functions can only be configured statically. When more than one Hook Set is defined, the individual hook functions of a common type are invoked in hook ID order. 3.4.8.1 Register Function The Register function is provided to allow a hook set to store its corresponding hook ID. This ID can be passed to Swi_setHookContext() and Swi_getHookContext() to set or get hook-specific context. The Register function must be specified if the hook implementation needs to use Swi_setHookContext() or Swi_getHookContext(). The registerFxn function is called during system initialization before interrupts have been enabled. The Register functions has the following signature:
Void registerFxn(Int id);
3.4.8.2 Create and Delete Functions The Create and Delete functions are called whenever a Swi is created or deleted. The Create function is passed an Error_Block that is to be passed to Memory_alloc() for applications that require additional context storage space. The createFxn and deleteFxn functions are called with interrupts enabled (unless called at boot time or from main()). These functions have the following signatures.
Void createFxn(Swi_Handle swi, Error_Block *eb); Void deleteFxn(Swi_Handle swi);
Threading Modules
3-37
Software Interrupts
3.4.8.3 Ready, Begin and End Functions The Ready, Begin and End hook functions are called with interrupts enabled. The readyFxn function is called when a Swi is posted and made ready to run. The beginFxn function is called right before the function associated with the given Swi is run. The endFxn function is called right after returning from the Swi function. Both readyFxn and beginFxn hooks are provided because a Swi may be posted and ready but still pending while a higher-priority thread completes. These functions have the following signatures:
Void readyFxn(Swi_Handle swi); Void beginFxn(Swi_Handle swi); Void endFxn(Swi_Handle swi);
3.4.8.4 Swi Hooks Example The following example application uses two Swi hook sets. This example demonstrates how to read and write the Hook Context Pointer associated with each hook set. The XDCtools configuration script and program output are shown after the C code listing. This is the C code for the example:
/* ======== SwiHookExample.c ======== * This example demonstrates basic Swi hook usage */ #include #include #include #include #include #include #include #include <xdc/std.h> <xdc/runtime/Error.h> <xdc/runtime/System.h> <xdc/runtime/Timestamp.h> <ti/sysbios/BIOS.h> <ti/sysbios/knl/Task.h> <ti/sysbios/hal/Timer.h> <ti/sysbios/knl/Swi.h>
3-38
Software Interrupts
/* ======== myRegister1 ======== * invoked during Swi module startup before main * for each HookSet */ Void myRegister1(Int hookSetId) { System_printf("myRegister1: assigned hookSet Id = %d\n", hookSetId); myHookSetId1 = hookSetId; } /* ======== myCreate1 ======== * invoked during Swi_create for dynamically created Swis */ Void myCreate1(Swi_Handle swi, Error_Block *eb) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId1); /* pEnv should be 0 at this point. If not, there's a bug. */ System_printf("myCreate1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId1, (Ptr)0xdead1); } /* ======== myReady1 ======== * invoked when Swi is posted */ Void myReady1(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId1); System_printf("myReady1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId1, (Ptr)0xbeef1); }
Threading Modules
3-39
Software Interrupts
/* ======== myBegin1 ======== * invoked just before Swi func is run */ Void myBegin1(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId1); System_printf("myBegin1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId1, (Ptr)0xfeeb1); } /* ======== myEnd1 ======== * invoked after Swi func returns */ Void myEnd1(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId1); System_printf("myEnd1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId1, (Ptr)0xc0de1); } /* ======== myDelete1 ======== * invoked upon Swi deletion */ Void myDelete1(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId1); System_printf("myDelete1: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); }
3-40
Software Interrupts
/* HookSet 2 functions */ /* ======== myRegister2 ======== * invoked during Swi module startup before main * for each HookSet */ Void myRegister2(Int hookSetId) { System_printf("myRegister2: assigned hookSet Id = %d\n", hookSetId); myHookSetId2 = hookSetId; } /* ======== myCreate2 ======== * invoked during Swi_create for dynamically created Swis */ Void myCreate2(Swi_Handle swi, Error_Block *eb) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId2); /* pEnv should be 0 at this point. If not, there's a bug. */ System_printf("myCreate2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId2, (Ptr)0xdead2); } /* ======== myReady2 ======== * invoked when Swi is posted */ Void myReady2(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId2); System_printf("myReady2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId2, (Ptr)0xbeef2); }
Threading Modules
3-41
Software Interrupts
/* ======== myBegin2 ======== * invoked just before Swi func is run */ Void myBegin2(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId2); System_printf("myBegin2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId2, (Ptr)0xfeeb2); } /* ======== myEnd2 ======== * invoked after Swi func returns */ Void myEnd2(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId2); System_printf("myEnd2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); Swi_setHookContext(swi, myHookSetId2, (Ptr)0xc0de2); } /* ======== myDelete2 ======== * invoked upon Swi deletion */ Void myDelete2(Swi_Handle swi) { Ptr pEnv; pEnv = Swi_getHookContext(swi, myHookSetId2); System_printf("myDelete2: pEnv = 0x%x, time = %d\n", pEnv, Timestamp_get32()); } /* ======== mySwiFunc ======== */ Void mySwiFunc(UArg arg0, UArg arg1) { System_printf("Entering mySwi.\n"); }
3-42
Software Interrupts /* ======== myTaskFunc ======== */ Void myTaskFunc(UArg arg0, UArg arg1) { System_printf("Entering myTask.\n"); System_printf("Posting mySwi.\n"); Swi_post(mySwi); System_printf("Deleting mySwi.\n"); Swi_delete(&mySwi); System_printf("myTask exiting ...\n"); } /* ======== myIdleFunc ======== */ Void myIdleFunc() { System_printf("Entering myIdleFunc().\n"); System_exit(0); } /* ======== main ======== */ Int main(Int argc, Char* argv[]) { Error_Block eb; Error_init(&eb); System_printf("Starting SwiHookExample...\n"); /* Create mySwi with default params * to exercise Swi Hook Functions */ mySwi = Swi_create(mySwiFunc, NULL, &eb); if (mySwi == NULL) { System_abort("Swi create failed"); } BIOS_start(); return (0); }
Threading Modules
3-43
Software Interrupts
3-44
Software Interrupts
Threading Modules
3-45
Tasks
3.5 Tasks
SYS/BIOS task objects are threads that are managed by the Task module. Tasks have higher priority than the Idle Loop and lower priority than hardware and software interrupts. The Task module dynamically schedules and preempts tasks based on the tasks priority level and the tasks current execution state. This ensures that the processor is always given to the highest priority thread that is ready to run. There are up to 32 priority levels available for tasks, with the default number of levels being 16. The maximum number of priority levels is 16 for MSP430 and C28x. The lowest priority level (0) is reserved for running the Idle Loop. The Task module provides a set of functions that manipulate task objects. They access Task objects through handles of type Task_Handle. The kernel maintains a copy of the processor registers for each task object. Each task has its own runtime stack for storing local variables as well as for further nesting of function calls. See Section 3.5.3 for information about task stack sizes. All tasks executing within a single program share a common set of global variables, accessed according to the standard rules of scope defined for C functions. Communication between the target and the SYS/BIOS Analysis Tools is performed in a Real-Time Analysis (RTA) task. The priority of this task is configurable and defaults to "1", the lowest priority. This ensures that the SYS/BIOS Analysis Tools do not interfere with higher-priority processing.
3.5.1
Creating Tasks
You can create Task objects either dynamically with a call to Task_create() or statically in the configuration. Tasks that you create dynamically can also be deleted during program execution.
3.5.1.1 Creating and Deleting Tasks Dynamically You can spawn SYS/BIOS tasks by calling the function Task_create(), whose parameters include the address of a C function in which the new task begins its execution. The value returned by Task_create() is a handle of type Task_Handle, which you can then pass as an argument to other Task functions.
3-46
Tasks
If NULL is passed instead of a pointer to an actual Task_Params struct, a default set of parameters is used. The "eb" is an error block that you can use to handle errors that may occur during Task object creation. See Section 3.5.3 for information about task stack sizes. A task becomes active when it is created and preempts the currently running task if it has a higher priority. The memory used by Task objects and stacks can be reclaimed by calling Task_delete(). Task_delete() removes the task from all internal queues and frees the task object and stack. Any Semaphores or other resources held by the task are not released. Deleting a task that holds such resources is often an application design error, although not necessarily so. In most cases, such resources should be released prior to deleting the task. It is only safe to delete a Task that is either in the Terminated or Inactive State.
Void Task_delete(Task_Handle *task);
3.5.1.2 Creating Tasks Statically You can also create tasks statically within a configuration script. The configuration allows you to set a number of properties for each task and for the Task Manager itself. For a complete description of all Task properties, see the Task module in the "ti.sysbios.knl" package documentation in the online documentation. (For information on running online help, see Section 1.7.1, Using the API Reference Help System, page 1-13.)
Threading Modules
3-47
Tasks
While it is running, a task that was created statically behaves exactly the same as a task created with Task_create(). You cannot use the Task_delete() function to delete statically-created tasks. See the XDCtools Consumer Users Guide for a discussion of the benefits of creating objects statically. The Task module automatically creates the Task_idle task and gives it the lowest task priority (0). It runs the functions defined for the Idle objects when no higher-priority Hwi, Swi, or Task is running. When you configure tasks to have equal priority, they are scheduled in the order in which they are created in the configuration script. Tasks can have up to 32 priority levels with 16 being the default. The maximum number of priority levels is 16 for MSP430 and C28x. The highest level is the number of priorities defined minus 1, and the lowest is 0. The priority level of 0 is reserved for the system idle task. You cannot sort tasks within a single priority level by setting the order property. If you want a task to be initially inactive, set its priority to -1. Such tasks are not scheduled to run until their priority is raised at runtime.
3.5.2
Tasks are scheduled for execution according to a priority level assigned by the application. There can be no more than one running task. As a rule, no ready task has a priority level greater than that of the currently running task, since Task preempts the running task in favor of the higher-priority ready task. Unlike many time-sharing operating systems that give each task its fair share of the processor, SYS/BIOS immediately preempts the current task whenever a task of higher priority becomes ready to run.
3-48
Tasks
The maximum priority level is Task_numPriorities-1 (default=15; maximum=31). The minimum priority is 1. If the priority is less than 0, the task is barred from further execution until its priority is raised at a later time by another task. If the priority equals Task_numPriorities-1, the task cannot be preempted by another task. A highest-priority task can still call Semaphore_pend(), Task_sleep(), or some other blocking call to allow tasks of lower priority to run. During the course of a program, each tasks mode of execution can change for a number of reasons. Figure 3-7 shows how execution modes change.
Figure 3-7.
Task_Mode_READY
Task_Mode_RUNNING
Task_Mode_TERMINATED
Task_Mode_BLOCKED
Functions in the Task, Semaphore, Event, and Mailbox modules alter the execution state of task objects: blocking or terminating the currently running task, readying a previously suspended task, re-scheduling the current task, and so forth. There is one task whose execution mode is Task_Mode_RUNNING. If all program tasks are blocked and no Hwi or Swi is running, Task executes the Task_idle task, whose priority is lower than all other tasks in the system. When a task is preempted by a Hwi or Swi, the task execution mode returned for that task by Task_stat() is still Task_Mode_RUNNING because the task will run when the preemption ends.
Threading Modules
3-49
Tasks
Note: Do not make blocking calls, such as Semaphore_pend() or Task_sleep(), from within an Idle function. Doing so causes the application to terminate.
When the Task_Mode_RUNNING task transitions to any of the other three states, control switches to the highest-priority task that is ready to run (that is, whose mode is Task_Mode_READY). A Task_Mode_RUNNING task transitions to one of the other modes in the following ways: The running task becomes Task_Mode_TERMINATED by calling Task_exit(), which is automatically called if and when a task returns from its top-level function. After all tasks have returned, the Task Manager terminates program execution by calling System_exit() with a status code of 0. The running task becomes Task_Mode_BLOCKED when it calls a function (for example, Semaphore_pend() or Task_sleep() ) that causes the current task to suspend its execution; tasks can move into this state when they are performing certain I/O operations, awaiting availability of some shared resource, or idling. The running task becomes Task_Mode_READY and is preempted whenever some other, higher-priority task becomes ready to run. Task_setpri() can cause this type of transition if the priority of the current task is no longer the highest in the system. A task can also use Task_yield() to yield to other tasks with the same priority. A task that yields becomes ready to run.
A task that is currently Task_Mode_BLOCKED transitions to the ready state in response to a particular event: completion of an I/O operation, availability of a shared resource, the elapse of a specified period of time, and so forth. By virtue of becoming Task_Mode_READY, this task is scheduled for execution according to its priority level; and, of course, this task immediately transitions to the running state if its priority is higher than the currently executing task. Task schedules tasks of equal priority on a first-come, firstserved basis.
3.5.3
Task Stacks
The kernel maintains a copy of the processor registers for each Task object. Each Task has its own runtime stack for storing local variables as well as for further nesting of function calls. You can specify the stack size separately for each Task object when you create the Task object statically or dynamically.
3-50
Tasks
Each task stack must be large enough to handle both its normal function calls and two full interrupting Hwi contexts. The following table shows the amount of task stack required to absorb the worst-case interrupt nesting. These numbers represent two full Hwi interrupt contexts plus space used by the task scheduler for its local variables. Additional nested interrupt contexts are pushed onto the common system stack.
When a Task is preempted, a task stack may be required to contain either two interrupting Hwi contexts (if the Task is preempted by a Hwi) or one interrupting Hwi context and one Task preemption context (if the Task is preempted by a higher-priority Task). Since the Hwi context is larger than the Task context, the numbers given are for two Hwi contexts. If a Task blocks, only those registers that a C function must save are saved to the task stack. Another way to find the correct stack size is to make the stack size large and then use Code Composer Studio software to find the stack size actually used. See Section 3.3.2 for information about system stack use by Hwis and Section 3.4.3 for information about system stack size.
Threading Modules
3-51
Tasks
3.5.4
Task_stat(Task_self(), &statbuf); /* call func to get status */ if (statbuf.used > (statbuf.stacksize * 9 / 10)) { Log_printf(&trace, "Over 90% of task's stack is in use.\n") }
"ti.sysbios.knl"
package
You can use the Runtime Object Viewer (ROV) to examine run-time Task stack usage. For information, see Section 6.5.3.
3.5.5
Task Hooks
The Task module supports the following set of Hook functions: Register. A function called before any statically created Tasks are initialized at runtime. The register hook is called at boot time before main() and before interrupts are enabled. Create. A function called when a Task is created. This includes Tasks that are created statically and those created dynamically using Task_create() or Task_construct(). The Create hook is called outside of a Task_disable/enable block and before the task has been added to the ready list. Ready. A function called when a Task becomes ready to run. The ready hook is called from within a Task_disable/enable block with interrupts enabled. Switch. A function called just before a task switch occurs. The 'prev' and 'next' task handles are passed to the Switch hook. 'prev' is set to NULL
3-52
Tasks
for the initial task switch that occurs during SYS/BIOS startup. The Switch hook is called from within a Task_disable/enable block with interrupts enabled. Exit. A function called when a task exits using Task_exit(). The exit hook is passed the handle of the exiting task. The exit hook is called outside of a Task_disable/enable block and before the task has been removed from the kernel lists. Delete. A function called when a task is deleted at runtime with Task_delete().
The following HookSet structure type definition encapsulates the hook functions supported by the Task module:
typedef struct Task_HookSet { Void (*registerFxn)(Int); /* Register Hook */ Void (*createFxn)(Handle, Error.Block *); /* Create Hook */ Void (*readyFxn)(Handle); /* Ready Hook */ Void (*switchFxn)(Handle, Handle); /* Switch Hook */ Void (*exitFxn)(Handle); /* Exit Hook */ Void (*deleteFxn)(Handle); /* Delete Hook */ };
When more than one hook set is defined, the individual hook functions of a common type are invoked in hook ID order. Task hook functions can only be configured statically. 3.5.5.1 Register Function The Register function is provided to allow a hook set to store its corresponding hook ID. This ID can be passed to Task_setHookContext() and Task_getHookContext() to set or get hook-specific context. The Register function must be specified if the hook implementation needs to use Task_setHookContext() or Task_getHookContext(). The registerFxn function is called during system initialization before interrupts have been enabled. The Register function has the following signature:
Void registerFxn(Int id);
3.5.5.2 Create and Delete Functions The Create and Delete functions are called whenever a Task is created or deleted. The Create function is passed an Error_Block that is to be passed to Memory_alloc() for applications that require additional context storage space.
Threading Modules
3-53
Tasks
The createFxn and deleteFxn functions are called with interrupts enabled (unless called at boot time or from main()). These functions have the following signatures.
Void createFxn(Task_Handle task, Error_Block *eb); Void deleteFxn(Task_Handle task);
3.5.5.3 Switch Function If a switch function is specified, it is invoked just before the new task is switched to. The switch function is called with interrupts enabled. This function can be used for purposes such as saving/restoring additional task context (for example, external hardware registers), checking for task stack overflow, and monitoring the time used by each task. The switchFxn has the following signature:
Void switchFxn(Task_Handle prev, Task_Handle next);
3.5.5.4 Ready Function If a Ready Function is specified, it is invoked whenever a task is made ready to run. The Ready Function is called with interrupts enabled (unless called at boot time or from main()). The readyFxn has the following signature:
Void readyFxn(Task_Handle task);
3.5.5.5 Exit Function If an Exit Function is specified, it is invoked when a task exits (via call to Task_exit() or when a task returns from its' main function). The exitFxn is called with interrupts enabled. The exitFxn has the following signature:
Void exitFxn(Task_Handle task);
3.5.5.6 Task Hooks Example The following example application uses a single Task hook set. This example demonstrates how to read and write the Hook Context Pointer associated with each hook set. The XDCtools configuration script and program output are shown after the C code listing.
3-54
Tasks
#include <ti/sysbios/BIOS.h> #include <ti/sysbios/knl/Task.h> Task_Handle myTsk0, myTsk1, myTsk2; Int myHookSetId, myHookSetId2; /* HookSet functions */ /* ======== myRegister ======== * invoked during Swi module startup before main() * for each HookSet */ Void myRegister(Int hookSetId) { System_printf("myRegister: assigned HookSet Id = %d\n", hookSetId); myHookSetId = hookSetId; } /* ======== myCreate ======== * invoked during Task_create for dynamically * created Tasks */ Void myCreate(Task_Handle task, Error_Block *eb) { String name; Ptr pEnv; name = Task_Handle_name(task); pEnv = Task_getHookContext(task, myHookSetId); System_printf("myCreate: task name = '%s', pEnv = 0x%x\n", name, pEnv); Task_setHookContext(task, myHookSetId, (Ptr)0xdead); }
Threading Modules
3-55
Tasks /* ======== myReady ======== * invoked when Task is made ready to run */ Void myReady(Task_Handle task) { String name; Ptr pEnv; name = Task_Handle_name(task); pEnv = Task_getHookContext(task, myHookSetId); System_printf("myReady: task name = '%s', pEnv = 0x%x\n", name, pEnv); Task_setHookContext(task, myHookSetId, (Ptr)0xc0de); } /* ======== mySwitch ======== * invoked whenever a Task switch occurs/is made ready to run */ Void mySwitch(Task_Handle prev, Task_Handle next) { String prevName; String nextName; Ptr pPrevEnv; Ptr pNextEnv; if (prev == NULL) { System_printf("mySwitch: ignoring dummy 1st prev Task\n"); } else { prevName = Task_Handle_name(prev); pPrevEnv = Task_getHookContext(prev, myHookSetId); System_printf("mySwitch: prev name = '%s', pPrevEnv = 0x%x\n", prevName, pPrevEnv); Task_setHookContext(prev, myHookSetId, (Ptr)0xcafec0de); } nextName = Task_Handle_name(next); pNextEnv = Task_getHookContext(next, myHookSetId); System_printf(" next name = '%s', pNextEnv = 0x%x\n", nextName, pNextEnv); Task_setHookContext(next, myHookSetId, (Ptr)0xc001c0de); }
3-56
Tasks /* ======== myExit ======== * invoked whenever a Task calls Task_exit() or falls through * the bottom of its task function. */ Void myExit(Task_Handle task) { Task_Handle curTask = task; String name; Ptr pEnv; name = Task_Handle_name(curTask); pEnv = Task_getHookContext(curTask, myHookSetId); System_printf("myExit: curTask name = '%s', pEnv = 0x%x\n", name, pEnv); Task_setHookContext(curTask, myHookSetId, (Ptr)0xdeadbeef); } /* ======== myDelete ======== * invoked upon Task deletion */ Void myDelete(Task_Handle task) { String name; Ptr pEnv; name = Task_Handle_name(task); pEnv = Task_getHookContext(task, myHookSetId); System_printf("myDelete: task name = '%s', pEnv = 0x%x\n", name, pEnv); } /* Define 3 identical tasks */ Void myTsk0Func(UArg arg0, UArg arg1) { System_printf("myTsk0 Entering\n"); System_printf("myTsk0 Calling Task_yield\n"); Task_yield(); System_printf("myTsk0 Exiting\n"); }
Threading Modules
3-57
Tasks Void myTsk1Func(UArg arg0, UArg arg1) { System_printf("myTsk1 Entering\n"); System_printf("myTsk1 Calling Task_yield\n"); Task_yield(); System_printf("myTsk1 Exiting\n"); } Void myTsk2Func(UArg arg0, UArg arg1) { System_printf("myTsk2 Entering\n"); System_printf("myTsk2 Calling Task_yield\n"); Task_yield(); System_printf("myTsk2 Exiting\n"); } /* ======== main ======== */ Int main(Int argc, Char* argv[]) { Task_Params params; Error_Block eb; Error_init(&eb); Task_Params_init(¶ms); params.instance->name = "myTsk0"; myTsk0 = Task_create(myTsk0Func, ¶ms, &eb); if (myTsk0 == NULL) { System_abort("myTsk0 create failed"); } params.instance->name = "myTsk1"; myTsk1 = Task_create(myTsk1Func, ¶ms, &eb); if (myTsk1 == NULL) { System_abort("myTsk1 create failed"); } params.instance->name = "myTsk2"; myTsk2 = Task_create(myTsk2Func, ¶ms, &eb); if (myTsk2 == NULL) { System_abort("myTsk2 create failed"); } BIOS_start(); return (0); }
3-58
Tasks
/* ======== myIdleFunc ======== */ Void myIdleFunc() { System_printf("Entering idleFunc().\n"); Task_delete(&myTsk0); Task_delete(&myTsk1); Task_delete(&myTsk2); System_exit(0); }
Threading Modules
3-59
Tasks
3-60
Tasks
3.5.6
Example 3-1.
Time-Slice Scheduling
/* * ======== slice.c ======== * This example utilizes time-slice scheduling among three * tasks of equal priority. A fourth task of higher * priority periodically preempts execution. * * A periodic Clock object drives the time-slice scheduling. * Every 4 milliseconds, the Clock object calls Task_yield() * which forces the current task to relinquish access to * to the CPU. * * Because a task is always ready to run, this program * does not spend time in the idle loop. Calls to Idle_run() * are added to give time to the Idle loop functions * occasionally. The call to Idle_run() is within a * Task_disable(), Task_restore() block because the call * to Idle_run() is not reentrant. */
Threading Modules
3-61
Tasks
#include <xdc/std.h> #include <xdc/runtime/System.h> #include <xdc/runtime/Error.h> #include #include #include #include #include #include <ti/sysbios/BIOS.h> <ti/sysbios/knl/Semaphore.h> <ti/sysbios/knl/Clock.h> <ti/sysbios/knl/Clock.h> <ti/sysbios/knl/Idle.h> <ti/sysbios/knl/Task.h>
#include <xdc/cfg/global.h> Void hiPriTask(UArg arg0, UArg arg1); Void task(UArg arg0, UArg arg1); Void clockHandler1(UArg arg); Void clockHandler2(UArg arg); Semaphore_Handle sem; /* ======== main ======== */ Void main() { Task_Params taskParams; Task_Handle myTsk0, myTski; Clock_Params clockParams; Clock_Handle myClk0, myClk1; Error_Block eb; UInt i; System_printf("Slice example started!\n"); Error_init(&eb); /* Create 1 task with priority 15 */ Task_Params_init(&taskParams); taskParams.stackSize = 512; taskParams.priority = 15; myTsk0 = Task_create((Task_FuncPtr)hiPriTask, &taskParams, &eb); if (myTsk0 == NULL) { System_abort("hiPriTask create failed"); }
3-62
Tasks
/* Create 3 tasks with priority 1 */ /* re-uses taskParams */ taskParams.priority = 1; for (i = 0; i < 3; i++) { taskParams.arg0 = i; myTski = Task_create((Task_FuncPtr)task, &taskParams, &eb); if (myTski == NULL) { System_abort("LoPri Task %d create failed", i); } } /* * Create clock that calls Task_yield() every * 4 Clock ticks */ Clock_Params_init(&clockParams); clockParams.period = 4;/* every 4 Clock ticks */ clockParams.startFlag = TRUE;/* start immediately */ myClk0 = Clock_create((Clock_FuncPtr)clockHandler1, 4, &clockParams, &eb); if (myClk0 == NULL) { System_abort("Clock0 create failed"); }
Threading Modules
3-63
Tasks
/* * Create clock that calls Semaphore_post() every * 16 Clock ticks */ clockParams.period = 16;/* every 16 Clock ticks */ clockParams.startFlag = TRUE;/* start immediately */ myClk1 = Clock_create((Clock_FuncPtr)clockHandler2, 16, &clockParams, &eb); if (myClk1 == NULL) { System_abort("Clock1 create failed"); } /* * Create semaphore with initial count = 0 * and default params */ sem = Semaphore_create(0, NULL, &eb); if (sem == NULL) { System_abort("Semaphore create failed"); } /* Start SYS/BIOS */ BIOS_start(); } /* ======== clockHandler1 ======== */ Void clockHandler1(UArg arg) { /* Call Task_yield every 4 ms */ Task_yield(); } /* ======== clockHandler2 ======== */ Void clockHandler2(UArg arg) { /* Call Semaphore_post every 16 ms */ Semaphore_post(sem); }
3-64
Tasks /* ======== task ======== */ Void task(UArg arg0, UArg arg1) { Int time; Int prevtime = -1; UInt taskKey; /* While loop simulates work load of time-sharing tasks */ while (1) { time = Clock_getTicks(); /* print time once per clock tick */ if (time >= prevtime + 1) { prevtime = time; System_printf("Task %d: time is %d\n", (Int)arg0, time); } /* check for rollover */ if (prevtime > time) { prevtime = time; } /* Process the Idle Loop functions */ taskKey = Task_disable(); Idle_run(); Task_restore(taskKey); } } /* ======== hiPriTask ======== */ Void hiPriTask(UArg arg0, UArg arg1) { static Int numTimes = 0; while (1) { System_printf("hiPriTask here\n"); if (++numTimes < 3) { Semaphore_pend(sem, BIOS_WAIT_FOREVER); } else { System_printf("Slice example ending.\n"); System_exit(0); } } }
Threading Modules
3-65
Tasks
3-66
Threading Modules
3-67
#include <xdc/cfg/global.h> typedef struct { List_Elem elem; UInt32 timeout; UInt32 period; Void (*fxn)(UArg); UArg arg; } Clock_Object;
3-68
Example Using Hwi, Swi, and Task Threads Clock_Object clk1, clk2; Timer_Handle timer; Semaphore_Handle sem; Swi_Handle swi; Task_Handle task; List_Handle clockList; /* Here on Timer interrupt */ Void hwiFxn(UArg arg) { Swi_post(swi); } /* Swi thread to handle Timer interrupt */ Void swiFxn(UArg arg1, UArg arg2) { List_Elem *elem; Clock_Object *obj; /* point to first clock object in the clockList */ elem = List_next(clockList, NULL); /* service all the Clock Objects in the clockList */ while (elem != NULL) { obj = (Clock_Object *)elem; /* decrement the timeout counter */ obj->timeout -= 1; /* if period has expired, refresh the timeout * value and invoke the clock func */ if (obj->timeout == 0) { obj->timeout = obj->period; (obj->fxn)(obj->arg); } /* advance to next clock object in clockList */ elem = List_next(clockList, elem); } }
Threading Modules
3-69
Example Using Hwi, Swi, and Task Threads /* Task thread pends on Semaphore posted by Clock thread */ Void taskFxn(UArg arg1, UArg arg2) { System_printf("In taskFxn pending on Sempahore.\n"); Semaphore_pend(sem, BIOS_WAIT_FOREVER); System_printf("In taskFxn returned from Sempahore.\n"); System_exit(0); } /* First Clock function, invoked every 5 timer interrupts */ Void clk1Fxn(UArg arg) { System_printf("In clk1Fxn, arg = %d.\n", arg); clk1.arg++; } /* Second Clock function, invoked every 20 timer interrupts */ Void clk2Fxn(UArg sem) { System_printf("In clk2Fxn, posting Semaphore.\n"); Semaphore_post((Semaphore_Object *)sem); } /* main() */ Int main(Int argc, char* argv[]) { Timer_Params timerParams; Task_Params taskParams; Error_Block eb; System_printf("Starting HwiSwiTask example.\n"); Error_init(&eb); Timer_Params_init(&timerParams); Task_Params_init(&taskParams); /* Create a Swi with default priority (15). * Swi handler is 'swiFxn' which runs as a Swi thread. */ swi = Swi_create(swiFxn, NULL, &eb); if (swi == NULL) { System_abort("Swi create failed"); }
3-70
Example Using Hwi, Swi, and Task Threads /* Create a Task with priority 3. * Task function is 'taskFxn' which runs as a Task thread. */ taskParams.priority = 3; task = Task_create(taskFxn, &taskParams, &eb); if (task == NULL) { System_abort("Task create failed"); } /* Create a binary Semaphore for example task to pend on */ sem = Semaphore_create(0, NULL, &eb); if (sem == NULL) { System_abort("Semaphore create failed"); } /* Create a List to hold the Clock Objects on */ clockList = List_create(NULL, &eb); if (clockList == NULL) { System_abort("List create failed"); } /* setup clk1 to go off every 5 timer interrupts. */ clk1.fxn = clk1Fxn; clk1.period = 5; clk1.timeout = 5; clk1.arg = 1; /* add the Clock object to the clockList */ List_put(clockList, &clk1.elem); /* setup clk2 to go off every 20 timer interrupts. */ clk2.fxn = clk2Fxn; clk2.period = 20; clk2.timeout = 20; clk2.arg = (UArg)sem; /* add the Clock object to the clockList */ List_put(clockList, &clk2.elem);
Threading Modules
3-71
Example Using Hwi, Swi, and Task Threads /* Configure a periodic interrupt using any available Timer * with a 1000 microsecond (1ms) interrupt period. * * The Timer interrupt will be handled by 'hwiFxn' which * will run as a Hwi thread. */ timerParams.period = 1000; timer = Timer_create(Timer_ANY, hwiFxn, &timerParams, &eb); if (timer == NULL) { System_abort("Timer create failed"); } BIOS_start(); return(0); }
3-72
Threading Modules
3-73
3-74
Chapter 4
Synchronization Modules
This chapter describes modules that can be used to synchronize access to shared resources. Topic
4.1 4.2 4.3 4.4
Page
4-1
Semaphores
4.1 Semaphores
SYS/BIOS provides a fundamental set of functions for inter-task synchronization and communication based upon semaphores. Semaphores are often used to coordinate access to a shared resource among a set of competing tasks. The Semaphore module provides functions that manipulate semaphore objects accessed through handles of type Semaphore_Handle. Semaphore objects can be declared as either counting or binary semaphores. They can be used for task synchronization and mutual exclusion. The same APIs are used for both counting and binary semaphores. Binary semaphores are either available or unavailable. Their value cannot be incremented beyond 1. Thus, they should be used for coordinating access to a shared resource by a maximum of two tasks. Binary semaphores provide better performance than counting semaphores. Counting semaphores keep an internal count of the number of corresponding resources available. When count is greater than 0, tasks do not block when acquiring a semaphore. The maximum count value for a semaphores plus one is the number of tasks a counting semaphore can coordinate. To configure the type of semaphore, use the following configuration parameter:
config Mode mode = Mode_COUNTING;
The functions Semaphore_create() and Semaphore_delete() are used to create and delete semaphore objects, respectively, as shown in Example 41. You can also create semaphore objects statically. See the XDCtools Consumer Users Guide for a discussion of the benefits of creating objects statically.
Example 4-1.
The semaphore count is initialized to count when it is created. In general, count is set to the number of resources that the semaphore is synchronizing. Semaphore_pend() waits for a semaphore. If the semaphore count is greater than 0, Semaphore_pend() simply decrements the count and returns. Otherwise, Semaphore_pend() waits for the semaphore to be posted by Semaphore_post().
4-2
Semaphores
The timeout parameter to Semaphore_pend(), as shown in Example 4-2, allows the task to wait until a timeout, to wait indefinitely (BIOS_WAIT_FOREVER), or to not wait at all (BIOS_NO_WAIT). Semaphore_pend()s return value is used to indicate if the semaphore was acquired successfully.
Example 4-2.
Example 4-3 shows Semaphore_post(), which is used to signal a semaphore. If a task is waiting for the semaphore, Semaphore_post() removes the task from the semaphore queue and puts it on the ready queue. If no tasks are waiting, Semaphore_post() simply increments the semaphore count and returns.
Example 4-3.
4.1.1
Semaphore Example
Example 4-4 provides sample code for three writer tasks that create unique messages and place them on a list for one reader task. The writer tasks call Semaphore_post() to indicate that another message has been put on the list. The reader task calls Semaphore_pend() to wait for messages. Semaphore_pend() returns only when a message is available on the list. The reader task prints the message using the System_printf() function. The three writer tasks, a reader task, a semaphore, and a list in this example program were created statically as follows:
Program.system = xdc.useModule('xdc.runtime.SysMin'); var Sem = xdc.useModule('ti.sysbios.knl.Semaphore'); Program.global.sem = Sem.create(0); Program.global.sem.mode = Sem.Mode_COUNTING; var Task = xdc.useModule('ti.sysbios.knl.Task'); Task.idleTaskVitalTaskFlag = false; var reader = Task.create('&reader'); reader.priority = 5; var writer0 = Task.create('&writer'); writer0.priority = 3; writer0.arg0 = 0;
Synchronization Modules
4-3
Semaphores var writer1 = Task.create('&writer'); writer1.priority = 3; writer1.arg0 = 1; var writer2 = Task.create('&writer'); writer2.priority = 3; writer2.arg0 = 2; /* uses List module in IPC distribution */ var List = xdc.useModule('ti.sdo.utils.List'); Program.global.msgList = List.create(); Program.global.freeList = List.create();
Since this program employs multiple tasks, a counting semaphore is used to synchronize access to the list. Figure 4-1 provides a view of the results from Example 4-3. Though the three writer tasks are scheduled first, the messages are read as soon as they have been put on the list, because the readers task priority is higher than that of the writer.
Example 4-4.
4-4
Semaphores /* The extern extern extern following objects are created statically. */ Semaphore_Handle sem; List_Handle msgList; List_Handle freeList;
/* ======== main ======== */ Int main(Int argc, Char* argv[]) { Int i; MsgObj *msg; Error_Block eb; Error_init(&eb); msg = (MsgObj *) Memory_alloc(NULL, NUMMSGS * sizeof(MsgObj), 0, &eb); if (msg == NULL) { System_abort("Memory allocation failed"); } /* Put all messages on freeList */ for (i = 0; i < NUMMSGS; msg++, i++) { List_put(freeList, (List_Elem *) msg); } BIOS_start(); System_exit(0); return(0); }
Synchronization Modules
4-5
Semaphores /* ======== reader ======== */ Void reader() { Msg msg; Int i; for (i = 0; i < NUMMSGS * NUMWRITERS; i++) { /* Wait for semaphore to be posted by writer(). */ Semaphore_pend(sem, BIOS_WAIT_FOREVER); /* get message */ msg = List_get(msgList); /* print value */ System_printf("read '%c' from (%d).\n", msg->val, msg->id); /* free msg */ List_put(freeList, (List_Elem *) msg); } System_printf("reader done.\n"); } /* ======== writer ======== */ Void writer(Int id) { Msg msg; Int i; for (i = 0; i < NUMMSGS; i++) { /* Get msg from the free list. Since reader is higher * priority and only blocks on sem, list is never * empty. */ msg = List_get(freeList); /* fill in value */ msg->id = id; msg->val = (i & 0xf) + 'a'; System_printf("(%d) writing '%c' ...\n", id, msg->val); /* put message */ List_put(msgList, (List_Elem *) msg); /* post semaphore */ Semaphore_post(sem); } System_printf("writer (%d) done.\n", id); }
4-6
Semaphores
Figure 4-1.
Synchronization Modules
4-7
Event Module
4-8
Event Module
Configuration example: These XDCtools configuration statements create an event statically. The Event object has an Event_Handle named "myEvent".
var Event = xdc.useModule("ti.sysbios.knl.Event"); Program.global.myEvent = Event.create();
Runtime example: The following C code creates an Event object with an Event_Handle named "myEvent".
Event_Handle myEvent; Error_Block eb; Error_init(&eb); /* Default instance configuration params */ myEvent = Event_create(NULL, &eb); if (myEvent == NULL) { System_abort("Event create failed"); }
Runtime example: The following C code blocks on an event. It wakes the task only when both events 0 and 6 have occurred. It sets the andMask to enable both Event_Id_00 and Event_Id_06. It sets the orMask to Event_Id_NONE.
Event_pend(myEvent, (Event_Id_00 + Event_Id_06), Event_Id_NONE, BIOS_WAIT_FOREVER);
Runtime example: The following C code has a call to Event_post() to signal which events have occurred. The eventMask should contain the IDs of the events that are being posted.
Event_post(myEvent, Event_Id_00);
Runtime Example: The following C code example shows a task that provides the background processing required for three Interrupt Service Routines:
task
Synchronization Modules
4-9
Event Module Event_Handle myEvent; main() { ... /* create an Event object. All events are binary */ myEvent = Event_create(NULL, &eb); if (myEvent == NULL) { System_abort("Event create failed"); } } isr0() { ... Event_post(myEvent, Event_Id_00); ... } isr1() { ... Event_post(myEvent, Event_Id_01); ... } isr2() { ... Event_post(myEvent, Event_Id_02); ... } task() { UInt events; while (TRUE) { /* Wait for ANY of the ISR events to be posted * events = Event_pend(myEvent, Event_Id_NONE, Event_Id_00 + Event_Id_01 + Event_Id_02, BIOS_WAIT_FOREVER);
4-10
Event Module
/* Process all the events if (events & Event_Id_00) processISR0(); } if (events & Event_Id_01) processISR1(); } if (events & Event_Id_02) processISR2(); } } }
4.2.1
Synchronization Modules
4-11
Event Module
Runtime example: The following C code example shows a task processing the messages posted to a Mailbox message as well as performing an ISRs post-processing requirements.
WriterTask
Mailbox
ISR
ReaderTask
Event_Handle myEvent; Mailbox_Handle mbox; typedef struct msg { UInt id; Char buf[10]; } main() { Mailbox_Params mboxParams; Error_Block eb; Error_init(&eb); myEvent = Event_create(NULL, &eb); if (myEvent == NULL) { System_abort("Event create failed"); } Mailbox_Params_init(&mboxParams); mboxParams.notEmptyEvent = myEvent; /* Assign Event_Id_00 to Mailbox "not empty" event */ mboxParams.notEmptyEventId = Event_Id_00; mbox = Mailbox_create(sizeof(msg), 50, &mboxParams, &eb); if (mbox == NULL) { System_abort("Mailbox create failed"); }
4-12
Event Module
/* Mailbox_create() sets Mailbox's notEmptyEvent to * counting mode and initial count = 50 */ } writerTask() { ... Mailbox_post(mbox, &msgA, FOREVER); /* implicitly posts Event_Id_00 to myEvent */ ... } isr() { Event_post(myEvent, Event_Id_01); } readerTask() { while (TRUE) {/* Wait for either ISR or Mailbox message */ events = Event_pend(myEvent, Event_Id_NONE, /* andMask = 0 */ Event_Id_00 + Event_Id_01, /* orMask */ BIOS_WAIT_FOREVER); /* timeout */ if (events & Event_Id_00) { /* Get the posted message. * Mailbox_pend() will not block since Event_pend() * has guaranteed that a message is available. * Notice that the special BIOS_NO_WAIT * parameter tells Mailbox that Event_pend() * was used to acquire the available message. */ Mailbox_pend(mbox, &msgB, BIOS_NO_WAIT); processMsg(&msgB); } if (events & Event_Id_01) { processISR(); } } }
Synchronization Modules
4-13
Gates
4.3 Gates
A "Gate" is a module that implements the IGateProvider interface. Gates are devices for preventing concurrent accesses to critical regions of code. The various Gate implementations differ in how they attempt to lock the critical regions. Since xdc.runtime.Gate is provided by XDCtools, the base module is documented in the online help and the RTSC-pedia wiki. Implementations of Gates provided by SYS/BIOS are discussed here. Threads can be preempted by other threads of higher priority, and some sections of code need to be completed by one thread before they can be executed by another thread. Code that modifies a global variable is a common example of a critical region that may need to be protected by a Gate. Gates generally work by either disabling some level of preemption such as disabling task switching or even hardware interrupts, or by using a binary semaphore. All Gate implementations support nesting through the use of a "key". For Gates that function by disabling preemption, it is possible that multiple threads would call Gate_enter(), but preemption should not be restored until all of the threads have called Gate_leave(). This functionality is provided through the use of a key. A call to Gate_enter() returns a key that must then be passed back to Gate_leave(). Only the outermost call to Gate_enter() returns the correct key for restoring preemption. (The actual module name for the implementation is used instead of "Gate" in the function name.)
4-14
Gates
Runtime example: The following C code protects a critical region with a Gate. This example uses a GateHwi, which disables and enables interrupts as the locking mechanism.
UInt gateKey; GateHwi_Handle gateHwi; GateHwi_Params prms; Error_Block eb; Error_init(&eb); GateHwi_Params_init(&prms); gateHwi = GateHwi_create(&prms, &eb); if (gateHwi == NULL) { System_abort("Gate create failed"); } /* Simultaneous operations on a global variable by multiple * threads could cause problems, so modifications to the global * variable are protected with a Gate. */ gateKey = GateHwi_enter(gateHwi); myGlobalVar = 7; GateHwi_leave(gateHwi, gateKey);
4.3.1
4.3.1.1 GateHwi GateHwi disables and enables interrupts as the locking mechanism. Such a gate guarantees exclusive access to the CPU. This gate can be used when the critical region is shared by Task, Swi, or Hwi threads. The duration between the enter and leave should be as short as possible to minimize Hwi latency.
Synchronization Modules
4-15
Gates
4.3.1.2 GateSwi GateSwi disables and enables software interrupts as the locking mechanism. This gate can be used when the critical region is shared by Swi or Task threads. This gate cannot be used by a Hwi thread. The duration between the enter and leave should be as short as possible to minimize Swi latency. 4.3.1.3 GateTask GateTask disables and enables tasks as the locking mechanism. This gate can be used when the critical region is shared by Task threads. This gate cannot be used by a Hwi or Swi thread. The duration between the enter and leave should be as short as possible to minimize Task latency.
4.3.2
4.3.2.1 GateMutex GateMutex uses a binary Semaphore as the locking mechanism. Each GateMutex instance has its own unique Semaphore. Because this gate can potentially block, it should not be used a Swi or Hwi thread, and should only be used by Task threads. 4.3.2.2 GateMutexPri GateMutexPri is a mutex Gate (it can only be held by one thread at a time) that implements "priority inheritance" in order to prevent priority inversion. Priority inversion occurs when a high-priority Task has its priority effectively "inverted" because it is waiting on a Gate held by a lower-priority Task. Issues and solutions for priority inversion are described in Section 4.3.3. Configuration example: The following example specifies a GateType to be used by HeapMem. (See section 6.8.1, HeapMem for further discussion.)
var GateMutexPri = xdc.useModule('ti.sysbios.gates.GateMutexPri'); var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem'); HeapMem.common$.gate = GateMutexPri.create();
4-16
Gates
4.3.3
Priority Inversion
The following example shows the problem of priority inversion. A system has three tasksLow, Med, and Higheach with the priority suggested by its name. Task Low runs first and acquires the gate. Task High is scheduled and preempts Low. Task High tries to acquire the gate, and waits on it. Next, task Med is scheduled and preempts task Low. Now task High must wait for both task Med and task Low to finish before it can continue. In this situation, task Low has, in effect, lowered task High's priority to that of Low. Solution: Priority Inheritance To guard against priority inversion, GateMutexPri implements priority inheritance. When task High tries to acquire a gate that is owned by task Low, task Low's priority is temporarily raised to that of High, as long as High is waiting on the gate. So, task High "donates" its priority to task Low. When multiple tasks wait on the gate, the gate owner receives the highest priority of any of the tasks waiting on the gate. Caveats Priority inheritance is not a complete guard against priority inversion. Tasks only donate their priority on the call to enter a gate, so if a task has its priority raised while waiting on a gate, that priority is not carried through to the gate owner. This can occur in situations involving multiple gates. For example, a system has four tasks: VeryLow, Low, Med, and High, each with the priority suggested by its name. Task VeryLow runs first and acquires gate A. Task Low runs next and acquires gate B, then waits on gate A. Task High runs and waits on gate B. Task High has donated its priority to task Low, but Low is blocked on VeryLow, so priority inversion occurs despite the use of the gate. The solution to this problem is to design around it. If gate A may be needed by a high-priority, time-critical task, then it should be a design rule that no task holds this gate for a long time or blocks while holding this gate. When multiple tasks wait on this gate, they receive the gate in order of priority (higher-priority tasks receive the gate first). This is because the list of tasks waiting on a GateMutexPri is sorted by priority, not FIFO. Calls to GateMutexPri_enter() may block, so this gate can only be used in the task context. GateMutexPri has non-deterministic calls because it keeps the list of waiting tasks sorted by priority.
Synchronization Modules
4-17
Mailboxes
4.4 Mailboxes
The ti.sysbios.knl.Mailbox module provides a set of functions to manage mailboxes. Mailboxes can be used to pass buffers from one task to another on the same processor. A Mailbox instance can be used by multiple readers and writers. The Mailbox module copies the buffer to fixed-size internal buffers. The size and number of these buffers are specified when a Mailbox instance is created (or constructed). A copy is done when a buffer is sent via Mailbox_post(). Another copy occurs when the buffer is retrieved via a Mailbox_pend(). Mailbox_create() and Mailbox_delete() are used to create and delete mailboxes, respectively. You can also create mailbox objects statically. See the XDCtools Consumer Users Guide for a discussion of the benefits of creating objects statically. Mailboxes can be used to ensure that the flow of incoming buffers does not exceed the ability of the system to process those buffers. The examples given later in this section illustrate just such a scheme. You specify the number of internal mailbox buffers and size of each of these buffers when you create a mailbox. Since the size is specified when you create the Mailbox, all buffers sent and received with the Mailbox instance must be of this same size.
Mailbox_Handle Mailbox_create(SizeT UInt Mailbox_Params Error_Block bufsize, numBufs, *params, *eb)
Mailbox_pend() is used to read a buffer from a mailbox. If no buffer is available (that is, the mailbox is empty), Mailbox_pend() blocks. The timeout parameter allows the task to wait until a timeout, to wait indefinitely (BIOS_WAIT_FOREVER), or to not wait at all (BIOS_NO_WAIT). The unit of time is system clock ticks.
Bool Mailbox_pend(Mailbox_Handle handle, Ptr buf, UInt timeout);
4-18
Mailboxes
Mailbox_post() is used to post a buffer to the mailbox. If no buffer slots are available (that is, the mailbox is full), Mailbox_post() blocks. The timeout parameter allows the task to wait until a timeout, to wait indefinitely (BIOS_WAIT_FOREVER), or to not wait at all (BIOS_NO_WAIT).
Bool Mailbox_post(Mailbox_Handle handle, Ptr buf, UInt timeout);
Mailbox provides configuration parameters to allow you to associate events with a mailbox. This allows you to wait on a mailbox message and another event at the same time. Mailbox provides two configuration parameters to support events for the reader(s) of the mailboxnotEmptyEvent and notEmptyEventId. These allow a mailbox reader to use an event object to wait for the mailbox message. Mailbox also provides two configuration parameters for the mailbox writer(s)notFullEvent and notFullEventId. These allow mailbox writers to use an event object to wait for room in the mailbox. When using events, a thread calls Event_pend() and waits on several events. Upon returning from Event_pend(), the thread must call Mailbox_pend() or Mailbox_post()depending on whether it is a writer or a readerwith a special timeout value of BIOS_EVENT_ACQUIRED. This timeout value allows the thread to get/put the message into the mailbox. This special timeout value is necessary for correct operation of the mailbox when using Events.
Synchronization Modules
4-19
Mailboxes
4-20
Chapter 5
Timing Services
This chapter describes modules that can be used for timing purposes. Topic
5.1 5.2 5.3 5.4
Page
Overview of Timing Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2 Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2 Timer Module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-6 Timestamp Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-6
5-1
5.2 Clock
The ti.sysbios.knl.Clock module is responsible for the periodic system tick that the kernel uses to keep track of time. All SYS/BIOS APIs that expect a timeout parameter interpret the timeout in terms of Clock ticks. The Clock module, by default, uses the ti.sysbios.hal.Timer module to create a timer to generate the system tick, which is basically a periodic call to Clock_tick(). See Section 5.3 for more about the Timer module. The Clock module can be configured not to use the timer with either of the following configuration statements:
ti.sysbios.knl.Clock.tickSource = Clock.tickSource_USER or ti.sysbios.knl.Clock.tickSource = Clock.tickSource_NULL
The period for the system tick is set by the configuration parameter Clock.tickPeriod. This is set in microseconds.
5-2
Clock
The Clock_tick() and the tick period are used as follows: If the tickSource is Clock.tickSource_TIMER (the default), Clock uses ti.sysbios.hal.Timer to create a timer to generate the system tick, which is basically a periodic call to Clock_tick(). Clock uses Clock.tickPeriod to create the timer. Clock.timerId can be changed to make Clock use a different timer. If the tickSource is Clock.tickSource_USER, then your application must call Clock_tick() from a user interrupt and set the tickPeriod to the approximate frequency of the user interrupt in microseconds. If the tickSource is Clock.tickSource_NULL, you cannot call any SYS/BIOS APIs with a timeout value and cannot call any Clock APIs. You can still use the Task module but you cannot call APIs that require a timeout, for example, Task_sleep(). Clock.tickPeriod values is not valid in this configuration.
Clock_getTicks() gets the number of Clock ticks that have occurred since startup. The value returned wraps back to zero after it reaches the maximum value that can be stored in 32 bits. The Clock module provides APIs to start, stop and reconfigure the tick. These APIs allow you to make frequency changes at runtime. These three APIs are not reentrant and gates need to be used to protect them. Clock_tickStop() stops the timer used to generate the Clock tick by calling Timer_stop(). Clock_tickReconfig() calls Timer_setPeriodMicroseconds() internally to reconfigure the timer. Clock_tickReconfig() fails if the timer cannot support Clock.tickPeriod at the current CPU frequency. Clock_tickStart() restarts the timer used to generate the Clock tick by calling Timer_start().
The Clock module lets you create Clock object instances, which reference functions that run when a timeout value specified in Clock ticks expires. All Clock functions run in the context of a Swi. That is, the Clock module automatically creates a Swi for its use and run the Clock functions within that Swi. The priority of the Swi used by Clock can be changed by configuring Clock.swiPriority. You can dynamically create clock instances using Clock_create(). Clock instances can be "one-shot" or continuous. You can start a clock instance when it is created or start it later by calling Clock_start(). This is controlled by the startFlag configuration parameter.
Timing Services
5-3
Clock
A function and a non-zero timeout value are required arguments to Clock_create(). The function is called when the timeout expires. The timeout value is used to compute the first expiration time. For one-shot Clock instances, the timeout value used to compute the single expiration time, and the period is zero. For periodic Clock instances, the timeout value is used to compute the first expiration time; the period value (part of the params) is used after the first expiration.
Func runs
...
Clock instances (both one-shot and periodic) can be stopped and restarted by calling Clock_start() and Clock_stop(). Notice that while Clock_tickStop() stops the timer used to generate the Clock tick, Clock_stop() stops only one instance of a clock object. The expiration value is recomputed when you call Clock_start(). APIs that start or stop a Clock InstanceClock_create(), Clock_start(), and Clock_stop()can only be called in the Swi context. The Clock module provides the Clock_setPeriod(), Clock_setTimeout(), and Clock_setFunc() APIs to modify Clock instance properties for Clock instances that have been stopped.
5-4
Clock
Runtime example: This C example shows how to create a Clock instance. This instance is dynamic (runs repeatedly) and starts automatically. It runs the myHandler function every 5 ticks. A user argument (UArg) is passed to the function.
Clock_Params clockParams; Clock_Handle myClock; Error_Block eb; Error_init(&eb); Clock_Params_init(&clockParams); clockParams.period = 5; clockParams.startFlag = TRUE; clockParams.arg = (UArg)0x5555; myClock = Clock_create(myHandler1, 5, &clockParams, &eb); if (myClock == NULL) { System_abort("Clock create failed"); }
Configuration example: This example uses XDCtools to create a Clock instance with the same properties as the previous example.
var Clock = xdc.useModule('ti.sysbios.knl.Clock'); var clockParams = new Clock.Params(); clockParams.period = 5; clockParams.startFlag = true; clockParams.arg = (UArg)0x5555; Program.global.clockInst1 = Clock.create("&myHandler1", 5, clockParams);
Runtime example: This C example uses some of the Clock APIs to print messages about how long a Task sleeps.
UInt32 time1, time2; . . . System_printf("task going to sleep for 10 ticks... \n"); time1 = Clock_getTicks(); Task_sleep(10); time2 = Clock_getTicks(); System_printf("...awake! Delta time is: %lu\n", (ULong) (time2 - time1));
Timing Services
5-5
Timer Module
Runtime example: This C example uses some of the Clock APIs to lower the Clock module frequency.
BIOS_getCpuFreq(&cpuFreq); cpuFreq.lo = cpuFreq.lo / 2; BIOS_setCpuFreq(&cpuFreq); key = Hwi_disable(); Clock_tickStop(); Clock_tickReconfig(); Clock_tickStart(); Hwi_restore(key);
5-6
Chapter 6
Memory
Page
Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2 Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-4 Placing Sections into Memory Segments . . . . . . . . . . . . . . . . . . . . . 6-11 Sections and Memory Mapping for MSP430, Stellaris M3, and C28x6-15 Stacks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-16 Cache Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-19 Dynamic Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-20 Heap Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23
6-1
Background
6.1 Background
This chapter deals with the configuration of static memory (memory mapping and section placement), caches, and stacks. It also provides information on dynamic memory allocation (allocating and freeing memory at run-time). Static memory configuration relates to the "memory map" available to the executable and the placement of code and data into the memory map. The memory map is made up of internal memory regions that exist within the CPU and external memory regions located on the hardware board. See Section 6.2 for details about the memory map. Code and data are placed in memory regions by the linker using the linker command file. The linker command file specifies a memory map. For each memory region, the linker command file specifies the origin or base address, length and attributes (read, write, and execute). A memory region specified in the linker command file is also called a "memory segment". The following is a memory map specification from a linker command file:
MEMORY { IRAM (RWX) : org = 0x800000, len = 0x200000 DDR : org = 0x80000000, len = 0x10000000 }
The linker command file also contains information on "memory section" placement, as shown in the following example. Sections are relocatable blocks of code produced by the compiler. The compiler produces some wellknown sections for placements of various types of code and data, for example: .text, .switch, .bss, .far , .cinit, and .const. For details, see the appropriate compiler users guide.
SECTIONS { .text: load >> DDR .switch: load >> DDR .stack: load > DDR .vecs: load >> DDR .args: load > DDR .sysmem: load > DDR .far: load >> DDR .data: load >> DDR .cinit: load > DDR .bss: load > DDR .const: load > DDR .pinit: load > DDR .cio: load >> DDR }
6-2
Background
The linker places "memory sections" (such as .text and .cinit) into "memory segments" (such as IRAM) as specified by SECTIONS portion of the linker command file. See Section 6.3 for details about section placement. Section 6.2 discusses the Memory Map for SYS/BIOS applications. (MSP430 users should see Section 6.4 instead.) Section 6.3 discusses section placement in a SYS/BIOS application. (MSP430 users should see Section 6.4 instead.) Section 6.5 discusses stacks, including how to configure the system stack and task stacks. Section 6.6 covers cache configuration specific to the C6000 and cache runtime APIs. Section 6.7 also discusses dynamic memory allocation. Runtime code can allocate and free memory from a "heap," which is a memory pool that has been set aside and managed for the purpose of dynamic memory allocation. Various heap implementations are described in Section 6.8.
Memory
6-3
Memory Map
6.2.1
6-4
Memory Map
To view the memory map for your platform, you can open the RTSC platform wizard by choosing Tools > RTSC Tools > Platform > Edit/View. Select the packages repository in your XDCtools installation. For example, C:\Program Files\Texas Instruments\xdctools_3_20\packages. Then, choose the RTSC platform package you are using and click Next.
In most cases, you begin application development using one of the evaluation boards, and can select one of the standard platforms from the drop-down list. You should select one of existing platforms if all of the following are true: You are in the development phase and are using an evaluation board. You do not care about cache sizes and are satisfied with the defaults set by the existing RTSC platform. You do not want to change the default section placement. You want the same clock rate as the evaluation board.
Memory
6-5
Memory Map
6.2.2
For such custom boards you will need to create a RTSC platform using the RTSC platform wizard. The platform wizard is a GUI tool that allows you to easily create a RTSC platform. Creating a custom RTSC platform gives you a lot of flexibility in terms of defining the memory map and selecting default memory segments for section placement. To run the RTSC platform wizard, follow these steps: 1) In CCS v4.x, choose Tools > RTSC Tools > Platform > New from the menus. This opens the New Platform wizard. 2) Type a name for the package. This will be the name of the directory created to contain the RTSC platform package, and will be the name you select when you choose the RTSC platform for the project to use. You can use a simple name or a period-delimited name. Periods correspond to directory levels when a platform package is created. For example, myBoards.DA830_bigCache will be created in C:\myRepository\packages\myBoards\DA830_bigCache if you are using C:\myRepository\packages as the repository location. 3) Next to the Platform Package Repository field, click Browse. Choose the location of the repository where you want to save your RTSC platform package. The default is C:\Users\<username>\myRepository\packages.
If you havent created a RTSC package repository before, and you dont want to use the default, create a new directory to contain the repository. In the directory you choose, create a sub-directory called "packages". For example, you might use C:\myRepository\packages as the repository. The full path to the repository should not contain any spaces.
6-6
Memory Map
4) Optionally, if you have already created a CCS project that you want to be able to use this RTSC platform, check the Add Repository to Project Package Path box. Then select the project that should have access to this repository. You dont need to do this now; you can also add repositories to projects from the projects Build Properties dialog. 5) Choose the Device Family and Device Name from the drop-down lists. For example:
6) Click Next. You see the Device Page of the RTSC platform wizard. Note: If you want another project to be able to use this RTSC platform, you can later add the repository that contains the RTSC platform to a projects properties by right-clicking on the project and choosing Build Properties. Choose the CCS Build category and then the RTSC tab. Click Add and browse the file-system for the repository you want the project to be able to search for RTSC platforms. See the subsections that follow for ways to specify the cache, segment, and section use for your RTSC platform. You can also visit http://rtsc.eclipse.org/docs-tip/Demo_of_the_RTSC_Platform_Wizard_in_CCSv4 to watch demonstrations that use the RTSC platform wizard. 6.2.2.1 Getting and Setting the Clock Speed and Default Memory Settings The Device Page opens with no clock speed setting, no external memory segments, and no memory section assignments. Generally, the first thing you will want to do is to import the default settings from an existing RTSC platform so that you can use those as a base for making the modifications you need. To import defaults, follow these steps: 1) Click the Import button next to the Clock Speed field. 2) In the Select Platform dialog, choose the RTSC platform whose defaults you want to import, and click OK.
Memory
6-7
Memory Map
3) Click Yes in the confirmation dialog that asks if you are sure you want to change the settings. 4) You see the default clock speed and external memory settings. You can change these if you like.
6.2.2.2 Determining Cache Sizes for Custom RTSC Platforms Since cache sizes affect the memory map, if you are using a C6000 target, you need to decide on the sizes you want to use when creating a RTSC platform. For example, if you are using the "ti.platforms.evmDA830" platform, the L1P, L1D, and L2 cache sizes affect the size of available L1PSRAM, L1DSRAM, and IRAM. Since cache sizes are set in the RTSC platform, executables that need different cache configurations will also need different RTSC platforms.
6-8
Memory Map
The following example steps use the Device Page of the RTSC platform wizard to specify the maximum cache sizes for TMS320DA830 platform: 1) Set the L1D Cache to 32K. Set the L1P Cache to 32K. Set the L2 Cache to 256K. 2) Notice that the sizes of L1PSRAM, L1DSRAM and IRAM are adjusted down to zero.
See Section 6.6 for more about cache configuration. 6.2.2.3 Selecting Default Memory Segments for Data, Code and Stack The RTSC platform also determines the default memory segment for placement of code, data and stack. If you do not explicitly place a section, then the defaults are used. For example, if you do not configure the location of a Task stack in the .cfg file, then the task stack will be placed in the stack memory segment specified by the RTSC platform. You can make coarse decisions on where you want your code, data, and stack to be placed by selecting values for data memory, code memory, and stack memory in your RTSC platform definition. For example on the evmDA830, you may want your code in SDRAM and data in RAM. You can achieve this by selecting Code memory to be SDRAM and Data memory to be IRAM in the RTSC platform wizard.
Memory
6-9
Memory Map
See Section 6.3 for details about how you can use the RTSC configuration files to be more specific about how memory sections should be placed in memory segments. 6.2.2.4 Setting Custom Base Addresses and Lengths for Segments You can customize the names, locations, sizes, type, and access for both internal and external memory segments. To customize internal memory segments, begin by checking the Customize Memory box in the Device Memory area. You can then click on fields in the list of memory segments and make changes. In the Name, Base, and Length columns, type the value you want to use. In the Space and Access columns, you can select from a list of options.
To customize external memory segments, you can right-click in the External Memory area and choose Insert Row or Delete Row.
In the new row, type a Name, Base address, and Length for the memory segment. Choose the type of memory Space and the permitted Access to this memory. To watch a demo that shows how to customize memory segments, see http://rtsc.eclipse.org/docs-tip/Demo_of_Customizing_Memory_Sections.
6-10
Note: To place sections into segments in the .cfg file, you will need to edit the source of your .cfg script in a text editor. Currently, you cannot use the XGCONF GUI editor.
6.3.1
This example would cause the IRAM segment to be used both for loading and running the .foo section.
Memory
6-11
6.3.2
The following .cfg file statements specify the memory segments where the .foo section is to be loaded and run.
Program.sectMap[".foo"] = new Program.SectionSpec(); Program.sectMap[".foo"].loadSegment = "FLASH"; Program.sectMap[".foo"].runSegment = "RAM";
If you specify only the loadSegment or runSegment for a section, the default behavior is to use the specified segment for both loading and running. The following statements place the Swi_post() function into the IRAM memory segment:
Program.sectMap[".text:_ti_sysbios_knl_Swi_post__F"] = new Program.SectionSpec(); Program.sectMap[".text:_ti_sysbios_knl_Swi_post__F"] = "IRAM";
6-12
The following statements place all static instances for the Task module into the .taskStatic section:
var Task = xdc.useModule('ti.sysbios.knl.Task'); Task.common$.instanceSection = ".taskStatic"; Program.sectMap[".taskStatic"] = new Program.SectionSpec(); Program.sectMap[".taskStatic"].loadSegment = "IRAM";
Configuration statements that specify sections using the sectMap array affect the section placement in the linker command file that is generated from the configuration.
6.3.3
Memory
6-13
6.3.4
The auto-generated linker command file uses a template specified by the platform associated with the program. This command file defines MEMORY and SECTIONS as determined during the configuration process. The placement of sections in the configuration file is reflected in this autogenerated command file.
6-14
Sections and Memory Mapping for MSP430, Stellaris M3, and C28x
You can customize the auto-generated linker command file using any of the following techniques: Exclude sections from the auto-generated command file. See the page at http://rtsc.eclipse.org/cdoc-tip/xdc/cfg/Program.html#sections.Exclude for examples that configure the Program.sectionsExclude parameter. Replace the entire SECTIONS portion of the generated linker command file. See information about the Program.sectionsTemplate parameter at http://rtsc.eclipse.org/cdoc-tip/xdc/cfg/Program.html#sections.Template. Specify a template for the program's linker command file. See http://rtsc.eclipse.org/cdoc-tip/xdc/cfg/Program.html#link.Template for information and examples that use the Program.linkTemplate parameter. The simplest way to create a template for your program is to first autogenerate the linker command file, then edit it to suit your applications needs, and then set the Program.linkTemplate parameter to reference your edited file. Important: This technique requires that you copy the auto-generated linker command file and edit it every time you change the configuration, the platform, or install a new version of XDCtools.
6.4 Sections and Memory Mapping for MSP430, Stellaris M3, and C28x
When you create a project in CCSv4.2, you must select a device as part of the project settings (for example, MSP430F5435A) in the CCS project creation wizard. A linker command file specific to the selected device is automatically added to the project by CCSv4.2. In the RTSC Configuration Settings page of the wizard, a RTSC target and a RTSC platform are automatically selected based on your previous selections. We recommend using "release" as the RTSC profile, even if you are in the debugging stage of code development. See Section 2.3.3 for more about build settings for reducing the size of your executable for the MSP430. The RTSC platforms for the MSP430, Stellaris Cortex-M3 microcontrollers, and C28x devices differ from other RTSC platforms in that they do not define the memory map for the device. Instead, the linker command file added by the project wizard is used directly. Any changes to the memory map and section placement can be made by editing the linker command file directly. See the MSP430 Optimizing C/C++ Compiler User's Guide for more details on linker command file options. Note that an additional RTSC-generated linker command file is added to the project; this file places a few sections that are SYS/BIOS specific. This command file assumes that "FLASH" and "RAM" are part of the memory map. Note: For hardware-specific information about using SYS/BIOS, see links on the http://processors.wiki.ti.com/index.php/Category:SYSBIOS page.
Memory
6-15
Stacks
6.5 Stacks
SYS/BIOS uses a single system stack for hardware interrupts and a separate task stack for each Task instance.
6.5.1
System Stack
You can configure the size of the System stack, which is used as the stack for hardware interrupts and software interrupts (and by the Idle functions if Task is disabled). You should set the System stack size to meet the application's needs. See Section 3.4.3 for information about system stack size requirements. You can use the .stack section to control the location of the system stack. For example, the following configuration statements place the system stack of size 0x400 in the IRAM segment.
Program.stack = 0x400; Program.sectMap[".stack"] = "IRAM";
Setting Program.stack generates appropriate linker options in the linker command file to allow the system stack to be allocated at link time. For example, the linker command file for a C6000 application might include the command option -stack 0x0400. See your projects auto-generated linker command file for symbols related to the system stack size and location. For C6000 these include __TI_STACK_END and __STACK_SIZE.
6.5.2
Task Stacks
If the Task module is enabled, SYS/BIOS creates an additional stack for each Task instance the application contains (plus one task stack for the Idle threads). See Section 3.5.3 for information about task stack size requirements.
6-16
Stacks
You can specify the size of a Tasks stack in the configuration file. (You can use XGCONF to do this or edit the .cfg file directly.) For example:
var Task = xdc.useModule('ti.sysbios.knl.Task'); /* Set default stack size for tasks */ Task.defaultStackSize = 1024; /* Set size of idle task stack */ Task.idleTaskStackSize = 1024; /* Create a Task Instance and set stack size */ var tskParams = new Task.Params; tskParams.stackSize = 1024; var task0 = Task.create('&task0Fxn', tskParams);
You can control the location of task stacks for statically-created Tasks by using Program.sectMap[]. For example:
/* Place idle task stack section */ Program.sectMap[".idleTaskStackSection"] = "IRAM"; /* Place other static task stacks */ Program.sectMap[".taskStackSection"] = "IRAM";
Memory
6-17
Stacks
6.5.3
The Module tab of the Task view shows the HwiStack (which is the system stack) information and status.
6-18
Cache Configuration
6.6.1
6.6.2
After the MAR bit is set for an external memory space, new addresses accessed by the CPU will be cached in L2 cache or, if L2 is disabled, in L1. See device-specific reference guides for MAR registers and their mapping to external memory addresses. At system startup, the Cache module writes to the MAR registers and configures them.
Memory
6-19
6.6.3
6.7.1
Memory Policy
You can reduce the amount of code space used by an application by setting the memoryPolicy on a global or per-module basis. This is particularly useful on targets where the code memory is significantly constrained. The options are: DELETE_POLICY. This is the default. The application creates and deletes objects (or objects for this module) at runtime. You need both the MODULE_create() functions and the MODULE_delete() functions to be available to the application. CREATE_POLICY. The application creates objects (or objects for this module) at runtime. It does not delete objects at runtime. You need the MODULE_create() functions to be available to the application, but not the MODULE_delete() functions. STATIC_POLICY. The application creates all objects (or all objects for this module) in the configuration file. You dont need the MODULE_create() or the MODULE_delete() functions to be available to the application.
For example, the following configuration statements set the default memory policy for all modules to static instance creation only:
var Defaults = xdc.useModule('xdc.runtime.Defaults'); var Types = xdc.useModule('xdc.runtime.Types'); Defaults.memoryPolicy = Types.STATIC_POLICY;
6-20
6.7.2
For example, you can configure the default system heap with XDCscript as follows:
var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.heapSize = 0x900; BIOS.heapSection = "systemHeap";
If you want to use a different heap manager for the system heap, you can specify the system heap in your configuration file and SYS/BIOS will not override the setting. Note: The SYS/BIOS system heap cannot be a HeapStd instance. The BIOS module detects this condition and generates an error message. The following configuration statements specify a system heap that uses HeapBuf instead of HeapMem:
/* Create a heap using HeapBuf */ var heapBufParams = new HeapBuf.Params; heapBufParams.blockSize = 128; heapBufParams.numBlocks = 2; heapBufParams.align = 8; heapBufParams.sectionName = "myHeap"; Program.global.myHeap = HeapBuf.create(heapBufParams); Program.sectMap["myHeap"] = "DDR"; Memory.defaultHeapInstance = Program.global.myHeap;
If you do not want a system heap to be created, you can set BIOS.heapSize to zero. The BIOS module will then use a HeapNull instance to minimize code/data usage.
Memory
6-21
6.7.3
extern IHeap_Handle systemHeap, otherHeap; Void main() { Ptr buf1, buf2; Error_Block eb; Error_init(&eb); /* Alloc and free using systemHeap */ buf1 = Memory_alloc(NULL, 128, 0, &eb); if (buf1 == NULL) { System_abort("Memory allocation for buf1 failed"); } Memory_free(NULL, buf1, 128); /* Alloc and free using otherHeap */ buf2 = Memory_alloc(otherHeap, 128, 0, &eb); if (buf2 == NULL) { System_abort("Memory allocation for buf2 failed"); } Memory_free(otherHeap, buf2, 128); }
6-22
Heap Implementations
6.7.4
If you do not specify a separate heap for instances, the heap specified for Memory.defaultHeapInstance will be used (see Section 6.7.2).
6.7.5
Memory
6-23
Heap Implementations
SYS/BIOS provides the following Heap implementations: HeapMem. Allocate variable-size blocks. Section 6.8.1 HeapBuf. Allocate fixed-size blocks. Section 6.8.2 HeapMultiBuf. Specify variable-size allocation, but internally allocate from a variety of fixed-size blocks. Section 6.8.3
ti.sysbios.heaps.HeapMem
Slower, non-deterministic
ti.sysbios.heaps.HeapBuf ti.sysbios.heaps.HeapMultiBuf
Different heap implementations optimize for different memory management traits. The HeapMem module (Section 6.8.1) accepts requests for all possible sizes of blocks, so it minimizes internal fragmentation. The HeapBuf module (Section 6.8.2), on the other hand, can only allocate blocks of a fixed size, so it minimizes external fragmentation in the heap and is also faster at allocating and freeing memory. The Inter-Processor Communication (IPC) product provides several heap implementations intended for use in multi-processor applications.
6.8.1
HeapMem
HeapMem can be considered the most "flexible" of the Heaps because it allows you to allocate variable-sized blocks. When the size of memory requests is not known until runtime, it is ideal to be able to allocate exactly how much memory is required each time. For example, if a program needs to store an array of objects, and the number of objects needed isn't known until the program actually executes, the array will likely need to be allocated from a HeapMem. The flexibility offered by HeapMem has a number of performance tradeoffs. External Fragmentation. Allocating variable-sized blocks can result in fragmentation. As memory blocks are "freed" back to the HeapMem, the available memory in the HeapMem becomes scattered throughout the
6-24
Heap Implementations
heap. The total amount of free space in the HeapMem may be large, but because it is not contiguous, only blocks as large as the "fragments" in the heap can be allocated. This type of fragmentation is referred to as "external" fragmentation because the blocks themselves are allocated exactly to size, so the fragmentation is in the overall heap and is "external" to the blocks themselves. Non-Deterministic Performance. As the memory managed by the HeapMem becomes fragmented, the available chunks of memory are stored on a linked list. To allocate another block of memory, this list must be traversed to find a suitable block. Because this list can vary in length, it's not known how long an allocation request will take, and so the performance becomes "non-deterministic".
A number of suggestions can aide in the optimal use of a HeapMem. Larger Blocks First. If possible, allocate larger blocks first. Previous allocations of small memory blocks can reduce the size of the blocks available for larger memory allocations. Overestimate Heap Size. To account for the negative effects of fragmentation, use a HeapMem that is significantly larger than the absolute amount of memory the program will likely need.
When a block is freed back to the HeapMem, HeapMem combines the block with adjacent free blocks to make the available block sizes as large as possible. Note: HeapMem uses a user-provided lock to lock access to the memory. For details, see Section 4.3, Gates. The following examples create a HeapMem instance with a size of 1024 MAUs. Configuration example: The first example uses XDCtools to statically configure the heap:
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem'); /* Create heap as global variable so it can be used in C code */ var heapMemParams = new HeapMem.Params(); heapMemParams.size = 1024; Program.global.myHeap = HeapMem.create(heapMemParams);
Memory
6-25
Heap Implementations
Runtime example: This second example uses C code to dynamically create a HeapMem instance:
HeapMem_Params prms; static char *buf[1024]; HeapMem_Handle heap; Error_Block eb; Error_init(&eb); HeapMem_Params_init(&prms); prms.size = 1024; prms.buf = (Ptr)buf; heap = HeapMem_create(&prms, &eb); if (heap == NULL) { System_abort("HeapMem create failed"); }
HeapMem uses a Gate (see the Gates section for an explanation of Gates) to prevent concurrent accesses to the code which operates on a HeapMem's list of free blocks. The type of Gate used by HeapMem is statically configurable through the HeapMem's common defaults. Configuration example: This example configures HeapMem to use a GateMutexPri to protect critical regions of code.
var GateMutexPri = xdc.useModule('ti.sysbios.gates.GateMutexPri'); var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
HeapMem.common$.gate = GateMutexPri.create();
The type of Gate used depends upon the level of protection needed for the application. If there is no risk of concurrent accesses to the heap, then "null" can be assigned to forgo the use of any Gate, which would improve performance. For an application that could have concurrent accesses, a GateMutex is a likely choice. Or, if it is possible that a critical thread will require the HeapMem at the same time as a low-priority thread, then a GateMutexPri would be best suited to ensuring that the critical thread receives access to the HeapMem as quickly as possible. See Section 4.3.2.2, GateMutexPri for more information.
6-26
Heap Implementations
6.8.2
HeapBuf
HeapBuf is used for allocating fixed-size blocks of memory, and is designed to be fast and deterministic. Often a program needs to create and delete a varying number of instances of a fixed-size object. A HeapBuf is ideal for allocating space for such objects, since it can handle the request quickly and without any fragmentation. A HeapBuf may also be used for allocating objects of varying sizes when response time is more important than efficient memory usage. In this case, a HeapBuf will suffer from "internal" fragmentation. There will never be any fragmented space in the heap overall, but the allocated blocks themselves may contain wasted space, so the fragmentation is "internal" to the allocated block. Allocating from and freeing to a HeapBuf always takes the same amount of time, so a HeapBuf is a "deterministic" memory manager. The following examples create a HeapBuf instance with 10 memory blocks of size 128. Configuration example: The first example uses XDCtools to statically configure the heap. In this configuration example, no buffer or bufSize parameter needs to be specified, since the configuration can compute these values and allocate the correct sections at link time.
var HeapBuf = xdc.useModule('ti.sysbios.heaps.HeapBuf'); /* Create heap as global variable so it can be used in C code */ var heapBufParams = new HeapBuf.Params(); heapBufParams.blockSize = 128; heapBufParams.numBlocks = 10; Program.global.myHeap = HeapBuf.create(heapBufParams);
Runtime example: This second example uses C code to dynamically create a HeapBuf instance. In this example, you must pass the bufSize and buf parameters. Be careful when specifying these runtime parameters. The blocksize needs to be a multiple of the worst-case structure alignment size.
Memory
6-27
Heap Implementations
And bufSize should be equal to blockSize * numBlocks. The worst-case structure alignment is target dependent. On C6x and ARM devices, this value is 8. The base address of the buffer should also be aligned to this same size.
HeapBuf_Params prms; static char *buf[1280]; HeapBuf_Handle heap; Error_Block eb; Error_init(&eb); HeapBuf_Params_init(&prms); prms.blockSize = 128; prms.numBlocks = 10; prms.buf = buf; prms.bufSize = 1280; heap = HeapBuf_create(&prms, &eb); if (heap == NULL) { System_abort("HeapBuf create failed"); }
6.8.3
HeapMultiBuf
HeapMultiBuf is intended to balance the strengths of HeapMem and HeapBuf. Internally, a HeapMultiBuf maintains a collection of HeapBuf instances, each with a different block size, alignment, and number of blocks. A HeapMultiBuf instance can accept memory requests of any size, and simply determines which of the HeapBufs to allocate from. A HeapMultiBuf provides more flexibility in block size than a single HeapBuf, but largely retains the fast performance of a HeapBuf. A HeapMultiBuf instance has the added overhead of looping through the HeapBufs to determine which to allocate from. In practice, though, the number of different block sizes is usually small and is always a fixed number, so a HeapMultiBuf can be considered deterministic by some definitions. A HeapMultiBuf services a request for any memory size, but always returns one of the fixed-sized blocks. The allocation will not return any information about the actual size of the allocated block. When freeing a block back to a HeapMultiBuf, the size parameter is ignored. HeapMultiBuf determines the buffer to free the block to by comparing addresses. When a HeapMultiBuf runs out of blocks in one of its buffers, it can be configured to allocate blocks from the next largest buffer. This is referred to as "block borrowing". See the online reference described in Section 1.7.1 for more about HeapMultiBuf.
6-28
Heap Implementations
The following examples create a HeapMultiBuf that manages 1024 MAUs of memory, which are divided into 3 buffers. It will manage 8 blocks of size 16 MAUs, 8 blocks of size 32 MAUs, and 5 blocks of size 128 MAUs as shown in the following diagram. Program.global.myHeap 16 MAUs 16 MAUs 16 MAUs 16 MAUs 16 MAUs 16 MAUs 16 MAUs 16 MAUs 32 MAUs 32 MAUs 32 MAUs 32 MAUs 32 MAUs 32 MAUs 32 MAUs 32 MAUs 128 MAUs 128 MAUs 128 MAUs 128 MAUs 128 MAUs Configuration example: The first example uses XDCtools to statically configure the HeapMultiBuf instance:
var HeapMultiBuf = xdc.useModule('ti.sysbios.heaps.HeapMultiBuf'); /* HeapMultiBuf without blockBorrowing. */ /* Create as a global variable to access it from C Code. */ var heapMultiBufParams = new HeapMultiBuf.Params(); heapMultiBufParams.numBufs = 3; heapMultiBufParams.blockBorrow = false; heapMultiBufParams.bufParams = [{blockSize: 16, numBlocks:8, align: 0}, {blockSize: 32, numBlocks:8, align: 0}, {blockSize: 128, numBlocks:5, align: 0}]; Program.global.myHeap = HeapMultiBuf.create(heapMultiBufParams);
Memory
6-29
Heap Implementations
Runtime example: This second example uses C code to dynamically create a HeapMultiBuf instance:
HeapMultiBuf_Params prms; HeapMultiBuf_Handle heap; Error_Block eb; Error_init(&eb); /* Create the buffers to manage */ Char buf0[128]; Char buf1[256]; Char buf2[640]; /* Create the array of HeapBuf_Params */ HeapBuf_Params bufParams[3]; /* Load the default values */ HeapMultiBuf_Params_init(&prms); prms.numBufs = 3; prms.bufParams = bufParams; HeapBuf_Params_init(&prms.bufParams[0]); prms.bufParams[0].align = 0; prms.bufParams[0].blockSize = 16; prms.bufParams[0].numBlocks = 8; prms.bufParams[0].buf = (Ptr) buf0; prms.bufParams[0].bufSize = 128; HeapBuf_Params_init(&prms.bufParams[1]); prms.bufParams[1].align = 0; prms.bufParams[1].blockSize = 32; prms.bufParams[1].numBlocks = 8; prms.bufParams[1].buf = (Ptr) buf1; prms.bufParams[1].bufSize = 256; HeapBuf_Params_init(&prms.bufParams[2]); prms.bufParams[2].align = 0; prms.bufParams[2].blockSize = 128; prms.bufParams[2].numBlocks = 5; prms.bufParams[2].buf = (Ptr) buf2; prms.bufParams[2].bufSize = 640; heap = HeapMultiBuf_create(&prms, &eb); if (heap == NULL) { System_abort("HeapMultiBuf create failed"); }
6-30
Chapter 7
Page
Hardware Abstraction Layer APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2 HWI Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-3 Timer Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12 Cache Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-18 HAL Package Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-20
7-1
The generic APIs are designed to cover the great majority of use cases. Developers who are concerned with ensuring easy portability between different TI devices are best served by using the generic APIs as much as possible. In cases where the generic APIs cannot enable use of a devicespecific hardware feature that is advantageous to the software application, you may choose to use the target/device-specific APIs, which provide full hardware entitlement. In this chapter, an overview of the functionality of each HAL package is provided along with usage examples for that packages generic API functions. After the description of the generic functions, examples of target/device-specific APIs, based on those provided for C64x+ devices, are also given. For a full description of the target/device-specific APIs available for a particular family or device, please refer to the API reference documentation. section 7.5, HAL Package Organization provides an overview of the generic HAL packages and their associated target/devicespecific packages to facilitate finding the appropriate packages. Note: For hardware-specific information about using SYS/BIOS, see the links at http://processors.wiki.ti.com/index.php/Category:SYSBIOS.
7-2
HWI Module
7.2.1
Runtime example: The C code required to configure the same interrupt dynamically would be as follows:
#include <ti/sysbios/hal/Hwi> #include <xdc/runtime/Error.h> #include <xdc/runtime/System.h> Hwi_Handle myHwi; Error_Block eb; Error_init(&eb); myHwi = Hwi_create(5, myIsr, NULL, &eb); if (myHwi == NULL) { System_abort("Hwi create failed"); }
The NULL argument is used when the default instance parameters are satisfactory for creating a Hwi object.
7-3
HWI Module
7.2.2
The configured "arg" parameter will be passed to the Hwi function when the dispatcher invokes it.
UArg arg = 0;
The "enabledInt" parameter is used to automatically enable or disable an interrupt upon Hwi object creation.
Bool enableInt = true;
The "eventId" accommodates 'C6000 devices that allow dynamic association of a peripheral event to an interrupt number. The default value of -1 leaves the eventId associated with an interrupt number in its normal (reset) state (that is, no re-association is required).
Int eventId = -1;
The "priority" parameter is provided for those architectures that support interrupt priority setting. The default value of -1 informs the Hwi module to set the interrupt priority to a default value appropriate to the device.
Int priority = -1;
7-4
HWI Module
7.2.3
Runtime example:
#include <ti/sysbios/hal/Hwi> #include <xdc/runtime/Error.h> Hwi_Params hwiParams; Hwi_Handle myHwi; Error_Block eb; /* initialize error block and hwiParams to default values */ Error_init(&eb); Hwi_Params_init(&hwiParams); hwiParams.arg = 10; hwiParams.enableInt = FALSE; myHwi = Hwi_create(5, myIsr, &hwiParms, &eb); if (myHwi == NULL) { System_abort("Hwi create failed"); }
7-5
HWI Module
7.2.4
Restores global interrupts to their previous enabled/disabled state. The "key" is the value returned from Hwi_disable() or Hwi_enable(). The APIs that follow are used for enabling, disabling, and restoring specific interrupts given by "intNum". They have the same semantics as the global Hwi_enable/disable/restore APIs.:
UInt Hwi_enableInterrupt(UInt intNum); UInt Hwi_disableInterrupt(UInt intNum); Hwi_restoreInterrupt(UInt key);
Hwi_clearInterrupt(UInt intNum);
Clears "intNum" from the set of currently pending interrupts. Disabling hardware interrupts is useful during a critical section of processing. On the C6000 platform, Hwi_disable() clears the GIE bit in the control status register (CSR). On the C2000 platform, Hwi_disable() sets the INTM bit in the ST1 register.
7.2.5
7-6
HWI Module /* Get handle to Hwi module for static configuration */ var Hwi = xdc.useModule('ti.sysbios.hal.Hwi'); /* Initialize hwiParams to default values */ var hwiParams = new Hwi.Params; /* Set myIsr5 argument */ hwiParams.arg = 10; /* Keep interrupt 5 disabled until later */ hwiParams.enableInt = false; /* Create a Hwi object for interrupt number 5 * that invokes myIsr5() with argument 10 */ Hwi.create(5, '&myIsr5', hwiParams); /* Add an idle thread 'myIdleFunc' that monitors interrupts. */ var Idle = xdc.useModule(ti.sysbios.knl.Idle); Idle.addFunc('&myIdleFunc');
Runtime example:
#include #include #include #include <xdc/std.h> <xdc/runtime/System.h> <xdc/runtime/Error.h> <ti/sysbios/hal/Hwi>
Bool Hwi5 = FALSE; Bool Hwi6 = FALSE; Main(Void) { Hwi_Params hwiParams; Hwi_Handle myHwi; Error_Block eb; /* Initialize error block and hwiParams to default values */ Error_init(&eb); Hwi_Params_init(&hwiParams); /* Set myIsr6 parameters */ hwiParams.arg = 12; hwiParams.enableInt = FALSE;
7-7
HWI Module /* Create a Hwi object for interrupt number 6 * that invokes myIsr6() with argument 12 */ myHwi = Hwi_create(6, myIsr6, &hwiParms, &eb); if (myHwi == NULL) { System_abort("Hwi create failed"); } /* enable both interrupts */ Hwi_enableInterrupt(5); Hwi_enableInterrupt(6); /* start BIOS */ BIOS_start(); } /* Runs when interrupt 5 occurs */ Void myIsr5(UArg arg) { If (arg == 10) { Hwi5 = TRUE; } } /* Runs when interrupt 6 occurs */ Void myIsr6(UArg arg) { If (arg == 12) { Hwi6 = TRUE; } } /* The Idle thread checks for completion of interrupts 5 & 6 * and exits when they have both completed. */ Void myIdleFunc() { If (Hwi5 && Hwi6) { System_printf("Both interrupts have occurred!"); System_exit(0); } }
7-8
HWI Module
7.2.6
On some platforms, such as the MSP430, the Hwi dispatcher is not provided. However, interrupt stubs are generated to provide essentially the same default functionality. However, the generated interrupt stubs do not automatically manage nested interrupts, because this is not supported on the MSP430. Note: For hardware-specific information about using SYS/BIOS, see the links on the http://processors.wiki.ti.com/index.php/Category:SYSBIOS wiki page. Note: The interrupt keyword or INTERRUPT pragma must not be used to define the C function invoked by the Hwi dispatcher (or interrupt stubs, on platforms for which the Hwi dispatcher is not provided, such as the MSP430). The Hwi dispatcher and the interrupt stubs contain this functionality, and the use of the C modifier will cause catastrophic results. Functions that use the interrupt keyword or INTERRUPT pragma may not use the Hwi dispatcher or interrupt stubs and may not call SYS/BIOS APIs.
7-9
HWI Module
7.2.7
7.2.8
These three APIs allow enabling, disabling and restoring a set of interrupts defined by a "mask" argument. These APIs provide direct manipulation of the C64x+'s internal IER registers. To gain access to these additional APIs, you use the target/device-specific Hwi module associated with the C64x+ target rather than the ti.sysbios.hal.Hwi module. For documentation on the target/device-specific Hwi modules, see the CDOC documentation for ti.sysbios.family.*.Hwi. For example, ti.sysbios.family.c28.Hwi. The following examples are modified versions of portions of the example in Section 7.2.5. The modifications are shown in bold.
7-10
HWI Module
Configuration example:
var Hwi = xdc.useModule('ti.sysbios.family.c64p.Hwi'); /* Initialize hwiParams to default values */ var hwiParams = new Hwi.Params; /* Set myIsr5 parameters */ hwiParams.arg = 10; hwiParams.enableInt = false; /* Create a Hwi object for interrupt number 5 * that invokes myIsr5() with argument 10 */ Hwi.create(5, '&myIsr5', hwiParams);
Runtime example:
#include <ti/sysbios/family/c64p/Hwi> #include <xdc/runtime/Error.h> Main(Void) { Hwi_Params hwiParams; Hwi_Handle myHwi; Error_Block eb; /* Initialize error block and hwiParams to default values */ Error_init(&eb); Hwi_Params_init(&hwiParams); /* Set myIsr6 parameters */ hwiParams.arg = 12; hwiParams.enableInt = FALSE; /* Create a Hwi object for interrupt number 6 * that invokes myIsr6() with argument 12 */ myHwi = Hwi_create(6, myIsr6, &hwiParms, &eb); if (myHwi == NULL) { System_abort("Hwi create failed"); } /* Enable interrupts 5 & 6 simultaneously using the C64x+ * Hwi module Hwi_enableIER() API. */ Hwi_enableIER(0x0060); . . .
7-11
Timer Module
In a C program, use:
myTimer = Timer_create(Timer_ANY, myIsr, &timerParams, &eb); if (myTimer == NULL) { System_abort("Timer create failed"); }
The timerParams includes a number of parameters to configure the timer. For example timerParams.startMode can be set to StartMode_AUTO or StartMode_USER. The StartMode_AUTO setting indicates that staticallycreated timers will be started in BIOS_start() and dynamically-created timers will be started at create() time. The StartMode_USER indicates that your program starts the timer using Timer_start(). See the example in Section 7.3.1.
7-12
Timer Module
You can get the total number of timer peripherals by calling Timer_getNumTimers() at runtime. This includes both used and available timer peripherals. You can query the status of the timers by calling Timer_getStatus(). If you want to use a specific timer peripheral or want to use a custom timer configuration (setting timer output pins, emulation behavior, etc.), you should use the target/device-specific Timer module. For example, ti.sysbios.family.c64.Timer. The Timer module also allows you to specify the extFreq (external frequency) property for the timer peripheral and provides an API to get the timer frequency at runtime. This external frequency property is supported only on targets where the timer frequency can be set separately from the CPU frequency. You can use Timer_getFreq() to convert from timer interrupts to real time. The Timer module provides APIs to start, stop, and modify the timer period at runtime. These APIs have the following side effects. Timer_setPeriod() stops the timer before setting the period register. It then restarts the timer. Timer_stop() stops the timer and disables the timer interrupt. Timer_start() clears counters, clears any pending interrupts, and enables the timer interrupt before starting the timer.
Runtime example: This C example creates a timer with a period of 10 microseconds. It passes an argument of 1 to the myIsr function. It instructs the Timer module to use any available timer peripheral:
Timer_Params timerParams; Timer_Handle myTimer; Error_Block eb; Error_init(&eb); Timer_Params_init(&timerParams); timerParams.period = 10; timerparams.periodType = Timer_PeriodType_MICROSECS; timerParams.arg = 1; myTimer = Timer_create(Timer_ANY, myIsr, &timerParams, &eb); if (myTimer == NULL) { System_abort("Timer create failed"); }
7-13
Timer Module
Configuration example: This XDCtools example statically creates a timer with the same characteristics as the previous C example. It specifies a timerId of 1:
var timer = xdc.useModule('ti.sysbios.hal.Timer'); var timerParams = new Timer.Params(); timerParams.period = 10; timerParams.periodType = Timer.PeriodType_MICROSECS; timerParams.arg = 1; timer.create(1, '&myIsr', timerParams);
Runtime example: This C example specifies a frequency for a timer that it creates. The extFreq.hi and extFreq.lo properties set the high and low 32-bit portions of the structure used to represent the frequency in Hz.
Timer_Params timerParams; Timer_Handle myTimer; Error_Block eb; Error_init(&eb); Timer_Params_init(&timerParams); timerParams.extFreq.lo = 270000000; /* 27 MHz */ timerParams.extFreq.hi = 0; ... myTimer = Timer_create(Timer_ANY, myIsr, &timerParams, &eb); if (myTimer == NULL) { System_abort("Timer create failed"); }
Configuration example: This XDCtools configuration example specifies a frequency for a timer that it creates.
var Timer = xdc.useModule('ti.sysbios.hal.Timer'); var timerParams = new Timer.Params(); timerParams.extFreq.lo = 270000000; timerParams.extFreq.hi = 0; ... Timer.create(1, '&myIsr', timerParams);
7-14
Timer Module
Runtime example: This C example creates a timer that runs the tickFxn() every 2 milliseconds using any available timer peripheral. It also creates a task that, when certain conditions occur, changes the timers period from 2 to 4 milliseconds. The tickFxn() itself prints a message that shows the current period of the timer.
Timer_Handle timerHandle; Int main(Void) { Error_Block eb; Timer_Params timerParams; Task_Handle taskHandle; Error_init(&eb); Timer_Params_init(&timerParams); timerParams.period = 2000; /* 2 ms */ timerHandle = Timer_create(Timer_ANY, tickFxn, &timerParams, &eb); if (timerHandle == NULL) { System_abort("Timer create failed"); } taskHandle = Task_create(masterTask, NULL, &eb); if (taskHandle == NULL) { System_abort("Task create failed"); } } Void masterTask(UArg arg0 UArg arg1) { ... // Condition detected requiring a change to timer period Timer_stop(timerHandle); Timer_setPeriodMicroSecs(4000); /* change 2ms to 4ms */ Timer_start(timerHandle(); ... } Void tickFxn(UArg arg0 UArg arg1) { System_printf("Current period = %d\n", Timer_getPeriod(timerHandle); }
7-15
Timer Module
7.3.1
7-16
Timer Module
Runtime example: This C example uses the myTimer created in the preceding XDCtools configuration example and reconfigures the timer with a different function argument and startMode in the programs main() function before calling BIOS_start().
#include <ti/sysbios/timers/timer64/Timer.h> #include <xdc/cfg/global.h> #include <xdc/runtime/Error.h> Void myIsr(UArg arg) { System_printf("myIsr arg = %d\n", (Int)arg); System_exit(0); } Int main(Int argc, char* argv[]) { Timer_Params timerParams; Error_Block eb; Error_init(&eb); Timer_Params_init(&timerParams); timerParams.arg = 2; timerparams.startMode = Timer_StartMode_AUTO; Timer_reconfig(myTimer, tickFxn, &timerParams, &eb); if (Error_check(&eb)) { System_abort("Timer reconfigure failed"); } BIOS_start(); return(0); }
7-17
Cache Module
7.4.1
These Cache APIs operate on an address range beginning with the starting address of blockPtr and extending for the specified byte count. The range of addresses operated on is quantized to whole cache lines in each cache.
7-18
Cache Module
The blockPtr points to an address in non-cache memory that may be cached in one or more caches or not at all. If the blockPtr does not correspond to the start of a cache line, the start of that cache line is used. If the byteCnt is not equal to a whole number of cache lines, the byteCnt is rounded up to the next size that equals a whole number of cache lines. If the wait parameter is true, then this function waits until the invalidation operation is complete to return. If the wait parameter is false, this function returns immediately. You can use Cache_wait() later to ensure that this operation is complete. Cache_wait(); Waits for the cache wb/wbInv/inv operation to complete. A cache operation is not truly complete until it has worked its way through all buffering and all memory writes have landed in the source memory.
As described in Section 7.5, this module is implemented using the RTSC proxy-delegate mechanism. A separate target/device-specific Cache module is provided for each supported family. Additional APIs are added to this module for certain target/device-specific implementations. For example, the ti.sysbios.family.c64p.Cache module adds APIs specific to the C64x+ caches. These extensions have functions that also have the prefix "Cache_". Currently the C64x+, C674x, and ARM caches are supported. C64x+ specific: The caches on these devices are Level 1 Program (L1P), Level 1 Data (L1D), and Level 2 (L2). See the TMS320C64x+ DSP Megamodule Reference Guide (SPRU871) for information about the L1P, L1D, and L2 caches.
7-19
7-20
ti.sysbios.hal.Cache
* For targets/devices for which a Timer or Cache module has not yet been developed, the hal.TimerNull or hal.CacheNull delegate is used. In TimerNull/CacheNull, the APIs defined in ITimer/ICache are implemented using null functions. For the proxy-delegate mechanism to work properly, both the proxy and the delegate modules must be implementations of a common RTSC interface specification. The Timer, Hwi, and Cache interface specifications reside in ti.sysbios.interfaces and are ITimer, IHwi, and ICache respectively. These interface specifications define a minimum set of general APIs that, it is believed, will satisfy a vast majority of application requirements. For those applications that may need target/device-specific functionality not defined in these interface specifications, the corresponding Timer, Hwi, and Cache delegate modules contain extensions to the APIs defined in the interface specifications. To access to these extended API sets, you must directly reference the target/device-specific module in your configuration file and include its corresponding header file in your C source files.
7-21
7-22
Chapter 8
Instrumentation
This chapter describes modules and other tools that can be used for instrumentation purposes. Topic
8.1 8.2 8.3 8.4 8.5 8.6
Page
Overview of Instrumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2 Load Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4 Real-Time Analysis Tools in CCS v4.x . . . . . . . . . . . . . . . . . . . . . . . . 8-6 RTA Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-17 Performance Optimization. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-21
8-1
Overview of Instrumentation
where: 'x' is the number of times the idle loop has been executed during the measurement window. 't' is the minimum time for a trip around the idle loop, meaning the time it takes to complete the idle loop if no work is being done in it. 'w' is the length in time of the measurement window.
Any work done in the idle loop is included in the CPU load. In other words, any time spent in the loop beyond the shortest trip around the idle loop is counted as non-idle time. The Load module relies on "update" to be called to compute load and execution times from the time when "update" was last called. This is automatically done for every period specified by Load.windowInMs (default = 500 ms) in a ti.sysbios.knl.Idle function when Load.updateInIdle is set to true (the default). The benchmark time window is the length of time between 2 calls to "update". The execution time is reported in units of xdc.runtime.Timestamp counts, and the load is reported in percentages. By default, load data is gathered for all threads. You can use the configuration parameters Load.hwiEnabled, Load.swiEnabled, and Load.taskEnabled to select which type(s) of threads are monitored.
8-2
Load Module
8.2.1
For information on advanced configuration and caveats of the Load module, see the online reference documentation.
8.2.2
The global Swi and Hwi load logs print two numbers: the time in the thread, and the length of the measurement window. For example:
LS_hwiLoad: 13845300,158370213 LS_swiLoad: 11963546,158370213
These evaluate to loads of 8.7% and 7.6%. The Task load log uses the same format, with the addition of the Task handle address as the first argument. For example:
LS_taskLoad: 0x11802830,56553702,158370213
Instrumentation
8-3
Error Handling
Runtime APIs. You can also choose to call Load_getTaskLoad(), Load_getGlobalSwiLoad(), Load_getGlobalHwiLoad() or Load_getCPULoad() at any time to obtain the statistics at runtime. The Load_getCPULoad() API returns an actual percentage load, whereas Load_getTaskLoad(), Load_getGlobalSwiLoad(), and Load_getGlobalHwiLoad() return a Load_Stat structure. This structure contains two fields, the length of time in the thread, and the length of time in the measurement window. The load percentage can be calculated by dividing these two numbers and multiplying by 100%. However, the Load module also provides a convenience function, Load_calculateLoad(), for this purpose. For example, the following code retrieves the Hwi Load:
Load_Stat stat; UInt32 hwiLoad; Load_getGlobalHwiLoad(&stat); hwiLoad = Load_calculateLoad(&stat);
8-4
Error Handling
Notice that in the previous example, the test to determine whether to call System_abort() compares the value returned by Swi_create() to NULL as follows:
if (swi0 == NULL) { System_abort("Swi create failed"); }
Most of the SYS/BIOS APIs that expect an error block also return a handle to the created object or the allocated memory. For this reason, it is simplest and provides the best performance to check the value returned by these APIs. For APIs that get passed an Error_Block but do not return a handle or other status value, you can use the Error_check() function to test the Error_Block as in the following example, which calls Timer_reconfig() to reconfigure a statically-created Timer object:
Timer_reconfig(myTimer, tickFxn, &timerParams, &eb); if (Error_check(&eb)) { System_abort("Timer reconfigure failed"); }
You may see examples in other documentation that pass NULL in place of the Error_Block argument. If an error occurs when creating an object or allocating memory and NULL was passed instead of an Error_Block, the application aborts and a reason for the error is output using System_printf(). This may be the best behavior in systems where any error is fatal, and you do not want to do any error checking. The advantage to passing and testing an Error_Block is that your program can have control over when it aborts. For example, instead of aborting when memory cannot be allocated, you might want to try to release other memory and try again or switch to a mode with more limited memory needs. Note that the System_abort() function calls a hook function from its System.SupportProxy module (for example, xdc.runtime.SysStd). You might want to use a different abort() hook function that performs a reset and starts the application over.
Instrumentation
8-5
In addition to the RTA tools, the Runtime Object Viewer (ROV) is a stop-mode debugging tool provided by XDCtools. You can use ROV with SYS/BIOS applications to see state information about all the modules in your application. For information, see the RTSC-pedia page on ROV at http://rtsc.eclipse.org/docs-tip/RTSC_Object_Viewer.
8-6
8.4.1
Raw Logs
By default, the Raw Logs tool displays complete unformatted log data. The default columns displayed are: time, seqID, module, formattedMsg, currentThread, and logger. You can open this tool by choosing Tools > RTA > Raw Logs from the CCS menu bar.
This table displays all the log records that have been sent from the target. This contains all the records used by the RTA tools to populate their graphs and tables. In addition, the following types of logs are also shown: Any Log records from other modules Any user-defined Logs or Log_print*() calls
This tool contains the following toolbar icons: Toggle View With Group setting on and off. (Shift+G) Open the Find In dialog for searching this log. Filter the log records to match a pattern by using the Set Filter Expression dialog. Freeze Data Updates from the target. This is useful when you are using the Find or Filter dialogs. (Shift+F5) Choose the type of log messages you want listed. Auto Fit Columns sets the column widths to fit their current contents. Refresh the GUI displays. This button does not collect data from the target. Stream RTA Data toggles the collection of RTA data at runtime. The default is on if the application is configured to use RTDX. Changing the setting of this toggle affects the setting in all RTA tools.
Instrumentation
8-7
Toggle Autorefresh Mode is available only when the target is stopped. Toggling this icon on causes the RTA tools to collect stop mode data from the target once per target halt. Changing the setting of this toggle affects the setting in all RTA tools. This mode is on by default. Refresh RTA Buffers in Stop Mode is available only when the target is stopped, and only when the Autorefresh Mode toggle is off. Clicking this icon causes the RTA tools to collect stop mode data from the target one time. See Section 8.5.2 and Section 8.5.3 for more about using the icons. , , and
"Groups" in the RTA views refers to synchronizing the views so that moving around in one view causes similar movement to happen automatically in another. For example, if you group the CPU load graph with Raw Logs, then if you click on the CPU Load graph, the Raw Log displays the closest record to where you clicked in the graph. You can right-click on this tool to choose from a menu of options. In addition to some of the toolbar commands, you can use the following additional commands from the right-click menu: Column Settings. This command opens a dialog that lets you hide or display various columns. You can also change the alignment, font, and display format of a column (for example, decimal, binary, or hex). Copy. This command copies the selected text to the clipboard. Enable Auto Scroll. This command allows the log to scroll automatically as new data is available. Data > Export Selected. This command lets you select a .csv (commaseparated value) file to contain the selected data. Data > Export All. This command lets you select a .csv (commaseparated value) file to contain all the data currently displayed in the log. Groups. This command lets you define groups to contain various types of log messages.
8-8
8.4.2
Printf Logs
The Printf Log is a convenient way to view all the user-generated trace and printf logs. By default, the Printf Log tool displays the time, seqID, formattedMsg, and logger. You can open this tool by choosing Tools > RTA > Printf Logs from the CCS menu bar.
The toolbar icons and right-click menu for the Printf Logs tool are the same as for the Raw Logs tool (Section 8.4.1).
8.4.3
Exec Graph
The Exec Graph shows which thread is running at a given time. You can open this tool by choosing Tools > RTA > Exec Graph from the CCS menus.
Instrumentation
8-9
In this graph, square brackets [] indicate the beginning and end of Hwi and Swi threads. Red flags facing right on the Swi timeline indicate Swi post events. Green flags on the Semaphore and Event timelines indicate post events, and red flags facing left indicate pend events. These icons do not indicate which Hwi, Swi, Semaphore, or Event instance the brackets and flags refer to. For this information, group the Exec Graph with the Raw Logs view (they are grouped by default). Then you can click anywhere on the Exec Graph to jump to the corresponding Raw Log record. This tool contains the following toolbar icons: Toggle Measurement Marker Mode selects a measuring mode for time marking. The mode choices are "Freeform" or "Snap to Data". The axis choices are X-Axis, Y-Axis, or Both. When you click on the graph, a marker of the type you have selected is placed. When you drag your mouse around the graph, the time is shown in red. Toggle View With Group setting on and off. (Shift+G) Align Horizontal Center can be used if you have enabled the View With Group toggle. This icon aligns a group by centering. Align Horizontal Range can be used if you have enabled the View With Group toggle. This icon aligns a group using a horizontal range. Click this icon to Zoom In on the graph by spreading out the x-axis. Click this icon to Zoom Out. Choose to Reset Zoom level to the default or use the drop-down list to choose a specific zoom level. Refresh the GUI displays. This button does not collect data from the target. Open the Find In dialog for searching this graph. Filter the log records to match a pattern by using the Set Filter Expression dialog. Freeze Data Updates data updates from the target. This is useful when you are using the Find or Filter dialogs. (Shift+F5) Stream RTA Data toggles the collection of RTA data at runtime. The default is on if the application is configured to use RTDX. Changing the setting of this toggle affects the setting in all RTA tools.
8-10
Toggle Autorefresh Mode is available only when the target is stopped. Toggling this icon on causes the RTA tools to collect stop mode data from the target once per target halt. Changing the setting of this toggle affects the setting in all RTA tools. This mode is on by default. Refresh RTA Buffers in Stop Mode is available only when the target is stopped, and only when the Autorefresh Mode toggle is off. Clicking this icon causes the RTA tools to collect stop mode data from the target one time. See Section 8.5.2 and Section 8.5.3 for more about using the icons. , , and
You can right-click on this tool to choose from a menu of options. In addition to some of the toolbar commands, you can use the following additional commands from the right-click menu: Legend. Toggle this command to hide the graph legend. Horizontal Axis. Toggle this command to hide the x-axis time markings. Vertical Axis. Toggle this command to hide the y-axis thread labels. Data > Export All. This command lets you select a .csv (commaseparated value) file to contain all the data currently displayed in the log. Groups. This command lets you define groups to contain various types of log messages. Insert Measurement Mark. Inserts a marker at the location where you right clicked. Remove Measurement Mark. Lets you select a marker to remove. Remove All Measurement Marks. Removes all markers you have placed. Display Properties. Opens a dialog that lets you change the colors, scales, display formats, and labels on the graph. RTA Time Unit. Choose between displaying units for the x axis in ticks or time (ns).
Instrumentation
8-11
8.4.4
The messages shown in this tool are the raw data used to plot the Exec Graph. The toolbar icons and right-click menu for the Exec Graph Data tool are the same as for the Raw Logs tool (Section 8.4.1).
8.4.5
CPU Load
The CPU Load tool shows the percentage of time the application is not in the idle loop. You can open this tool by choosing Tools > RTA > CPU Load from the CCS menu bar. See Section 8.2, Load Module for details on load statistics.
8-12
The CPU Load tool has the same toolbar icons and right-click menu as the Exec Graph tool (Section 8.4.3). However, in addition, the following right-click menu commands are provided: Show Grid Lines. Toggle on or off the x-axis and y-axis grid lines you want to see. Display As. Choose the marker you want to use to display the data. The default is a connected line, but you can choose from various marker styles and sizes. Auto Scale. Scales the load data to fit the range in use. For example, if the range is between 70% and 90%, it zooms in on that range to make changes more visible. When auto scale is turned on, the scale may change as new data arrives. Reset Auto Scale. Resets the scale to better display the current data.
8.4.6
Thread Load
The Thread Load tool shows the percentage of time the application spend in each thread. You can open this tool by choosing Tools > RTA > Thread Load from the CCS menu bar. See Section 8.2, Load Module for details on load statistics.
The toolbar icons and right-click menu for the Thread Load tool are the same as for the Exec Graph tool (Section 8.4.3).
Instrumentation
8-13
8.4.7
The messages in this tool are raw data used to plot the CPU Load graph. The toolbar icons and right-click menu for the Load Data tool are the same as for the Raw Logs tool (Section 8.4.1).
8.4.8
8-14
The RTA Control Panel contains the following toolbar icons: Close All RTA Views, including this one. Refresh Runtime Configuration information gets the current runtime settings for this tool from the target application. RTA Update Rate sets the minimum wait time between attempts for the Task that collects RTA information and sends it to the host. In practice, the time may be longer than what you specify if the Task needs to wait to run because its priority is lower than that of other threads. Auto Fit Columns sets the column widths to fit their current contents. Expand All nodes in the Logger Buffer column. Collapse All nodes in the Logger Buffer column. Duration for RTA Streaming sets how long data is sent from the target to the host computer. The duration is in minutes. The default is to stream as long as the target application is running. Disk Usage Quota is the amount of disk space on the host computer available to the RTA tools for temporary data storage. The default is 2 GB. This button will take you to the CCS Preferences dialog to set the size and location of these temporary files. Toggle Autorefresh Mode is available only when the target is stopped. Toggling this icon on causes the RTA tools to collect stop mode data from the target once per target halt. Changing the setting of this toggle affects the setting in all RTA tools. Refresh RTA Buffers in Stop Mode is available only when the target is stopped, and only when the Autorefresh Mode toggle is off. Clicking this icon causes the RTA tools to collect stop mode data from the target one time. Stream RTA Data toggles the collection of RTA data at runtime. The default is on if the application is configured to use RTDX. Changing the setting of this toggle affects the setting in all RTA tools. See Section 8.5.2 and Section 8.5.3 for more about using the icons. , , and
The RTA Control Panel lets you make runtime changes to the types of Log events that are logged on the target. This allows you to make better use of limited buffer sizes and transport bandwidth and to focus on the events that are currently important to you.
Instrumentation
8-15
The RTA Control Panel provides control over logging in two ways: You can enable or disable particular LoggerBuf instances You can enable or disable specific Diags levels on a per-module basis.
The RTA Control Panel lists the LoggerBuf instances in the left column. If a name was given to the instance, that name is shown. Otherwise, a LoggerBuf instance is labeled with its address. Click on a LoggerBuf instance name to see a drop-down menu that allows you to enable or disable that instance. Disabling a LoggerBuf instance means that when the target calls Log_write() or Log_print() to that LoggerBuf, the record will not actually be written into the bufferany record written to a disabled LoggerBuf is discarded.
Expanding a LoggerBuf instance shows you the list of modules that are logging to that buffer. For each module you can see the current Diags settings for that module (See http://rtsc.eclipse.org/cdoc-tip/xdc/runtime/Diags.html for an explanation of Diags levels and settings). If the checkmark or checkbox is gray, that item is configured as ALWAYS_ON or ALWAYS_OFF. If a particular Diags level is configured as "RUNTIME_OFF" or "RUNTIME_ON", then you can enable or disable that particular Diags level for a given module. Click on the Diags setting to see a drop-down menu that lets you to turn the level on or off. (You may need to make the columns wider before you can open the drop-down list of settings.) Note: Modifying log settings requires a data transport. Changing settings in the RTA Control Panel is not possible if your configuration sets the Agent.transport property to Agent.Transport_STOP_MODE_ONLY. If you know you will never be interested in receiving a particular set of events, you may want to disable them in your applications configuration by setting them to ALWAYS_OFF. (If your configuration uses the Agent module to
8-16
RTA Agent
automatically configure logging, you can still change individual Diags levels, and your settings will override those of the Agent. See http://rtsc.eclipse.org/docs-tip/Using_xdc.runtime_Logging for information.) If you used the RTA Agent to configure system logging for your application, you should see three LoggerBuf instances whose labels begin with "RTA": RTASystemLog is a large buffer that holds all system log records from modules such as Hwi, Swi, Task, Idle, Clock, Event, Semaphore, and Timer. See the documentation for each of these modules for details on the events they log. The RTA tools use User1 and User2 logging for this LoggerBuf. RTALoadLog is a separate log that holds the Load module records. These records are logged relatively infrequently, but are required to support the CPU and Thread Load graphs. They are sent to a dedicated LoggerBuf instance so that they are not overwritten by other more frequent events. The RTA tools use User4 logging for this LoggerBuf. RTASupportLog is used internally by RTA. It is used to log a single record when the application starts to provide RTA with the Timestamp frequency so that timestamps can be converted into seconds.
Instrumentation
8-17
RTA Agent
8.5.1
Alternately, you can set the transport to Transport_USER (if your application has a custom data gathering setup) or Transport_STOP_MODE_ONLY. DSP/BIOS 5.x configurations required significantly more statements to configure RTDX. These are no longer needed if you set the Agent.transport property. In the configuration editor, you can examine the properties of the ti.sysbios.rta.Agent and ti.rtdx.RtdxModule modules. For example, the following statement sets the priority of the Task used by the Agent to collect log information to a higher priority than an example Task called "taskLoadTask".
Agent.priority = taskLoadTask.priority + 1;
The following statement sets the size of the buffer used to hold log messages before transferring them to the host:
Agent.numSystemRecords = 2048;
For more examples of statements used to configure the Agent, create a CCS project and use the "RTA Example" template. By default, the target is expected to be a hardware target with a JTAG connection to the host. If your target is a simulator, your configuration should include the following lines:
var RtdxModule = xdc.useModule('ti.rtdx.RtdxModule'); RtdxModule.protocol = RtdxModule.PROTOCOL_SIMULATION;
For more information, see the CDOC online documentation for the ti.rtdx package in SYS/BIOS and the TI Embedded Processors Wiki at www.tiexpressdsp.com.
8-18
RTA Agent
8.5.2
8.5.3
Instrumentation
8-19
RTA Agent
8.5.4
ti.sysbios.family.*
ti.sysbios.utils
For all of these modules except Load, the Agent sets diags_USER1 and diags_USER2 to RUNTIME_ON. For the Load module, it sets diags_USER4 to RUNTIME_ON. To change any of these settings (for example, to set the diags to ALWAYS_ON), set Agent.configureSystemLog to false and perform the configuration manually. To perform the configuration manually, create a LoggerBuf instance and give it the instance name "RTASystemLog". Ensure that all of the above modules log to this "RTASystemLog" LoggerBuf.
8-20
Performance Optimization
8.6.1
Configuring Logging
Logging can significantly impact the performance of a system. You can reduce the impact of logging by optimizing the configuration. There are two main ways to optimize the logging used in your application: No logging. In SYS/BIOS, logging is not enabled by default. However, if you enable the ti.sysbios.rta.Agent module as described in Section 8.5.1, logging is performed for all SYS/BIOS system modules. To configure your application without logging support, do not enable the RTA Agent, and no logging will occur by default. Optimizing logging. If you need some logging enabled in your application, there are some configuration choices you can make to optimize performance. These are described in the following subsections.
8.6.1.1 Diags Settings There are four diagnostics settings for each diagnostics RUNTIME_OFF, RUNTIME_ON, ALWAYS_OFF, and ALWAYS_ON. level:
The two runtime settings (RUNTIME_OFF and RUNTIME_ON) allow you to enable or disable a particular diagnostics level at runtime. However, a check must be performed to determine whether logging is enabled or disabled every time an event is logged.
Instrumentation
8-21
Performance Optimization
If you use ALWAYS_OFF or ALWAYS_ON instead, you will not be able to change the setting at runtime, but the logging call will either be a direct call to log the event (ALWAYS_ON) or will be optimized out of the code altogether (ALWAYS_OFF).
var Defaults = xdc.useModule('xdc.runtime.Defaults'); var Diags = xdc.useModule('xdc.runtime.Diags'); /* 'RUNTIME' settings allow you to turn it off or on at runtime, * but require a check at runtime. */ Defaults.common$.diags_USER1 = Diags.RUNTIME_ON; Defaults.common$.diags_USER2 = Diags.RUNTIME_OFF; /* These settings cannot be changed at runtime, but optimize out * the check for better performance. */ Defaults.common$.diags_USER3 = Diags.ALWAYS_OFF; Defaults.common$.diags_USER4 = Diags.ALWAYS_ON;
8.6.1.2 Choosing Diagnostics Levels SYS/BIOS modules only log to two levels: USER1 and USER2. They follow the convention that USER1 is for basic events and USER2 is for more detail. To improve performance, you could only turn on USER1, or turn on USER2 for particular modules only. Refer to each module's documentation to see which events are logged as USER1 and which are logged as USER2. 8.6.1.3 Choosing Modules to Log To optimize logging, enable logging only for modules that interest you for debugging. For example, Hwi logging tends to be the most expensive in terms of performance due to the frequency of hardware interrupts. Two Hwi events are logged on every Clock tick when the Clock's timer expires.
8.6.2
Configuring Diagnostics
By default, ASSERTS are enabled for all modules. SYS/BIOS uses asserts to check for common user mistakes such as calling an API with an invalid argument or from an unsupported context. Asserts are useful for catching coding mistakes that may otherwise lead to confusing bugs.
8-22
Performance Optimization
To optimize performance after you have done basic debugging of API calls, your configuration file can disable asserts as follows:
var Defaults = xdc.useModule('xdc.runtime.Defaults'); var Diags = xdc.useModule('xdc.runtime.Diags'); /* Disable asserts in all modules. */ Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;
8.6.3
8.6.4
Hwi Configuration
The hardware interrupt dispatcher provides a number of features by default that add to interrupt latency. If your application does not require some of these features, you can disable them to reduce interrupt latency. dispatcherAutoNestingSupport. You may disable this feature if you don't need interrupts enabled during the execution of your Hwi functions. dispatcherSwiSupport. You may disable this feature if no Swi threads will be posted from any Hwi threads. dispatcherTaskSupport. You may disable this feature if no APIs are called from Hwi threads that would lead to a Task being scheduled. For example, Semaphore_post() would lead to a Task being scheduled. dispatcherIrpTrackingSupport. This feature supports the Hwi_getIrp() API, which returns an interrupt's most recent return address. You can disable this feature if your application does not use that API.
Instrumentation
8-23
Performance Optimization
8.6.5
Stack Checking
By default, the Task module checks to see whether a Task stack has overflowed at each Task switch. To improve Task switching latency, you can disable this feature the Task.checkStackFlag property to false.
8-24
Appendix A
This appendix describes SYS/BIOS emulation when using the Microsoft Windows operating system. Topic Page
A-1
Motivation
A.1
Motivation
You can use SYS/BIOS emulation on Windows to model your SYS/BIOS applications on a high-level operating system before moving to simulators or hardware. When you are developing a software module, such as a codec, a common practice is to first write a "Golden C" version. Once the software module is functioning properly, the Golden C version is used as a baseline for porting the software to specific target platforms. When developing the Golden C version, it is often preferable to do this work on a High-Level Operating System (HLOS), such as Windows. This allows the use of HLOS tool-chains for code profiling and validation. Providing a SYS/BIOS Emulation layer that runs on Windows makes this effort more efficient. It allows a native SYS/BIOS application to be built and run as a native Windows executable.
A.2
High-Level Description
SYS/BIOS emulation is supported by implementing the platform proxy modules for Windows. These modules are contained in the ti.sysbios.family.windows package. These proxy modules provide interfaces for the following: hardware interrupts thread context switching general purpose timers system clock tick counter
To implement these interfaces, some hardware functionality is emulated in the proxy modules because Windows does not allow direct access to the hardware.
A-2
High-Level Description
The following figure shows a block diagram of both a C64x+ implementation and a Windows implementation.
Application
BIOS
Windows HLOS
C64x+ Hardware
Hardware
Application code written in C that makes only SYS/BIOS API calls should not require any changes. However, any code written for peripheral control will need to be replaced. Peripherals are not modeled in the emulation package. The SYS/BIOS Kernel does not require any changes. Through XDCtools configuration, the kernel binds with the appropriate proxy modules relevant to the target platform (that is, for the Windows platform or a hardware platform). When building the application, XDCtools configuration is used to select the runtime platform. Through configuration options, the application can bind with the appropriate modules that are hardware or emulation specific. This will most likely pertain to peripheral and/or test framework code. On hardware platforms, peripheral devices typically raise interrupts to the CPU, which then invokes the Hwi dispatcher to service the interrupt. To emulate this behavior, the SYS/BIOS Emulation package simulates an interrupt that preempts the currently running task and invokes the Hwi Dispatcher. This is done asynchronously with respect to SYS/BIOS tasks.
A-3
The Windows Emulation package faithfully emulates the SYS/BIOS scheduler behavior. That is to say that task scheduling will occur in the same order on Windows as on hardware. However, interrupts are not real-time. Therefore, interrupt preemption will differ, and this may invoke the scheduler in a different sequence than observed when running on hardware. Windows Win32 API functions may be invoked along side SYS/BIOS API functions. This should be kept to a minimum in order to encourage code encapsulation and to maximize code reuse between hardware and the Windows platforms.
A.3
A-4
Appendix B
Rebuilding SYS/BIOS
This appendix describes how to rebuild the SYS/BIOS source code. Topic Page
B.1 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B2 B.2 Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B2 B.3 Build Procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B3 B.4 Building Your Project Using a Rebuilt SYS/BIOS. . . . . . . . . . . . . . . B5
B-1
Overview
B.1
Overview
The SYS/BIOS product includes source files and RTSC build scripts that allow you to modify the SYS/BIOS sources and rebuild its libraries. You can do this in order to modify, update, or add functionality. Note that starting with SYS/BIOS v6.32, you can cause the SYS/BIOS libraries to be rebuilt as part of the application build. See Section 2.3.3, Compiler and Linker Optimization for details. The custom-built libraries will contain only modules and APIs that your application needs to access. You can cause such a custom build to occur by configuring the BIOS.libType property as follows. var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.libType = BIOS.LibType_Custom; If you edit the SYS/BIOS source code and/or corresponding RTSC build scripts, you must also rebuild SYS/BIOS in order to create new libraries containing these modifications. The instructions in this appendix are tailored for rebuilding on Microsoft Windows. However, SYS/BIOS may also be re-built on Linux using these instructions by changing the DOS commands and paths to the equivalents for Linux. Warning: This appendix provides details about rebuilding the SYS/BIOS source code. We strongly recommend that you copy the SYS/BIOS installation to a directory with a different name and rebuild that copy, rather than rebuilding the original installation.
B.2
Prerequisites
In order to rebuild SYS/BIOS, the SYS/BIOS, XDCtools, and IPC products must be installed. The IPC installation location must be referenced in the definition of the XDCPATH environment variable. It is important to build SYS/BIOS with compatible versions of XDCtools and IPC. To find out which versions are compatible, see the Dependencies section of the Release Notes in the top-level directory of your SYS/BIOS installation. If you have the Cygwin tools installed, you must temporarily remove the directory that contains Cygwin from your PATH environment variable before rebuilding SYS/BIOS. Alternately, you can move the Cygwin directory to the end of your PATH definition (that is, after the SYS/BIOS, XDCtools, and other RTSC product directories).
B-2
Build Procedure
B.3
Build Procedure
Follow these steps to rebuild the SYS/BIOS source code: 1) Create a new directory on your computer that you will use to store a copy of the SYS/BIOS installation. This directory will act as a container for your own SYS/BIOS modifications. The full path to this directory cannot contain any spaces. For example, we recommend making a directory called "C:\myBiosBuilds" rather than using a location in the My Documents directory tree. 2) Using Windows Explorer, copy the entire SYS/BIOS installation into the directory you just created. For example, if you installed SYS/BIOS in the default installation location, then you should copy the following folder and all of its contents: C:\Program Files\Texas Instruments\bios_6_##_##_## into the folder: C:\myBiosBuilds After this step, the folder C:\myBiosBuilds should contain the folder bios_6_##_##_##, which is a copy of your SYS/BIOS installation. 3) Rename the folder that contains the copy of SYS/BIOS. For example, rename the following directory: C:\myBiosBuilds\bios_6_##_##_## to: C:\myBiosBuilds\custom_bios_6_##_##_## 4) Copy the file config.bld.default from the etc folder of your original SYS/BIOS installation into the "packages" folder of the copy of the SYS/BIOS installation you just made. For example, copy: C:\Program Files\Texas Instruments\bios_6_##_##_## \etc\config.bld.default to the following location: C:\myBiosBuilds\custom_bios_6_##_##_##\packages 5) Rename the copied config.bld.default file to be "biosConfig.bld". 6) Open the biosConfig.bld file for text editing. 7) Near the end of the file, look for the array called "Build.targets". This array contains the list of targets for which SYS/BIOS should be built. Ensure that the target for which you want SYS/BIOS built is
Rebuilding SYS/BIOS
B-3
Build Procedure
uncommented. For example, if you want to build SYS/BIOS for the C64P target only, then your Build.targets array would look like this: Build.targets = [ //C28_large, C64P, //C67P, //setup elf compiler path at top of page //C674, //Arm9, //M3, //MSP430, //Win32, ]; 8) Save and exit the file. You are now ready to rebuild SYS/BIOS. 9) If you have the Cygwin tools installed, you must temporarily remove the directory that contains Cygwin from your PATH environment variable before rebuilding SYS/BIOS. Alternately, you can move the Cygwin directory to the end of your PATH definition (that is, after the SYS/BIOS, XDCtools, and other RTSC product directories). 10) Open a DOS command prompt window. 11) If you have not already added IPC to your XDCPATH environment variable, do so now. For example, Type: set XDCPATH=%XDCPATH%;C:/Program Files/ Texas Instruments/ipc_1_##_##_##/packages 12) Change directories to the location of your copy of SYS/BIOS. For example: cd C:\myBiosBuilds\custom_bios_6_##_##_##\packages 13) Build SYS/BIOS using the biosConfig.bld file as follows: xdc XDCBUILDCFG=./biosConfig.bld -PR . If you want to clear out intermediate and output files from a previous build, you can use the following command xdc clean For details about the XDCPATH environment variable, http://rtsc.eclipse.org/docs-tip/Managing_the_Package_Path in RTSC-pedia. For more about the xdc command line, http://rtsc.eclipse.org/docs-tip/Command_-_xdc. see the see
B-4
B.4
5) Click the Add button next to the Products and Repositories tab.
Rebuilding SYS/BIOS
B-5
6) Choose Select repository from file-system, and browse to the packages directory of the location where you copied and rebuilt SYS/BIOS. For example, the location may be C:\myBiosBuilds\custom_bios_6_##_##-##\packages.
7) Click OK to apply these changes to the project. 8) You may now rebuild your project using the re-built version of SYS/BIOS.
B-6
Appendix C
Timing Benchmarks
C.1 Timing Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C2 C.2 Interrupt Latency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C2 C.3 Hwi-Hardware Interrupt Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . C2 C.4 Swi-Software Interrupt Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . C4 C.5 Task Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C5 C.6 Semaphore Benchmarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C8
C-1
Timing Benchmarks
C.1
Timing Benchmarks
This appendix describes the timing benchmarks for SYS/BIOS functions, explaining the meaning of the values as well as how they were obtained, so that designers may better understand their system performance. The sections that follow explain the meaning of each of the timing benchmarks. The name of each section corresponds to the name of the benchmark in the actual benchmark data table. The explanations in this appendix are best viewed along side the actual benchmark data. Since the actual benchmark data depends on the target and the memory configuration, and is subject to change, the data is provided in HTML files in the ti.sysbios.benchmarks package (that is, in the BIOS_INSTALL_DIR\packages\ti\sysbios\benchmarks directory). The benchmark data was collected with the RTSC Build-Profile set to release and the BIOS.libType configuration parameter set to BIOS.LibType_Custom. See Section 2.3.3 for more on these settings.
C.2
Interrupt Latency
The Interrupt Latency benchmark is the maximum number of instructions during which the SYS/BIOS kernel disables maskable interrupts. Interrupts are disabled in order to modify data shared across multiple threads. SYS/BIOS minimizes this time as much as possible to allow the fastest possible interrupt response time. The interrupt latency of the kernel is measured across the scenario within SYS/BIOS in which maskable interrupts will be disabled for the longest period of time. The measurement provided here is the cycle count measurement for executing that scenario.
C.3
C-2
Hwi dispatcher. These are execution times of specified portions of Hwi dispatcher code. This dispatcher handles running C code in response to an interrupt. The benchmarks provide times for the following cases: Interrupt prolog for calling C function. This is the execution time from when an interrupt occurs until the user's C function is called. Interrupt epilog following C function call. This is the execution time from when the user's C function completes execution until the Hwi dispatcher has completed its work and exited.
Hardware interrupt to blocked task. This is a measurement of the elapsed time from the start of an ISR that posts a semaphore, to the execution of first instruction in the higher-priority blocked task, as shown in Figure C1.
Interrupt asserted Interrupt response Hwi dispatcher prolog Hwi dispatcher epilog
Task 1 executing
Semaphore_post()
Task 2 executing
Hwi dispatcher
Swi_post()
Hwi dispatcher
Swi 2 executing
Timing Benchmarks
C-3
C.4
Swi 1 executing
Swi_post() of Swi 2
Swi 1 executing
Swi_post() of Swi 2
Swi 1 executing
Time
Post Software Interrupt, No Context Switch
C-4
Task Benchmarks
Post software interrupt, context switch. This is a measurement of the elapsed time between a call to Swi_post() (which causes preemption of the current Swi) and the execution of the first instruction in the higher-priority software interrupt, as shown in Figure C5. The context switch to Swi2 is performed within the Swi executive, and this time is included within the measurement.
Swi 1 executing Swi_post() of Swi 2 Swi 2 executing
C.5
Task Benchmarks
Task_enable(). This is the execution time of a Task_enable() function call, which is used to enable SYS/BIOS task scheduler. Task_disable(). This is the execution time of a Task_disable() function call, which is used to disable SYS/BIOS task scheduler. Task_create(). This is the execution time of a Task_create() function call, which is used to create a task ready for execution. Benchmark data is provided for the following cases of Task_create(): Create a task, no context switch. The executing task creates and readies another task of lower or equal priority, which results in no context switch. See Figure C6.
(Readies lower priority new Task 2) Task 1 executing
Task_create()
Timing Benchmarks
C-5
Task Benchmarks
Create a task, context switch. The executing task creates another task of higher priority, resulting in a context switch. See Figure C7.
(Readies higher priority new Task 2, Task Context Switch) Task 2 executing
Task 1 executing
Task_create()
Time
Task 1 executing
C-6
Task Benchmarks
Lower the current task's own priority, context switch. This case measures execution time of Task_setpri() API when it is called to lower the priority of currently running task. The call to Task_setpri() would result in context switch to next higher-priority ready task. Figure C9 shows this case.
(Lower Task 1s priority, Task Context Switch) Task 2 executing
Task 1 executing
Task_setpri()
The execution time measurement includes the context switch time as shown in Figure C10.
Task 1 executing Task_setpri() (Raise Task 2s priority, Task Context Switch) Task 2 executing
Task 1 executing
Time
Task yield
Timing Benchmarks
C-7
Semaphore Benchmarks
C.6
Semaphore Benchmarks
Semaphore benchmarks measure the time interval between issuing a Semaphore_post() or Semaphore_pend() function call and the resumption of task execution, both with and without a context switch. Semaphore_post(). This is the execution time of a Semaphore_post() function call. Benchmark data is provided for the following cases of Semaphore_post(): Post a semaphore, no waiting task. In this case, the Semaphore_post() function call does not cause a context switch as no other task is waiting for the semaphore. This is shown in Figure C12.
Task 1 executing Task 1 executing
Semaphore_post()
C-8
Semaphore Benchmarks
Semaphore_pend(). This is the execution time of a Semaphore_pend() function call, which is used to acquire a semaphore. Benchmark data is provided for the following cases of Semaphore_pend(): Pend on a semaphore, no context switch. This is a measurement of a Semaphore_pend() function call without a context switch (as the semaphore is available.) See Figure C15.
Task 1 executing Task 1 executing
Timing Benchmarks
C-9
Semaphore Benchmarks
C-10
Appendix D
Size Benchmarks
D.1 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . D2 D.2 Comparison to DSP/BIOS 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . D3 D.3 Default Configuration Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . D4 D.4 Static Module Application Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . D4 D.5 Dynamic Module Application Sizes . . . . . . . . . . . . . . . . . . . . . . . . . . D9 D.6 Timing Application Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . D10
D-1
Overview
D.1
Overview
This appendix contains information on the size impact of using SYS/BIOS modules in an application. Tradeoffs between different SYS/BIOS 6 modules and their impact on system memory can be complex, because applying a module usually requires support from other modules. Also, even if one module's code is linked in by another module, it does not necessarily link in the entire module, but typically only the functions referenced by the applicationan optimization that keeps the overall size impact of the SYS/BIOS 6 kernel to a minimum. Because of the complexity of these tradeoffs, it is important to understand that this appendix does not provide an analytical model of estimating SYS/BIOS 6 overhead, but rather gives sizing information for a number of SYS/BIOS configurations. The size benchmarks are a series of applications that are built on top of one another. Moving down Table D1, each application includes all of the configuration settings and API calls in the previous applications. Applications lower on the table generally require the modules in the applications above them (The Clock module, for example, requires the Hwi module), so this progression allows for measuring the size impact of a module by subtracting the sizes of all of the other modules it depends upon. (The data in the table, however, is provided in absolute numbers.) The actual size benchmark data is included in the SYS/BIOS 6 installation in the ti.sysbios.benchmarks package (that is, in the BIOS_INSTALL_DIR\packages\ti\sysbios\benchmarks directory). There is a separate HTML file for each target. For example, the C64x sizing information is in the c6400Sizing.html file. The sections that follow should be read alongside the actual sizing information as a reference. The benchmark data was collected with the RTSC Build-Profile set to release and the BIOS.libType configuration parameter set to BIOS.LibType_Custom. See Section 2.3.3 for more on these settings. For each benchmark application, the table provides four pieces of sizing information, all in 8-bit bytes. Code Size is the total size of all of the code in the final executable. Initialized Data Size is the total size of all constants (the size of the .const section). Uninitialized Data Size is the total size of all variables. C-Initialization Size is the total size of C-initialization records.
D-2
Comparison to DSP/BIOS 5
D.2
Comparison to DSP/BIOS 5
Where possible, SYS/BIOS 6 size benchmarks have been designed to match the DSP/BIOS 5 benchmarks so that the results can be compared directly. The following table shows which data to compare.
1 SYS/BIOS 6 does not have a PRD module. Instead, the SYS/BIOS 6 Clock module supports the functionality of both the DSP/BIOS 5 CLK and PRD modules. 2 The RTA application is not yet implemented for SYS/BIOS 6. 3 The new benchmark is the application used to generate the timing benchmarks for SYS/BIOS 6 (see Appendix C). This application leverages all of the key components of the operating system in a meaningful way. It does not utilize any of the size-reducing measures employed in the base configuration of the size benchmarks.
Size Benchmarks
D-3
D.3
D.4
D-4
D.4.1
Hwi Application
The Hwi Application configuration script creates a Hwi instance, and the C code calls the Hwi_plug() API. Configuration Script Addition // Use target/device-specific Hwi module. var Hwi = xdc.useModule('ti.sysbios.family.c64.Hwi'); var hwi5 = Program.global.hwi5 = Hwi.create(5, '&oneArgFxn'); C Code Addition Hwi_plug(7, (Hwi_PlugFuncPtr)main);
D.4.2
Clock Application
The Clock Application enables the Clock module, creates a Clock instance, and pulls in the modules necessary to call the Timestamp_get32() API in the C code. Configuration Script Addition var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.clockEnabled = true; var Clock = xdc.useModule('ti.sysbios.knl.Clock'); Clock.create("&oneArgFxn", 5, {startFlag:true,arg:10}); xdc.useModule('xdc.runtime.Timestamp'); C Code Addition Timestamp_get32();
D.4.3
The Clock Object Application statically creates an additional Clock instance to illustrate the size impact of each Clock instance. Configuration Script Addition Clock.create("&oneArgFxn", 5, {startFlag:true,arg:10});
Size Benchmarks
D-5
D.4.4
Swi Application
The Swi Application enables the Swi module and creates a static Swi instance in the configuration script. It calls the Swi_post() API in the C code. Configuration Script Addition var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.swiEnabled = true; var Swi = xdc.useModule('ti.sysbios.knl.Swi'); Program.global.swi0 = Swi.create('&twoArgsFxn'); C Code Addition Swi_post(swi0);
D.4.5
The Swi Object Application creates an additional Swi instance to illustrate the size impact of each new Swi instance. Configuration Script Addition Program.global.swi1 = Swi.create('&twoArgsFxn');
D.4.6
Task Application
The Task Application configuration script enables Tasks and creates a Task instance. It also configures the stack sizes to match the sizes in the DSP/BIOS 5 benchmarks (for comparison). In the C code, the Task application makes a call to the Task_yield() API. Configuration Script Addition var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.taskEnabled = true; var Task = xdc.useModule('ti.sysbios.knl.Task'); Program.global.tsk0 = Task.create("&twoArgsFxn"); Task.idleTaskStackSize = 0x200; Program.global.tsk0.stackSize = 0x200; C Code Addition Task_yield();
D-6
D.4.7
The Task Object Application creates an additional Task instance to illustrate the size impact of each new Task instance. Configuration Script Addition Program.global.tsk1 = Task.create("&twoArgsFxn"); Program.global.tsk1.stackSize = 0x200;
D.4.8
Semaphore Application
The Semaphore Application configuration script creates a Semaphore instance and disables support for Events in the Semaphore for an equitable comparison with the DSP/BIOS 5 SEM module. In the C code, the Semaphore application makes a call to the Semaphore_post() and Semaphore_pend() APIs. Configuration Script Addition var Sem = xdc.useModule('ti.sysbios.knl.Semaphore'); Sem.supportsEvents = false; Program.global.sem0 = Sem.create(0); C Code Addition Semaphore_post(sem0); Semaphore_pend(sem0, BIOS_WAIT_FOREVER);
D.4.9
The Semaphore Object Application configuration script creates an additional Semaphore instance to illustrate the size impact of each new Semaphore instance. Configuration Script Addition Program.global.sem1 = Sem.create(0);
Size Benchmarks
D-7
D-8
D.5
D.5.1
The Dynamic Task Application demonstrates the size impact of dynamically (in the C code) creating and deleting a Task instance. This application comes after the Memory application because it must use the Memory module to allocate space for the new Task instance. C Code Addition Task_Handle task; Error_Block eb; Error_init(&eb); task = Task_create((Task_FuncPtr)main, NULL, &eb); if (task == NULL) { System_abort("Task create failed"); } Task_delete(&task);
D.5.2
The Dynamic Semaphore Application demonstrates the size impact of dynamically (in the C code) creating and deleting a Semaphore instance. This application comes after the Memory application because it must use the Memory module to allocate space for the new Semaphore instance. C Code Addition Semaphore_Handle sem; Error_Block eb; Error_init(&eb); sem = Semaphore_create(1, NULL, &eb); if (sem == NULL) { System_abort("Semaphore create failed"); } Semaphore_delete(&sem);
Size Benchmarks
D-9
D.6
D-10
Appendix E
This appendix describes how to minimize the size of a SYS/BIOS application. Topic Page
E.1 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E2 E.2 Reducing Data Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E2 E.3 Reducing Code Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E5 E.4 Basic Size Benchmark Configuration Script. . . . . . . . . . . . . . . . . . . E7
E-1
Overview
E.1
Overview
This section provides tips and suggestions for minimizing the memory requirements of a SYS/BIOS-based application. This is accomplished by disabling features of the operating system that are enabled by default and by reducing the size of certain buffers in the system. All of the tips described here are applied to the base configuration for the size benchmarks. The final section of this chapter presents the configuration script used for the base size benchmark. The actual size benchmark data is included in the SYS/BIOS 6 installation in the ti.sysbios.benchmarks package (that is, in the BIOS_INSTALL_DIR\packages\ti\sysbios\benchmarks directory). There is a separate HTML file for each target. For example, the C64x sizing information can be found in the c6400Sizing.html file. The following sections simply describe different configuration options and their effect on reducing the application size. For further details on the impact of these settings, refer to the documentation for the relevant modules. Because the code and data sections are often placed in separate memory segments, it may be more important to just reduce either code size data size. Therefore the suggestions are divided based on whether they reduce code or data size. In general, it is easier to reduce data size than code size.
E.2
Calls to malloc are satisfied by a separate heap, whose size is configurable. The following code minimizes the size of this heap. (Some targets do not support a heap size of 0, so this command sets it to 1.) Program.heap = 0x1; The Program variable is automatically available to all scripts. It defines the "root" of the configuration object model. It comes from the xdc.cfg.Program module, and is implicitly initialized as follows: var Program = xdc.useModule('xdc.cfg.Program');
E-2
E.2.2
A special section in memory is created to store any arguments to the main() function of the application. The size of this section is configurable, and can be reduced depending on the application's needs. Program.argSize = 0x4;
E.2.3
See Section 3.4.3 for information about system stack size requirements and Section 3.5.3 for information about task stack size requirements. The size of the System stack, which is used as the stack for interrupt service routines (Hwis and Swis), is configurable, and can be reduced depending on the application's needs. Program.stack = 0x400; Likewise, the size of each Task stack is individually configurable, and can be reduced depending on the application's needs. See Section 3.5.3 for information about task stack sizes. var tskParams = new Task.Params; tskParams.stackSize = 512; var task0 = Task.create('&task0Fxn', tskParams); You can configure the size of the Idle task stack as follows: Task.idleTaskStackSize = 512;
E.2.4
The space used to store module name strings can be reclaimed with the following configuration setting: var Defaults = xdc.useModule('xdc.runtime.Defaults'); Defaults.common$.namedModule = false;
E-3
E.2.5
By default, all of the text strings in the system, such as module and instance names and error strings, are loaded into the target's memory. These strings can be left out of the application using the following settings. var Text = xdc.useModule('xdc.runtime.Text'); Text.isLoaded = false;
E.2.6
Modules that inherit from an interface (such as GateHwi, which inherits from IGate) by default have a generated function table that is used in supporting abstract handles for instances. For example, an API takes a handle to an IGate as one of its parameters. Because the type of the gate is abstract, it must use a function table to access the module functions for that gate. Likewise, several Memory module APIs expect an IHeap Handle as an parameter. If the instances of one of these modules are never used in an abstract way, however, then the function table for the module is unnecessary and can be removed. For example, if none of the GateHwi instances in a system are ever cast to IGate instances, then the GateHwi function table can be disabled as follows. var GateHwi = xdc.useModule('ti.sysbios.gates.GateHwi'); GateHwi.common$.fxntab = false; It is generally not safe to set the common$.fxntab property to false for Heap implementation modules, because access to their function table may be needed when internal calls to Memory APIs by the Heap implementation use abstract handles.
E.2.7
By default, up to 8 System_atexit() handlers can be specified that will be executed when the system is exiting. You can save data space by reducing the number of handlers that can be set at runtime to the number you are actually intending to use. For example: System.maxAtexitHandlers = 0;
E-4
E.3
Set the RTSC Build-Profile (shown when you are creating a new CCS project or modifying the CCS Build settings) to release. In the configuration settings, change the BIOS.libType parameter to BIOS.LibType_Custom. This causes the SYS/BIOS libraries to be compiled so that they include only the modules and APIs your application uses. var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.libType = BIOS.LibType_Custom; See Section 2.3.3 for more on these settings.
E.3.2
Disabling Logging
Logging can be disabled with the following configuration settings: var Defaults = xdc.useModule('xdc.runtime.Defaults'); var Diags = xdc.useModule('xdc.runtimg.Diags'); Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;
E.3.3
The Memory module supports different memory policies for creating and deleting objects. If all of the objects in an application can be statically created in the configuration script, then all of the code associated with dynamically creating instances of modules can be left out of the application. This is referred to as a static memory policy. var Defaults = xdc.useModule('xdc.runtime.Defaults'); var Types = xdc.useModule('xdc.runtime.Types'); Defaults.common$.memoryPolicy = Types.STATIC_POLICY;
E.3.4
Some of the core features of SYS/BIOS can be enabled or disabled as needed. These include the Swi, Clock, and Task modules. var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.swiEnabled = false; BIOS.clockEnabled = false; BIOS.taskEnabled = false;
E-5
Applications typically enable at least one of the Swi and Task handlers. Some applications may not need to use both Swi and Task, and can disable the unused thread type.
E.3.5
Eliminating printf()
There is no way to explicitly remove printf from the application. However, the printf code and related data structures are not included if the application is free of references to System_printf(). This requires two things: The application code cannot contain any calls to System_printf(). The following configuration settings need to be made to SYS/BIOS:
var System = xdc.useModule('xdc.runtime.System'); var SysMin = xdc.useModule('xdc.runtime.SysMin'); SysMin.bufSize = 0; SysMin.flushAtExit = false; System.SupportProxy = SysMin; //Remove Error_raiseHook, which brings System_printf var Error = xdc.useModule('xdc.runtime.Error'); Error.raiseHook = null; See the module documentation for details. Essentially, these settings will eliminate all references to the printf code.
E.3.6
If an application does not require the RTS library to be thread safe, it can specify to not use any Gate module in the RTS library. This can prevent the application from bringing in another type of Gate module. var BIOS = xdc.useModule('ti.sysbios.BIOS'); BIOS.rtsGateType = BIOS.NoLocking;
E.3.7
If you are not concerned that any of your Task instances will overrun their stacks, you can disable the checks that make sure the top of the stack retains its initial value. This saves on code space. See Section 3.5.3 for information about task stack sizes. By default, stack checking is performed. Use these statements if you want to disable stack checking for all Tasks: Task.checkStackFlag = false; Task.initStackFlag = false;
E-6
E.4
//Assumption: app needs SysMin at a minimum, //but may not use printf, so buf can be zero. SysMin.bufSize = 0; SysMin.flushAtExit = false; System.SupportProxy = SysMin; //Get rid of Error_raiseHook which brings in System_printf Error.raiseHook = null; //Heap used by malloc is set to zero length. Program.heap = 0x1; //arg and stack size made same as BIOS 5.00 Program.argSize = 0x4; Program.stack = 0x400; //Logger disabled for benchmarking Defaults.common$.logger = null;
E-7
//Set isLoaded for Text module Text.isLoaded = false; //Set STATIC_POLICY Defaults.common$.memoryPolicy = Types.STATIC_POLICY; Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF; Defaults.common$.namedModule = false; BIOS.swiEnabled = false; BIOS.clockEnabled = false; BIOS.taskEnabled = false; BIOS.rtsGateType = BIOS.NoLocking; //App not using abstract GateHwi instances GateHwi.common$.fxntab = false; HeapStd.common$.fxntab = false;
E-8
Index
A
Agent module 8-17 alloc() function, Memory module 6-23 andn() function, Swi module 3-28, 3-30, 3-33 application stack size 3-27 Assert module 8-2 optimizing 8-22 atexit() handlers E-4 checkStackFlag property E-6 class constructor 1-12 class destructor 1-12 class methods 1-11 clock application size D-5 Clock module 5-2 clocks 3-5, 5-2 creating dynamically 5-3, 5-5 creating statically 5-5 disabling E-5 speed 6-7 starting 5-3 stopping 5-4 tick rate, for Windows emulation A-4 ticks for, manipulating 5-3, 5-6 ticks for, tracking 5-5 when to use 3-6 Code Composer Studio (CCS) plug-ins, replaced by Eclipse Plug-ins 1-3 Code Composer Studio Mediawiki 1-13 code size, reducing E-5 compiler options 2-23 configuration script XDCtools technology used for 1-3 configuration size basic size benchmark configuration script E-7 default D-4 counting semaphores 4-2 CPU Load Data tool 8-14 CPU Load tool 8-12 Create hook function for hardware interrupts 3-18 for software interrupts 3-36, 3-37 for tasks 3-52, 3-53 create() function Clock module 5-3 Hwi module 3-16 Mailbox module 4-18 Semaphore module 4-2 Swi module 3-26 Task module 3-46, C-5 Timer module 7-12 critical regions, protecting (see gates) custom libType 2-22
B
background thread (see Idle Loop) Begin hook function for hardware interrupts 3-18, 3-19 for software interrupts 3-36, 3-38 binary semaphores 4-2 BIOS module libType parameter 2-21 BIOS_Start() function 3-2 blocked state 3-48, 3-50 books (resources) 1-13
C
C++ 1-10 C28x devices 6-15 Cache interface 7-18 caches 7-18 coherency operations for 7-18 disabling all caches 7-18 enabling all caches 7-18 invalidating range of memory 7-18 size 6-8, 6-19 waiting on 7-19 writing back a range of memory 7-18 writing back and invalidating a range of memory 718 CCS (Code Composer Studio) plugs-ins, replaced by Eclipse Plug-ins 1-3 CCS Build Configuration 2-21 CDOC reference help system 1-13
Index-1
Index customCCOpts parameter Cygwin tools B-2 2-23 creating dynamically 4-9 creating statically 4-9 examples of 4-9 posting 4-8, 4-9 posting implicitly 4-11 waiting on 4-8, 4-9 Exec Graph Data tool 8-12 Exec Graph tool 8-9 execution states of tasks 3-48 Task_Mode_BLOCKED 3-48, 3-50 Task_Mode_INACTIVE 3-48 Task_Mode_READY 3-48, 3-50 Task_Mode_RUNNING 3-48, 3-49 Task_Mode_TERMINATED 3-48, 3-50 execution states of threads 3-7 Exit hook function, for tasks 3-53, 3-54 exit() function, Task module 3-50 eXpress Dsp Components (see XDCtools) external memory 6-4
D
data size, reducing E-2 debug build profile 2-21 debugging 8-2 dec() function, Swi module 3-28, 3-30, 3-35 Delete hook function for hardware interrupts 3-18 for software interrupts 3-37 for tasks 3-53 delete() function Mailbox module 4-18 Semaphore module 4-2 Swi module 3-36 Task module 3-47, C-6 Diags module 8-2 optimizing 8-21 disable() function Cache interface 7-18 Hwi module 3-10, 7-6, C-2 Swi module 3-10, 3-36, C-4 Task module 3-10, C-5 disableInterrupt() function, Hwi module 3-10 dispatcher 7-9 optimization 8-23 documents (resources) 1-13 DSP/BIOS 5 migration from 1-13 size benchmark comparisons D-3 dynamic configuration 1-2 dynamic module application sizes D-9
F
function names 1-10 functions (see also hook functions)
G
Gate module 4-14 Gate object 1-3 GateHwi implementation 4-15 GateMutex implementation 4-16 GateMutexPri implementation 4-16, 4-17 gates 4-14 preemption-based implementations of 4-15 priority inheritance with 4-17 priority inversion, resolving 4-17 semaphore-based implementations of 4-16 GateSwi implementation 4-16 GateTask implementation 4-16 getFreq() function, Timer module 7-13 getHookContext() function, Swi module 3-37 getNumTimers() function, Timer module 7-13 getStatus() function, Timer module 7-13 getTicks() function, Clock module 5-3 getTrigger() function, Swi module 3-30
E
Eclipse Plug-ins, replacing CCS plug-ins 1-3 enable() function Cache interface 7-18 Hwi module 3-15, 7-6, C-2 Swi module C-4 Task module C-5 End hook function for hardware interrupts 3-18, 3-19 for software interrupts 3-36, 3-38 enter() function, Gate module 4-14 error block 8-4 Error module 8-2, 8-4 Error_check() function 8-5 errors, handling 8-4 Event module 4-8 Event object 1-3 events 4-8 associating with mailboxes 4-19 Index-2
H
hardware interrupt application size D-10 hardware interrupts 3-4, 3-15 compared to other types of threads 3-7 creating 3-16
Index disabling 7-6 enabled at startup 3-2 enabling 7-6 hook functions for 3-18, 3-19 interrupt dispatcher for 7-9, 8-23 priority of 3-9 registers saved and restored by 7-10 timing benchmarks for C-2 when to use 3-6 HeapBuf implementation 6-27 HeapMem implementation 6-24 system heap 6-21 HeapMultiBuf implementation 6-28 heaps 6-23 HeapBuf implementation 6-27 HeapMem implementation 6-24 HeapMultiBuf implementation 6-28 implementations of 6-24 optimizing 8-23 system 6-21 help system 1-13 hook context pointer 3-13 hook functions 3-8, 3-13 for hardware interrupts 3-18, 3-19 for software interrupts 3-36 for tasks 3-52, 3-54 new features of 1-3 hook sets 3-13 host tools, plug-ins used by 1-3 host-native execution 1-4 Hwi dispatcher 7-9 Hwi module 3-15, 3-18 Hwi threads (see hardware interrupts) rupts) inter-task synchronization (see semaphores) inv() function, Cache interface 7-18 IPC 1-8 ISR stack (see system stack) ISRs (Interrupt Service Routines) (see hardware interrupts)
J
JTAG 8-18
L
leave() function, Gate module 4-14 libraries, SYS/BIOS 2-22 libType parameter 2-21 linker command file customizing 6-14 supplemental 6-13 linking 2-22 List module 1-8 Load module 8-2 Log module 8-2 LoggerBuf module 8-2, 8-17 LoggerSys module 8-2 logging disabling E-5 implicit, for threads 3-8 optimizing 8-21 records on host 8-7
I
ICache interface 7-18 Idle Loop 3-5, 3-67 compared to other types of threads 3-7 priority of 3-9 when to use 3-6 Idle Manager 3-67 IGateProvider interface 4-14 inactive state 3-48 inc() function, Swi module 3-28, 3-30, 3-31 instrumentation 8-2 instrumented libType 2-22 inter-process communication 1-8 interrupt keyword 7-9 Interrupt Latency benchmark C-2 INTERRUPT pragma 7-9 Interrupt Service Routines (ISRs) (see hardware interrupts) interrupts (see hardware interrupts, software inter-
M
Mailbox module 4-18 mailboxes 4-18 associating events with 4-19 creating 4-18 deleting 4-18 posting buffers to 4-19 posting implicitly 4-11 reading buffers from 4-18 main() function, reducing argument space for malloc heap, reducing size of E-2 MAR registers 6-19 MAUs (Minimum Addressable Units) 6-20 memory allocation of (see heaps) manager for, new features of 1-3 policies for, setting E-5 requirements for, minimizing E-2 memory application size D-8
E-3
Index-3
Index memory map 6-4 Memory module 6-23 memoryPolicy property 6-20 microcontrollers 6-15 migration 1-13 Minimum Addressable Units (MAUs) 6-20 module function table, disabling E-4 modules list of 1-9 named, disabling E-3 upward compatibility of 1-3 MSP430 device 6-15 multithreading (see threads) mutual exclusion (see semaphores) priority levels of threads 3-7 Program.sectMap array 6-11
R
Raw Logs tool 8-7 Ready hook function for software interrupts 3-36, 3-38 for tasks 3-52, 3-54 ready state 3-48, 3-50 Real-Time Analysis tools 8-6 Register hook function for hardware interrupts 3-18 for software interrupts 3-36, 3-37 for tasks 3-52, 3-53 release build profile 2-21 repository 6-7 resources 1-13 restore() function Hwi module 7-6 Swi module 3-36 ROV tool 6-18 RTA Control Panel 8-14 RTA tools 8-6 RTDX transport 8-18 RtdxModule module 8-18 RTS thread protection, disabling E-6 RTSC Build-Profile field 2-21 RTSC platform 6-4 custom 6-6 RTSC target 6-4 RTSC-pedia wiki 1-13, 6-7 running state 3-48, 3-49
N
name mangling 1-10, 1-11 name overloading 1-11 named modules, disabling E-3 naming conventions 1-10 non-instrumented libType 2-22 NULL, in place of error block 8-5
O
online help 1-13 optimization 8-21 or() function, Swi module 3-28, 3-30, 3-34
P
packages, list of 1-9 pend() function Event module 4-8, 4-9, 4-19 Mailbox module 4-18 Semaphore module 4-2, C-9 performance 8-21 PIP module, not supported 1-3 platform wizard 6-6 platform, RTSC 6-4 plug() function, Hwi module 3-15 post() function Event module 4-8, 4-9 Mailbox module 4-19 Semaphore module 4-3, C-8 Swi module 3-28, 3-30, C-4 preemption-based gate implementations 4-15 Printf Logs tool 8-9 printf() function, removing E-6 priority inheritance, with gates 4-17 priority inversion problem, with gates 4-17
S
sections configuration 6-12 placement 6-11 segment placement 6-9 SectionSpec structure 6-12 sectMap array 6-11 segments 6-4 configuration 6-11 section placement 6-9 semaphore application size D-7, D-9 Semaphore module 4-2 semaphore-based gate implementations 4-16 semaphores 4-2 binary semaphores 4-2 configuring type of 4-2 counting semaphores 4-2 creating 4-2
Index-4
Index deleting 4-2 example of 4-3 posting implicitly 4-11 signaling 4-3 timing benchmarks for C-8 waiting on 4-2 setHookContext() function, Swi module 3-37 setPeriod() function, Timer module 7-13 setpri() function, Task module C-6 simulator 8-18 size benchmarks D-2 compared to version 5.x D-3 default configuration size D-4 dynamic module application sizes D-9 static module application sizes D-4 timing application size D-10 software interrupt application size D-6 software interrupts 3-5, 3-25 compared to other types of threads 3-7 creating dynamically 3-26 creating statically 3-26, 3-27 deleting 3-36 disabling E-5 enabled at startup 3-2 enabling and disabling 3-36 hook functions for 3-36, 3-38 posting, functions for 3-25, 3-28 posting multiple times 3-29 posting with Swi_andn() function 3-33 posting with Swi_dec() function 3-35 posting with Swi_inc() function 3-31 posting with Swi_or() function 3-34 preemption of 3-29, 3-36 priorities for 3-9, 3-27 priority levels, number of 3-27 timing benchmarks for C-4 trigger variable for 3-29 when to use 3-6, 3-35 speed, clock 6-7 stacks used by threads 3-7, 6-16 optimization 3-52, 8-24 tasks 3-50 standardization 1-2 start() function Clock module 5-3 Timer module 7-12, 7-13 startup sequence for SYS/BIOS 3-2 stat() function, Task module 3-52 static configuration 1-2 static module application sizes D-4 statistics, implicit, for threads 3-8 Stellaris Cortex-M3 microcontrollers 6-15 Stop Mode 8-19 stop() function Clock module 5-4 Timer module 7-13 Swi Manager 3-25 Swi module 3-25 Swi threads (see software interrupts) Switch hook function, for tasks 3-52, 3-54 synchronization (see events; semaphores) SYS/BIOS 1-2 benefits of 1-2 new features 1-3 packages in 1-9 relationship to XDCtools 1-4 startup sequence for 3-2 SYS/BIOS Getting Started Guide 1-13 SYS/BIOS libraries 2-22 SYS/BIOS Release Notes 1-13 system heap 6-21 system stack 3-10 configuring size 6-16 reducing size of E-3 threads using 3-7 System_abort() function 8-5
T
target, RTSC 6-4 target/device-specific timers 7-16 task application size D-6, D-9 Task module 3-46 task stack configuring size 6-16 determining size used by 3-51 overflow checking for 3-52 threads using 3-7 task synchronization (see semaphores) Task_Mode_BLOCKED state 3-48, 3-50 Task_Mode_INACTIVE state 3-48 Task_Mode_READY state 3-48, 3-50 Task_Mode_RUNNING state 3-48, 3-49 Task_Mode_TERMINATED state 3-48, 3-50 tasks 3-5, 3-46 begun at startup 3-3 blocked 3-10, 3-50 compared to other types of threads 3-7 creating dynamically 3-46 creating statically 3-47 deleting 3-47 disabling E-5 execution states of 3-48 hook functions for 3-52, 3-54 idle 3-49 priority level of 3-48 priority of 3-9 scheduling 3-48 terminating 3-50
Index-5
Index timing benchmarks for C-5 when to use 3-6 yielding 3-61 terminated state 3-48, 3-50 text strings, not storing on target E-4 Thread Load tool 8-13 thread scheduler, disabling 3-7 threads 3-4 creating dynamically 3-8 creating statically 3-8 execution states of 3-7 hook functions in 3-8, 3-13 implicit logging for 3-8 implicit statistics for 3-8 pending, ability to 3-7 posting mechanism of 3-7 preemption of 3-10, 3-11 priorities of 3-9 priorities of, changing dynamically 3-8 priority levels, number of 3-7 sharing data with 3-8 stacks used by 3-7 synchronizing with 3-8 types of 3-4 types of, choosing 3-6 types of, comparing 3-7 yielding of 3-10 yielding, ability to 3-7 ti.sysbios.benchmark package 1-9 ti.sysbios.family.* packages 1-9 ti.sysbios.gates package 1-9 ti.sysbios.genx package 1-9 ti.sysbios.hal package 1-9 ti.sysbios.heaps package 1-9 ti.sysbios.interfaces package 1-9 ti.sysbios.knl package 1-9 ti.sysbios.utils package 1-9 tick() function, Clock module 5-2 tickReconfig() function, Clock module 5-3 tickStart() function, Clock module 5-3 tickStop() function, Clock module 5-3 Timer module 5-6 timer peripherals number of 7-13 specifying 7-13 status of 7-13 timers 5-2, 5-6 clocks using 5-2 converting from timer interrupts to real time 7-13 creating 7-12, 7-13, 7-14 frequency for, setting 7-14 initialized at startup 3-2 modifying period for 7-13 starting 7-12, 7-13 stopping 7-13 target/device-specific 7-16 when to use 3-7 time-slice scheduling 3-61 timestamps 5-2, 5-6 timing application size D-10 timing benchmarks C-2 hardware interrupt benchmarks C-2 Interrupt Latency benchmark C-2 semaphore benchmarks C-8 software interrupt benchmarks C-4 task benchmarks C-5 timing services (see clocks; timers; timestamps) Transport_RTDX 8-19 Transport_STOP_MODE_ONLY 8-19 trigger variable for software interrupts 3-29
W
wait() function, Cache interface 7-19 wb() function, Cache interface 7-18 wbInv() function, Cache interface 7-18 whole_program build profile 2-21 whole_program_debug build profile 2-21 wiki 1-13 Windows, emulation for A-2 clock rate considerations for A-4 reasons for A-2 wrapper function 1-11
X
xdc.runtime.Gate module 4-14 XDCtools (eXpress Dsp Components) configuration using 1-3 relationship to SYS/BIOS 1-4 XDCtools Release Notes 1-13 --xp option, xs command 1-14 xs command --xp option 1-14
Y
yield() function, Task module C-7
Index-6