Thumbs up with Assembly on ARM Cortex

Sometimes it is necessary to write an interrupt service routine in assembly language. This is the case as well for the ARM Cortex-M0+ which is found in the KL25Z on my Freedom board. But there is something important about the ARM Cortex architecture: Thumb Mode.

Thumb mode the ‘ARM way’ to reduce the code size with a reduced (16bit wide) instruction set. The ARM architecture can implement a ‘mixed’ mode, on a function level. To distinguish between ‘normal’ ARM functions and ‘thumb’ functions, the processor is checking if the LSB (Least Significant Bit) of a function pointer (or function call destination) is set. So a jump address of 0x410 is for a ‘normal’ function, while a function jump to the address 0x411 (even if the function is located at the address 0x410) denotes a ‘thumb’ function.

Even with the ARM Cortex-M0+ implementing ‘thumb only’, it is using the same convention. So building an application for Cortex-M0+, it is important that this bit is set.

For the GNU ARM gcc compiler (and assembler) the tool chain is directed to use thumb mode with that option:

Gcc Thumb Code Generation Option

Gcc Thumb Code Generation Option

With that option set, the compiler tells the linker that this bit must be set for function pointer calls.

However, for assembly programs, a bit more is needed. Beside of passing the -mthumb option to the assembler as well, I need to use other directives to tell that a label is a function, and that this function is in thumb mode. A good summary of the different ARM assembler directives can be found here: http://microcross.com/GNU-ARM-Assy-Quick-Ref.pdf

But things are best shown with a simple example:

/*
* asm_isr.s
*
*  Created on: Nov 13, 2012
*      Author: Erich Styger
*/

    .global MyAsmISR             /* external linkage for my ISR name */
    .text                        /* put into linker code section */
    .thumb_func                  /* we are a thumb function */
    .type   MyAsmISR, %function  /* optional: mark it as a function */

MyAsmISR:
        push {r7,lr}    /* push registers */
        add  r7,sp,#0

        /* do something usefule here */

        mov sp, r7      /* restore registers */
        pop {r7, pc}

    .end  /* end directive */

The magic directive is ‘.thumb_func’. Linking my interrupt routine shows that the LSB is set, and properly pointing to the interrupt routines:

Reset Vector and my Interrupt with Thumb bit set in address

Reset Vector and my Interrupt with Thumb bit set in address

Happy Arming 🙂

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.