ARM Technical Support Knowledge Articles

What type of memory access does armcc use for different C constructs?

Applies to: RealView Development Suite (RVDS)

Answer

It is often important to know what type of memory access instruction is being used by armcc, particularly when peripherals are being accessed.

All memory regions used by C must support the full range of access sizes for both reads and writes (byte, halfword, and word). However, in some cases, a memory controller may not decode the access size information for certain memory regions, for example, if byte write enables are not available on the memory or peripheral devices. If a memory system does not support sub-word writes, STRB and STRH should not be used. But if they are, the whole word might be written, and memory corruption will occur due to byte or halfword replication across the ARM data bus. Such memory cannot be generally used from C, because both the compiler and C library assume that STRB and STRH are available.

This may be an important issue for peripherals, where a full register-width access is the only access size permitted, due to the design of the peripheral. Sub-word accesses might cause problems for a number of reasons:

For these reasons, ARM recommends that peripherals are accessed safely by ensuring that appropriate C constructs are specified in the user program and that any variables are marked with the volatile keyword to ensure the compiler does not perform any optimizations with regard to the access.  For more information, please see the FAQ: Placing C variables at specific addresses - memory-mapped registers.

The main types of C memory access are described below. To control the type of access used by the C compiler, only C constructs (1) and (2) should be used.

1) variables of type int, short, char

The compiler will use an architecturally-defined memory access (word, halfword, byte). (The compiler may sometimes access local chars and shorts using LDR/STR if this is easier or faster).

The compiler aligns these variables appropriately, and the code generator uses LDR/STRLDRH/STRHLDRB/STRB on Architecture 4 and later.1

If pointers to these types are used, be aware that the address must be aligned on the appropriate alignment for that type (word alignment for words, halfword alignment for halfwords).

For information on how to access unaligned data, please see the 

For more details, see the section Unaligned data access in C and C++ code from the ARM Compiler toolchain's 'Using the Compiler' guide.

2) Structures (structs)

The ARM compilers will insert padding in order to align structure members. This means a single architecturally-defined memory access can be used exactly as for (1) above. When structures are copied, the compiler may use different access widths (usually word copies).

3) Packed structures (__packed structs)

Packed structures do not contain any internal padding. Some members will be on non-natural alignment, so the ARM compiler cannot use a single word or halfword access instruction.

Instead, armcc will construct a sequence of modified accesses combined with shifting and masking which could produce the following side-effects:

Hence, '__packed structs' should not be used to map peripherals, where the number and type of memory accesses can cause major problems. Also note that __packed structs have a high performance overhead, so should be converted to normal padded form if they are extensively used.

In general, for an unaligned word read, armcc will use an LDM of two words, followed by an alignment-specific shift and mask. For an unaligned word write, armcc will use four STRB instructions.

4) Bitfields

For example:

struct timer_reg

{
  int a : 3;
  int b : 12;
  int c : 7;
};

volatile struct timer_reg *ptr_timer_reg = 0xA800000;

The type of memory access used when a bitfield is accessed is not defined by the ANSI C standard. One way for bitfields to be implemented is for the compiler to use byte read and write operations to only change the bytes of the bitfield that need changing. Alternatively, a compiler may perform a word read/modify/write process, preserving all unmodified bits.  Also, if the user compiles for an ARM processor that supports the BFI (bitfield insert) and BFX (bitfield extract) instructions, then the compiler may make use one of these more efficient operations.

The fields can be specified as long longlongintshort and char, and the compiler will use word, word, word, halfword and byte accesses respectively.2

If the bitfield is specified as volatile, the ARM compiler will perform a load/store of the entire container as specified by the field type (int in this case). Please note this is a non-ANSI extension, so other C compilers might not behave in this way.

As noted above, ARM recommends that peripheral registers are always accessed using a explicit full-width memory access. This should be coded in C by using architecturally-defined types, for example, int *, to perform the peripheral access in conjunction with explicit shifting and masking to change sub-fields:

volatile int *ptr = (int *)0x801000;

/* Perform word accesses */

*ptr = (*ptr & ~0x0300) | (0x2 << 8);


1
 On Architecture 3, halfword access is not available so a word access (plus masking) or byte accesses are performed instead. This also applies to __packed int *p and __packed short *p.

2 In strict 1990 ISO Standard C, the only types permitted for a bit field are intsigned int, and unsigned int. For non int bitfields, the compiler displays an error.

See also:

Article last edited on: 2012-05-28 11:12:30

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential