The ARM Memory map

Definitions:

  • AHB-Advanced high performance bus.
  • APB-Advanced peripheral bus.
  • AMBA-Advanced micro-controller bus architecture.

The memory map is a way to access peripherals and other micro-controller features via a memory write or read. The memory map on 32 bit ARM micro-controllers is 4GB (2^32 unsigned) and contiguous.

The reason it is this large is to allow room for expansion and not limit the micro-controller vendors. Examples include adding extra peripherals to a chip revision. The above definitions define buses on the ARM micro-controllers. If the APB is set to 0.5GB, the vendor only has to worry about the APB space but can add numerous peripherals to this bus.

The image below shows the memory map obtained from the data-sheet of the STM32F446RE,note the memory map may be different for different micro-controllers:

mmap

The 4GB memory map is split into eight 512MB sections. To link the memory map to the physcal chip we need the functional block diagram. The image below is part of the block diagram, which shows how the buses are connected to the CPU core:

func

Top section of block diagram

func2

Bottom section of block diagram

The bus matrix is the core of the whole system that controls which master(Cortex core,DMA,USB OTG) controls the slaves(AHB,flash and SRAM).

bus_matrix

By definition from the datasheet:The 32-bit multi-AHB bus matrix interconnects all the masters (CPU, DMAs, USB HS) and the slaves Flash memory, RAM, QuadSPI, FMC, AHB and APB peripherals and ensures a seamless and efficient operation even when several high-speed peripherals work simultaneously.

We shall focus on the GPIO as an example of how the memory map works.

pinout

We would like to write to PB13 above maybe to turn on an led (Port B pin 13/ GPIO B pin 13). We thus look for GPIOB in the memory map

ahb

We can thus see GPIOB is addressable from 0x40020400-0x400207FF . That is great but how do we choose pin 13?

We thus turn from the datasheet to the technical reference manual(TRM).

gpio_registers

As from the above,each GPIO port has 10, 32 bit registers that just determine how the GPIO port works. Remember that this is on the STM32F446RE. It may be different on a different microcontroller even from the same vendor.

Now we turn to the GPIO register map:

gpio_map

We stop temporarily here and recall,

  • AHB1 location  starts at 0x4002 0000. We take this as our base  location(AHB1_BASE) as it is referenced from 0x0000 0000. This is absolute referencing.
  • GPIOB location starts at AHB1_BASE + 0x400, we shall call this GPIOB_BASE. This is relative referencing.

We can now reference GPIOB registers as offsets from GPIOB. Notice the Image above from the reference manual lists the registers as offsets so we can define GPIOB_MODER as GPIOB+0x0 as it is at offset 0.

CHECKING HOW THIS IS APPLIED IN A PRE-WRITTEN DRIVER LIBRARY

Notice that for different GPIOx, the offsets are the same from the base whether it is GPIOA or GPIOB. We can thus define a GPIO as a structure and reference it via a pointer. This is how it is defined in the STM32CUBEF4 drivers:

typedef struct
{
__IO uint32_t GPIOx_MODER;
__IO uint32_t GPIOx_OTYPER;
__IO uint32_t GPIOx_OSPEEDER;
__IO uint32_t GPIOx_IDR;
__IO uint32_t GPIOx_ODR;
__IO uint32_t GPIOx_BSRR;
__IO uint32_t GPIOx_LCKR;
__IO uint32_t GPIOx_AFRL;
} GPIO_TypeDef;

 

 

The __IO modifier is expanded as:

#ifdef __cplusplus
#define __I volatile
/*!<defines ’read only’ permissions */
#else
#define __I volatile const /*!<defines ’read only’ permissions */
#endif
#define __O volatile /*!<defines ’write only’ permissions */
#define __IO volatile /*!<defines ’read / write’ permissions */

Due to the nature of structures in ANSI C, the structure elements are placed in contiguous memory locations in memory. The structure elements must have the same type. Thus the structure is a perfect data structure for this application.

I usually use eclipse to program arm microcontrollers. A good feature is that it allows one to transparently see macro expansions.

eclipse

We thus check GPIOB macro expansion:

eclipse2

Peripheral base is defined as:

#define PERIPH_BASE ((uint32_t)0x40000000)

The peripheral base can be seen in the memory map below. All peripherals are on the right block.

mmap

AHB1 is offset at 0x20000 from the peripheral base

#define AHB1PERIPH_BASE (PERIPH_BASE + 0x20000)

Remembering that GPIOB is offset at 0x400 from AHB1PERIPH_BASE

 

#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400)

 

 

Finally to link the struct defining the GPIOx egisters and the definitions above:

#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)

 

 

GPIO B thus is macro expanded to:

  • GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
  • GPIOB ((GPIO_TypeDef *) (AHB1PERIPH_BASE + 0x0400))
  • GPIOB ((GPIO_TypeDef *) ((PERIPH_BASE + 0x20000) + 0x0400))
  • GPIOB ((GPIO_TypeDef *) ((((uint32_t)0x40000000) + 0x20000) + 0x0400))
  • GPIOB ((GPIO_TypeDef *) ((0x40000000 + 0x20000) + 0x0400))

 

The U suffix after the memory locations such as 0x4000000U are to force the compiler to regard the literal as an unsigned integer. This makes the code more portable across compilers as the drivers are to be used with the customers preferred compiler which could be Keil from arm or the open source bare metal arm toolchain gcc-arm-none-eabi.

Pin 13 is defined as:

#define pin13 ((uint16_t)0x4000U)

 

To toggle a pin:

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->ODR ^= GPIO_Pin;
}

 

Understanding the convoluted world of ARM cortex-M documentation.

 

ARM processors are usually powerful and most likely you are reading this on a device with an ARM cortex application processor(Your phone).

Usually when one starts out programming ARM micro controllers , it gets very intimidating to start programming so I will give a few hints to the rules of thumb I use to wade through the multitude of documentation available. This applies to almost all Cortex M micro controllers across various vendors.

  1. Look through ARM micro controller vendor’s portfolio of chips such as those from NXP, STM32 etc . Base your decision on the peripherals the micro-controller offers versus its price.Peripherals include HDMI controllers, USB controllers, Infared controllers(IRDA) depending on the application.
  2. Look though the micro controller data sheet second. It is a relatively short manual usually of about 100-200 pages that talks of the physical properties of the chip including the peripherals. This should be enough to make you decide what chip you want.
  3. Next document to look at from the micro controller vendor is the errata which lists uncorrected errors in the data sheet revisions and also in the technical reference manual(TRM) below. It may also list changes on the micro controller itself including peripheral limitations. Reading this is a must for mass product development.
  4. Look for drivers, (they may be in C) that expose a common API by which to communicate with and control the hardware using the firmware you will write. Examples are the current STM32cube and the older standard peripheral library(spl) by ST Micro(Works only on STM devices) or the open source libopencm3 (Works across numerous ARM vendors)
  5. Setup tool chain, IDE and link with the drivers.
  6. Set up debug interface via GDB and openocd.
  7. After your mind is made up and you have set up a tool chain on your computer that can program the chosen chip, the next reference should be the technical reference manual that describes the register layout of each peripheral and the different modes allowed(~1000pages). Use this only for reference to check peripheral descriptions, operating modes or if you need a way to access a register using assembly if a C function isn’t available or it isn’t prudent to use C.
  8. Use the driver library documentation and reference if the documentation is in line with the technical reference manual.
  9. Program away!

Here is example documentation for the stm32f446RE:

Just  remember the tool chain and IDE may be the most painful to setup but you only have to do it once!