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