ARM VERSION 1.2 Guide de l'utilisateur Page 99

  • Télécharger
  • Ajouter à mon manuel
  • Imprimer
  • Page
    / 133
  • Table des matières
  • DEPANNAGE
  • MARQUE LIVRES
  • Noté. / 5. Basé sur avis des utilisateurs
Vue de la page 98
6.9 Using scatter loading with memory-mapped I/O
In most ARM embedded systems, peripherals are located at specific addresses in memory. You often need to
access a memory-mapped register in a peripheral by using a C variable. In your code, you will need to consider not
only the size and address of the register, but also its alignment in memory.
ARM recommends word alignment of peripheral registers even if they are 16-bit or 8-bit peripherals. In a little-endian
system, the peripheral databus can connect directly to the least significant bits of the ARM databus and there is no
need to multiplex (or duplicate) the peripheral databus onto high bits of the ARM databus. In a big-endian system,
the peripheral databus can connect directly to the most significant bits of the ARM databus and there is no need to
multiplex (or duplicate) the peripheral databus onto low bits of the ARM databus.
The AMBA™ APB bridge uses this technique to simplify the bridge design. The result is that only word-aligned
addresses should be used (whether byte, halfword, or word transfer), and a read will read garbage on any bits that
are not connected to the peripheral.
6.9.1 Using pointers to access I/O
The simplest way to implement memory-mapped variables is to use pointers to fixed addresses. If the memory is
changeable by external factors, for example by some hardware, it must be labelled as volatile. For example:
volatile unsigned *port = (unsigned int *) 0x40000000;
The data on the port can be accessed by:
*port = value; /* write to port */
value = *port; /* read from port */
The use of volatile ensures that the compiler always carries out the memory accesses, rather than optimizing
them out. If the access was in a loop and the variable was not volatile, only one read of the memory address
would be done.
This approach can be used to access 8-bit, 16-bit, or 32-bit registers, but you must declare the variable with the
appropriate type for its size, int for 32-bit registers, short for 16-bit, and char for 8-bit. This ensures that the
compiler generates the correct single load/store instructions, LDR/STR, LDRH/STRH, or LDRB/STRB.
You must also ensure that the memory-mapped registers lie on appropriate address boundaries. Alignment must be
either all word-aligned or on their natural size boundaries. The natural size of 16-bit registers is on half-word
addresses. ARM recommends that all registers, whatever their size, be aligned on word boundaries, see Using
arrays or structs.
You can use #define to simplify your code. For example, the source code in Example 6-11 produces the
interleaved code in Example 6-12.
Example 6-11
#define PORTBASE 0x40000000 /* Counter/Timer Base */
#define PortLoad ((volatile unsigned int *) PORTBASE) /* 32 bits */
#define PortValue ((volatile unsigned short *)(PORTBASE + 0x04)) /* 16 bits */
#define PortClear ((volatile unsigned char *)(PORTBASE + 0x08)) /* 8 bits */
void init_regs(void)
{
unsigned int int_val;
unsigned short short_val;
unsigned char char_val;
*PortLoad = (unsigned int) 0xF00FF00F;
int_val = *PortLoad;
*PortValue = (unsigned short) 0x0000;
short_val = *PortValue;
*PortClear = (unsigned char) 0x1F;
char_val = *PortClear;
}
Example 6-12 Output fragment from compiler using -S and -fs
AREA ||.text||, CODE, READONLY
init_regs PROC
;;;7 {
;;;8 unsigned int int_val;
;;;9 unsigned short short_val;
;;;10 unsigned char char_val;
;;;11 *PortLoad = (unsigned int) 0xF00FF00F;
000000 e59f1024 LDR r1,|L1.44|
Writing Code for ROM
Copyright ?1999 2001 ARM Limited 6-24
Vue de la page 98
1 2 ... 94 95 96 97 98 99 100 101 102 103 104 ... 132 133

Commentaires sur ces manuels

Pas de commentaire