Test Driven Development

One of my (unwritten) goals was to enable a Test-Driven-Development work-flow.

Qemu

A way to implement this, is to let meson call a simulator.
And of course there is already a cpu simulator here which can emulate a cortex-m microcontroller!

It’s called qemu

it is also in the ubuntu repositories:

sudo apt-get install qemu-system-arm

usage

we can use qemu the following way:

qemu-system-arm -kernel <path/file.elf> -machine lm3s811evb -cpu cortex-m3 -semihosting -nographic

with the kernel parameter we define the exectable to run. With machine we tell qemu to emulate a specific hardware. In this case the development board of a TI Sitara microcontroller (cortex-m3). With cpu we define of course the cpu architecture. And with the semihosting we can use ARM-Semihosting commands to signaling a successful or failed test. The nografic option is needed because our emulated system does not have a standard visual output.

We can also use this machine / cpu combo for cortec-m0 / 1 / 0+ controllers, because the cortex-m3 is a superset of the other cortex architectures. For the cortex-m4 / m7 i didn’t found a suitable combo (qemu-syste-arm V2.5)

arm-semihosting

Of course I first needed arm-semihosting to work.

I implemented 3 functions in assembler for this.
I began with an inline assembly code. But the compiler optimized me a single command inside, so that Register R1 was overwritten. With an handwritten assembly code this didnt happend again.

I don’t want to explain arm-semihosting here and just add a link to some ressources and my assembly / header files.

helloWorld Test

We can now write TDD Test files and let them simulate them with qemu to find runtime errors or to implement integration tests to find regression bugs.

First we write a simple helloWorld test:

/*********************************************************
* helloWorld.c
*
* A positive test with the intention to test the testing-environment
* Together with an always false test (like mustFail.c) we can check
* if the testing env is working properly
*********************************************************/

#include <stdint.h>

#define DEBUG // to use ARM_Semihosting only in testing
#include "arm_semi.h"

int  main(void);

int main(void) {

  // use semihosting to write a debug message
  arm_semi_syswrite0("HelloWorld\n");

  // end test with semihosting command ReportException with the rigth Exit Code
  arm_semi_angel_swireason_reportexception(ADP_Stopped_ApplicationExit); /*Exit, no Error*/

  HAL_Init(); // strange linker bug. it needs to be called so ld.lld does not segfault.
              // needs further investigation...

}

At first we see the include of the arm_semi.h to use arm-semihosting.
the function arm_semi_syswrite0 writes a string to stdout.
And with the arm_semi_angel_swireason_reportexception(ADP_Stopped_ApplicationExit) we say to qemu to end the test with a success every other value inside the reportexception function does return a failure.

At the moment I have a string bug with the llvm linker.
If I delete the HAL_Init() function, ld.lld will segfault
And I have no idea at the moment why thats happening

conclusion

We can now use qemu to implement a TDD work-flow and simulate the cortex-m0 to cortex-m3 series

Written on January 16, 2018