SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Inline assembly in C code for Cortex-M0/M0+
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Inline assembly in C code for Cortex-M0/M0+

Neil Salmon
Last updated: October 5, 2023 9:58 am
Neil Salmon 7 Min Read
Share
SHARE

Inline assembly allows inserting assembly language code directly into C/C++ code. This can be useful for Cortex-M0/M0+ programming when you need more control than C/C++ alone provides, such as for low-level hardware access, performance optimizations, and access to specialized processor instructions.

Contents
When to use inline assemblyCortex-M0/M0+ inline assembly syntaxAccessing Cortex-M0/M0+ registersMemory accessConditional executionLoops and branchesFunction callsInserting assembly commentsMixing C and assemblyGuidelines for efficient inline assemblyDebugging inline assemblyConclusion

When to use inline assembly

Typical situations where you would want to use inline assembly on Cortex-M0/M0+ include:

  • Implementing time-critical routines
  • Optimizing performance by using special registers or instructions
  • Accessing features not available from C/C++
  • Controlling hardware registers and peripherals
  • Writing interrupt service routines and exception handlers

Inline assembly should be used judiciously. Only use it when necessary as it breaks portability and can hamper code comprehension. Prefer to use C/C++ if performance is not critical.

Cortex-M0/M0+ inline assembly syntax

The basic syntax for inline assembly in C code is: asm(“assembly instructions”);

For example: asm(“mov r0, #1”); // Move immediate value 1 into register r0

The assembly code can include almost any valid Cortex-M0/M0+ assembly instruction. Global labels are not allowed but you can use local labels prefixed with a ‘.’.

To interface the inline assembly with the C code, you need to map C variables to assembly registers. This is done by adding modifiers before the registers: int x = 1; int y; asm(“mov r0, %[input]” : [output] “=r” (y) : [input] “r” (x)); // Move x into r0, and output r0 into y

“r” indicates a general purpose register, “=” that it is written to, and the names in [] map the C variables to these registers.

Accessing Cortex-M0/M0+ registers

To access registers like the program status register in inline assembly, the CMSIS headers define register aliases: #include “cmsis.h” asm(“mov %[result], #0” : [result] “=r” (result) : ); asm(“mrs %[result], primask” : [result] “=r” (result) :: );

This allows reading the PRIMASK register into the C variable result. Other common registers like CONTROL, MSP and PSP are accessible in the same way.

Memory access

The Cortex-M0/M0+ has byte, halfword and word memory access. To load/store memory in inline assembly use: asm(“ldrb r0, [%[addr]]” : : [addr] “r” (&data)); // Load byte asm(“strh r0, [%[addr]]” : : [addr] “r” (&data)); // Store halfword

The square brackets are used to dereference the address. You can also use offsets: asm(“ldr r0, [%[addr], #8]” : : [addr] “r” (&data)); // Load word offset

Conditional execution

Assembly conditions can be used like: int x = 10; asm(“cmp %[input], #0” : : [input] “r” (x)); asm(“ite eq” : : : “cc”); asm(“movgt r0, #1” : : : “cc”); asm(“moveq r0, #0” : : : “cc”);

This compares x to 0, then conditionally moves 1 or 0 into r0 based on the result. “cc” indicates the instructions use condition flags.

Loops and branches

Branches and loops are also supported in inline assembly. Local labels prefixed with ‘.’ can be used with branches: int count; asm(“mov r0, #0”); asm(“loop:”); asm(“add r0, r0, #1”); asm(“cmp r0, %[input]” : : [input] “r” (count)); asm(“bne .loop” : : : “cc”);

This is a simple loop that increments r0 count times. “cc” indicates it branches based on condition flags.

Function calls

You can call C functions within inline assembly using: void foo(int x) { // … } asm(“mov r0, %[val]” : : [val] “r” (x)); asm(“bl foo” : : : “r0”);

This passes x in r0 and calls foo(). Any registers used for parameter passing need to be listed after the asm statement.

Similarly, assembly functions can be called from C by declaring them as: extern void asm_func(int x); int result; asm(“mov r0, %[val]” : : [val] “r” (x)); asm(“bl asm_func” : “=r” (result) : : “r0”);

This stores the return value from asm_func() into result. Parameter and return registers need to be declared.

Inserting assembly comments

Assembly comments inside inline ASM code are inserted by escaping them with \n: asm(“mov r0, #1 \n\t” “// Load immediate value \n\t” “bx lr”);

Mixing C and assembly

With careful planning, it is possible to mix C statements and assembly within the same asm() statement: int x = 10; int y; asm(“mov r0, %[input] \n\t” “mov r1, #3 \n\t” “mul r0, r1 \n\t” “str r0, %[output]” : [output] “=m” (y) : [input] “r” (x)); // x * 3 stored in y

This allows creating inline functions that combine C and assembly.

Guidelines for efficient inline assembly

Some guidelines for writing efficient inline assembly code:

  • Minimize exchanging data between C and assembly. This can be slow.
  • If using floating point, ensure VFP is enabled and aware of context switching.
  • Understand assembly effects on condition flags and registers used by C code.
  • Use registers r0-r3, r12 for scratch to avoid corrupting C values.
  • For critical routines, use assembly only to maximize performance.
  • Prefer word and halfword memory access over byte access.
  • Take advantage of load/store multiple instructions for efficiency.

Also ensure any assembly code is commented well to aid comprehension and maintenance.

Debugging inline assembly

Debugging inline assembly code can be challenging. Some tips:

  • Use a debugger that understands mixed C/assembly like GDB.
  • Single step through assembly while examining register/memory contents.
  • Break on the asm() statement and step into the assembly.
  • Print register values before and after inline assembly to check logic.
  • Compare expected vs actual register values for mismatches.
  • Add liberal comments explaining the purpose of each instruction.
  • Break complex routines into smaller helper assemblies to isolate bugs.

Testing inline assembly thoroughly before integration is highly recommended. Bugs can easily corrupt memory contents and registers.

Conclusion

Inline assembly is a powerful tool for Cortex-M0/M0+ programming when used properly. Insert it sparingly where C/C++ alone is insufficient. Focus on the minimal assembly needed to achieve your goal. Rigorously validate your inline asm code, add ample comments, and design it to interoperate cleanly with the C code.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Key factors in upgrading legacy Cortex-M0 designs to Cortex-M0+
Next Article Syntax for inline assembly operands in GCC
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

What is Computer architecture in Arm Cortex-M series?

The ARM Cortex-M series of processors are designed for embedded…

9 Min Read

What is the ARM SWD protocol?

The ARM Serial Wire Debug (SWD) protocol is a two-pin…

8 Min Read

Will The Arm Architecture Replace The X86/X64 Architecture?

The short answer is that while ARM is making inroads…

6 Min Read

Do I Need to Run a Separate Flash Programmer Software for Custom SOC with Cortex M0?

If you are working with a custom system-on-chip (SOC) that…

10 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account