One data type of the same size as another, savs flash size

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

I have read some great info about proper data types, when to use and what to use. More specifically how to keep the over all flash size down. But there is one thing that remains, that I dont understand. This is why unsigned char, char, and uint8_t are not the same size?

I know that uchar just counts from 0 and up ( 8 bytes total) where char includes negative numbers but still 8 bytes total, same goes for u ints8 and int8. but I have seen cases where using a uchar save flash size and via vera.

also, I have seen in some cases substituting an int for a char save flash size? I just don see how that is possible unless there is some truncation that I cant find . An int to a char, I get but char to int, saving flash size?

And this one really gets me. Replacing chars with uint8_t, and visa verse will gain flash size room. Are the universal data types better in some cases?

I know this all has to do with how its used, but I was wondering if there was a global answer to this strangeness.

If needed I can demonstrate with some example code. I'm going to venture to say its just some understand I need to acquire.

one quick example:
If do this
uint8_t CLOCK_BIT, LATCH_BIT, DATA_BIT, DATA_PIN;
my flash size is at 100.0

but changing to
char _t CLOCK_BIT, LATCH_BIT, DATA_BIT, DATA_PIN;
I'm now at 101.2

CLOCK_BIT, LATCH_BIT, DATA_BIT, DATA_PIN are set in my init and used throughout as static data, mainly for if statements.

So why does the char use so much more?

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

for signed vs unsigned it gets smaller because checking for sign is not required. Also as in any calculation the char will get promoted to int for the calculation, so for unsigned there is no sigh extension checks required.

By using int instead of char, you remove the need for adding code to silently promote, thus saving space.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Quote:
for signed vs unsigned it gets smaller because checking for sign is not required.
This does make sense,. when working with avr code, most of the time uchar is best. You generally wont need a negative number.. So I figure since There is no code I wrote that needs a total of 128, why not change all of my chars to u-chars. And doing this made the flash larger, so I started going one by one.

I took this.

static void readController( unsigned char bits[2])
{
	bits[0] = PINC;
	bits[1] = PINB;
}

I get 100.1 flash size but making it a char gives me 100 flash size. This is where I get confused. What makes that so?

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

off hand I can't think of why, I'd have to see the generated code to see what changed. Also your example is too trivial to really prove anything. Not to mention the change could be a result of teh rest of your test case code, as what you have presented here is not a complete program.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

S_K_U_N_X wrote:
I get 100.1 flash size ...
What do you mean by that? One hundred bytes and one tenth? One thousand and one bytes?

Stefan Ernst

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

Quote:

What do you mean by that?

Percent, probably.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Another issue is that overflow in signed type is undefined so (int8_t)400 > 300 may be true, whilst (uint8_t)400 > 300 is definitely false.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
static void readController( unsigned char bits[2])
{
   bits[0] = PINC;
   bits[1] = PINB;
}

I know that you were using a Mega168. So I would be very surprised if you are overfilling 16kB of flash.

If you are using some library or foreign code, you should be careful with fiddling with variable types.

David.

Whereas I normally reckon that readability is far more important than overall program size, this example is asking for trouble.

Look at the generated code. Passing a pointer to a function, and then doing such a trivial operation will probably take a lot of code and time.

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

JohanEkdahl wrote:
Percent, probably.
Yes, makes some sense now. ;-)

Stefan Ernst

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

david.prentice wrote:

static void readController( unsigned char bits[2])
{
   bits[0] = PINC;
   bits[1] = PINB;
}

Look at the generated code. Passing a pointer to a function, and then doing such a trivial operation will probably take a lot of code and time.

Curious... that looks like a very good candidate to inline the function. Seems much more effort to copy registers to the stack, get the pointer into the register, offset, place the PINC in there and then return, pulling registers back off the stack.

Even including a copy every time wouldn't be as much flash as the register shuffling... would it? And even if it was worse, can't the compiler ignore the inline recommendation?

-- Damien

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

Quote:

so I started going one by one

Some objection to -funsigned-char then? (of course this relies on you having originally used just "char var;" and not specifically "signed char var;").

[both Studio's auto-makefile and the Mfile template both specify -funsigned-char by default anyway so I wonder how you managed to avoid it?]

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

Quote:
I know that you were using a Mega168. So I would be very surprised if you are overfilling 16kB of flash.
No, I;m out of flash, but I do have 15 C files in this project and usb code on that. I dont think I will need to up my 168 this Dream Cast is my last bit of code. It will fit damn it. ;)

Curious... that looks like a very good candidate to inline the function

Yeah, you know I do inlines where I can. In this case it is only called twice so it was not much help but normally its a big savings.. but sometimes even that does not help and I dont know why.

Example:

char spi_mSend ( char podatek)
{
	/* Start transmission */
	SPDR = podatek;
	while(!(SPSR & (1<<SPIF)));
	return SPDR;
}

This code is called 50 times In a section of code. If I inline it I get 0 savings.
Now correct me if I'm wrong, but inlining a func is only best when its called frequently, no?

Quote:
Look at the generated code. Passing a pointer to a function, and then doing such a trivial operation will probably take a lot of code and time.
So you would suggest a pass and return? This bit of code was exampled from. To be honest I never new why it just didnt pass in the info and then return it. I may try ti to see what I save.

Quote:
Some objection to -funsigned-char then? (of course this relies on you having originally used just "char var;" and not specifically "signed char var;").
Yes I never use signed char, so this option auto types it for me?

Last Edited: Wed. Aug 5, 2009 - 12:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now correct me if I'm wrong, but inlining a func is only best when its called frequently, no?

Inlining saves both the code space and the time of CALL and RET. If you are very tight on code space I would try as much inlining as you can but not something called multiple times. Don't forget to make the functions 'static' in the file that they appear because otherwise both the inlined copy and externally callable copy of the same thing will be generated.

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

Thx Clawson,

I would normally do this.

static inline char spi_mSend ( char podatek) __attribute__((always_inline));
static inline char spi_mSend ( char podatek)	
{

	/* Start transmission */
	SPDR = podatek;
	while(!(SPSR & (1<<SPIF)));
	return SPDR;
}

At least that is what I read works best.

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

S_K_U_N_X wrote:

This code is called 50 times In a section of code. If I inline it I get 0 savings.
Now correct me if I'm wrong, but inlining a func is only best when its called frequently, no?

Inlining has it's benefits and drawbacks. It works by "copying and pasting" code to where it's called, much in the same way a #define macro works**.

The compiler may at any time choose to ignore your inline advisory.

The best explanation is at the C++ FAQ: see http://www.parashift.com/c++-faq...

Because there's no such thing as cache misses on an AVR, an inline function will almost always result in a speed increase because there is no longer the overhead of arranging the variables in registers (or on the stack), or returning from the function at the end.

The cost is that most of the time, it will result in larger code (more flash) because all the instructions are copy-and-pasted.

However, if the function is short, the overhead in calling the function may be greater than the function itself, hence it can actually make the code smaller by inlining certain functions.

The only sure-fire way to tell is compiling with and without inline :)

-- Damien.

** Except it words syntactically like a function, unlike a macro, which does an expansion and can sometimes be dangerous.

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

Quote:
The only sure-fire way to tell is compiling with and without inline
thx Damien, so far that is what I find is best as well. But never hurts to try to learn, well most of the time.

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

What are your compile options? We might be able to suggest some switches to reduce the code size, like linker relaxations.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Hi Dean, I did some testing with them as well. Here is what I found works best see comments.

CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS)
CFLAGS += -O$(OPT)

CFLAGS += -Wall

#force smaller size!
CFLAGS += --param inline-call-cost=2 
CFLAGS += -fno-inline-small-functions
# causes issues ---    CFLAGS += -fwhole-program --combine



CFLAGS += -Wstrict-prototypes
#CFLAGS += -mshort-calls
#CFLAGS += -fno-unit-at-a-time
#CFLAGS += -Wundef
#CFLAGS += -Wunreachable-code
#CFLAGS += -Wsign-compare
CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X,

Throw in some garbage collecting for any unused functions and some linker relaxations to convert large CALLs into RCALLs where possible:

CFLAGS += -ffunction-sections

LDFLAGS += -Wl,--relax 
LDFLAGS += -Wl,--gc-sections

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I added them, but noticed my make file does not include any LDFLAGS's at all? I guess I would need to set up my make file to use them , how are they used? Or what is ld? maybe its just names different.

Also can anyone off en explanation for static data? If I Predefine data with static I get small flash size. Same with functions. As far as I know static just means to define it once. but in the case of functions, yeah
its going to be defined once.. But in either case why is static helping? Is it just that it tells the compiler , you only need to declare this variable once? And woudl all functions reach a gain if done statically?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
static type a_function(type var) {
}

Defines a function that is only visible within the single compilation unit (.c file). The compiler may choose to inline it and save the register setup and CALL/RET overhead

static a_var;

Outside a function body this defines a variable that is only visible within the compilation unit.

Within a function it creates a "permanent" variable (held in .bss) that retains its value from one invocation of the function to the next. In other words it prevents the variable being dynmically created (and destroyed) on the stack frame.

Cliff

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

thx cliff, that is what I was looking for.

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

Get a copy of K&R - all is explained there:

http://en.wikipedia.org/wiki/The...(book)

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

Ok one more, and coincidentally not related to my attempt to shrink the hex, but rather unexpectedly being disappointing.

NOP's?? I have a section of code that I must use AMS to processes a very tight waveForm. I need to skip on the order of 100's of nops to move about within the waveFrom, I just found out every nop increases the size. Yet using a "_delay_us(150);" ( and I can't in my ASM case ) does not.

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

_delay_us uses a loop. Look in util/delay_basic.h to see _delay_loop_1 and _delay_loop_2 (which is what _delay_us and _delay_ms use).

Regards,
Steve A.

The Board helps those that help themselves.

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

S_K_U_N_X,

Do you remember this post I made in another thread you started:

======================================================
Tell me how many cycles are used in each of the folllowing:

nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop $ nop
 ldi r24,31 
loop: 
 dec r24 
 brne loop

====================================================
The first of those occupies 92 words (184 bytes). The second occupies 3 words (6 bytes).

Seems to be yet another argument for coding a loop rather than flooding flash with NOPs

Cliff

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

Yes I would agree.