Split from: Data type sizes

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the default data type in AVR GCC? I mean is if I need one byte unsigned value and memory is not a constraint, uint16_t is better or uint8_t is better? Any performance difference between these two other than memory? Also does it matter between RAM and flash? Flash is 16 bit wide and hence I guess it does not matter if it is 16 bit or 8 bit. But in case of RAM, I guess 16bit may be accessed as two 8 bit values right? 

Last Edited: Tue. Oct 4, 2016 - 01:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surely that depends on what you want to store?

 

If you want to store chars ('a', 'R', '!' etc) you use char

If you want to store -128..+127 you use int8_t

If you want to store 0..255 you use uint8_t

If you want to store -32768..+32767 you use int16_t

If you want to store 0..65535 you use uint16_t

If you want to store -2,147,483,648..+2,147,483,647 you use int32_t

If you want to store 0..4,294,967,295 you use uint32_t

If you want to store (about) -1.0e38  .. +1.0e+38 you use float

 

On the whole (because it is an 8 bit micro so does not need data aligned on 2 or 4 byte memory boundaries - like 16bit or 32 bit micros do) you should always pick the smallest type possible to hold the range of numbers you want to represent (in the above ranges). If you start using int16_t (say) when you are only holding 0..9 or something then you are going to be loading/storing to memory an upper byte that is always 0 making the access code slower and larger.

 

PC programmers have a rather nasty habit of just throwing int at everything as a kind of "one size fits all" variable type. Don't be lulled into this laziness.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And, as a matter of readability, even though the char types will store the same numeric range as the int8_t types, PLEASE use char for characters and ints for numbers! I am porting some code, right now, where the author uses char for anything representable in 8 bits. Its a pain!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PLEASE use char for characters and ints for numbers!

It's a particularly good habit to get into now that "char" is frequently bigger than 8 bits (not so much in embedded C, but in other "similar" languages that deal by default with multinational character sets.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock wrote:

What is the default data type in AVR GCC? I mean is if I need one byte unsigned value and memory is not a constraint, uint16_t is better or uint8_t is better? Any performance difference between these two other than memory? Also does it matter between RAM and flash? Flash is 16 bit wide and hence I guess it does not matter if it is 16 bit or 8 bit. But in case of RAM, I guess 16bit may be accessed as two 8 bit values right? 

 

clawson already said this in #8, but I just want to emphasize that the AVR is an 8 bit MCU, so almost in any situation there is a penalty for using variables of larger sizes (namely int). This is not just a memory issue, I'm talking clock cycles and code size. Always use uint8_t when possible, this will generate smaller and faster code. For example, if some loop is executed let's say 20 times, do not declare the counter as an int, use uint8_t. Yes, the AVRs have a few 16 bit instructions like adiw and movw to help mitigate the penalty, but it's always there.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Reference:  https://gcc.gnu.org/wiki/avr-gcc  (Google "avr-gcc data types")

 

there's some philosophy somewhere in the older C language definitions that says something like "an 'int' should be the size of the native integer on the target cpu", but the original C CPU was 16bits, and attempts to make C compilers on 8bit CPUs with
"int" being only 8bits have been ... not very successful.   avr-gcc does seem to have a "-mint8" switch to cause "int" to be only 8bits, but I don't think I'd recommend using it.  (Far better to use the explicitly sized types like int8_t from the newer C standards.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lint may flag some of those.

Visual Studio and Xcode have some static source code analysis that might aid finding those.


http://www.gimpel-online.com/OnlineTesting.html (PC-lint, MISRA C 2012 Example (C), "// an essential type violation")

https://msdn.microsoft.com/en-us/library/ms182025.aspx (Visual Studio IDE, Analyzing C/C++ Code Quality by Using Code Analysis)

https://developer.apple.com/xcode/features/ ("Static Analysis")

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Tue. Oct 4, 2016 - 03:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An int is supposed to be the most natural 16+ bit integer available on the machine.

On a 5-bit machine, expect 20-, 30- or 35-bit ints.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
PC programmers have a rather nasty habit of just throwing int at everything

Actually, in general, that is a very good habit - because (as already noted) the 'C' standard specifies that int should have the "natural" word size of the underlying machine.

 

But 8-bit micros are an exception to this rule - because the 'C' standard also specifies that int must have at least 16 bits.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Oct 4, 2016 - 07:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apart from the exact-width types (int8_t, et al) already mentioned, note that the standard also defines a whole load of other types with "well-known" properties; eg,

 

 

  • Minimum-width integer types;
  • Fastest minimum-width integer types;

  • Integers wide enough to hold pointers.

 https://en.wikibooks.org/wiki/C_...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
Actually, in general, that is a very good habit -
No it is not. For code portability stdint.h types should be used where possible. short/int/long have almost no place in well structured code. Also there's a tendency for lazy programmers to use int when they really mean unsigned int if they are sticking to the base types.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PC programmers have a rather nasty habit of just throwing int at everything as a kind of "one size fits all" variable type

I would also blame text books for this. Most text books use int i, int j etc and in fact even some programmers still use i,j,k because it probably became vocabulary of C programmers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What exactly is the problem with using int for a variable whose range is 0..12?

I see where there *might* be efficiency issues on a machine that does not do 16+ bit arithmetic,

but alternatives have their own problems.

Suppose one might want to port to a machine with a 9- or 10-bit byte.

Such things existed at one time.  Perhaps they still do.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock wrote:
some programmers still use i,j,k because it probably became vocabulary of C programmers.
Way back when, Fortran used i, j, k, l, m, and n as default integer variables, while x, y and z were floating point. Some think "i" was a short hand for "index"; however, math had used these much earlier than Fortran.

Consider a simple series summation (courtesy Wikipedia):

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Even in AVR GCC, uint8_t is promoted to int during arithmetic operations I guess. I had trouble with my test code for implementing millis() functionality using 8 bit only.

Below code (not complete code) was not working as expected, but it worked after I used diff in place of (LEDCountCurr-LEDCountPrev) inside if statement for comparison.

 

 volatile uint8_t oneMsecCounter=0;

#define millis() (oneMsecCounter)

 ISR(TIMER0_COMPA_vect)
{
	oneMsecCounter++;

}

void initializeTimer0ForOneMilliSec (void)
{

	// In case 16 Mhz crystal is connected, each clock pulse = 1/16 = 0.0625 usec.
	// If we use prescaler of 64, each clock pulse = 0.0625*64 = 4 usec.
	//So we can get 1 msec, we need couner of 1000/4 = 250

	OCR0A = 250-1;          // Set for 1 msec.

	TIMSK0 |= 1<<(OCIE0A);  // Output Compare Interrupt Enable for TimerCounter0 Compare Match A
	TCCR0A |= 1<<(WGM01);  // Mode = CTC: WGM01=1, WGM00=0 in TCCR0A and  WGM02=0 in TCCR0B

	// Timer is activated as soon as the clock source is selected
	TCCR0B = (1<<CS01)|(1<<CS00);   // Timer Prescaler Clock/64, TCCR0B: CS00 - 1, CS01 - 1, CS02 - 0
}

int main(void)
{
    pinMode(LED_SCK,OUTPUT);
    initializeTimer0ForOneMilliSec();

    while(1)
    {
        static uint8_t LEDCountPrev=0;
        static uint8_t LEDCountCurr=0;
        static uint8_t currentLEDState=LOW;
        LEDCountCurr=millis();

        uint8_t diff=LEDCountCurr-LEDCountPrev;

        if((currentLEDState==HIGH)&&(LEDCountCurr-LEDCountPrev>=254))
        {
            currentLEDState=LOW;
            digitalWrite(LED_SCK,LOW);
            LEDCountPrev=LEDCountCurr;
        }
        else if((currentLEDState==LOW)&&(diff>=125))
        {
            digitalWrite(LED_SCK,HIGH);
            LEDCountPrev=LEDCountCurr;
            currentLEDState=HIGH;
        }
    }
}

Problem started when overflow occured and LEDCountPrev was less than LEDCountCurr. Since variables are promoted to int, I guess (LEDCountCurr-LEDCountPrev) leads to a negative number and comparison fails. But since I split the steps into two by calculating diff separately, diff was reporting correct value even if  LEDCountPrev is less than LEDCountCurr.

So internally, even if we make use of uint8, operation happens with 16 bit signed data type? Is it not a burden on 8 bit micro?

Last Edited: Sat. Dec 30, 2017 - 05:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joneewheelock wrote:
Is it not a burden on 8 bit micro?

Yes.

 

This is why, for example, Keil C51 has an option to disable the standard "ANSI" integer promotions: http://www.keil.com/support/man/...

 

But doing so, of course, makes the compiler non-conforming to the standards ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Suggest that you get yourself a good book (or internet article) about C, it will explain the rules of promotion. This isn't something specific to AVR, it applies to C anywhere and a C programmer needs to understand it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am sorry if I confused you in my last post. My question was not mainly related to promotion (but definitely it is also related). Since uint8_t is promoted to int16_t, internally 16 bit operation is happening and @awneil gave answer regarding that. My question was again related to original post "default data type in AVR GCC" and this was the confusion I had. But I could not ask this question in a correct way and after I got this bug I realized the actual problem. 

 

So even if we declare variables as uint8_t, internally arithmetic operations will happen in int16_t, meaning default data type even in AVR GCC is int16_t bit. 

 

Now coming to performance Issue:

Since internally promotions happen, performance wise is it faster if we use int16_t is what my fundamental doubt (assuming memory is not a constraint) . How much overhead will be there internally in promoting and again in demoting? Or no additional overhead at all? According C standards, all bitwise operations on characters promote them to int. In fact in embedded, we have lot of bitwise operations.

 

In the below code, avr -size returns me 138 bytes even if I use uint16_t and use values greater than 255 for two variables. I have optimization -os set. Why this size is not getting changed when change to 16 bit? So how/where can I see the saving? Is performance not directly proportional to code size?  

int main()
{
    uint8_t a;
    uint8_t startTime=7;
    uint8_t endTime=5;
    if((endTime-startTime)>=200)
    {
        a=1;
    }
    else
    {
        a=0;
    }
    return 0;
}

Extract from Log:

avr-gcc.exe -Os -Wextra -Wall -mmcu=atmega328p -std=gnu99 -fshort-enums -ffunction-sections -fdata-sections -DF_CPU=16000000UL -g -Os -Wmain -Wextra -Wall  -c main.c -o Debug\main.o

 

avr-g++.exe  -o Debug\AVR_Project1.elf Debug\main.o  -mmcu=atmega328p -Wl,-Map=Debug\AVR_Project1.map -Wl,--gc-sections  

Program:     138 bytes (0.4% Full)
(.text + .data + .bootloader)
Data:          0 bytes (0.0% Full)
(.data + .bss + .noinit)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To be clear, at almost any level of optimization, unless bit-fields or volatiles are involved

adding two items to get a byte-sized item is likely to be done with a single ADD instruction.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

None of this need remain a mystery. Just write some test code (using globals so they aren't optimized away) then study the LSS. A u8 = u8 op u8 should all be done using 8 bit arithmetic.