ArticlesEmbedded C & MCU

Bit-banding Explained: A Key Feature of ARM Cortex-M3/M4

Introduction

If you have ever taken a part of developing a firmware for a complex device, then you should have seen or used bit-field variables. In my previous job, I was in the development team of a complex tracking device for vehicles, where the firmware was written from the ground-up using bare metal C. The firmware was responsible for controlling the communication between the GSM, GPS, Zigbee/WiFi, RS-232 Serial, and the RTC module with the MCU, besides sampling analog data and controlling the device digital outputs. With this level of complexity and interleaved tasks, you’ll either use an off-the-shelf RTOS with scheduling between tasks feature, or use bare metal C, like my case here. You have to develop a flag-driven firmware, where most events in the device have a special 1-bit-long flag variable (true or false).

Now, if you allocate a dedicated integer variable for each flag, then you will end up with running out the free memory space. In this case bit-field variables can save a lot of space for you. Using this method, the same location in the memory is shared among more than one variable in the struct.

Another usage of the bit-field variables is implementing some protocol with a variable bit-long fields frame. In this case building a struct with bit-field variables also saves you a good amount of memory space. Please revise the article about struct and union in embedded C if you need to know more.

As any other solution in engineering world, it’s not always a win-win situation. Bit-fields save place in data memory, and they also provide a simplified way to set and get values (that aren’t byte-aligned) rather than using bitwise operations. On the other hand, this means that whenever you use a bit-field variable, the processor/compiler will perform READ-MODIFY-WRITE operations. As the memory location is shared with others, then the compiler will read the entire variable to store it in a temporary place, mask other fields, change the value and then restore the value of unchanged fields (this is what is called READ-MODIFY-WRITE operation).

So it’s true that we saved some space in the SRAM, but we will need more instructions for each reading/writing operation in the Flash memory.

Accessing a bit-field variable is another concern when using bit-field. It is not an atomic operation, which could lead to faults in some critical code sections. Especially for shared bit-field variables between interrupts and processes.

In a nutshell, using bit-field variables is more efficient for SRAM but less for flash and the speed of the code execution. To this end, the feature of bit-banding was introduced in ARM Cortex-M3 MCUs to enhance the process considerably. So let’s know more about it!

What Is Bit-Banding Feature?

Writing a portable code is one of the concerns for developers, and while dealing with bit-fields is not standard in all compilers, it is not very advisable to use.

When a feature is available in the hardware itself, you will not have any issues in porting the code from vendor to vendor while both are using the same ARM Cortex-M3 core.

ARM Cortex-M3 features a 1 MB area in SRAM memory called bit-band region. In this region each bit can be accessed individually. To access to bit-band region bits you need to do so via an aliased region, where each word in this region is an alias to one bit in the bit-band region.
ARM Cortex-M3 Memory Map: The bit-band region starts with 0x20000000 address and the alias starts with 0x22000000. Adapted from: Cortex-M3 Technical Reference Manual
To map each bit in bit-band region you need 1 word in the alias region. Apparently, the size of bit-band alias will be 32-MB.

As a condensed explanation, to set the value of a bit from bit-band region you need to change the value of the LSB in the alias address. However, you still can deal with variables in bit-band region as normal.

ARM Cortex-M3 Memory Map: The bit-band region starts with 0x20000000 address and the alias starts with 0x22000000. Adapted from: Cortex-M3 Technical Reference Manual
ARM Cortex-M3 Memory Map: The bit-band region starts with 0x20000000 address and the alias starts with 0x22000000. Adapted from: Cortex-M3 Technical Reference Manual

Access to this region is carried out via the system interface bus. An atomic READ-MODIFY-WRITE operation to the bit-band region will be executed when a data write access is done to the bit-band alias memory range, but this time all are done by the hardware and no extra code will be produced from the compiler.

Moreover, the same feature is available for another 1 MB region for the peripheral region.

Adapted from: Cortex-M3 Technical Reference Manual
Adapted from: Cortex-M3 Technical Reference Manual

What was in SRAM bit-band region applies here. To access the 1 MB bits you need to access them using the alias region. This is very useful to make an easier way to access and program  the control and status registers.

Cortex-M3 does not have special instructions for bit operations. We need to use direct access to the address in the bit-band region. We will see later how to do that using C code.

Adapted from: Cortex-M3 Technical Reference Manual
Adapted from: Cortex-M3 Technical Reference Manual

Advantages of Bit-Banding (Applications)

  1. Changing the value of register’s bits using atomic operation (the READ-MODIFY-WRITE operation cannot be interrupted by other activities).
  2. Make flags variables instead of packed bit fields in C (which is not advised for a portable code).
  3. Implement serial data transfers using GPIO ports to serial devices (AKA bit-banging).

Example 1

First, you need to know the following formula to calculate each bit (from bit-band region) alias address. This formula is adapted from Cortex-M3 technical reference manual:

bit_word_offset = (byte_offset x 32) + (bit_number × 4)
bit_word_addr = bit_band_base + bit_word_offset

Where:
Bit_word_offset is the position of the target bit in the bit-band memory region.
Bit_word_addr is the address of the word in the alias memory region that maps to
the targeted bit.
Bit_band_base is the starting address of the alias region.
Byte_offset is the number of the byte in the bit-band region that contains the targeted bit.
Bit_number is the bit position (0-7) of the targeted bit.

We will use bit-band feature to set a pin high if a button is pressed and set a pin low if it’s released.

I will use EFM32LG starter kit (EFM32LG STK3600) to program the ARM Cortex-M3 CPU.

Cortex-M3 Memory Map
Cortex-M3 Memory Map

In this application, we need to write a pin connected to an LED high/low and read another pin from GPIO connected with a push button.

I will add a set of defines to do calculations needed for bit-banding, let’s break them down:

PERI_BASE is the base address of bit-band region for peripherals.

BITBAND_PERI_BASE is the base address of bit-band alias region for peripherals.

This define is a C micro processor function to calculate the address of word alias of the bit from bit-band region. According to the previously mentioned formula:

bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)

These are the base addresses for the related registers to set the pin high/low from portE (one register to set the pin and one to clear it) and to read a pin value from the portB.

Note: I used C Preprocessor #if #else condition to switch between normal and bit-banding options.

Example 2

1- Let’s set the address 0x20000000 to a value of 0x00000AAC using direct access (without using bit-banding).

0x00000AAC
0x00000AAC

2- If we read the address 0x22000008 (The alias address of bit[2] in 0x20000000 word), the return value will be

3- Now let’s write 1 to 0x22000010.

4- The value of 0x20000000 word will become 0x00000ABC.

References and Read More

Yahya Tawil

Embedded Hardware Engineer interested in open hardware and was born in the same year as Linux. Yahya is the editor-in-chief of Atadiat and believes in the importance of sharing free, practical, spam-free and high quality written content with others. His experience with Embedded Systems includes developing firmware with bare-metal C and Arduino, designing PCB&schematic and content creation.

6 Comments

  1. If the ARM series had separate “ports” instead of memory mapped I/O…
    You could have instructions that only set a particular bit high / low, with the rest in a high impedance “don’t change” mode.
    Think of it as a variation on the 74245 (octal transceiver bus)

    Oh well – gotta make do with what we have, I suppose. Unless you want to emulate an ARM with an FPGA, of course.

  2. Hi Yehya,

    I am trying some stuff on a Tiva board and writing/reading some code in C and Assembly.
    If I understood correctly, some ports/addresses are natively mapped to a bit-band region ?
    Not every port/address can be used with bitband?
    lets say i want to use a peripheral at address 0x41000000 with bit-banding would that be possible?
    who specifies what is bit-banded and what is not?

    Thanks in advance,
    Alfred

    1. Hello Alfred

      According to the images from ARM Cortex-M3 Technical Reference Manual you can see that there is a 1-MB-long area in peripheral are starting from 0x40000000 and ends 0x40100000, so 0x41000000 is out of this range and accessed normally (not bit by bit).

      Generally, any peripheral address (register) within 0x40000000 – 0x40100000 has an alias address for each bit in it.

      You’re most welcomed 🙂

  3. Shouldn’t this line #define PB_b9 *((volatile unsigned int *)(BITBAND_PERI(PB_DIN_BASE,9))) be #define PB_b9 *((volatile unsigned int *)(BITBAND_PERI(PB_DIN_BASE,6))) … the number 9 is actually 6

    1. I don’t think so. This invokes a call for the macro-function to calculate the bit-band-address of the bit number 9 of port B. This should be explicitly the same of the bit order number.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.