Sunday, January 27, 2013

CMSIS DSP Software Library

We all know the real fun when working with microcontrollers, is interfacing the real world. Sometimes, you only need to read some pushbuttons and drive some LEDs. But now you have a powerful ARM 32-bit, 80 MHz, FPU enabled microcontroller. Sure you can do more complex things than driving LEDs. You can, for example, connect a microphone to the ADC and do some audio signal processing. You could build a frequency analyzer, like the nice one EuphonistiHack made some time ago.

If you want to do some signal processing, you'll be happy to read, there's an open library containing over 60 signal processing functions, including FIR and IIR filters, FFT, convolution, etc. This library is part of CMSIS (Cortex Microcontroller Software Interface Standard), and is called CMSIS DSP Software Library (we will call it CMSIS DSPLib or just DSPLib for short).


Building CMSIS DSPLib

  1. The first step is downloading the library. Go to the ARM website and clic the "Download CMSIS" tab. You need an ARM account to access the downloads, so if you don't have one, register first. Then access the download. At the time of writing this tutorial, CMSIS version is 3.0.
  2. Once downloaded, extract the contents to "~/src/stellaris/cmsis-src". If you did it right, inside the "cmsis-src" directory you should have the directories CMSIS, Device and packages, and the file Version 3.00 (or whatever version it is the library you downloaded). DSPLib already comes with some projects for building it using Keil uVision IDE and some compilers (including GCC). But to use these projects, you need a Windows machine, and of course a copy of Keil uVision IDE. If you want to build the library using Code Composer Studio, there's also an application note with a step by step guide. Be warned it is a bit outdated though, and some CCS dialogs have changed and will require you to guess what are the correct options.
  3. To build the library under GNU/Linux using GCC, I have coded some makefiles. The first one has some common definitions. The second one is the top level makefile, that will call the bottom level makefiles. You have to download and copy them to the right locations:
cd ~/src/stellaris/cmsis-src/CMSIS
wget http://pastebin.com/raw.php?i=613Lz661 -O Makefile.inc
wget http://pastebin.com/raw.php?i=ESqnApg8 -O Makefile
cd DSP_Lib/Source
wget http://pastebin.com/raw.php?i=82VTqR4F -O Makefile
cp Makefile BasicMathFunctions
cp Makefile CommonTables
cp Makefile ComplexMathFunctions
cp Makefile ControllerFunctions
cp Makefile FastMathFunctions
cp Makefile FilteringFunctions
cp Makefile MatrixFunctions
cp Makefile StatisticsFunctions
cp Makefile SupportFunctions
mv Makefile TransformFunctions
  1. Supposing you have already set-up the toolchain, we can start building the library now:
cd ~/src/stellaris/cmsis-src/CMSIS
make
And that's all. Easy, right? When make finishes doing its magic, the library should be sitting at "~/src/stellaris/cmsis-src/CMSIS/Lib/libdsplib_lm4f.a". We can test it building an example.

Using CMSIS DSPLib

Once the library is built, using DSPLib is very easy. You have just to add the include directory and the library file and path to your project/makefile. You can test the library with the included examples under "~/src/stellaris/cmsis-src/CMSIS/DSP_Lib/Examples". I'll tell you how to build "arm-fir-example" (a FIR filter example) using Eclipse.
  1. Create a new project. Complete the first 3 steps detailed here.
  2. When doing step 4, replace "template" with "arm-fir-example" and continue until step 7.
  3. When doing step 7, also add the symbol __FPU_PRESENT
  4. Then jump to step 8, and also add the path to the CMSIS headers. It should be in the "src/stellaris/cmsis-src/CMSIS/Include" directory, under your home:
  1. Continue until step 10. In step 10 also add "m" and "dsplib_lm4f" to libraries. Then add the path to the CMSIS DSPLib library ("src/stellaris/cmsis-src/CMSIS/Lib" under your home):
  1. Continue until step 16. For the step 16, copy the files required for startup, but don't copy main.c. We will replace it with arm-fir-example.c. We will also have to copy the math_helper files:
cd ~/src/stellaris/stellaris-launchpad-template-gcc
cp LM4F.ld LM4F_startup.c ../projects/arm-fir-example
cd ~/src/stellaris/cmsis-src/CMSIS/DSP_Lib/Examples
cp arm_fir_example/*.c ~/src/stellaris/projects/arm-fir-example
cp Common/Source/math_helper.c Common/Include/math_helper.h ~/src/stellaris/projects/arm-fir-example
  1. Complete the tutorial, but each time it tells you to enter "template", replace it with "arm-fir-example". Before building and debugging the code, you will have to do just one modification to the "arm_fir_example_f32.c" file. Scompo's startup code doesn't initialize the FPU, so we will have to do it ourselves. Open "arm_fir_example_f32.c" and add the following includes after the "#include "math_helper.h" line:
#include <inc/hw_nvic.h>
#include <inc/hw_types.h>
  1. Then add the following code inside the "main()" function, just after the "float32_t *inputF32, *outputF32;" declaration line:
  HWREG(NVIC_CPAC) = ((HWREG(NVIC_CPAC) &
                       ~(NVIC_CPAC_CP10_M | NVIC_CPAC_CP11_M)) |
                      NVIC_CPAC_CP10_FULL | NVIC_CPAC_CP11_FULL);
And that's all. Now you should be able to build and debug the code. You can add a breakpoint at the last line of the main function (the last "while(1)" sentence), and if when you run the program, it stops at the breakpoint, everything went fine. CMSIS DSPLib is working and has passed the test!

This tiny microcontroller is powerful enough to perform some signal processing algorithms that in the past were only possible for DSPs. If you do something interesting with this library, please drop me a comment, I'll be pleased to see your project!

13 comments:

  1. Hi! I modified your Makefile.inc to compiler CMSIS for Eclipse Codesourcery based toolchain. But when i try compiling my final project i get the following error,

    /home/cuil/workspace/btp/CMSIS-SP-00300-r3p2-00rel1/CMSIS/Lib/libdsplib_lm3s.a(arm_cmplx_mag_f32.o): In function `arm_sqrt_f32':
    arm_cmplx_mag_f32.c:(.text.arm_sqrt_f32+0x12): undefined reference to `sqrtf'
    collect2: ld returned 1 exit status

    I am trying to compile the ARM_FFT example from the CMSIS library

    ReplyDelete
    Replies
    1. The problem is in the link step. It looks like you are not linking against the math library. Did you add '-lm' to the linker command line?

      Delete
    2. Yes, i did my linker build looks like,

      arm-none-eabi-gcc -T"/home/cuil/workspace/arm/ti/official_package/firmware_development_package/boards/stellaris-guru/cmsis_example_m4/main.ld" -Xlinker --gc-sections -L/home/cuil/workspace/arm/ti/official_package/firmware_development_package/driverlib/gcc-cm4f -L/home/cuil/workspace/btp/StellarisWare/CMSIS-SP-00300-r3p2-00rel1/CMSIS/Lib -Wl,-Map,cmsis_example_m4.map -mcpu=cortex-m4 -mthumb -g3 -gdwarf-2 -o "cmsis_example_m4.elf" ./utils/ELT240320TP.o ./utils/cmdline.o ./utils/cpu_usage.o ./utils/crc.o ./utils/flash_pb.o ./utils/isqrt.o ./utils/ringbuf.o ./utils/scheduler.o ./utils/sine.o ./utils/softi2c.o ./utils/softssi.o ./utils/softuart.o ./utils/uartstdio.o ./utils/ustdlib.o ./driverlib/adc.o ./driverlib/can.o ./driverlib/comp.o ./driverlib/cpu.o ./driverlib/eeprom.o ./driverlib/epi.o ./driverlib/ethernet.o ./driverlib/fan.o ./driverlib/flash.o ./driverlib/fpu.o ./driverlib/gpio.o ./driverlib/hibernate.o ./driverlib/i2c.o ./driverlib/i2s.o ./driverlib/interrupt.o ./driverlib/lpc.o ./driverlib/mpu.o ./driverlib/peci.o ./driverlib/pwm.o ./driverlib/qei.o ./driverlib/ssi.o ./driverlib/sysctl.o ./driverlib/sysexc.o ./driverlib/systick.o ./driverlib/timer.o ./driverlib/uart.o ./driverlib/udma.o ./driverlib/usb.o ./driverlib/watchdog.o ./arm_fft_bin_data.o ./arm_fft_bin_example_f32.o ./math_helper.o ./startup_gcc.o -ldriver-cm4f -lm -ldsplib_lm4f

      I have www.ti.com/tool/eks-lm4f232 board..

      Delete
    3. Hi doragasu, I have compiled the library using ur makefile and the suggested steps....
      I am using arm-none-eabi-gcc in linux, configured to eclipse and debugging enabled....

      I tried the example code and while debugging it seems to be that it is going into hardfault_handler. I found that it is happening in the function arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&firCoeffs32[0], &firStateF32[0], blockSize);

      Have u tried the same and getting into hardfault?

      Delete
    4. I suffered exactly the same issue when I first started experimenting with CMSIS library. The problem is I was not linking properly. I was linking to the ARM libgcc library, instead of the thumb one. To solve the problem, I used gcc instead of ld to link, because gcc automagically choses the right libraries for you. You can read a bit more about it in the forum post I created to document my work with this library:

      http://forum.stellarisiti.com/topic/333-anyone-building-cmsis-under-linux/?p=2178

      Delete
  2. While entering arm_fir_init_f32() program goes to hardfault_handler() .

    ReplyDelete
    Replies
    1. Please read the comments before posting. The comment right above yours deals with exactly the same problem you have. Link the files using gcc instead of ld and the problem should vanish.

      Delete
    2. I'm ashamed that I missed comment right above. I'm not familiar with cross comiling chains, so I would ask for more detailed instruction for changing ld to gcc.
      As I see, toolchain configured in eclipse is using gcc, and makefiles seems not using linker (but changing ld for gcc in Makefile.inc causes no change nor error). Also I can't find BLX command in arm_fir_init_f32() code.
      Thank for you help and nice tutorial.

      Delete
    3. IIRC Makefiles linked in this entry are OK. They do not use ld because you don't have to link anything when building libraries.

      If you are using Eclipse to build the project, you'll have to double-check the linker configuration. It should look like this (check the "Command" and "All options" text boxes, only paths should differ): http://s2.postimg.org/e9hju2ut5/flags.png

      Delete
    4. My tab in Eclipse looks same as yours. Only -mthumb is before -mcpu=cortex-m4, but I think it isn't important.

      Delete
    5. If that's not the problem I'm afraid I don't know what else it could be :(

      Delete
  3. Program seems to crash at memset() around:
    00001950: adds\tr3, r6, #4
    00001952: str.w\tr5, [r3, #-4]
    00001956: adds\tr3, #4
    or in loop just before.

    Do you have an idea, how to allow CMSIS C-files debugging in eclipse? Now there is only disassembly, and is very difficult to find an error.

    ReplyDelete
    Replies
    1. I suppose you should be able to remove CMSIS library from the linking step, and then add the relevant .c and .h files you are using from the CMSIS sources to your project.

      When I suffered the hard fault myself, the problem was also in the memset call. Have you identified the memset call in the assembly code after the linking step (e.g. while debugging?). Are you sure it is using a "BL" instruction and not a "BLX" one? It is important to analyze the code after linking because linking can change some instructions (like subroutine calls), if you disassembled the compiled code but before the linking steps, there is a possibility that code could be modified while linking.

      Also have you checked the Eclipse build logs? Have you confirmed it is using arm-none-eabi-gcc for linking, and -mthumb flag?

      It looks like the problem you are facing is exactly the same I had. If it's not the same, it's really weird it behaves exactly the same, produces the same fault in the same piece of code (the memset call).

      Delete