Basics of porting C-code to and between ARM CPUs: From 8-/16-Bit MCUs to Cortex-M0

In the final third part in a series, Joseph Yiu, author of “The definitive guide to the ARM Cortex-M0,” takes you through means by which to port your code base from an 8/16-bit MCU to the ARM Cortex-M0.

Some application developers might need to port applications from 8-bit or 16-bit microcontrollers to the Cortex-M0. By moving from these architectures to the Cortex-M0, often you can get better code density, higher performance, and lower power consumption.

Common Modifications: When porting applications from these microcontrollers to the Cortex-M0, the modifications of the software typically involve the following:

1- Startup code and vector table. Different processor architectures have different startup code and interrupt vector tables. Usually the startup code and the vector table will have to be replaced.

2- Stack allocation adjustment. With the Cortex-M processors, the stack size requirement can be very different from an 8-bit or 16-bit architecture. In addition, the methods to define stack location and stack size are also different from 8-bit and 16-bit development tools.

3- Architecture-specific/tool-chain-specific C language extensions. Many of the C compilers for 8-bit and 16-bit microcontrollers support a number of C language extensions features. These include special data types like Special Function Registers (SFRs) and bit data in 8051, or various “#pragma” statements in various C compilers.

4-Interrupt control. In 8-bit and 16-bit microcontroller programming, the interrupt configuration is usually done by directly writing to various interrupt control registers.

When porting the applications to the ARM Cortex-M processor family, these codes should be converted to use the CMSIS interrupt control functions.

For example, the enable and disable functions of interrupts can be converted to ” enable irq () ” and ” disable irq () “. The configuration of individual interrupts can be handled by various NVIC functions in CMSIS.

5-Peripheral programming. In 8-bit and 16-bit microcontroller programming, the peripherals control is usually handled by programming to registers directly. When using ARM microcontrollers, many microcontroller vendors provide device driver libraries to make use of the microcontroller easier.

You can use these library functions to reduce software development time or write to the hardware registers directly if preferred. If you prefer to program the peripherals by accessing the registers directly, it is still beneficial to use the header files in the device driver library as these have all the peripheral registers defined and can save you time preparing and validating the code.

6-Assembly code and inline assembly. Obviously all the assembly and inline assembly code needs to be rewritten. In many cases, you can rewrite the required function in C when the application is ported to the Cortex-M0.

7-Unaligned data. Some 8-bit or l6-bit microcontrollers might support unaligned data. Because the Cortex-M0 does not support unaligned data, some data structures definitions or pointer manipulation codes might need to be changed.

For data structures that require unaligned data handling, we can use the _packed attribute when defining the structure. However, the Cortex-M0 requires multiple instructions to access unaligned data. So it is best to convert the data structures so that all elements inside are aligned.

8-Be aware of data size differences. The integers in most 8-bit and 16-bit processors are 16-bit, whereas in ARM architectures integers are 32-bit. This difference causes changes in behavior of overflow situations, it can also affect the memory size required for storing the data.

For example, when a program file defines an array of integers from 8-bit or 16-bit architecture, we might want to change the code to use “short int” or “intl6_t” (in “stdint.h,” introduced in C99) when porting the code to ARM architecture so that the size remains unchanged.

9-Floating point. Many 8-bit and 16-bit microcontrollers define “double” (double precision floating point) as 32-bit data. In ARM architecture, “double” is 64-bit.

When porting applications containing floating point operations, you might need to change the double precision floating point data to “float” (single precision floating point).

Otherwise the processing speed would be reduced and the program size could increase because of the requirement to process the data in extra precision.

For the same reason, some function calls for mathematical operation might need to be changed to ensure that the single precision version is used. For example, by default “cos () ” is the double precision version of the cosine function; for single precision operation, use “cosf()” instead.

10-Adding fault handlers. In many 8-bit and 16-bit microcontrollers, there are no fault exceptions. Although embedded applications can operate without any fault handlers, the addition of fault handlers can help an embedded system to recover from error (e.g., data corruption caused by voltage drop or electromagnetic interference).