*
*Home|Chinese|Japanese*About ARM|Forums|Events|News|Employment|Contact Us|Investors*
dotted rule
*ARM - the architecture for the digital worldARM - the architecture for the digital world
search
*
*
***
*MARKETS:PRODUCTS & SOLUTIONS:CONNECTED COMMUNITY:TECHNICAL SUPPORT:DOCUMENTATION*
*
technical support
*
*
****
*.Technical Support
*
*
*>>Home Page*
*
*.Obtaining Support*
*
*.FAQs*
*
**Development Tool FAQs*
**IP FAQs*
**Embedded Software FAQs*
**Artisan Physical IP FAQs (Login Required)*
*
*.Downloads*
*
*.Documentation*
*
*.Training*
*
*.Where To Buy*
*
*.Keil MCU Tools*
*
*.What's New*
*
*.ARM Newsgroups*
*
*.Active Assist On-site Services*
*
*
*
technical support FAQsask ARM*
*

Technical Support Search
*     (Advanced Search)
  FAQs   Documentation   Downloads   Forums

*

 
downarrowWhat type of memory access does armcc/tcc use for different C constructs?
Applies to: Compilers, Software Development Toolkit (SDT)

Description
It is often important to know what type of memory access instruction is being used by armcc/tcc - particularly when peripherals are being accessed. For details on ARM memory accesses please see How does the memory controller know whether the current access is aligned / non aligned word/ half word /byte?

All memory regions used by C must support the full range of access sizes for both reads and writes, i.e. byte, halfword, and words.

In some cases, a memory controller may not decode the size specifier (MAS or BSIZE signals) to certain memory regions (e.g. 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 will 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 may be the only access size permitted, due to the design of the peripheral. There are various problems when sub-word (e.g. byte) access is allowed to
peripherals:

- a peripheral must only be accessed once if it is read or write sensitive and the C source code describes one access (e.g. FIFO)

- multiple sub-word reads may read inconsistent data from changing register (e.g. reading a 16-bit timer value using 2 byte loads, the timer value might be incremented in the delay between the 2 reads)
- multiple sub-word writes will not update all the bits of the register at the same time (e.g. updating a control register using 2 byte stores)

For these reasons, ARM recommends that peripherals are accessed using a single full-width (word) access. Note peripheral accesses should always be specified as 'volatile' to ensure the compiler does not optimize away the access. See the FAQ:  armcc/tcc: 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 intshortchar

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/STR, LDRH/STRH, LDRB/STRB on Architecture 4. 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.

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 more details, see Accessing unaligned data from C

2) 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 structs

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

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

more than 1 access
byte, halfword, or word accesses
access to nearby locations

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 examples:

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 field is accessed is not defined by the ANSI C standard. One way for C 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.

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

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 will probably 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 type (e.g. 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);





back to top

*
**
*4 dots*Other ARM Websites
*
shadow *LEGAL STATEMENTshadow