Atmel Studio 7 - Not optimizing away variables during simulation

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

I am running Atmel Studio 7 with pretty much default settings

 

Is there a way to turn off optimizing away variables when doing a simulation?  For example, suppose I have something like

 

uint8_t foo(uint8_t x)
{
    float y = x / 2.0;
    uint8_t z = (uint8_t) y;
    return z;
}

The compiler will optimize away y (and possibly z).  But I want to be able to see the y while stepping through the simulation, to see how the float form before it's converted to an int. 

 

Is there a way to do this?  I suspect some sort of optimization flag, but can't seem to find it.  Do I just turn off optimization entirely by setting it to O0? (it's currently set to -O1)

 

For bonus points, I deploy to the chip using avrdude, which I've set up as an external tool in AS7.  Works great.  Is there a way to have the build process set up so that it creates one version (unoptimized in this fashion) for simulation and one version (fully debugged) for deployment to the uC?  So that I don't have to change settings between simulation and deployment?

 

Thanks.

This topic has a solution.
Last Edited: Tue. Mar 13, 2018 - 08:54 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The word that means "this must exists however pointless it seems" in C is volatile. So try

uint8_t foo(uint8_t x)
{
    volatile float y = x / 2.0;
    volatile uint8_t = (uint8_t) y;
    return z;
}

BTW what is "z" here?? (Is that the missing name of the uint8_t ?)

 

EDIT: I wrote this a long time ago in a place far far away. Still relevant though...

 

https://www.avrfreaks.net/forum/...

Last Edited: Mon. Mar 12, 2018 - 02:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

The word that means "this must exists however pointless it seems" in C is volatile.

Ah! Yes, I knew that, but didn't think to use in this context.

Quote:
BTW what is "z" here?? (Is that the missing name of the uint8_t ?)

Oops. Yes . that's what it was supposed to be. Fixed.

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

jgalak wrote:
Is there a way to have the build process set up so that it creates one version (unoptimized in this fashion) for simulation and one version (fully debugged) for deployment to the uC? So that I don't have to change settings between simulation and deployment?
Missed that bit. You have two options really. Atmel already provide each project with "Debug" and "Release"builds. One truly dreadful way to make "Debug" the one that is debuggable is to change it's optimization setting from -O1 to -O0 which switches off the optimiser all together but be warned you will see some utterly terrible looking code if you do - nothing like the final code. A recently added option is to try -Og which is a special version of optimization aimed specifically at keeping code "debuggable". You could try that. Yet another way to do this is to recognize that in "Debug" the macro DEBUG is defined and in "Release" the macro NDEBUG is defined. So you could do something like:

#ifdef DEBUG
  #define DBG_VOLATILE volatile
#else
  #define DBG_VOLATILE
#endif

uint8_t foo(uint8_t x)
{
    DBG_VOLATILE float y = x / 2.0;
    DBG_VOLATILE uint8_t z = (uint8_t) y;
    return z;
}

This will only apply "volatile" when DEBUG is defined. I think this would be my preferred solution.

 

Of course the other approach is to learn to debug optimized code. Sure, the optimizer will not create y or z in that routine but it will probably keep y in R25:R24:R23:R22 and z in R21:R20 so learn to read the Asm and just look at where the variables are held. In fact:

C:\SysGCC\avr\bin>nano avr.c
ff

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

uint8_t foo(uint8_t x)
{
    float y = x / 2.0;
    uint8_t z = (uint8_t) y;
    return z;
}

int main(void)
{
    while (1)
    {
    }
}

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -g avr.c -o avr.elf

C:\SysGCC\avr\bin>avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 2a 00     jmp     0x54    ; 0x54 <__ctors_end>
   4:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
   8:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
   c:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  10:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  14:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  18:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  1c:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  20:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  24:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  28:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  2c:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  30:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  34:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  38:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  3c:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  40:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  44:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  48:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  4c:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>
  50:   0c 94 34 00     jmp     0x68    ; 0x68 <__bad_interrupt>

00000054 <__ctors_end>:
  54:   11 24           eor     r1, r1
  56:   1f be           out     0x3f, r1        ; 63
  58:   cf e5           ldi     r28, 0x5F       ; 95
  5a:   d4 e0           ldi     r29, 0x04       ; 4
  5c:   de bf           out     0x3e, r29       ; 62
  5e:   cd bf           out     0x3d, r28       ; 61
  60:   0e 94 46 00     call    0x8c    ; 0x8c <main>
  64:   0c 94 71 01     jmp     0x2e2   ; 0x2e2 <_exit>

00000068 <__bad_interrupt>:
  68:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>

0000006c <foo>:

uint8_t foo(uint8_t x)
{
    float y = x / 2.0;
    uint8_t z = (uint8_t) y;
    return z;
  6c:   68 2f           mov     r22, r24
  6e:   70 e0           ldi     r23, 0x00       ; 0
  70:   80 e0           ldi     r24, 0x00       ; 0
  72:   90 e0           ldi     r25, 0x00       ; 0
  74:   0e 94 78 00     call    0xf0    ; 0xf0 <__floatsisf>
  78:   20 e0           ldi     r18, 0x00       ; 0
  7a:   30 e0           ldi     r19, 0x00       ; 0
  7c:   40 e0           ldi     r20, 0x00       ; 0
  7e:   5f e3           ldi     r21, 0x3F       ; 63
  80:   0e 94 dc 00     call    0x1b8   ; 0x1b8 <__mulsf3>
  84:   0e 94 47 00     call    0x8e    ; 0x8e <__fixunssfsi>
  88:   86 2f           mov     r24, r22
}
  8a:   08 95           ret

I didn't quote the entire code (which includes a chunk of floating library) but this shows the code of "foo()". On entry ''x is in R25:R24 (that is dictated by the avr-gcc ABI) and because it's 8bit it'll only be R24 that is relevant. So it's first moved into a 4 register group (R25 down to R22) for the call to __floatsisf() which is clearly going to promote into to float. So on the return from that (again as dictated by the ABI) the result will be in IEEE754 format in R25..R22.

 

The 4 register group R21..R18 is loaded with 0x3F000000 which as you can see here:

 

https://www.h-schmidt.net/FloatC...

 

is 0.5 ... (/2.0 is obviously the same as * 0.5):

 

 

And once again, the result will come back in R25..R22 so the call at 84: will then convert IEEE754 format back to unsigned int and the lowest part of the result R23:R22 is MOV'd to R25:R24 for the RET (again conforming to the ABI). So just keep a watch on R25:R22 and you will see all this happening. Even use that website I just linked to convert IEEE754 to recognizable decimal.

 

(this latter approach is actually the one I personally would use).

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

 

This, along with the post below, was fantastically helpful.  I knew some, but not all of that, and seeing it all in one place made things much more clear.