In this video, we'll cover the compiler in a little bit
more detail, we'll talk about how the language was
implemented, what kind of coding restrictions you can expect,
and any calling conventions for functions.
So let's get started with the language implementation.
The compiler supports C code only.
There is no support for C++ for GCC extensions.
Data types supported on the CLA are shown here.
Note that there are some differences from C28,
most notably the integer.
So integers C28 and 16 bits wide, while on the CLA
they are 32 bits wide.
So this may cause a problem if you are sharing variables
between the CLA and the C28.
If I define an array of 16 bit integers on the C28 and I use
and modify this same array on the CLA,
the CLA will treat them as an array of 32 bit data types.
It does not convert them, it just interprets them
as 32 bit data types.
So this might effectively corrupt all subsequent data
locations from the first location in the array.
So you have to be very careful when
using the integer type across CLA and the C28
at the same time.
So my advice is use explicit data types, like int32 or uint16
for variable declarations.
The CLA architecture is geared for 32-bit data type.
16-bit data types incur sign extension overhead.
So it's probably not best to use these
for arithmetic or in array indexing
and those kind of operations.
If you must use them, try to minimize their usage to load
and store operations only.
All C28 pragmas are accepted except for the FAST_FUNC_CALL.
The C standard library is not supported.
And finally, the keyword cregister, far, and ioport
are also not supported.
Next, we have the language restrictions.
The first being that defining and initializing global data
is not supported.
The CLA does not have a runtime support library associated
with it, so there isn't a routine
to initialize global data on startup.
Second, 64-bit data type operations are not supported.
So double precision math is definitely out of the question.
We merely define a 64-bit data type, in this case long double,
as two contiguous 32-bit memory locations.
We do this so as to avoid breaking compatibility
with some of the USB code that's on the 28069.
Next, local variables and compiler temporaries
are placed in an area that we call the scratchpad memory area.
It's accessed by these two variables,
you have a start variable and an end variable.
This section is meant solely for use by the compiler.
The user is cautioned to avoid using this location to store
his or her own variables.
It is expected that you, the user,
will manage this scratchpad area and you
will define these symbols using a linker command file.
So this is shown on the next slide.
This scratchpad also serves as a CLA stack, so function calls
and returns will use this memory location to perform context,
saves, and restores.
And this was the linker command file I was talking about.
So right off at the top, you can see
that we have defined a variable that's
called CLA_SCRATCHPAD_SIZE.
And we set it equal to hex 100.
So this is where you set the length of the scratchpad area.
Now, if you see here I have commented this line out.
Basically there are two ways of defining the scratchpad size,
you can either do it in the linker command file here,
that's why I'm commenting that line,
or you can do it through the Build
Settings of the project in CCS.
We also allocate space for the CLA scratchpad
in a section called CLA Scratch.
Continuing with the language restrictions.
Function nesting, you can only have
two levels of call stack depth.
And I will illustrate this in the next slide.
Recursive function calls are not supported.
Function pointers are also not supported.
And there are some additional operations
that are just too expensive to implement
with the native instructions available on the CLA,
these are namely integer divide and modulus
and integer unsigned compares.
So this is an illustration of the restrictions
that we have on the call depth.
So let's say we have 8 tasks in the CLA, task 1 through 8,
and we trigger task 1.
Task 1 will hook into the CLA math library
and call the sine function.
Sine function executes, returns.
Task 1 runs to completion and issues an interrupt
to the C28 saying, hey, I've finished, everything is fine.
This works perfectly fine.
Let's take a look at task 2.
It does the same thing.
The C28 triggers task 2.
Task 2 will call CLA cosine.
Cosine runs to end.
Task 2 will run to completion and then trigger
an interrupt to the C28.
Everything is fine here as well.
Now, task 8.
The C28 will trigger task 8.
Task 8 will make a call to a function called Foo.
Now, Foo attempts to make another call
to a function in the math library square root.
This is disallowed.
Basically, you're limited to two levels of function calls.
So now if I replace the function Foo
and it does something simple like just return y plus,
plus, this is fine.
This will not give you an error.
The CLA compiler supports the following intrinsics.
I won't go over these in detail, but you can always
find out more information about each of these from the compiler
documentation itself.
The one intrinsic that I did want to highlight
is the mdebugstop intrinsic.
Now, on the C28, you can actually
set a breakpoint in your code through CCS itself.
This functionality is not available on the CLA.
If you want to set a breakpoint in your code in the CLA,
you actually have to insert the mdebugstop
intrinsic at that location in code
where you want execution to stop.
We'll cover more about debugging in a later tutorial.
Next, we talk about the compiler generated sections.
Uninitialized global data will be placed in the section
.bss_cla.
Initialized constant data is placed in another section called
.const_cla.
An example of constant initialized data is shown here.
| scratchpad is placed in a memory location
called CLA Scratch.
Its size is determined by a variable called CLA Scratchpad
Size, which we defined at the top of our linker command file
in one of our previous slides.
So those are the three locations that you have in the data
RAM, which we've assigned to CLA RAM 1.
And on the actual physical device, it resides in RAM L2.
The CLA program is placed in a section
that we call CLA 1 Program.
This is the convention that we've been
using for CLA assembly code.
And so this will carry over to our C programming model as well.
And this section is always residing
in RAM L3 on all of our CLA enabled devices.
As a final note, there is no support
for operations such as malloc or free,
so there is no need for a C system heap for the CLA.
This is the last part of the video
where we cover function structure and calling
conventions.
As I've indicated previously, the compiler
only supports two levels of function calls.
There is a convention associated with function arguments,
the first being that you can only have up to two arguments.
Pointers are passed through MAR0 and MAR1.
Integer or float arguments are passed through MR0, MR1.
Return values that are either integer or float
are passed an MR0.
And pointer or return by reference values from functions
are passed in MAR0.
As far as register saves and restores go,
all registers except for MR3 are saved on call,
whereas MR3 is saved on entry.
This is true for C functions only.
If you are using mixed C and Assembly, it is up to you
to save MR3 when you enter the function and to restore it
when you exit from the function.
Local variables.
The scratchpad is used as a stack or a local frame
to store all the local variables from a function.
So it is very important that you allocate a sufficient size
for the scratchpad area.
This should make sure that none of your variables are corrupted.
You can do this using the linker command file.
And a final note is that if you're
mixing CLA C and Assembly, it is very important
to follow the calling conventions defined above,
especially the one where you save and restore MR3 on entry
and exit from a function.
Thanks for watching this video.
In the next segment, we will cover the programming model
used in all of our examples.