Welcome to the Modern Embedded Systems Programming course.
My name is Miro Samek and in this lesson I have decided to finally give in to the popular
demand and switch the development toolset from IAR to the free and unlimited GNU-ARM
compiler and the Eclipse-based Integrated Development Environment.
The switch of the toolset is actually a good opportunity to review the code and to see
what "code portability" means in practice.
As you will see, much of the code you've written so far will work with GNU-ARM without any
changes, mostly due to the compliance with the Cortex Microcontroller Software Interface
Standard (CMSIS).
However, a few IAR-specific extensions in the startup code as well as board-support
package will have to be replaced with the GNU compiler equivalents.
So, today let's start with downloading and installing a development toolset for GNU-ARM.
Actually, there are many choices of such toolsets, but you need to look for a toolset that supports
your board.
Here, the most important factor is the support for the specific debugger interface, which
in case of the TivaC LaunchPad board is the Stellaris-ICDI.
Since the board comes from Texas Instruments, the logical place to look is the TI.com website,
where indeed you can find the toolset called Code Composer Studio (CCS).
As usual these days, instead of giving you a specific fixed URL for downloading CCS,
which most likely won't work in a few weeks from now, I recommend using the search box.
So, when you search for CCS, you quickly find the right web-page.
In the download section, you can read that the CCS tool can be used for free with no
limits with the GNU-GCC toolset.
Click on the Windows download, which will lead you to the registration page and some
more forms to fill to comply with the export control regulations.
Eventually, however, you should be able to download the CCS setup program for windows.
You need to launch it and agree to the licensing terms.
In the next step you can choose the installation directory.
You can leave the default, or choose your own destination, but I would strongly recommend
against using directory names with spaces or any non-standard characters.
The following step allows you to choose the CCS components.
Here you need to expand the 32-bit ARM MCUs and select the Tiva-C Series support.
Also, you need to explicitly select the GCC ARM Compiler.
You don't need to make any more choices, so finally click Finish to start the installation,
which can take a couple of minutes.
When you launch Code Composer Studio, it will ask you for the location of the "workspace".
The concept of a "workspace" is common in all toolsets based on Eclipse and is intended
to group together related projects.
From what I see, most people tend to use one default workspace for everything they do,
but I recommend that you use separate workspaces for your different project groups.
Specifically, I recommend that you use a dedicated workspace for this Embedded Programming Course.
Since I keep all the projects for this course in the directory "embedded_programming", I
also create the CCS workspace there, in the "ccs" subdirectory.
So finally you are ready to create your first project.
This part is specific to this particular re-packaging of Eclipse as Code Composer Studio, but the
general process is similar in all Eclipse based Integrated Development Environments.
The first selection you make for a new project is the embedded target.
Here you need to choose the Tiva-C family and the specific TM4C MCU within the family
that is soldered on your TivaC LaunchPad board.
Next, you choose the connection to the target, which in case of your LanchPad is the Stellaris-ICDI.
Please note that the support for this particular debugger interface was the primary reason
you selected the CCS toolset in the first place.
In the next step, you need to name the project.
Here, I suggest a generic name "lesson" for projects belonging to this Embedded Programming
Course.
This name will be fitting for all upcoming lessons, because you will simply clone this
original project instead of creating a new project from scratch each time.
For the same reason, you also cannot create the project in the default location inside
the workspace directory.
Instead, you create the project in the directory where you keep all previous lessons.
On my machine this is "embedded_programming", but it can be different on your computer.
To finish with the project location, you need to add the lesson19 sub-directory for this
particular project
The next and final step is critical.
Here, you need to select the GNU toolset instead of the default TI compiler for ARM.
When you click Finish, CCS will create your "lesson" project in the workspace and in the
"lesson19" directory on disk.
You can build the project by clicking on the hammer button at the top.
As you can see, the build process succeeds with 0 problems.
So, let's take a quick look at the code that CCS has generated for this project.
First, you get the main.c file with the empty main() function.
Second, is the startup code, which is very typical for code supplied by silicon vendors.
Unfortunately, it has all the shortcomings that I've covered in lessons 13, 14, and 15.
For starters, this startup code uses proprietary exception names that are not compliant with
CMSIS.
Also, the vector table requires editing every time you start or stop using a given interrupt
handler.
For example, to use the SysTick_Handler interrupt, you would need to modify the appropriate entry
in the vector table, and you would also need to declare a prototype of the handler at the
top of the file.
And finally, the provided implementation of the exception handlers contains endless loops
that tie up the CPU.
In other words, if any of such exception handler is ever executed, the system will freeze,
which the user will perceive as denial of service.
This is not acceptable in any production-grade code.
The generated code also contains the file with the extension .lds, which is the linker
script.
The purpose of this file is to tell the linker where ROM and RAM are located in the address
space and where to place various program sections.
You saw an example of a linker script for the IAR toolset in lesson 14.
Here you have a linker script for the GNU toolset.
This is again a beaten-path linker script, which matches the startup code.
Among others, it allocates the stack as the last section in RAM.
In my opinion this is a mistake, because stack grows toward the lower addresses on ARM, and
so a stack overflow could damage the RAM sections above it.
In fact, this seems the likely cause of failure in the infamous Toyota unintended acceleration
cases, as I have described in the article "Are we shooting ourselves in the foot with
stack overflow?".
I provide a link to this article in the comment section for this video.
So, it seems that the code generated by CCS is not terribly usable, but the good news
is that you can fix all the issues in it by applying the lessons you've learned so far.
The first thing then is to copy all the relevant code from the previous lesson18 to the lesson19
folder.
The usable files are: bsp.h, bsp.c, main.c, startup_tm4c.c, and the master header file
for your TM4C MCU.
Interestingly, the files copied to the lesson19 folder immediately show up inside your project.
This is how all Eclipse projects behave.
All source files in the project directory are automatically included in the project
and you don't need to explicitly add them as it was the case in the IAR Embedded Workbench
IDE.
This Eclipse policy has its disadvantages too, however.
For example, now you have two startup files in the project, so you need to delete one
of them.
So let's try to build this project.
This time you get some errors.
The first error is that the compiler cannot find the include file "core_cm4.h".
This file is part of the CMSIS, which is not directly available in Code Composer Studio,
as it was in IAR Embedded Workbench.
This is not a big problem, as you can easily provide CMSIS yourself.
Here, I have prepared for you a directory CMSIS, which contains the core header files
in the sub-directory Include.
You should copy the CMIS directory into the folder where you keep the lessons of this
video course.
That way you can re-use CMSIS in all upcoming lessons.
Of course copying a directory is not enough, because you also need to tell the compiler
to look for include files in this new directory CMSIS/Include.
You accomplish this by means of the project Properties dialog box, which you open by right-clicking
on the project and selecting the Properties pop-up menu.
Specifically, you need to choose the Directories property in the GNU Compiler group, where
you find the "include paths" pane.
To add a new include directory, click on the plus button.
The first, easy way is to simply browse your file system for the CMSIS/Include directory.
But the big drawback of doing this is that you add an *absolute* include path, which
will only work on your computer with this specific C:\embedded_programming\CMSIS directory.
A much better approach is to create a *relative* include path, which will work on any computer.
The Eclipse IDE allows you to create relative paths by means of system variables.
Specifically, in the list of these variables, you can choose PROJECT_LOC, which will create
paths relative to the project location.
From the project location, you need to go one level up, and then you append CMSIS/Include.
Regarding the use of directory separators, on Windows you can use either back-slashes
or forward-slashes.
I use forward-slashes, because they seem more universal.
When you build again, you see that the previous include error is gone, but you got a bunch
of new errors.
As it turns out, most of these new errors come from the startup code.
This should not be actually that surprising, because this code was written with IAR-specific
extensions to the C language, which the GNU compiler does not recognize.
So, here I have prepared for you the startup code, which was re-written with the GNU-specific
extensions to the C language.
As you will see in a minute, the startup code must closely match the linker script, so I
included a matching linker script as well.
To include these files in the project is simply copy them over to the lesson19 directory.
I need to allow overwriting the previous linker script and I also need to remove the previous
startup code.
When you switch over to the Eclipse IDE, you can see that the project is immediately updated
with the new files.
Let's quickly review the code, so that you know how it works.
First, in contrast to the old file, the new GNU-specific startup code is compliant with
the latest CMSIS 4.3.0.
When you scroll down to the vector table, you can see that it has a special attribute
section(.isr_vector).
This is a GNU-specific extension, which tells the compiler to place the following symbol--the
vector table in this case--in the specified section.
To see where this section is, you can open the new GNU linker script, where you can see
that .isr_vector is the first section in ROM.
The ROM section, in turn, is located at address 0, so the vector table at the beginning of
ROM is also at zero, which is exactly what the ARM CPU needs.
As you hopefully remember from lesson-15, the first element of the ARM vector table
is the initial top of stack.
So, here you see an ampersand, meaning address of, __stack_end__ cast on an int.
The symbol __stack_end__ comes again from the linker script.
Indeed, when you scroll a bit down, you can see the .stack section as the first section
in RAM.
Contrary to the beaten-path approach of putting the stack at the last section in RAM, I recommend
placing it at the first section.
That way, a stack overflow won't be able to damage any other RAM sections.
As an additional bonus, a stack overflowing into the un-mapped memory below RAM will be
detected automatically by executing the HardFault exception, so you will know about it.
Speaking of the stack, you can change its size by adjusting the symbol STACK_SIZE at
the top of the linker script.
Going back to the startup code, the symbol __stack_end__ is provided by the linker, but
the compiler does not know about it.
To tell the compiler, you need to declare __stack_end__ as an external variable at the
top of the startup file.
The other elements of the vector table are addresses of handler functions for Cortex-M
exceptions and interrupts.
As you can see, the table already contains all the specific handlers, with names compliant
with CMSIS, so you don't need to edit the code at all to use any of these exceptions
or interrupts.
At the same time, if you don't use a given exception or interrupt, it will be automatically
replaced by the Default_Handler implementation.
This is accomplished by means of a pair of GNU-specific attributes weak and alias.
The weak attribute means that such a symbol definition can be overridden by another definition
and the linker will quietly discard the weak definition without reporting that the symbol
was defined twice.
The alias attribute means that if the symbol is not defined, the alias symbol should be
used instead.
So, for example, if you define the SysTick_Handler in your application, the linker will take
your non-weak definition.
If you don't define SysTick_Hanler, the linker will take the Default_Handler alias for it
and will not report any errors.
Notice though that not all handlers are aliased.
This is because the standard fault handlers are actually defined in this startup code
so that you don't need to define them in your application.
But these handlers are not the primitive endless loops with denial of service.
The provided implementations use inline assembly to carefully avoid any use of the stack, which
might be corrupted at this point.
The assembly code stores the information about the fault in r0 and r1 registers and then
branches to assert_handler, where you can perform your last damage control.
Here you encounter another GNU-specific attribute "naked", which instructs the compiler not
do any stack operations for this function.
When you try to build again, you still have an error, this time in bsp.c.
By now you should know what's the problem.
The extended keyword __stackless was an IAR extension for stackless functions.
In GNU, you need to replace it with attribute("naked").
Another build.
This time, the compiler complains about __enable_interrupts(), which was an IAR intrinsic function.
In GNU this operation needs to be replaced with __enable_irq().
Another build, and..., Hallelujah!
No errors at all.
Congratulations, you have just finished your first port of deeply embedded code from one
toolset to another.
Time to plug in your Tiva LaunchPad board and test the code.
To download the code to the flash memory of the board and start debugging, you press the
bug button at the top.
The debugger is setup to stop at the beginning of main and indeed it does.
Press the Go button to run the code and watch the LED change color once a second.
Click on the Pause button to break into the code and watch it stop in the background loop.
By the way, the different screen layout that you see now is called in Eclipse the "Debug
Perspective".
This is to differentiate this view from the "Edit Perspective" that you saw during editing
the code.
The Debug Perspective provides all the debugger views that you encountered in IAR, such as
the disassembly view.
You can also single-step through the code and watch the LED being turned on and off.
You can set breakpoints, like for example inside the SysTick_Handler to see how frequently
this interrupt is called.
To stop the debug session and return to the "Edit Perspective", press the Stop button
at the top.
As the final step of this lesson, let's modify the behavior of the code copied from your
previous IAR project to change the color of the toggled LED from red to blue, for example.
Build the project and start debugging as before...
Watch the LED blink in a new color.
This concludes this lesson about switching the toolset to GNU-ARM and Eclipse-based Code
Composer Studio (CCS).
The startup code and linker script from this lesson are much closer to production-quality
than the typical code distributed by silicon vendors.
The code is compliant with CMSIS and will work with any toolset based on GNU-ARM, not
just with CCS.
You can easily adapt it for any ARM Cortex-M microcontroller.
If you'd like to learn more about using the free GNU-ARM toolset, I would recommend my
10-part article "Building Bare Metal ARM Systems with GNU", which in 2007 was the most popular
article on Embedded.com.
This article talks about the classic ARM7/ARM9 cores, but much of the information still applies
to Cortex-M as well.
I provide a link to this article in the description of this video on YouTube.
The project download for this lesson will come in two parts: the usual lesson19.zip
archive with the CCS project and code for the lesson and the CMSIS.zip archive with
the CMSIS code.
In the next lesson I will finally talk about race conditions, which is a concept that you
absolutely need to understand to effectively work with interrupts.
If you like this channel, please subscribe to stay tuned.
You can also visit state-machine.com/quickstart for the class notes and project file downloads.