In the first of a three part series, Joseph Yiu, author of “The definitive guide to the ARM Cortex-M0,” provides some basic guidelines for porting your code base from other 8/16 bit MCUs to ARM and between various ARM processors starting here with the ARM 7TDMI and Cortex-M0.
As software reuse becomes more common, software porting is becoming a more common task or embedded software developers. In this three part series, we will look into differences between various common ARM processors for microcontrollers and what areas in a program need to be modified when porting software between them.
This series will conclude with issues relating to the software porting of software from 8-bit and 16-bit architectures.
Some of the ARM processors used in a range of microcontroller products is shown in Table 21.1 below.
Table 21.1: Commonly Used ARM Processors on MCUs
The main differences between the Cortex-M processors are illustrated in Figure 21.1 . In this series we will cover the detailed differences between the Cortex-M0 and some of these processors.
Figure 21.1: The Cortex-M processor family( To view larger image, click here).
Differences between the ARM7TDMl and the Cortex-M0
There are a large number of differences between the ARM7TDMI and the Cortex-M0.
Operation Mode. The ARM7TDMI has a number of operation modes, whereas the Cortex-M0 only has two modes, as described in Table 21.2 below.
Table 21.2: ARM7TDM1/Cortex-M0 Operation Modes Comparison
Some of the exception models from the ARM7TDMI are combined in Handler mode in the Cortex-M0 with different exception types. Consider the example presented in Table 21.3 below . The reduction of operation modes simplifies Cortex-M0 programming.
Table 21.3: Exception Comparison between the ARM7TDMl and the Cortex-M0
Registers. The ARM7TDMI has a register bank with banked registers based on current operation mode. In Cortex-M0, only the SP is banked (Figure 21.2 below ). And in most simple applications without an OS, only the MSP is required.
There are some differences between the CPSR (Current Program Status Register) in the ARM7TDMl and the xPSR in the Cortex-M0. For instance, the mode bits in CPSR are removed, replaced by IPSR, and interrupt masking bit 1-bit is replaced by the PRIMASK register, which is separate from the xPSR.
Figure 21.2. Register bank differences between ARM7TDMI and Cortex-M0 (To view larger image click here) .
Despite the differences between the register banks, the programmer's model or RO to R15 remains the same. As a result, Thumb instruction codes on the ARM7TDMI can be reused on the Cortex-M0, simplifying software porting.
Instruction Set. The ARM7TDMI supports the ARM instructions (32-bit) and Thumb instructions (16-bit) in ARM architecture v4T. The Cortex-M0 supports Thumb instructions in ARMv6-M, which is a superset of the Thumb instructions supported by the ARM7TDMI. However, the Cortex-M0 does not support ARM instructions. Therefore, applications for the ARM7TDMI must be modified when porting to Cortex-M0.
Interrupts. The ARM7TDMI supports an IRQ interrupt input and a Fast Interrupt (FIQ) input. Normally a separate interrupt controller is required in an ARM7TDMI microcontroller to allow multiple interrupt sources to share the IRQ and FIQ inputs.
Because the FIQ has more banked registers and its vector is located at the end of the vector table, it can work faster by reducing the register stacking required, and the FIQ handler can be placed at the end of vector table to avoid branch penalty.
Unlike the ARM7TDMI, the Cortex-M0 has a built-in interrupt controller called NVIC with up to 32 interrupt inputs. Each interrupt can be programmed at one of the four available priority levels. There is no need to separate interrupts into IRQ and FIQ, because the stacking of registers is handled automatically by hardware.
In addition, the vector table in the Cortex-M0 stores the starting address of each interrupt service routine, while in the ARM7TDMI the vector table holds instructions (usually branch instructions that branch to interrupt service routines).
When the ARM7TDMI receives an interrupt request, the interrupt service routine starts in ARM state (using ARM instruction). Additional assembly wrapper code is also required to support nested interrupts. In the Cortex-M0, there is no need to use assembly wrappers for normal interrupt processing.
Porting Software from the ARM7TDMl to the Cortex-M0
Application code for the ARM7TDMI must be modified and recompiled to be used on the Cortex-M0.
Startup Code and Vector Table . Because the vector table and the initialization sequence are different between the ARM7TDMI and the Cortex-M0, the startup code and the vector table must be replaced (Table 21.4 below ).
Interrupt. Because the interrupt controller used in microcontrollers with the ARM7TDMI would be different from the NVIC in the Cortex-M0, all the interrupt control code needs to be updated.
It is recommended to use the NVIC access functions defined in CMSIS for portability reason. The interrupt wrapper function for nested interrupt support in the ARM7TDMI must be removed.
Table 21.4: Vector Table Differences between the ARM7TDM1 and the Cortex-M0
If the interrupt service routine was written in assembly, the handler code will probably require rewriting because many ARM instructions cannot be directly mapped to Thumb instructions.
For example, the exception handler in the ARM7TDMI can be terminated by “MOVS PC, LP” (ARM instruction). This is not valid for the Cortex-M0 and must be replaced by “BX LR”. FIQ handlers for the ARM7TDMI might rely on the banked registers R8 to R14 in the ARM7TDMI to save execution time.
For example, constants used by the FIQ handler might be preloaded into these banked registers before the FIQ is enabled so that the FIQ handler can be simplified. When porting such handlers to the Cortex-M0 processor, the banked registers are not available and therefore these constants must be loaded into the registers within the handler.
In some cases you might find assembly code being used to enable or disable interrupts by modifying the 1-bit in CPSR. In the Cortex-M0, this is replaced by the PRIMASK interrupt masking register. Note that in the ARM7TDMI you can carry out the exception return and change the I-bit in a single exception return instruction.
In the Cortex-M0 processor, PRIMASK and xPSR are separate registers, so if the PRIMASK is set during the exception handler, it must be cleared before the exception exit. Otherwise the PRIMASK will remain set and no other interrupt can be accepted.
C program code. Apart from the usual changes caused by peripherals, memory maps, and system-level feature differences, the C applications might require changes in the following areas:
1 – Compile directives like “#pragma arm´and pragama thumb´are no longer required because the Cortex-M0 supports Thumb instructions only.
2 – For ARM RVDS or Keil MDK, all inline assembly has to be rewritten, either using embedded assembler, separate assembly code, or as C functions. Inline assembly in these tools only supports ARM instructions. Users of the GNU C compiler might also need to modify their inline assembly code.
3 – Exception handlers can be simplified because in the Cortex-M0, each interrupt has its own interrupt vector. There is no need to use software to determine which interrupt service is required, and there is no software overhead in supporting nested interrupts.
4 – Although the “_irq” directive is not essential in the Cortex-M0 exception handlers, this directive for interrupt handlers can be retained in ARM RVDS or Keil MDK projects for clarity. It might also help software porting if the application has to be ported to other ARM processors in the future.
The C code should be recompiled to ensure that only Thumb jnstructions are used and no attempt to switch to ARM state should be contained in the compiled code. Similarly, library files must also be updated to ensure they will work with the Cortex-
Assembly Code. Because the Cortex-M0 does not support the ARM instruction set, assembly code that uses ARM instructions has to be rewritten.
Be careful with legacy Thumb programs that use the CODE 16 directive. When the CODE 16 directive is used, the instructions are interpreted as traditional Thumb syntax. For example, data processing op-codes without S suffixes are converted to instructions that update APSR when the CODE 16 directive is used.
However, you can reuse assembly files with the CODE16 directive because it is still supported by existing ARM development tools.
For new assembly code, the THUMB directive is recommended, which indicates to the assembly that the Unified Assembly Language (UAL) is used. With UAL syntax, data processing instructions updating the APSR require the S suffix.
Fault handlers and system exception handlers like SWI must also be updated to work with the Cortex-M0.
Atomic Access. Because Thumb instructions do not support swap (SWP and SWPB instructions), the code for handling atomic access must be changed. For single processor systems without other bus masters, you can use either the exception mechanism or PRIMASK to achieve atomic operations.
For example, because there can only be one instance of the SVC exception running (when an exception handler is running, other exceptions of the same or lower priority levels are blocked), you can use SVC as a gateway to handle atomic operations.
Optimizations. After getting the software working on the Cortex-M0, there are various areas you can look into to optimize your application code.
For assembly code migrated from the ARM7TDMI, the data type conversion operation is one of the potential areas for improvement because of new instructions available in the ARMv6-1VI architecture.
If the interrupt handlers were written in assembly, there might be chance that the stacking operations can be reduced because the exception sequence automatically stacks RO-R3 and R 12.
More sleep modes features are available in the Cortex-M0 that can be used to reduce power consumption. To take the full advantages of the low-power features on a Cortex-M0 microcontroller, you will need to modify your application code to make use of the power management features in the microcontroller.
These features are dependent on the microcontroller product, and the information in this area can usually be found in user manuals or application notes provided by the microcontroller vendors.
With the nested interrupts being automatically handled by processor hardware and the availability of programmable priority levels in the NVIC, the priority level of the exceptions can be rearranged for best system performance.
Used with permission from Newnes, a division of Elsevier.Copyright 2011, from “The definitive guide to the ARM Cortex-M0,” by Joseph Yiu. For more information about this title and other similarbooks, please visit www.elsevierdirect.com.