Test Driven Development – Unity + Microchip

ID-10085393Hello!

Today I’ll explain a little bit about unit tests, test driven development (TDD) and how to set up an environment to be able to use TDD concepts in Microchip microcontrollers.

Nowadays there are several approaches in high level software design. Some of them has been adapted to embedded systems, for example, TDD – Test Driven Development. Unlike the conventional approach where all code is first develop and then test and search for bugs is done after the whole development, in TDD we develop the firmware and routine tests at same time, in incremental form. Why and what are the advantages?

  • The whole system tends to be better designed
  • In the end of the project, the code has been already tested
  • Retest entire firmware after new release or changes in source is easy and gives confidence to the developer
  • Avoid pressure from boss to release project as soon as possible, leading to cut time in test/debug step
  • etc

In order to help developers, there are several frameworks, such as Unity. Basically, Unity is a set of macros and functions used to automatise unit tests. I won’t cover in this post how we use Unity, I suggest you to read James Grenning’s book Test Driven Development for Embedded C. Besides Unity, this book introduces the TDD approach and give examples and how to develop embedded systems using TDD technics.

Let’s create a small project to show how configure and how we can use TDD in Microchip microcontrollers, in our case PIC24FJ256GA106. We’ll use a tool called ceedling who will create our environment in order to use Unity. here I’m using Linux Mint and normally I use MPLABx for PICmicro. In the end of the post I’ll leave some references where you can find more details and examples.

1) Inside MPLABx’s project folder (the .X folder), we’ll create a new project with ceedling.

ceedling new pic_unit_test

2) In this new folder, we find the project configuration file, project.yml. In our case, we’ll adapt it to use Microchip xc16-gcc compiler and sim30 simulator. The configuration file should be edit as follow:

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: FALSE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
  :test_file_prefix: test_

:extension:
  :executable: .out

:paths:
  :include:
    - /opt/microchip/xc16/v1.11/support/PIC24F/h
    - /opt/microchip/xc16/v1.11/include/
  :test:
    - +:test/**
    - -:test/support
  :source:
    - ../**
  :support:
    - test/support

:defines:
  # in order to add common defines:
  #  1) remove the trailing [] from the :common: section
  #  2) add entries to the :common: section (e.g. :test: has TEST defined)
  :commmon: &common_defines
    - __PIC24FJ256GA106__
    #- UNITY_EXCLUDE_STDINT_H
    - 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
  :test:
    - *common_defines
  :test_preprocess:
    - *common_defines

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    - :ignore
    - :callback
    - :array
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8
  :when_ptr:
    - :smart
#:tools:
# Ceedling defaults to using gcc for compiling, linking, etc.
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
# See documentation to configure a given toolchain for use

:tools:
  :test_compiler:
    :executable: xc16-gcc
    :arguments:
      - -mcpu=24FJ256GA106
      - -x c
      - -c
      - -g
      - -omf=elf
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -I"./"
      - -Wall
      - -mlarge-code
      - -mlarge-data
      - -mlarge-scalar
      - -mconst-in-code
      - -msmart-io=1
      - -msfr-warn=off
  :test_linker:
    :executable: xc16-gcc
    :arguments:
      - -mcpu=24FJ256GA106
      - -omf=elf      
      - ${1}
      - -o "./build/test.out"
      - -Wl,-Tp24FJ256GA106.gld,-Map=./build/test.map,--report-mem
  :test_fixture:
    :executable: ruby
    :name: "Microchip simulator test fixture"
    :stderr_redirect: :UNIX #inform Ceedling what model of $stderr capture to use
    :arguments:
      - test/simulation/sim_test_fixture.rb

  :release_compiler:
    :executable: xc16-gcc
    :arguments:
      - -x c
      - -c
      - -g
      - -omf=elf
      - "${1}"
      - -o "${2}"
      - -mcpu=24FJ256GA106
      - -Wall
      - -Werror
      - -Os
      - -mlarge-code
      - -mlarge-arrays
      - -mlarge-data
      - -mlarge-scalar
      - -mconst-in-code
      - -msmart-io=1
      - -msfr-warn=off
      - -I"$": COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE
      - -D$: COLLECTION_DEFINES_RELEASE_AND_VENDOR      
  :release_linker:
    :executable: xc16-gcc
    :arguments:
      - -mcpu=24FJ256GA106
      - ${1}
      - -o "${2}"
      - -omf=elf
      - -Wl,--defsym=__MPLAB_BUILD=1,,--script="p24FJ256GA106.gld",--check-sections,--data-init,--pack-data,--handles,--isr,--no-gc-sections,--fill-uper=0,--stackguard=16,--no-force-link,--smart-io,--report-mem
:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
...

Note 1: Add compiler’s path to PATH system variable

Note 2: Here the compiler is in /opt/microchip/xc16/v1.11. You have to set it properly in your case

:paths:
  :include:
    - /opt/microchip/xc16/v1.11/support/PIC24F/h
    - /opt/microchip/xc16/v1.11/include/

3) Inside pic_unit_test/test folder, create new directory and name it simulation.

cd test
mkdir simulation

4) In pic_unit_test/test/simulation folder, create new file sim_instruction.txt with the following instructions

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

5)  In pic_unit_test/test/simulation folder, create another file sim_test_fixture.rb with the following instructions

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

6) Put test code in pic_unit_test/test folder. Production code should be in pic_unit_test/src folder or you can change its path as desired. You can change it in project file in :paths:source and :path:test section.

7) To run all tests

rake test:all

or if you want to run only a specific test, type rake test:test_<test_name>. In my case here, I had test_misc.

rake test:test_misc

run_test

 

 

 

 

 

 

 

 

 

 

 

Here some really useful links:

http://spin.atomicobject.com/2012/10/22/getting-started-with-tdd-for-microchips-pics/

https://github.com/ThrowTheSwitch/CeedlingExample_Microchip

See next time!

Marcelo Jo

Marcelo Jo is an electronics engineer with 10+ years of experience in embedded system, postgraduate in computer networks and masters student in computer vision at Université Laval in Canada. He shares his knowledge in this blog when he is not enjoying his wonderful family – wife and 3 kids. Live couldn’t be better.

LinkedIn 

One Response to 'Test Driven Development – Unity + Microchip'

  1. Roney says:

    nice article!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.