## Introduction to Microcontrollers

Chapter 1: Beginnings

Chapter 2: Further Beginnings

Chapter 3: Hello World

Chapter 4: More On GPIO

Chapter 5: Interrupts

Chapter 6: More On Interrupts

Chapter 7: Timers

Chapter 8: Adding Some Real-World Hardware

Chapter 9: More Timers and Displays

Chapter 10: Buttons and Bouncing

Chapter 11: Button Matrix & Auto Repeating

Chapter 12: Driving WS2812 RGB LEDs

# Embedded Systems Blogs > fabien le mentec > [ C Programming Techniques: integer type optimization ]

fabien le mentec (contact)
My experience spans several domains, ranging from robotics, to industrial control monitoring and wireless networking. I am currently a software engineer at the European S...show full bio

Would you like to be notified by email when fabien le mentec publishes a new blog?

Pageviews: 3443

# [ C Programming Techniques: integer type optimization ]

Posted by fabien le mentec on May 22 2013 under Software Development | Optimization

I am currently working on a voltage controller running on a ATMEGA328P, ATMEL AVR 8 bits microcontroller. The controller logic is implemented in the main() routine and relies on a periodical timer whose frequency is fixed at application setup. Among other things, the timer ISR handler increments some per tick counters which are then used by the main routine to implement the voltage controller timing logic.

By looking at the code, one noticed that I use the uint8_t type for counters instead of unsigned int. He enumerated some potential issues involved in the context of this project, and I explained him the reasons and implications. I thought it may be a short but interesting topic to blog on.

There are actually more than one counter, and some additionnal related logic. But in this post, we can assume the ISR handler looks like this:


#include < stdint.h >
#include < avr/io.h >

/* current version: */ static volatile uint8_t counter = 0;
/* initial version: static volatile unsigned int counter = 0; */

ISR(TIMER1_COMPA_vect)
{
/* ... */

if (counter != TIMER_MS_TO_TICKS(100))
{
++counter;
}

/* ... */
}

Due to the timing logic and constraints (very interesting, but would take more time to explain ... maybe for another post), I came to the point I had to optimize the ISR code a bit, and looked for places to reduce the cycle count. This is the reason of using uint8_t instead of unsigned int: the AVR-GCC compiler integer type width is 16 bits by default for the target platform. As ATMEGA328P are 8 bits microcontrollers, one can assume that 8 bits arithmetics lead to a faster code. Lets compare the generated assembly code for the 2 versions:

/* uint8_t version */

#include < stdint.h >
#include < avr/interrupt.h >

static volatile uint8_t counter = 0;

ISR(TIMER1_COMPA_vect)
{
/* ... */

/* avr-gcc -mmcu=atmega328p -O2 */
lds r24,counter
cpi r24,lo8(100)
brne .L1
lds r24,counter
subi r24,lo8(-(1))
sts counter,r24
.L1:

/* ... */
}

/* unsigned int version */
#include < avr/interrupt.h >

static volatile unsigned int counter = 0;

ISR(TIMER1_COMPA_vect)
{
/* ... */

/* avr-gcc -mmcu=atmega328p -O2 */
lds r24,counter
lds r25,counter+1
cpi r24,100
cpc r25,__zero_reg__
brne .L4
lds r24,counter
lds r25,counter+1
sts counter+1,r25
sts counter,r24
.L4:

/* ... */
}



As expected, one can see that the instruction count is reduced. Plus, the ATMEL 8 bit AVR instruction set manual (www.atmel.com/images/doc0856.pdf) specifies that the adiw instruction requires 2 clock cycles to complete. Thus, the cycle count of the unsigned int version is twice the uint8_t one.

While it is not the important point, note that the variable volatility adds extra loads and stores that could be removed by using a non volatile local variable, and commit it at the end of the operation.

While changing a variable type changes a single line of code, it has several important implications.

First, it reduces the counter capacity. In this case, I had to make sure that the timing related logic still works when maximum values move from 0xffff to 0xff. Such points are easily missed, so it must be considered carefully. This situation is even worth when you come back on the code to add features long time after is has been written. Here, commenting helps a lot.

A second, less obvious implication, is that the optimization does not work on architectures where the arithmetic word size is not 8 bits. It would still work and compile, but 8 bits arithmetics may have the inverse effects, ie. add extract operations. To solve this issue, one can define a type whose width defaults to the actual architecture word size, as seen from the instruction set point of view:

#if defined(__AVR_ATmega328P__)
typedef uint8_t uint_word_t;
#else
typedef unsigned int uint_word_t;
#endif

EDIT: As pointed to here, the solution is to use the uint_fast8_t type. Thanks to the author of this post.

I am always interested in techniques to reduce cycle count in C codes, especially in ISR handlers. If you have any, please share.

4

posted by fabien le mentec
My experience spans several domains, ranging from robotics, to industrial control monitoring and wireless networking. I am currently a software engineer at the European Synchrotron Radiation Facility (ESRF), working on high performance data acquisition systems. I am also an open {source,hardware} and DIY enthusiast. My full resume can be found here: https://github.com/texane/resume/raw/master/output/resume_fabien_lementec.pdf

Previous post by fabien le mentec: Interfacing LINUX with microcontrollers
Next post by fabien le mentec: Introducing the VPCIe framework
all articles by fabien le mentec