19 Comments

Getting Started with TDD for Microchip’s PICs

In my last blog post, I discussed why embedded developers should not use IDEs. To summarize:

  • IDEs slow down development and make it too easy to develop code without knowing what you’re actually doing.
  • Developing outside of an IDE
    • allows you the opportunity to work with tools that were actually designed for highly-efficient coding,
    • forces you to get up-close and personal with the build tools, and
    • allows you to easily incorporate Test Driven Development (TDD) practices into your development process.

In this post, I’ll walk through the process of setting up a development environment for Microchip PIC microcontrollers. As an end result, you will be able to write code, write unit tests, compile tests, run them on Microchip’s simulator, and compile your release executable — all without ever opening an IDE.

Why Microchip? Because I have a lot of experience with their products, and I know a lot of you are using them as well. I will be demonstrating the setup on a Windows. I chose Windows because I believe it is the most common environment for embedded developers. If you are developing on Linux or a Mac, you’re in luck. All of the tools we will be using have cross-platform support, including Microchip’s compiler, linker, and simulator.

1. Install the Compiler

The first thing we need to do is install a compiler. Microchip has several available, as do other vendors that support Microchip devices. For this example, I will be using a PIC24HJ128GP202 device, so I installed Microchip’s XC-16 compiler. This is a 16-bit C compiler. You should, of course, install the compiler that is appropriate for your device. After you’ve installed the compiler, add the bin path of the compiler to the Windows PATH environment variable. This site explains how to edit environment variables for several versions of Windows.

2. Install a Text Editor

All operating systems come with a text editor, but they are usually very basic. I would recommend downloading a full-featured text editor to allow you to write code more productively. My editor of choice is Sublime Text 2. It is free to download and has a lot of excellent features that make it a joy to use.

3. Install a Build Automation Tool

You could build your code by manually typing the instructions provided by the compiler’s command-line interface. However, as most projects will include many source files, typing in these commands would take a lot of time. Build automation tools simplify this process by wrapping up all of the necessary commands required to build a project into a single task.

Ceedling is a great build automation tool that incorporates support for Test-Driven Development (TDD). If you are unfamiliar with TDD, I would suggest that you take a look at throwtheswitch.org and read through the documentation.

Ceedling uses a scripting library called Ruby to automate many tasks. If you do not already have Ruby installed, go to RubyInstaller.org and install it. Once the installation is finished, make sure you add the Ruby bin directory to your system PATH environment variable. To verify that Ruby is installed and the PATH variable is set up correctly, open command prompt and type ruby -v. You should see the version number of Ruby printed to the console.

Now that Ruby is installed, you can use its package management system, RubyGems, to install Ceedling. Open command prompt and type gem install ceedling. This will download the latest version of the Ceedling gem and place it inside your Ruby installation directory.

4. Create a New Project

Ceedling is a very flexible tool. It does not enforce any particular directory structure for your source code and can be configured to work with any existing project. For this example, we will create a new project using a handy ceedling task. In the command prompt, navigate to a directory where you want to create the new project and enter ceedling new PIC_Demo.

The new task will create several folders and files in our project directory. If you downloaded Sublime Text, open it, then drag the new project directory from Windows Explorer into the Sublime Text window. This will open the entire directory structure for you to view. Let’s look at what’s inside.

  • build – This is where Ceedling will place intermediate files produced while building, as well as the final executable.
  • src – This is where Ceedling expects to find source code.
  • test – This is where Ceedling expects to find test files.
  • vendor – This is where the Ceedling tools exist in the project. Open vendor/ceedling/docs to view the documentation included with Ceedling.
  • project.yml – This is the main configuration file for Ceedling.
  • rakefile.rb – This is where you can set up additional extra tasks if needed.

5. Add Files to the Project

Explaining the entire process of TDD is beyond the scope of this post. To move forward with this tutorial, we will need some source files and test files to compile for this demonstration. I have created some sample files that define a simple led controller. Add the following files to the src directory of your project.

Then, add the following files to the test directory in your project.

6. Configure the Project

By default, Ceedling will attempt to build the project using GCC. We want to use Microchip’s compiler, so we need to modify the project configuration file. Open the project.yml file. (For a full explanation of how to configure Ceedling, read the documentation in the docs folder. For now, I’ll just point out some important changes.)

First we need to define a test compiler and linker so that Ceedling knows what compiler to use to build the tests. We also have to specify the command line arguments to be passed to these tools. Read your compiler’s documentation to determine which compiler arguments are important for your device. Add the following section to the bottom of the project.yml file.


:tools:
  :test_compiler:
    :executable: xc16-gcc
    :arguments:
      - -mcpu=24HJ128GP202
      - -x c
      - -c
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -Wall
      - -Os
      - -mlarge-code
      - -mlarge-arrays
  :test_linker:
    :executable: xc16-gcc
    :arguments:
      - -mcpu=24HJ128GP202
      - ${1}
      - -o "./build/release/TestBuild.out"
      - -Wl,-Tp24HJ128GP202.gld,-Map=./build/release/TestOutput.map,--report-mem

After making that change, we can try to build and run the tests. In the command prompt, navigate to the project’s root directory and type rake test:all. You should see the following error:

p24HJ128GP202.h: No such file or directory

The error is produced because the file p24HJ128GP202.h is referenced in our project, but the compiler can’t find it. The file exists inside the compiler’s support directory. We’ll add an include section to the paths section of the configuration file and add the directory that contains this file. This will tell Ceedling to instruct the compiler to include this directory when it compiles.


  :include:
    - "C:/Program Files (x86)/Microchip/xc16/v1.10/support/PIC24H/h"

The p24HJ128GP202.h file contains a check that makes sure that a compiler symbol is defined that matches the processor type. We need to add a define so that the check doesn’t fail. We also need to add a few other defines to alter the functionality of Unity, the unit testing framework, and CMock (the mocking framework). By default, these frameworks assume the code is targeting a 32-bit device. Our device is 16-bit, so we’ll need to update the common defines section of the config file as shown below:


:commmon: &common_defines
    - __PIC24HJ128GP202__
    - UNITY_INT_WIDTH=16
    - CMOCK_MEM_INDEX_TYPE=uint16_t
    - CMOCK_MEM_PTR_AS_INT=uint16_t
    - CMOCK_MEM_ALIGN=1
    - CMOCK_MEM_SIZE=4096

After making these changes, try running the tests again with the rake test:all command. All of the files now compile and link successfully but we still get an error message.

ERROR: Test executable "test_gpio_access.out" failed.
> Produced no final test result counts in $stdout: 'build\test\out\test_gpio_access.out' is not recognized as an internal or external command, operable program or batch file.

After the test files are compiled and linked, the test fixture attempts to run the executable that was produced. The problem is that this executable is compiled for a 16-bit PIC architecture, and our development machine’s architecture is completely different. It doesn’t recognize the output file as something it can run.

This is where a simulator comes into play. A simulator can load a hex file produced for a PIC device and execute the instructions just as a real device would. The Microchip compiler’s include a command line simulator called sim30.exe. We need to configure Ceedling to run the test on this simulator.

Inside the test directory, create a new folder named simulation, then add a file to it called sim_instructions.txt. Paste the following into this file:


LD pic24super                  
LC build/release/TestBuild.out
IO NULL test/simulation/out.txt
RP
E
quit

These are the instructions must be passed to the simulator so that it will load and run our code. They tell the simulator to:

  1. Run as a PIC24 device.
  2. Load a hex file from build/release/TestBuild.out.
  3. Output stdout data to test/simulation/out.txt.
  4. Reset, execute, and quit.

Next, add another file to the simulation folder named sim_test_fixture.rb. Paste the following in this file:


OUT_FILE = "test/simulation/out.txt"
File.delete OUT_FILE if File.exists? OUT_FILE 
IO.popen("sim30 test/simulation/sim_instructions.txt")
sleep 1
if File.exists? OUT_FILE 
    file_contents = File.read OUT_FILE
    file_contents.gsub!("\n", "")
    print file_contents
end

This file contains a Ruby script that will be used as our test fixture. It deletes any previous output files, calls the simulator with the instruction file, and then prints out results to stdout.

The last thing we need to do is add the test fixture to our project.yml file so that Ceedling knows to use it. Add the following to the project.yml file, below the test_linker section.


:test_fixture:
  :executable: ruby
  :name: "Microchip simulator test fixture"
  :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use
  :arguments:
    - test/simulation/sim_test_fixture.rb

Finally, run the tests again. This time the process should succeed and you will see the following test summary printed to the console.

-------------------------
OVERALL UNIT TEST SUMMARY
-------------------------
TESTED: 25
PASSED: 25
FAILED: 0
IGNORED: 0

Congratulations! You now have a fully-functional TDD environment. To add support for a release build, copy the test_compiler and test_linker sections of the project.yml file and paste them at the bottom. Change their names to release_compiler and release_linker, and you should be good to go. When you execute the command rake release, Ceedling will build the entire project, using just the source code (not tests), and produce your release executable.

You can download the source code for this demo project at GitHub.