4 Uses for Assembly Language

An embedded tip that I often advocate is that developers should avoid using assembly language. The reason for avoiding assembly language is that it is specific to the processor architecture being used, it is difficult to read, understand and maintain. Today, we are going to look at the few areas where I believe it is still appropriate to use assembly language and how that code looks.

Before we look at each area that assembly language can still be used, please keep in mind that how these assembly instructions are integrated into a code base will vary based on the development environment that is used. There is usually some custom compiler function that is used to let the compiler language know that assembly instructions are being used such as _asm(). The code snippets we examine may require modification before use.

 

Assembly Usage #1 – To Execute a Breakpoint Instruction

Whenever I start on a new project, one of the first things I do during implementation is to put assembly instructions for breakpoints in interrupt service routines for CPU faults, watchdog time-outs and within special RTOS events such as malloc failed (even though I avoid using malloc). The reason that I do this is that I want to be notified when I reach one of these functions unexpectedly, but I do not want to manage those breakpoints. Many modern processors have a breakpoint assembly instruction and using assembly code to execute that instruction is completely appropriate.

For an Arm Cortex-M processor, using an IDE similar to STM32 CubeIDE, such an assembly implementation might look like the following:

__asm(“bkpt”);

When this instruction is reached, the processor will halt execution.

 

Assembly Usage #2 – Transition from Bootloader to Application

A second area where developers should be looking to use assembly language is at the transition point from bootloader to application code. In many systems, the bootloader is the first application that executes. It sets up the processor, validates memory and potentially performs several other functions. At some point though, the bootloader gives way and jumps to the main application. In order to make that jump successfully, many components within the processor need to be set to their initial condition such as the system stack and then the function pointer for reset vector of the application needs to be loaded.

When I integrate assembly language into an application, I often make use of the IDE’s assembly functions. These functions can vary from one IDE to the next. For example, below is a function that accepts the start address location for where the reset vector for an application is located. This code was put together for an NXP Kinetis-L processor using NXP specific toolchains:

void Flash_StartApplication(uint32_t startAddress)
{
    // Set up stack pointer
    asm("LDR     r1, [r0]");
    asm("mov     r13, r1");

    // Jump to application reset vector
    asm("ADDS     r0,r0,#0x04 ");
    asm("LDR      r0, [r0]");
    asm("BX       r0");
}

Another example for the same function but in this case the code is written for a Texas Instruments C2000 processor:

void Flash_StartApplication(uint32_t startAddress)
{
    asm ("      C28OBJ"); //Select C28x object mode
    asm ("      C28ADDR") ; //Select C27x/C28x addressing

    asm ("      SETC INTM");
    asm ("      ZAPA");
    asm ("      MOV @SP,#0");
    asm ("      PUSH ACC");
    asm ("      PUSH AL");
    asm ("      MOV AL, #0x0a08");
    asm ("      PUSH AL");
    asm ("      MOVL XAR7, #0x003F3FFE");
    asm ("      PUSH XAR7");

    asm ("      POP RPC");
    asm ("      POP ST1");
    asm ("      POP ST0");
    asm ("      POP IER");
    asm ("      POP DBGIER");
    asm ("      LRETR");
}

Again, this provides low-level control over the processor to ensure that everything gets put back to square one and that the application can then execute from a clean slate.

 

Assembly Usage #3 – Code Optimization in a Control Loop

There are still instances where it may be necessary to optimize the code in a high frequency control loop that requires the use of assembly. Hand coding a fast control loop in assembly used to be quite common. While this may still be appropriate at times, in today’s development environments with ultra-fast processors and compilers that use sophisticated optimization techniques, more and more I feel that this last case for assembly language usage is disappearing.

 

Assembly Usage #4 – To Teach Microcontroller Fundamentals

When I first started out in embedded systems, I was essentially forced to develop my applications in assembly. The microcontroller vendors back then did not offer free C compilers. That was something that you had to pay for; However, their assembly language tools were completely free. While many of us today want to start at the highest level of abstraction possible, for embedded systems it’s critical that developers understand what is going on under the hood and there is no better way to learn that then to write software in assembly.

I don’t think that a lot of time should be spent writing in assembly, but I think developers new to embedded systems should write some very basic applications like a “Hello World” application and a blinky LED application to understand how to initialize the processor, control registers and get a little familiar with the underlying instruction set. These details can help developers write far more efficient code in C/C++ if they understand the underlying architecture and the only way to truly understand it, is to work with it in its own natural language.

 

Conclusions

Assembly language should be avoided in most circumstances today, but when it is used it can provide a mechanism for very fine control over how an application behaves. We explored three areas where a developer might consider using assembly language. In my mind, these are the only areas where it may still be appropriate to use assembly language and even then, there may be alternatives available. If you do decide to use some assembly language, make sure that you heavily document that area of code so that it will be easier for yourself and other developers to maintain that code in the future.

Share >