Well until now everything has been done manually. Write some code, compile it and put it on the target to see if it is working.
However would it not be nice to start to create reusable functions that is proved to work, that you then can put to use at any given time?
So what do we need to start doing this type of work with what we have today?
A good place would probably be to write a simple main.c file, that calls a function to test and then looks at the return value to find out if that was ok or not.
Filename: main.c
void test_failed(int number) { while(1); } void test_success(int number) { while(1); } int main(void) { int status = start_test(); if( status > 0 ) { test_failed(status); } test_success(0); while(1); }
And then write a function to test.
Filename: test01.c
#include <stdio.h> #include <stdlib.h> /** * Start test ... * * @return 0 is success, 1.n is failure at subpart n */ int start_test() { int test = 1; int a = 0; int b = 0; int c = 0; a = 10; b = 10; c = a * b; if (c != 100) { return test; } return 0; }
This test checks if we can multiply two int:s with each other, so it is really a basic test. But it is good to know that this basic building block is working.
Then if we build and flash and debug the code we can end up in 3 different places.
So why not add those three places as breakpoints in gdb, and start him directly? So let's create a file that gdb can read, and call it commands.gdb.
Filename: commands.gdb
break test_success break test_failed break Default_Handler target remote localhost:3333 cont frame
To start gdb with this file would then look something like this.
arm-none-eabi-gdb --command=commands.gdb main.elf
If we look at what will happen is more or less, that we first add some break points. Then connect to the gdb server and then cont (continue) until something happens. Then we execute frame and get some stack information.
But since we then have to quit gdb manually, and the solution is to add the magic word --batch. Batch will force gdb into batch mode that more or less mean that he will execute what he can and then quit.
arm-none-eabi-gdb --batch --command=commands.gdb main.elf
And the printout more or less looks like this, and here we can see that it was a big success since we ended up in test_success.
Breakpoint 1 at 0x28: file src/main.c, line 9. Breakpoint 2 at 0x1c: file src/main.c, line 5. Breakpoint 3 at 0x10 main () at src/main.c:13 13 int main(void) { Breakpoint 1, test_success (number=0) at src/main.c:9 9 while(1); #0 test_success (number=0) at src/main.c:9 9 while(1);
So that was nice, but what if we would like to add more tests? I mean we can't just have this one silly test?
Let's write another test but with floats.
Filename: test02.c
#include <stdio.h> #include <stdlib.h> /** * Start test with foat:s * * @return 0 is success, 1.n is failure at subpart n */ int start_test() { int test = 1; float x = 2; float y = 4; float z = 8; if( (x*y) != 8 ) { return test; } test++; if ( (y/x) != 2 ) { return test; } return 0; }
As you can see we use the same start function, start_test. So we can't compile this at the same time as the other test. But on the other hand we do not want one big program with all future test either!
Therefor we must play a little with make so we can link test01 and test02 separately against main.
Normally we would have a part in the Makefile that looks like this.
main.elf: src/stm32.ld main.o startup_stm32f10x.o test01.o $(LD) $(LFLAGS) -o main.elf main.o startup_stm32f10x.o test01.o test01.o: test01/test01.c $(CC) $(CFLAGS) -o test01.o test01/test01.c
But that would mean that we have a dependency from main towards test01, and if we then add test02 we still have that collision. So if we add a variable in the Makefile that tells us to link with test01.
TEST = "" main.elf: src/stm32.ld main.o startup_stm32f10x.o $(TEST).o $(LD) $(LFLAGS) -o main.elf main.o startup_stm32f10x.o $(TEST).o
And then provide the variable TEST as a argument, like this.
make TEST=test01
Then $(TEST).o will be replaced by test01.o and we have exactly the same rules as before.
And if we provide something else, like test02, he will search for the a build rule to create test02.o and not test01.o. And we therefor do not have a strict dependency between those anymore.
make TEST=test02
Therefor a Makefile can look something like this.
Filename: Makefile
CC = arm-none-eabi-gcc LD = arm-none-eabi-gcc AR = arm-none-eabi-ar AS = arm-none-eabi-as CP = arm-none-eabi-objcopy OD = arm-none-eabi-objdump MCUFLAGS = -mcpu=cortex-m3 -mthumb CFLAGS = -I./ -c -fno-common -O0 -g $(MCUFLAGS) -mfix-cortex-m3-ldrd AFLAGS = -ahls $(MCUFLAGS) LFLAGS = -Tsrc/stm32.ld -nostartfiles $(MCUFLAGS) -mfix-cortex-m3-ldrd CPFLAGS = -Obinary ODFLAGS = -S all: test #TEST := test01 TEST = "" test: main.elf @ echo "...copying" $(CP) $(CPFLAGS) main.elf main.bin $(OD) $(ODFLAGS) main.elf > main.lst main.elf: src/stm32.ld main.o startup_stm32f10x.o $(TEST).o @ echo "..linking" $(LD) $(LFLAGS) -o main.elf main.o startup_stm32f10x.o $(TEST).o # # OBJ # main.o: src/main.c @ echo ".compiling" $(CC) $(CFLAGS) src/main.c startup_stm32f10x.o: src/startup_stm32f10x.s @ echo ".assembling" $(AS) $(AFLAGS) -o startup_stm32f10x.o src/startup_stm32f10x.s > startup_stm32f10x.lst # # OBJ from Tests # test01.o: test01/test01.c @ echo ".compiling" $(CC) $(CFLAGS) -o test01.o test01/test01.c test02.o: test02/test02.c @ echo ".compiling" $(CC) $(CFLAGS) -o test02.o $< .PHONY: flash flash: all scripts/do_flash.pl main.bin .PHONY: clean clean: -rm -f main.o startup_stm32f10x.o test*.o -rm -f main.lst main.elf main.bin startup_stm32f10x.lst .PHONY: clean_all clean_all: clean -rm -f log.*
Now we are getting close, we can now select what test code to execute and flash (since we have this do_flash.pl script). So we should add a test runner script on top.
Filename: run_test.bsh
#!/bin/bash function did_it_work { code=$1 #echo "did: "$code if [ ! $code = 0 ] then echo "Error failure: code $code " exit 1 fi } function run_test { tes=$1 log=$2 echo "Begin test: $tes ( $log )" make flash TEST=$tes did_it_work $? #sleep 3 if [ ! -f main.elf ]; then echo "Missing main.elf" exit 1 fi arm-none-eabi-gdb \ --eval-command="set logging on" \ --batch --command=res/commands.gdb \ main.elf #scripts/gdb_runner.pl main.elf $log did_it_work $? mv gdb.txt $log make clean did_it_work $? sleep 1 echo "End test: $f" echo "" } CWD=`pwd` #source ~/stm32/bin/stm32_setup.sh #did_it_work $? scripts/start_server.bsh did_it_work $? make clean # No check. #for f in $(find -name *.c | sed 's/\.\/\(test[0-9][0-9]\).*/\1/'); for f in $( ls -1 -d test* ); do log=$CWD/log.$f.txt run_test $f $log done echo echo "Results:" echo " OK: "`grep test_success log.test*.txt | grep Breakpoint | wc -l` echo " FAIL "`grep test_failed log.test*.txt | grep Breakpoint | wc -l` echo " Err "`grep Default_Handler log.test*.txt | grep Breakpoint | wc -l` echo for f in $( ls -1 log.test*.txt ); do if [ "`grep test_success $f | grep Breakpoint | wc -l`" = "1" ] then echo $f" - OK" fi if [ "`grep test_failed $f | grep Breakpoint | wc -l`" = "1" ] then echo $f" - FAIL" fi if [ "`grep Default_Handler $f | grep Breakpoint | wc -l`" = "1" ] then echo $f" - Error" fi done exit 0; # kill `ps -A | grep openocd | awk '{print $1}'`
This script more or less check for dirs called testXX and then assumes that we have a make rule in the Makefile that matches that name. And then make, flash and start it on the target device. Just to start over with the next one.
Then at the end it closes down with a little sum up, on how the test did.
More or less.
A simple but effective test suite, that could now be modified into what you need.
I use this test suite to verify that gcc is working, it is nice to know that the basic tool chain is doing it's job.
You can find this little project over at github where I call it FunTechCortexMX_test.
/Have fun.
Fatal error: Uncaught Error: Call to a member function link_ext() on null in /customers/9/b/5/fun-tech.se/httpd.www/stm32/class.page.php:62 Stack trace: #0 /customers/9/b/5/fun-tech.se/httpd.www/stm32/TestSuite/index.php(288): page->getFoot() #1 {main} thrown in /customers/9/b/5/fun-tech.se/httpd.www/stm32/class.page.php on line 62