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
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!
nice article!