STM32 L1 Tutorial #2: Boost your code (Bit-Banding)

stm32_icon_bigToday I’m going to show you how to speed things up in your code by using marvelous feature called “Bit-Banding”. Basically bit-banding is a new way of addressing (and accessing) things in some parts of your Cortex-M3 uC address space. Well, you would probably ask if there is any room for any useful improvement in such simple and straightforward thing as addressing. Actually, there is.

Remember those moments when you tried to access and change value of separate bits in registers? It always involved three operations: read-update-write. Read operation was just to copy full register value to temporary variable, update was either ‘logical AND’ or ‘logical OR’ to clear or set bit of your interest respectively, and finally a write to update register’s value. As one can see, lots of computing power is involved in such simple thing as bit’s value altering. Bit-banding removes all that overhead by giving you the possibility to access register bits as separate memory locations. Neat!

Accessing bits as they were words!

To be able to access register bits with bit-banding programmer must locate that bit’s memory location. According to STM32L Reference Manual mapping is done by this formula:

bit_word_addr = bit_band_base + (byte_offset * 32) + (bit_number * 4)

where:

  • bit_band_base – a constant that indicates which memory area you are trying to access. For SRAM bits use 0x2200 0000, for peripheral registers use 0x4200 0000.
  • byte_offset – this is a byte offset of register that you are trying to access. Offset is derived from actual address minus starting address of its memory section. Example 1: byte_offset of SRAM (SRAM starts at 0x2000 0000) variable located at 0x2000 0100 is 0x2000 0100 – 0x2000 0000 = 0x0000 0100. Example 2: byte_offset of GPIOA->ODR (address: 0x4002 0014) register is 0x0002 0014, since peripheral memory section starts at 0x4000 0000.
  • bit_number – just the number of bit you are trying to map. Plain and simple.

Accessing bits as they were words! With my lib!

Using bit-banding wouldn’t be that much fun if you had to use and compute above’s formula value all the time, but hey, that’s what libraries are for. Mine does it all for you, with simple macros.

Last time (Tutorial #1) we were experimenting with GPIO pin that was used to drive LED. Let’s see how it could be done with bit-banding.

/* system entry point */
int main(void)
{
	/* gpio init struct */
	gpio_init_t gpio;

	/* reset rcc */
	RCC_DeInit();

	/* enable clock to GPIOC */
	RCC_AHBPeriphClockCmd(AHB_GPIOC, ENABLE);

	/* initialize gpio structure */
	GPIO_StructInit(&gpio);
	/* use pin 13 */
	gpio.pins = GPIO_P13;
	/* mode: output */
	gpio.mode = GPIO_OUTPUT;
	/* output type: push-pull */
	gpio.otype = GPIO_OT_PP;
	/* apply configuration */
	GPIO_Init(GPIOC, &gpio);

	/* bit-band definition */
	bitband_t pin = BITBAND_PERIPH(&GPIOC->ODR, 13);

	/* main program loop */
	for (;;) {
		/* set led on */
		*(pin) = 1;
		/* clear led */
		*(pin) = 0;
	}

	/* never reached */
	return 0;
}

Here (line #25) we have defined variable of type bitband_t which now represents bit 13 in GPIOC ODR (Output Data Register). bitband_t is a pointer to mapped memory location, so writing/reading values is done in a ‘pointer-ish’ way, with use of ‘*’ operator.  Simple as that. Using bit-banding with SRAM bits is very similar:

	uint32_t sram_flags;
	/* bit-band definition */
	bitband_t sram_flags_bit_0 = BITBAND_SRAM(&sram_flags, 0);
	/* change value */
	for (;;) {
		*(sram_flags_bit_0) = 1;
		*(sram_flags_bit_0) = 0;
	}

Pros and Cons.

As it was mentioned before bit-banding speeds things up. By what factor? Well, it will probably depend on whole context, but one can easily find many different use-cases that are perfectly tailored for this technique, such as:

  • Clearing Interrupt status bits – saves you some cycles during interrupt routine execution
  • Fast GPIO toggling – pretty much explains itself, useful when implementing bit-banging (with ‘G’ :) ) interfaces
  • Preemption safe bit value changing – no read-update-write, just write, good for dealing with data that is processed by interrupt routines and main program loop simultaneously.

In above’s example frequency of LED toggling with bit-banding as opposed to GPIO_SetPin(), GPIO_ClearPin() increased by over 170% (was 96kHz, is 262kHz, using Os level of GCC optimization, 1MHz MSI clock (default one)). Definitely worth the effort!

There are some situations that make the use of bit-banding not practical. Those include:

  • Complex Initialization – things done only once per device’s power cycle, like GPIO inits. Using bit-banding there makes hard code hard to read and debug.
  • Updating multiple bit fields in registers – many of those exist, updating those bits one-by-one may lead to unwanted (and, in some cases, even unpredictable) device’s behavior.

Limitations

Two that I am aware of. Bit-banding is available only for SRAM and Peripheral section, so no funny business operations in FLASH. Bit-banding  accesses are only legit if done by Cortex-M3 core itself. What that means is, that you cannot use DMA transfers to bit-band memory locations.

Files

Posted in Uncategorized and tagged , .