C2000 CLA C Compiler

From Texas Instruments Wiki
Jump to: navigation, search


The CLA Compiler

The goal of the CLA compiler is to implement enough of the C programming environment to make it easier to access the capabilities of the CLA architecture and to make it easier to integrate CLA task code and data into a C28x application.

CLA C Language Support

The CLA compiler supports much of the C language, but not all of it. All the details are in the chapter titled CLA Compiler in the C28x compiler manual (PDF) (HTML).

Getting Started

Create Your Own Project

The simplest way to start writing code is to copy over an existing project (from the header files CLA C examples) and to edit it. Lets take an example: I would like to create a new test, atan, from an existing test case exp2
  1. Copy a Project:
    • Make a copy of the exp2 folder in the test directory and rename it to atan
  2. Rename Files:
    • Rename all files exp2*.* to atan*.*. (Notice the naming convention.
    • All files have the test folder name as a prefix, see below)

      Naming Convention of Files

  3. Edit the Project Files:
    • Open the .cdtbuild and .project files in any text editor and replace all instances of the word exp2 with atan.
    • This will ensure all the object files come out with the correct name and any directory dependencies are taken care of.
    • Each project has a predefined symbol, TEST_NAME=<test_name>. For e.g. the exp2 project will have a predefined symbol, TEST_NAME=exp2. By altering the .cdtbuild files in the manner described you wont have to change the build settings for each new project .
  4. Import the Project:
    • Import the atan project into your workspace (see figure below).
    • The files highlighted in the red box are common to all projects and are linked in by the .project file. The rest of the source files are specific to each test case
      C2000 CLA C Compiler ImportedProjectNew.jpg

  5. Modify the Source:
    • Edit the test specific source files.

Suggested Build and Link Options

The following table lists build and link options that may be useful/instructive for CLA C code. You can setup build properties that apply only to *.cla file by right clicking the file and selecting Properties->C/C++ Build.

Option Notes
Basic Options->Debugging Model->Full Symoblic debug (-g) If you would like to access watch variables etc while debugging.
  • This is set by default.
Basic Options->Debugging Model->Suppress all symbolic debug information View compiler generated assembly code without all the debug information.This is useful when estimating the cycle count for a small routine.
Basic Options->Optimization Level = none - O2 NoteNote: Due to the small number of registers available less aggressive optimization may yield better results (eg. -O1 vs -O2).
Assembly Options -> Keep generated assembly files (-k) Useful if you want to compare compiler generated code with hand coded assembly.
Linker Options-> Diagnostics -> --diag_suppress=16002
NoteNote: Object files(created with CGT 5.1 and prior) without build attributes will cause the linker to emit a warning if they are linked with object files that do have build attributes. The purpose of the warning is to let users know that they are responsible for enforcing compatibility between these files. You can suppress this warning using the diag_suppress option


C Language Tips and Tricks

Dealing with Pointers

Pointers are interpreted differently on the C28x and the CLA. The C28x treats them as 32-bit data types (address bus size being 22-bits wide can only fit into a 32-bit data type) while the CLA only has an address bus size of 16 bits. Assume the following structure is declared in a shared header file(i.e. common to the C28 and CLA) and defined and allocated to a memory section in a .c file
/********************************************************************
Shared Header File
********************************************************************/
typedef struct{                                                      
  float a;                                                           
  float *b;                                                          
  float *c;                                                          
}foo;                                                                
/********************************************************************
main.c
********************************************************************/
#pragma(X,"CpuToCla1MsgRam") //Assign X to section CpuToCla1MsgRam   
foo X;                                                               
/********************************************************************
test.cla
********************************************************************/
__interrupt void Cla1Task1 ( void )                                  
{                                                                    
  float f1,f2;                                                       
  f1 = *(X.b);                                                       
  f2 = *(X.c); //Pointer incorrectly dereferenced                    
               //Tries to access location 0x1503 instead             
               //of 0x1504                                           
}
Assume that the C28 compiler will allocate space for X at the top of the section CpuToCla1MsgRam as follows:
Element Address
X.a 0x1500
X.b 0x1502
X.c 0x1504
The CLA compiler will interpret this structure differently
Element Address
X.a 0x1500
X.b 0x1502
X.c 0x1503
The CLA compiler treats pointers b and c as 16-bits wide and, therefore, incorrectly de-references pointer c. The solution to this is to declare a new pointer as follows:
/********************************************************************
Shared Header File
********************************************************************/
typedef union{
  float *ptr; //Aligned to lower 16-bits
  Uint32 pad; //32-bits
}CLA_FPTR;
 
typedef struct{
  float a;
  CLA_FPTR b;
  CLA_FPTR c;
}foo;
 
/********************************************************************
main.c
********************************************************************/
#pragma(X,"CpuToCla1MsgRam") //Assign X to section CpuToCla1MsgRam
foo X;
/********************************************************************
test.cla
********************************************************************/
__interrupt void Cla1Task1 ( void )
{
  float f1,f2;
  f1 = *(X.b.ptr);
  f2 = *(X.c.ptr); //Correct Access
}
The new pointer CLA_FPTR is a union of a 32-bit integer and a pointer to a float. The CLA compiler recognizes the size of the larger of the two elements(the 32 bit integer) and therefore aligns the pointer to the lower 16-bits. Now both the pointers b and c will occupy 32-bit memory spaces and any instruction that tries to de-reference pointer c will access the correct address 0x1504.


Profiling

The CLA does not support the clock function and therefore it is not possible to get a direct cycle count of a particular task. The user can configure the time base module on an ePWM to keep track of the execution time of a task
Setup the time base of ePWM1(or any ePWM) to run at SYSCLKOUT in the up-count mode as shown below:
void InitEPwm(void)
{
  // Setup TBCLK
  EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
  EPwm1Regs.TBPRD = 0xFFFF; // Set timer period
  EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
  EPwm1Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
  EPwm1Regs.TBCTR = 0x0000; // Clear counter
  EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Clock ratio to SYSCLKOUT
  EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;
}
Proceed to define two macros READ_CLOCK and RESTART_CLOCK, the former to freeze the ePWM timer and copy the elapsed time to a variable, and the latter to restart the ePWM timer.
#define READ_CLOCK(X) __meallow();\
                      EPwm1Regs.TBCTL.bit.CTRMODE = TB_FREEZE;\
                      X = EPwm1Regs.TBCTR;\
                      __medis();
#define RESTART_CLOCK __meallow();\
                      EPwm1Regs.TBCTL.bit.CTRMODE = TB_FREEZE;\
                      EPwm1Regs.TBCTR = 0;\
                      EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;\
                      __medis();
Define a variable e.g. ulCycleCount to hold the cycle count
#pragma DATA_SECTION(ulCycleCount,"Cla1ToCpuMsgRAM");
unsigned long ulCycleCount;
Place the macro RESTART_CLOCK at the beginning of a task to restart the ePWM timer and place READ_CLOCK at the end of the task to read the value of the timer. The elapsed time will be give you the cycle count plus a minimal overhead from the two macros
__interrupt void Cla1Task1 ( void )
{
  //Local Variables
  float a;
  __mdebugstop();
  RESTART_CLOCK;
  a = 10;
  ...
  ...
  ...
  READ_CLOCK(ulCycleCount);
}

Debugging

The user can follow these steps to start debugging their code on the CLA
  1. Add _mdebugstop()
    • Place an __mdebugstop() at the beginning of the CLA task you wish to debug. For example task 1 of exp2.cla.
  2. Set build options:
    • You can setup individual build properties for the *.cla file seperately from the rest of the application.
    • Right click the .cla file and select Properties->C/C++ Build.
  3. Connect to the CLA:
    • Once you have built your project and launched the debug session CCS, by default, will connect to only the C28 core.
    • To be able to debug CLA code you will need to connect to the CLA core.
    • NoteNote: Build the code with -g (Full Symbolic Debug) to generate the symbols that will be loaded to the debugger.
    1. Click on the CLA debug session (highlighted in figure below)
    2. Select Target->Connect to Target or hit Alt-C.
    3. Once the CLA core is connected proceed to load the project symbols by clicking on Target->Load Symbols-><test>.out (e.g. exp2.out).

      Connecting to the CLA debug session

  4. Run the C28x:
    • In the exp2 example we have enabled task 1 of the CLA and we trigger it in software on the C28 side. When we run the code on the C28 debug session it seems to stall at the Cla1ForceTask1andWait() routine. It is waiting for the CLA task 1 to run to completion. When we switch over to the CLA session we see that execution has stopped at the __mdebustop() intrinsic
  5. Debug the Code:
    • At this point we can proceed to single step through the code.
    • There are some restrictions to debugging the CLA and they are discussed next.

Known Debugging Issues

  1. The CLA pipeline is not flushed on a single step and so results may not be visible until a few instructions later. Please refer to the CLA user guide for more details about the pipeline.
  2. If you plan to debug (single step) code on the CLA it is necessary that MNOPs are placed prior to any MSTOP to ensure the instructions prior to the MSTOP proceed through the pipeline before the MSTOP executes. The compiler will insert these MNOPs if compiling with debug (-g). The MNOPs are unnecessary if you are not debugging the CLA code.(This behavior was verified when running the exp2 project)
  3. Run-to-Line is not supported. Step over works the same as Step into

Compiler Software Collateral

All CLA supported devices will have several demos in the examples folder under their respective device_support directories

Tutorials

The following tutorials are available (zip file below) to help you get started with the CLA compiler. The videos can be accessed from the training site (focus.ti.com/docs/training/catalog/events/event.jhtml) or downloaded from the link below

  1. Tutorial 1: Compiler Basics
  2. Tutorial 2: Example Code Structure
  3. Tutorial 3: Demo
  4. Tutorial 4: Debugging

DOWNLOAD LINK:Tutorial Videos (.zip)

Important Links

CLA C Compiler Training Series
https://training.ti.com/c2000-cla-c-compiler-4-part-series