Keil Logo

Technical Support

On-Line Manuals

Compiler User Guide

Preface Overview of the Compiler Getting Started with the Compiler Compiler Features Compiler intrinsics Performance benefits of compiler intrinsics ARM assembler instruction intrinsics Generic intrinsics Compiler intrinsics for controlling IRQ and FIQ in Compiler intrinsics for inserting optimization bar Compiler intrinsics for inserting native instructi Compiler intrinsics for Digital Signal Processing Compiler support for European Telecommunications S Overflow and carry status flags for C and C++ code Texas Instruments (TI) C55x intrinsics for optimiz Compiler support for accessing registers using nam Pragmas recognized by the compiler Compiler and processor support for bit-banding Compiler type attribute, __attribute__((bitband)) --bitband compiler command-line option How the compiler handles bit-band objects placed o Compiler support for thread-local storage Compiler support for literal pools Compiler eight-byte alignment features Precompiled Header (PCH) files Automatic Precompiled Header (PCH) file processing Precompiled Header (PCH) file processing and the h Precompiled Header (PCH) file creation requirement Compilation with multiple Precompiled Header (PCH) Obsolete Precompiled Header (PCH) files Manually specifying the filename and location of a Selectively applying Precompiled Header (PCH) file Suppressing Precompiled Header (PCH) file processi Message output during Precompiled Header (PCH) pro Performance issues with Precompiled Header (PCH) f Default compiler options that are affected by opti Compiler Coding Practices Compiler Diagnostic Messages Using the Inline and Embedded Assemblers of the AR Compiler Command-line Options Language Extensions Compiler-specific Features C and C++ Implementation Details What is Semihosting? Via File Syntax Summary Table of GNU Language Extensions Standard C Implementation Definition Standard C++ Implementation Definition C and C++ Compiler Implementation Limits

Compiler support for accessing registers using named register variables

3.12 Compiler support for accessing registers using named register variables

You can use named register variables to access registers of an ARM architecture-based processor.

Named register variables are declared by combining the register keyword with the __asm keyword. The __asm keyword takes one parameter, a character string, that names the register. For example, the following declaration declares R0 as a named register variable for the register r0:
register int R0 __asm("r0");
Any type of the same size as the register being named can be used in the declaration of a named register variable. The type can be a structure, but bitfield layout is sensitive to endianness.

Note

Writing to the current stack pointer, "r13" or "sp", can give unpredictable results at either compile-time or run-time.
You must declare core registers as global rather than local named register variables. Your program might still compile if you declare them locally, but you risk unexpected runtime behavior if you do. There is no restriction on the scope of named register variables for other registers.

Note

A global named register variable is global to the source file in which it is declared, not global to the program. It has no effect on other files, unless you use multifile compilation or you declare it in a header file.
A typical use of named register variables is to access bits in the Application Program Status Register (APSR). The following example shows how to use named register variables to set the saturation flag Q in the APSR.
#ifndef __BIG_ENDIAN // bitfield layout of APSR is sensitive to endianness
typedef union
{
    struct
    {
        int mode:5;
        int T:1;
        int F:1;
        int I:1;
        int _dnm:19;
        int Q:1;
        int V:1;
        int C:1;
        int Z:1;
        int N:1;
    } b;
    unsigned int word;
} PSR;
#else /* __BIG_ENDIAN */
typedef union
{
    struct 
    {
        int N:1;
        int Z:1;
        int C:1;
        int V:1;
        int Q:1;
        int _dnm:19;
        int I:1;
        int F:1;
        int T:1;
        int mode:5;
    } b;
    unsigned int word;
} PSR;
#endif /* __BIG_ENDIAN */
/* Declare PSR as a register variable for the "apsr" register */
register PSR apsr __asm("apsr");
void set_Q(void)
{
    apsr.b.Q = 1;
}
The following example shows how to use a named register variable to clear the Q flag in the APSR.
register unsigned int _apsr __asm("apsr");
void ClearQFlag(void)
{
    _apsr = _apsr & ~0x08000000; // clear Q flag
}
Compiling this example using --cpu=7-M results in the following assembly code:
ClearQFlag
    MRS    r0,APSR ; formerly CPSR
    BIC    r0,r0,#0x80000000
    MSR    APSR_nzcvq,r0; formerly CPSR_f
    BX     lr
The following example shows how to use named register variables to set up stack pointers.
register unsigned int _control __asm("control");
register unsigned int _msp     __asm("msp");
register unsigned int _psp     __asm("psp");
void init(void)
{
    _msp = 0x30000000;        // set up Main Stack Pointer
    _control = _control | 3;  // switch to User Mode with Process Stack
    _psp = 0x40000000;        // set up Process Stack Pointer
}
Compiling this example using --cpu=7-M results in the following assembly code:
init
    MOV    r0,0x30000000
    MSR    MSP,r0
    MRS    r0,CONTROL
    ORR    r0,r0,#3
    MSR    CONTROL,r0
    MOV    r0,#0x40000000
    MSR    PSP,r0
    BX     lr
You can also use named register variables to access registers within a coprocessor. The string syntax within the declaration corresponds to how you intend to use the variable. For example, to declare a variable that you intend to use with the MCR instruction, look up the instruction syntax for this instruction and use this syntax when you declare your variable. The following example shows how to use a named register variable to set bits in a coprocessor register.
register unsigned int PMCR __asm("cp15:0:c9:c12:0");
void __reset_cycle_counter(void)
{
    PMCR = 4;
}
Compiling this example using --cpu=7-M results in the following assembly code:
__reset_cycle_counter PROC
    MOV    r0,#4
    MCR    p15,#0x0,r0,c9,c12,#0      ; move from r0 to c9
    BX     lr
    ENDP
In the above example, PMCR is declared as a register variable of type unsigned int, that is associated with the cp15 coprocessor, with CRn = c9, CRm = c12, opcode1 = 0, and opcode2 = 0 in an MCR or MRC instruction. The MCR encoding in the disassembly corresponds with the register variable declaration.
The physical coprocessor register is specified with a combination of the two register numbers, CRn and CRm, and two opcode numbers. This maps to a single physical register.
The same principle applies if you want to manipulate individual bits in a register, but you write normal variable arithmetic in C, and the compiler does a read-modify-write of the coprocessor register. The following example shows how to manipulate bits in a coprocessor register using a named register variable
register unsigned int SCTLR __asm("cp15:0:c1:c0:0");
/* Set bit 11 of the system control register */
void enable_branch_prediction(void)
{
    SCTLR |= (1 << 11);
}
Compiling this example using --cpu=7-M results in the following assembly code:
__enable_branch_prediction PROC
    MRC    p15,#0x0,r0,c1,c0,#0
    ORR    r0,r0,#0x800
    MCR    p15,#0x0,r0,c1,c0,#0
    BX     lr
    ENDP
Non-ConfidentialPDF file icon PDF versionARM DUI0375H
Copyright © 2007, 2008, 2011, 2012, 2014-2016 ARM. All rights reserved. 
  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.