Developing for AVR with JetBrains CLion on Mac OS
Previously I used Atmel Studio 7 to do AVR development. This integrated development environment is unsurpassed in terms of ease of use and features for AVR development. However, it is Windows only. I grew tired of the hassle with virtual machines and dual boot setups as my working device is a Mac running Mac OS 10.12. I already used some JetBrains products for C/C++ and PHP development so I wanted to try to get the whole AVR toolchain working on Mac OS in combination with JetBrains CLion.
To serve as a suitable alternative to Atmel Studio I need both compilation, uploading and debugging to integrate nicely with CLion. Furthermore, I wanted to be able to use the Atmel Software Framework to have access to an enormous resource of services, drivers and examples.
Prerequisites
To keep things concise I assume that:
- Homebrew is installed and working.
- A recent version of CLion (2016.3 at the time of writing) is installed and working.
- Build tools are available (either through XCode or another way). This because some package will be compiled (for Mac OS) from source by Homebrew.
- Working GIT
Installing AVR Tools
Homebrew will install the required tools and take care of building and installing. Let’s check if our environment is ready to compile sources:
xcode-select --install
This will check whether command line developer tools are installed. Make sure that this is confirmed or otherwise check your XCode installation.
Now the AVR tools are to be installed. This relies on Homebrew formulae provided by the osx-cross repository. So, we will tap into a new repository of Homebrew formulae and install the required packages. It might take some time to compile everything.
# make Homebrew tap from osx-cross $ brew tap osx-cross/avr # Install the most recent version of all AVR tools $ brew install avr-gcc
Advanced/Atmel Software Framework
I want to make use of the extensive library of source-code and application examples called Atmel (Advanced) Software Framework. Download the standalone ASF framework to for example ~/Downloads.
# Ensure that /opt exists or create it, then go there. $ sudo mkdir -p /opt # Unpack the ASF libary to /opt $ sudo unzip ~/Downloads/asf-standalone-archive-3.33.0.50.zip -d /opt
The ASF source is now available. For details about its structure, refer to the ASF user guide. Some ASF code will be used in the example project below but without a detailed explanation of its purpose or usage.
Configuring CLion
CLion uses CMake as its central build system. CMake enables cross-platform development by generating platform specific build files using a universal CMake configuration. We will use a CMake template that simplifies a lot of our work. Next, CLion needs to be configured to use the right compiler and debugger binaries.
First, create a new CLion project.
CMake
To configure CMake, a copy of mkleemann/cmake-avr will be obtained. This repository contains CMake files for AVR development.
# Clone the repository in a suitable location (e.g. ~/Repos) $ git clone https://github.com/mkleemann/cmake-avr
It’s mainly about the file generic-gcc-avr.cmake{#0c97e871bbcd2241fb04f525ae28cc05-18bcb2adccd70c7a7b90d9c8b69154d37fbd8ced.js-navigation-open}. We could either keep it centrally stored in the local copy we just created or copy it to our CLion project.
Next, configure CMake in CLion by going to CLion > Preferences > Build, Execution, Deployment > CMake
Ensure that at least a ‘Debug’ profile is available. In CMake options, enter the path to the Toolchain file we just obtained. For example:
-DCMAKE_TOOLCHAIN_FILE=/Users/username/Repos/cmake-avr/generic-gcc-avr.cmake
Now that CMake is aware of the toolchain file, the CMakeLists.txt is to be configured. We will be using CMakeLists.txt.sample (available in the cmake-avr repo) as a starting point. Copy the template right into CLion’s CMakeLists.txt. Let’s go through its relevant sections step by step.
Setting tools, parts and fuses
Somewhere at the beginning of the file some configuration variables are set.
################################################################################## # tools to be used for programming the AVR ################################################################################## set(AVR_UPLOADTOOL avrdude) set(AVR_PROGRAMMER jtag2) set(AVR_UPLOADTOOL_PORT usb) # AVR and fuses needs to be set set(AVR_MCU atmega8) set(AVR_H_FUSE 0xd9) set(AVR_L_FUSE 0xc3)
Select the appropriate programmer. Check all available programmers by running avrdude -c ? in your terminal. Select the port (for example /dev/ppi0 or usb).
Next, select the MCU. Run avrdude -p ? to find out supported devices. The setting for the fuses can be found out by either reading the manual or using an online tool like fusecalc.
Set the project name. The ASF library contains some Assembly files so adding ASM support is useful in this case.
project(MY_AVR_PROJECT C ASM)
Set the AVR MCU speed. For example, 32MHz:
set(MCU_SPEED "32000000UL")
Libaries and Source-files
Optionally, set a variable containing the path to the ASF installation. Such a fixed path is not really portable of course but for simplicity, it will be used anyhow:
set(ASF /opt/xdk-asf-3.33.0)
Add a variable containing the list of source files. The actual list really depends on your project. You could start off with a bare project containing only a main.c file. When using ASF libraries, consult the quick starts and examples to see what files are to be included.
set(SOURCE_FILES main.c init.c ${ASF}/xmega/drivers/cpu/ccp.S ${ASF}/xmega/drivers/nvm/nvm_asm.S ${ASF}/xmega/drivers/dac/dac.c ${ASF}/common/services/clock/xmega/sysclk.c ${ASF}/common/services/ioport/xmega/ioport_compat.c ${ASF}/common/services/sleepmgr/xmega/sleepmgr.c )
If you are compiling ASM files (.s extension) you have to indicate this to CMake:
set_property(SOURCE ${ASF}/xmega/drivers/cpu/ccp.S PROPERTY LANGUAGE ASM) set_property(SOURCE ${ASF}/xmega/drivers/nvm/nvm_asm.S PROPERTY LANGUAGE ASM)
Add the relevant _include-_directories to your project. For example:
include_directories(conf) include_directories(${ASF}/xmega/utils) include_directories(${ASF}/xmega/utils/preprocessor) include_directories(${ASF}/xmega/drivers/dac) include_directories(${ASF}/common/boards) include_directories(${ASF}/common/utils) include_directories(${ASF}/common/services/clock) include_directories(${ASF}/common/services/ioport) include_directories(${ASF}/common/services/gpio) include_directories(${ASF}/common/services/hugemem) include_directories(${ASF}/common/services/sleepmgr)
The gcc/config.mk files supplied with the ASF example projects contains information about what to add to the project to get it up and running. For example, the config.mk file for the XMEGA example application xmega-inertial-demo can be found in:
/opt/xdk-asf-3.33.0/xmega/applications/xmega-inertial-demo/gcc/config.mk
Adding the executable
Add CMake targets for an AVR executable called for example ‘my_avr_project’. Use the ${SOURCE_FILES} variable to indicate the sources.
add_avr_executable( my_avr_project ${SOURCE_FILES} )
Tuning
Tuning the CMakeLists.txt file is really project dependent. You might want to add a ‘define’ containing the BOARD definition for use with ASF add_definitions(“-DBOARD=XMEGA_A3BU_XPLAINED”) . You also might want to comment out the -pendantic option to make the compiler a bit less stressful.
Debugging the Project
Debugging only makes sense if you are using a programming device that is capable of debugging is well. Examples include the JTAGICE mkII and Dragon ISP. It is possible however to simulate an AVR device with the simavr tool. This, however, is out of the scope of this guide.
Debugging tools
To support debugging, the debug tool avr-gdb (GNU debugger) is required as well as the AVaRICE program. AVaRICE interfaces the GNU debugger with the physical debugging device (Atmel JTAGICE mkII for example). If you do not have a debugging enabled device, you can skip these steps.
Version 2.13 does not support the Atmel-ICE yet. However, it seems that the development version does. Downloading the source instead of the archive and configure/make should work. See AVaRICE website for sourcecode.
# Install the most recent version of all AVR tools $ brew install avr-gdb # Create a temporary folder for the AVaRICE source $ mkdir -p ~/tmp/avarice $ cd ~/tmp/avarice # Download AVaRICE, latest is 2.13 at time of writing and untar $ wget https://downloads.sourceforge.net/project/avarice/avarice/avarice-2.13/avarice-2.13.tar.bz2 $ tar -xvf avarice-2.13.tar.bz2 $ cd avarice-2.13 # Build! $ ./configure $ make $ make install
Using AVR-GDB binary in CLion
CLion needs to be instructed on what application it should use to debug the project. The built-in debugger is not suitable for debugging embedded hardware.
Open CLion > Preferences > Build, Execution, Deployment > Toolchains
Select /usr/local/bin/avr-gdb as Custom GDB debugger. All checks should pass.
GDB Remote Debug Configuration
To connect to a specific GDB server (AVaRICE), a GDB Remote Debug configuration has to be added. Go to project configurations (upper right corner or CLion) and click Edit Configurations…. Now use the ‘+’ button to add a new GDB Remote Debug configuration. Select tcp:localhost:4242 as ‘target remote’ argument (or whatever port you are using). Select the Symbol file (.elf) that contains debugging symbols. Make sure you do not select the Release build because it does not contain any debug information.
Actual debugging
To start debugging you have to:
- Start the AVaRICE server
- Connect AVR-GDB in CLion
- Set some breakpoints and debug
Start the AVaRICE server with options relevant for your situation. The following example is for an XMEGA using JTAG with a JTAGICE mkII debugger connected through USB and an XMega256A3 micro-controller. The server will be started at port 4242.
avarice -x --mkII --capture --part atxmega256a3 -j usb :4242
Now, select the GDB Avarice configuration in CLion and click the Debug icon.
Finally, add a breakpoint at a suitable point in code by clicking in the left margin of the code. Let’s start debugging!
Frequently Asked Questions (FAQ)
The Frequently Asked Questions section is to be extended…
1. Error: junk at end of line, first unrecognized character is `/’
Typical errors include:
/opt/xdk-asf-3.33.0/xmega/drivers/cpu/ccp.s:45: Error: junk at end of line, first unrecognized character is `/' /opt/xdk-asf-3.33.0/xmega/drivers/cpu/ccp.s:58: Error: unknown opcode `public_fun' /opt/xdk-asf-3.33.0/xmega/drivers/cpu/ccp.s:62: Error: garbage at end of line xmega/drivers/nvm/nvm_asm.s:46: Error: junk at end of line, first unrecognized character is `/'
The ASM files contain directives that have to be processed by the preprocessor before compiling the ASM itself.
Solution:
The preprocessor directives will be parsed in ASM files when their extension is .S (capital S instead of s). Make sure that in the set_property call the file extension is uppercase. Because of the default case insensitive filesystem, the actual casing of the file is not of importance.
2. No source file named /path/to/project/file.c. when debugging
This happens when there are no debug symbols loaded into the executable. (my_avr_project.elf)
Solution:
Make sure that:
- You configured to use a Debug build in CLion or CMake.
- You have selected the .elf executable that resides in the debug build folder (instead of the release executables).