Volatile for successful debug, then remove afterwards ???

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

I have an interesting question (to me at least) about using volatile to prevent variables being optimised away. This is for the purpose of successful debugging / single stepping, etc.

 

I don't use -O0 because my experience is that it actually corrupts code even though it allows successful debugging.

 

So assuming I write correct C code and use volatile to allow successful single stepping through the code (after compiling using -O1), if afterwards I removed all the volatile keywords to allow the code to be optimised, should the code run "effectively" the same once programmed into the AVR.

 

My code is normally small so the reduction in code speed will probably be negligible with my use of volatile on a few variables, but I am curious about the effects of removing volatile.

 

Keith

Last Edited: Sun. Nov 13, 2016 - 09:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The volatile keyword is not made for debugging.

That it disables a bunch of optimisations is just a happy side effect for when you are debugging.

 

Sometimes the volatile keyword is mandatory to get working code and if that's the case then removing it will introduce errors.

A classical example is when a global variable is shared between interrupts and normal code.

A controversial example is in a delay loop to keep the whole loop from being optimized to the dark side of the moon.

 

The code below could make it a bit easier to check for changes / speed between the debug and you release versions.

#ifdef DEBUG
#warning Debugging turned on
#define VOLATILE volatile
#else
#define VOLATILE
#endif

Another plus of the macro is that if your code is (nearly) finished you can find & replace all the "VOLATILE" references without touching the "volatile".

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Thanks very much Paul.

 

That's good to know. It does make some sense to me that removing volatile could make the code unworkable. I've just had a very good example where using local variables causes the compiled code to loop inside an if/else block. The code is then stuck forever in that block so I can't see how that could possibly work.

 

If I made those local variables volatile OR I declared them as global variable (but not volatile) the simulation worked good. I've since came across some chat that said global variables are placed in data memory whereas local variables are placed in registers.

 

Seems there's just as much to learn about compiling and optimisation as there is learning basic C coding.

 

Keith.

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

We generally want the compiler to generate the smallest/fastest code -so you need optimisations. The AVR being a RISC processor - you have to do the operations on registers. Thus you have a number of registers and the compiler tries to make good use of them. This creates a challenge for the debugger. With a bit of experience you can identify what registers the compiler is using.
Depending on what you're trying to observe, the debugger may not be the best choice. I've rarely needed to use a debugger on the AVR. For most of my life i have toggled bits and spat data out the serial port. For real time stuff this is frequently necessary as the debugger is useless.
The secret is to write correct code so you don't need to debug!

Last Edited: Mon. Nov 14, 2016 - 04:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Kartman,

 

I see some of the "ancient teachings" you mentioned.

 

I always thought I was moving away from that method, having the debugger, but yes I've recently experienced "non debugger debugging" with the C# which is written in what's called a plugin of my cnc software (effectively a dll) and it can't use Visual Studio for single stepping. Therefore I had to create "LEDs" and "digital read outs" on the PC screen for the plugin to write the values and "flags" to. That was the only method of debugging I had at hand.

 

Oh yes, and the logic analyser which is absolutely magic for viewing serial transmission.

 

Looks like I'll be heading a similar way with the AVR too.

 

Keith

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

with the newer GCC compilers the is a -g option that should optimize so it's still easy to debug.

 

but I don't know how to do it from studio7 because there the default optimizing flags don't have g

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

sparrow2 wrote:
but I don't know how to do it from studio7 because there the default optimizing flags don't have g
I don't think they've added it yet. The drop list for optimizing settings only as 1,2,3,s for now I think. You could try entering "-Og" in "miscellaneous" so it would be passed towards the end of the command line but given that Studio is already passing a -O earlier on the command I'm not sure which would "win".

 

But this whole thing seems like a storm in a tea-cup to me. In what practical example do you ever actually hit a circumstance that there is some variable that is so vital it must be watched but the compiler thinks is so insignificant it has been optimsed away anyway? Are you talking about some 'i' in a for() loop or something?

 

As you've already discovered there is always volatile. So if you did build:

for (int i=0; i<10; i++) {
    PORTB = i;
}

or something and then found you cannot watch "i" then just modify it to be:

for (volatile int i=0; i<10; i++) {
    PORTB = i;
}

(though in this case why would you? You simply monitor PORTB and you can see "i" there anyway). My preferred solution is actually this:

0000006c <main>:
#include <stdio.h>
#include <avr/io.h>


int main(void) {
    for (int i=0; i < 10; i++) {
  6c:	80 e0       	ldi	r24, 0x00	; 0
  6e:	90 e0       	ldi	r25, 0x00	; 0
        PORTB = i;
  70:	88 bb       	out	0x18, r24	; 24
#include <stdio.h>
#include <avr/io.h>


int main(void) {
    for (int i=0; i < 10; i++) {
  72:	01 96       	adiw	r24, 0x01	; 1
  74:	8a 30       	cpi	r24, 0x0A	; 10
  76:	91 05       	cpc	r25, r1
  78:	d9 f7       	brne	.-10     	; 0x70 <main+0x4>

In this "i" is clearly R25:R24 (that will teach me to use 8 bit variables!) so I'd just watch R25:R24 to observe "i".

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

The flag is not -g (that specifies the format and level of debugging info. The optimisation flag is  -Og.

 

https://gcc.gnu.org/gcc-4.8/changes.html

 

Oh! In -Os compiled code; single step can jump about to seemingly randomly ordered lines of code. -Og does help linearise the program flow but unfortunately doesn't help the debugger find auto / local variables in registers which still leaves one inspecting the disassembly and viewing register contents.

 

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

In another thread about this Morten seemed to confirm that if you build -gdwarf-4 and switch the debugger to avr-gdb you may have more success (until avr-gdb has some other "issue" ;-)

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

Kartman & Cliff, thanks.

 

Kartman, I now see what what you meant about using the registers for debugging. I just tried out Cliffs example and put his code in AS and tried to do a watch window for "i" and it came up with "unknown location", yet there it is counting up in a register.

 

Cliff, sorry if this all seems a nuisance but it does take a little getting our heads round all this when you're a beginner. I seems mainly forums where you get taught about all this. Hopefully one day this is all chickenfeed to me.

 

Keith

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

Kartman wrote:
The secret is to write correct code so you don't need to debug!

 

THAT is the bit the has me pulling my hair out.

 

There's times when I can't see what could possibly be wrong with my C code yet when I run it in the simulator it does things that appear completely wrong.

 

The last time I had code perpetually looping in a nested if else block, and thus if my input pin went high it would never get seen. This happened when I made variables local. When I changed them to global, the code ran properly in the simulator and worked exactly as I had written it.

 

Keith.

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

Artandsparks wrote:
This happened when I made variables local. When I changed them to global, the code ran properly in the simulator and worked exactly as I had written it.
Sounds like extremely poor design then.

 

About the only justification for anything to be global in AVR code is for the communication between main() and ISR()s.

 

Certainly if switching local/global "fixes" code it smacks of a serious name scope problem.

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

clawson wrote:

Sounds like extremely poor design then.

About the only justification for anything to be global in AVR code is for the communication between main() and ISR()s.

Certainly if switching local/global "fixes" code it smacks of a serious name scope problem.

 

I'm more than happy to be told I'm doing something very wrong so here's the code that "goes all wrong" when compiled with local variables and simulated. I've commented the place where the code gets locked in a perpetual loop inside the if block.

You can also see where I commented out the variables when they were global. The code worked perfectly then.

 

Of course making the local variable volatile caused the code to work fine again.

 

Keith

 

/*
 * M3 M10 CONTROL (2).c
 *
 * Created: 14/11/2016 11:05:03 AM
 * Author : BEEFY
 */ 

#include <avr/io.h>

#define      Torch_ON      PORTB |= (1<<PORTB0)
#define      Torch_OFF     PORTB &= ~(1<<PORTB0)

/*
uint8_t synchMode_ON;
uint8_t M3_ON;
uint8_t M10_ON;
*/

int main(void)
{
	DDRB |= (1<<DDB0);                        // Port B0 as output

	while (1)
	{
		uint8_t synchMode_ON = PINB & (1<<PINB2);	// Pin B2 input to set normal or synchronous torch on/off mode

		if (!synchMode_ON)        // NOT in synchronous torch on mode
		{
			uint8_t M3_ON = PINB & (1<<PINB3);   // Pin B3 as input for spindle on/off (M3/M5)

			// ????????????????????????????????????????????
			if (M3_ON)		// CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
			{
				Torch_ON;
			}

			else
			{
				Torch_OFF;	// TO HERE
			}
			// ????????????????????????????????????????????
		}

		else                  // Then we ARE in synchronous torch mode
		{
			uint8_t M10_ON = PINB & (1<<PINB4);   // Pin B4 as input for laser on/off (M10/M11)

			if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
			{
				Torch_ON;
			}

			else               // Torch off if lose M10 (and loss of M3 (M5) will cause loss of M11 too)
			{
				Torch_OFF;
			}
		}
	}
}

 

Last Edited: Mon. Nov 14, 2016 - 08:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Something does seem suspect. I'm not in front of a computer with Atmel Studio to run this.
As an aside, avoid using variables in capitals - convention suggests #define'd values are in capitals.
Especially for machine controlls, don't directly base your program logic on the port pins -ensure you filter these inputs in software to remove glitches and to ensure you read a true state. A pushbutton or limit switch must be active for 10's of milliseconds before being considered active by your code. Always filter external inputs.

Last Edited: Tue. Nov 15, 2016 - 04:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So is the inability to debug highly optimized code inherent, a limitation of the debug information in the binary file, or just an insufficiently intelligent debugger?

I don't know if it's possible to keep single-stepping from "jumping around" once the optimizer has re-ordered code, but it seems like it should be possible to keep track of local variables, even if they happen to be in registers (but difficult or impossible to do watch-points, I guess...)

 

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

I would just single step in the ASM code, there it don't jump around the same way, and you can see how your "end"code is.

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

 

Something does seem suspect.

Aargh Kartman, the suspense. I won't sleep tonight now.

As an aside, avoid using variables in capitals - convention suggests #define'd values are in capitals.

Especially for machine controlls, don't directly base your program logic on the port pins -ensure you filter these inputs in software to remove glitches and to ensure you read a true state. A pushbutton or limit switch must be active for 10's of milliseconds before being considered active by your code. Always filter external inputs.

Noted about the convention, I guess I tend to go with what "looks right". Very beginner like I know, I must improve.

 

Would the filtering of external inputs also apply to signals coming from another TTL output board. In this particular case the 3 input signals are all 5v TTL from my cnc motion controller. I also like to have strong pull-ups / pull-downs on inputs because of the noisy plasma environment, but I will have to learn a bit more about "filtering" inputs.

 

Thanks for the tips.

 

Keith.

 

 

Last Edited: Tue. Nov 15, 2016 - 10:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Apart from the coding style already mentioned, there is nothing seriously wrong here.  I cannot see any bug.

 

You say you are running in the Simulator: How are you providing I/O stimulus to exercise all your if statements ?

 

As is. PINB2 reads low and your program will always loop where you indicate. As soon as you turn on the PORTB2 pullup your "synchronous torch mode" section runs.

 

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

sparrow2 wrote:

I would just single step in the ASM code, there it don't jump around the same way, and you can see how your "end"code is.

 

I've been doing that very thing today, even looking at the dissassembly asm code at the beginning where it initialises the stack pointer, etc. It's quite interesting seeing how the asm code accomplished the C code but I'm going to have to learn a lot more to understand all the assembly instructions. 

 

Volatile seems to be the key to stopping the code jumping around but I still can't understand why without volatile the code would get optimised so that it gets locked looping in an if/else block. To me that just seems to be broken code.

 

This is one of the attractions of Codevision for me and I'm beginning to understand why I hear CV recommended for beginners. At low optimisation the code seems to single step the way you wrote it (my experience so far).

 

Keith.

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

N.Winterbottom wrote:

Apart from the coding style already mentioned, there is nothing seriously wrong here.  I cannot see any bug.

 

You say you are running in the Simulator: How are you providing I/O stimulus to exercise all your if statements ?

 

As is. PINB2 reads low and your program will always loop where you indicate. As soon as you turn on the PORTB2 pullup your "synchronous torch mode" section runs.

 

 

Thanks.

 

I'm changing the input pin values by clicking the square boxes for PINB in the I/O window.

 

The program is shown in its "idle" state but I've been through all possible input pin variations. Like I said I've got the code running flawlessly if either I use global variables (non volatile), or as is shown here, local variables (but made volatile). It's when the local variables are NOT volatile (the example here) that the optimisation seems to break the code.

 

I don't turn on any PORTB2 pullup in the code so you've lost me on that one. I only have a PORTB0 output.

 

Keith.

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

Would the filtering of external inputs also apply to signals coming from another TTL output board. In this particular case the 3 input signals are all 5v TTL from my cnc motion controller.

You should alway try to characterise your input - if you expect a slow changing signal, then a 1us pulse clearly is a transient. If your signals are travelling more than a few inches, then you might want to take extra precautions. With your plasma cutter you've got significant amounts of amps at relatively high frequencies, so it doesn't take much to couple enough energy into your signals to cause problems. You want your systems to be tolerant of transients and if possible, detect errant signals. One example i found in a car ECU when it processed the throttle position. It compared the raw a/d value with a filtered value and tested for a difference. With a properly functioning sensor, the raw signal should be close to the filtered signal so a signal that isn't suggests the sensor is faulty. If it detected a fault it would fake a throttle position based on engine rpm and flag the error. When resistive throttle position sensors wear out, they go noisy - the ECU was nice enough to detect this, work around the problem and tell you what was wrong. That is what i'd call a robust system.

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

I built your code above in 4.5.3 using -Os (for mega16 though I doubt it matters) and got:

0000006c <main>:
uint8_t M10_ON;
*/

int main(void)
{
    DDRB |= (1<<DDB0);                        // Port B0 as output
  6c:	b8 9a       	sbi	0x17, 0	; 23

    while (1)
    {
        uint8_t synchMode_ON = PINB & (1<<PINB2);   // Pin B2 input to set normal or synchronous torch on/off mode
  6e:	86 b3       	in	r24, 0x16	; 22

        if (!synchMode_ON)        // NOT in synchronous torch on mode
  70:	82 fd       	sbrc	r24, 2
  72:	04 c0       	rjmp	.+8      	; 0x7c <main+0x10>
        {
            uint8_t M3_ON = PINB & (1<<PINB3);   // Pin B3 as input for spindle on/off (M3/M5)
  74:	86 b3       	in	r24, 0x16	; 22

            // ????????????????????????????????????????????
            if (M3_ON)      // CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
  76:	83 ff       	sbrs	r24, 3
  78:	06 c0       	rjmp	.+12     	; 0x86 <main+0x1a>
  7a:	03 c0       	rjmp	.+6      	; 0x82 <main+0x16>
            // ????????????????????????????????????????????
        }

        else                  // Then we ARE in synchronous torch mode
        {
            uint8_t M10_ON = PINB & (1<<PINB4);   // Pin B4 as input for laser on/off (M10/M11)
  7c:	86 b3       	in	r24, 0x16	; 22

            if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
  7e:	84 ff       	sbrs	r24, 4
  80:	02 c0       	rjmp	.+4      	; 0x86 <main+0x1a>
            {
                Torch_ON;
  82:	c0 9a       	sbi	0x18, 0	; 24
  84:	f4 cf       	rjmp	.-24     	; 0x6e <main+0x2>
            }

            else               // Torch off if lose M10 (and loss of M3 (M5) will cause loss of M11 too)
            {
                Torch_OFF;
  86:	c0 98       	cbi	0x18, 0	; 24
  88:	f2 cf       	rjmp	.-28     	; 0x6e <main+0x2>

I simply don't see a loop it could get stuck in here. There are only 8 paths of execution as I see it predicated on PB2, PB3 and PB4

 

Forget 6C as it's only executed once to set PB0 as output but after that the 8 paths through this are

 

PB432=000

6e, 70, 74, 76, 78, 86 (off), 88 then back to 6e

 

PB432=001

6e, 70, 72, 7c, 7e, 80, 86 (off), 88 then back to 6e

 

PB432=010

6e, 70, 74, 76, 7a, 82 (on), 84 then back to 6e

 

PB432=011

6e, 70, 72, 7c, 7e, 80, 86 (off), 88 then back to 6e

 

PB432=100

6e, 70, 74, 76, 78, 86 (off), 88 then back to 6e

 

PB432=101

6e, 70, 72, 7c, 7e, 82 (on), 84 then back to 6e

 

PB432=110

6e, 70, 74, 76, 7a, 82 (on), 84 then back to 6e

 

PB432=111

6e, 70, 72, 7c, 7e, 82 (on), 84 then back to 6e

 

None of these involve getting "stuck" where you said.

 

Build the code, debug it in Studio, switch to the mixed C+Asm view and try every one of the 8 combinations of 2/3/4 inputs and tell me which of those paths it does not take.

 

Sure you might get some kind of "yellow arrow" effect if you step at the C level but that's just one of the things you learn to avoid when debugging micro code.

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

Hi Cliff,

 

this is the dissassembly code for the -O1 compilation where it gets stuck in the if/else loop:

 

I'll try -Os after this.

00000000  JMP 0x00000034		Jump 
00000002  JMP 0x0000003E		Jump 
00000004  JMP 0x0000003E		Jump 
00000006  JMP 0x0000003E		Jump 
00000008  JMP 0x0000003E		Jump 
0000000A  JMP 0x0000003E		Jump 
0000000C  JMP 0x0000003E		Jump 
0000000E  JMP 0x0000003E		Jump 
00000010  JMP 0x0000003E		Jump 
00000012  JMP 0x0000003E		Jump 
00000014  JMP 0x0000003E		Jump 
00000016  JMP 0x0000003E		Jump 
00000018  JMP 0x0000003E		Jump 
0000001A  JMP 0x0000003E		Jump 
0000001C  JMP 0x0000003E		Jump 
0000001E  JMP 0x0000003E		Jump 
00000020  JMP 0x0000003E		Jump 
00000022  JMP 0x0000003E		Jump 
00000024  JMP 0x0000003E		Jump 
00000026  JMP 0x0000003E		Jump 
00000028  JMP 0x0000003E		Jump 
0000002A  JMP 0x0000003E		Jump 
0000002C  JMP 0x0000003E		Jump 
0000002E  JMP 0x0000003E		Jump 
00000030  JMP 0x0000003E		Jump 
00000032  JMP 0x0000003E		Jump 
00000034  CLR R1		Clear Register 
00000035  OUT 0x3F,R1		Out to I/O location 
00000036  SER R28		Set Register 
00000037  LDI R29,0x08		Load immediate 
00000038  OUT 0x3E,R29		Out to I/O location 
00000039  OUT 0x3D,R28		Out to I/O location 
0000003A  CALL 0x00000040		Call subroutine 
0000003C  JMP 0x0000004F		Jump 
0000003E  JMP 0x00000000		Jump 
--- C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\Debug/.././main.c 
{
	DDRB |= (1<<DDB0);                        // Port B0 as output
00000040  SBI 0x04,0		Set bit in I/O register 
		if (!synchMode_ON)        // NOT in synchronous torch on mode
00000041  SBIC 0x03,2		Skip if bit in I/O register cleared 
00000042  RJMP PC+0x0007		Relative jump 
			if (M3_ON)		// CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
00000043  SBIS 0x03,3		Skip if bit in I/O register set 
00000044  RJMP PC+0x0003		Relative jump 
				Torch_ON;
00000045  SBI 0x05,0		Set bit in I/O register 
00000046  RJMP PC-0x0005		Relative jump 
				Torch_OFF;	// TO HERE (IF LOCAL VARS NOT VOLATILE)
00000047  CBI 0x05,0		Clear bit in I/O register 
00000048  RJMP PC-0x0007		Relative jump 
			if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
00000049  SBIS 0x03,4		Skip if bit in I/O register set 
0000004A  RJMP PC+0x0003		Relative jump 
				Torch_ON;
0000004B  SBI 0x05,0		Set bit in I/O register 
0000004C  RJMP PC-0x000B		Relative jump 
				Torch_OFF;
0000004D  CBI 0x05,0		Clear bit in I/O register 
0000004E  RJMP PC-0x000D		Relative jump 
--- No source file -------------------------------------------------------------
0000004F  CLI 		Global Interrupt Disable 
00000050  RJMP PC-0x0000		Relative jump 
00000051  NOP 		Undefined 
00000052  NOP 		Undefined 
00000053  NOP 		Undefined 

Keith

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

And here's compiled with -Os.

It still gets stuck in a loop but simply does it in a different way. It's only ever checking "if(M3_ON)" and does not go outside that.

So if PINB2 was to go high it would never get seen.

 

00000000  JMP 0x00000034		Jump 
00000002  JMP 0x0000003E		Jump 
00000004  JMP 0x0000003E		Jump 
00000006  JMP 0x0000003E		Jump 
00000008  JMP 0x0000003E		Jump 
0000000A  JMP 0x0000003E		Jump 
0000000C  JMP 0x0000003E		Jump 
0000000E  JMP 0x0000003E		Jump 
00000010  JMP 0x0000003E		Jump 
00000012  JMP 0x0000003E		Jump 
00000014  JMP 0x0000003E		Jump 
00000016  JMP 0x0000003E		Jump 
00000018  JMP 0x0000003E		Jump 
0000001A  JMP 0x0000003E		Jump 
0000001C  JMP 0x0000003E		Jump 
0000001E  JMP 0x0000003E		Jump 
00000020  JMP 0x0000003E		Jump 
00000022  JMP 0x0000003E		Jump 
00000024  JMP 0x0000003E		Jump 
00000026  JMP 0x0000003E		Jump 
00000028  JMP 0x0000003E		Jump 
0000002A  JMP 0x0000003E		Jump 
0000002C  JMP 0x0000003E		Jump 
0000002E  JMP 0x0000003E		Jump 
00000030  JMP 0x0000003E		Jump 
00000032  JMP 0x0000003E		Jump 
00000034  CLR R1		Clear Register 
00000035  OUT 0x3F,R1		Out to I/O location 
00000036  SER R28		Set Register 
00000037  LDI R29,0x08		Load immediate 
00000038  OUT 0x3E,R29		Out to I/O location 
00000039  OUT 0x3D,R28		Out to I/O location 
0000003A  CALL 0x00000040		Call subroutine 
0000003C  JMP 0x0000004C		Jump 
0000003E  JMP 0x00000000		Jump 
--- C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\Debug/.././main.c 
{
	DDRB |= (1<<DDB0);                        // Port B0 as output
00000040  SBI 0x04,0		Set bit in I/O register 
		if (!synchMode_ON)        // NOT in synchronous torch on mode
00000041  SBIC 0x03,2		Skip if bit in I/O register cleared 
00000042  RJMP PC+0x0004		Relative jump 
			if (M3_ON)		// CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
00000043  SBIS 0x03,3		Skip if bit in I/O register set 
00000044  RJMP PC+0x0006		Relative jump 
00000045  RJMP PC+0x0003		Relative jump 
			if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
00000046  SBIS 0x03,4		Skip if bit in I/O register set 
00000047  RJMP PC+0x0003		Relative jump 
				Torch_ON;
00000048  SBI 0x05,0		Set bit in I/O register 
00000049  RJMP PC-0x0008		Relative jump 
				Torch_OFF;
0000004A  CBI 0x05,0		Clear bit in I/O register 
0000004B  RJMP PC-0x000A		Relative jump 
--- No source file -------------------------------------------------------------
0000004C  CLI 		Global Interrupt Disable 
0000004D  RJMP PC-0x0000		Relative jump 
0000004E  NOP 		Undefined 
0000004F  NOP 		Undefined 
00000050  NOP 		Undefined 

 

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

Artandsparks wrote:
where it gets stuck in the if/else loop:
WHAT gets "stuck" - are you simply talking about the irritating yellow arrow? Like I said, build/debug this, switch to the C+Asm view, each step is one AVR opcode rather than an attempt to step one line of C at a time. Now exactly which of the opcodes is it "stuck" on for any of the 8 potential input patterns?

 

(none? yup, that's what I thought)

 

[oh and use .lss not that crap disassembly from Studio!]

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

And this is where I make the local variables volatile and compile with -O1.

Everything works perfectly and single steps as I've wrote the C code.

 

00000000  JMP 0x00000034		Jump 
00000002  JMP 0x0000003E		Jump 
00000004  JMP 0x0000003E		Jump 
00000006  JMP 0x0000003E		Jump 
00000008  JMP 0x0000003E		Jump 
0000000A  JMP 0x0000003E		Jump 
0000000C  JMP 0x0000003E		Jump 
0000000E  JMP 0x0000003E		Jump 
00000010  JMP 0x0000003E		Jump 
00000012  JMP 0x0000003E		Jump 
00000014  JMP 0x0000003E		Jump 
00000016  JMP 0x0000003E		Jump 
00000018  JMP 0x0000003E		Jump 
0000001A  JMP 0x0000003E		Jump 
0000001C  JMP 0x0000003E		Jump 
0000001E  JMP 0x0000003E		Jump 
00000020  JMP 0x0000003E		Jump 
00000022  JMP 0x0000003E		Jump 
00000024  JMP 0x0000003E		Jump 
00000026  JMP 0x0000003E		Jump 
00000028  JMP 0x0000003E		Jump 
0000002A  JMP 0x0000003E		Jump 
0000002C  JMP 0x0000003E		Jump 
0000002E  JMP 0x0000003E		Jump 
00000030  JMP 0x0000003E		Jump 
00000032  JMP 0x0000003E		Jump 
00000034  CLR R1		Clear Register 
00000035  OUT 0x3F,R1		Out to I/O location 
00000036  SER R28		Set Register 
00000037  LDI R29,0x08		Load immediate 
00000038  OUT 0x3E,R29		Out to I/O location 
00000039  OUT 0x3D,R28		Out to I/O location 
0000003A  CALL 0x00000040		Call subroutine 
0000003C  JMP 0x00000061		Jump 
0000003E  JMP 0x00000000		Jump 
--- C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\Debug/.././main.c 
{
00000040  PUSH R28		Push register on stack 
00000041  PUSH R29		Push register on stack 
00000042  RCALL PC+0x0001		Relative call subroutine 
00000043  PUSH R1		Push register on stack 
00000044  IN R28,0x3D		In from I/O location 
00000045  IN R29,0x3E		In from I/O location 
	DDRB |= (1<<DDB0);                        // Port B0 as output
00000046  SBI 0x04,0		Set bit in I/O register 
		volatile uint8_t synchMode_ON = PINB & (1<<PINB2);	// Pin B2 input to set normal or synchronous torch on/off mode
00000047  IN R24,0x03		In from I/O location 
00000048  ANDI R24,0x04		Logical AND with immediate 
00000049  STD Y+3,R24		Store indirect with displacement 
		if (!synchMode_ON)        // NOT in synchronous torch on mode
0000004A  LDD R24,Y+3		Load indirect with displacement 
0000004B  CPSE R24,R1		Compare, skip if equal 
0000004C  RJMP PC+0x000B		Relative jump 
			volatile uint8_t M3_ON = PINB & (1<<PINB3);   // Pin B3 as input for spindle on/off (M3/M5)
0000004D  IN R24,0x03		In from I/O location 
0000004E  ANDI R24,0x08		Logical AND with immediate 
0000004F  STD Y+2,R24		Store indirect with displacement 
			if (M3_ON)		// CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
00000050  LDD R24,Y+2		Load indirect with displacement 
00000051  TST R24		Test for Zero or Minus 
00000052  BREQ PC+0x03		Branch if equal 
				Torch_ON;
00000053  SBI 0x05,0		Set bit in I/O register 
00000054  RJMP PC-0x000D		Relative jump 
				Torch_OFF;	// TO HERE (IF LOCAL VARS NOT VOLATILE)
00000055  CBI 0x05,0		Clear bit in I/O register 
00000056  RJMP PC-0x000F		Relative jump 
			volatile uint8_t M10_ON = PINB & (1<<PINB4);   // Pin B4 as input for laser on/off (M10/M11)
00000057  IN R24,0x03		In from I/O location 
00000058  ANDI R24,0x10		Logical AND with immediate 
--- C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\Debug/.././main.c 
00000059  STD Y+1,R24		Store indirect with displacement 
			if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
0000005A  LDD R24,Y+1		Load indirect with displacement 
0000005B  TST R24		Test for Zero or Minus 
0000005C  BREQ PC+0x03		Branch if equal 
				Torch_ON;
0000005D  SBI 0x05,0		Set bit in I/O register 
0000005E  RJMP PC-0x0017		Relative jump 
				Torch_OFF;
0000005F  CBI 0x05,0		Clear bit in I/O register 
00000060  RJMP PC-0x0019		Relative jump 
--- No source file -------------------------------------------------------------
00000061  CLI 		Global Interrupt Disable 
00000062  RJMP PC-0x0000		Relative jump 
00000063  NOP 		Undefined 
00000064  NOP 		Undefined 
00000065  NOP 		Undefined 

 

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

Those disassemblies are close to unreadable - so don't bother. If you are going to post code then make it the .lss and just the stuff from main: to the final rjmp back (as I did).

 

But, as I say, the way to see what's going on here is to load it into the debugger (simulator at a pinch) and step it in Asm view. I predict it is not actually "stuck" anywhere. As far as I can see there are 8 scenarios and they all involve executing a sequence of 7 different opcodes repeatedly (once you are into the infinite part of main()).

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

clawson wrote:

Artandsparks wrote:
where it gets stuck in the if/else loop:
WHAT gets "stuck" - are you simply talking about the irritating yellow arrow? Like I said, build/debug this, switch to the C+Asm view, each step is one AVR opcode rather than an attempt to step one line of C at a time. Now exactly which of the opcodes is it "stuck" on for any of the 8 potential input patterns?

 

(none? yup, that's what I thought)

 

[oh and use .lss not that crap disassembly from Studio!]

 

Ah sorry Cliff.

 

Yes I have been basing everything off the "irritating yellow line" LOL. Sometimes there doesn't seem like a lot of point learning high level C when we have to go through the code in assembly (just a beginner ranting, sorry).

 

I see I have a new adventure to tackle. I'm going to have to sit down for some time and go through it all. I don't even know what .lss is.

 

Keith.

Last Edited: Tue. Nov 15, 2016 - 11:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you. Interesting idea !!!

 

Michael.

User of:
IAR Embedded Workbench C/C++ Compiler
Altium Designer

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

Artandsparks wrote:
Sometimes there doesn't seem like a lot of point learning high level C when we have to check stuff out in assembly
These are micros - the opcodes are the important bit!

 

Of course I have a slightly biased view - the first 15..20 years of my programming life were programming Asm so I *still* think of C as nothing more than a macro assembler - only C++ has started to make me think their may be a bit more to it - but at the C level you still study the Asm to see how the compiler got on with implementing what you asked for.

 

For C I spent 20+ years since around the mid 90's doing that and certainly for the first 10 to 15 of those you just accepted the fact that variables would not always be watchable or that the path of execution would "jump around". for() loops (as I showed in my tutorial article about optimisation) are something of a classic. You write them on one line of C, they are really 2 if not 3 very distinctive pieces of code (the init, the test/branch, the end of loop code) so you often find 2 or 3 different blocks of opcodes annotated as part of the single for() statement.

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

Cliff, thanks very much.

 

I'm realising I'm only scratching the surface of this game and have a long way to go to properly understand things. I now even know I have to get to learn about the compiler itself and the different files it spits out. Just Googling now I found out there are both assembly and dissassembly files produced (said with head spinning). 

 

My next task is to find out about this .lss file and how the assembly code in that is different from what I've been giving here.

 

Keith.

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

And before your head are totally spinning, some of the ASM instructions have some limitations so sometimes the output change a lot if you just add one line 

 

like the brance instructions normally brance on the same as you write in C, but if the distance gets above the limit, the compiler make the opposite check and then

a (r)jmp to where it what to brance.(and a brance over the (r)jmp ) 

Last Edited: Tue. Nov 15, 2016 - 12:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Artandsparks wrote:
found out there are both assembly
My ultimate view of Asm is those assembly (.s) files. The only thing is that by default they only contain directives/opcodes but no visible link to the C that created them. For that reason I wrote this:

 

https://spaces.atmel.com/gf/proj...

 

If I apply that to your code above I get:

$ cat avr.source.s 
	.file	"avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.Ltext0:

	.section	.text.startup,"ax",@progbits
.global	main
	.type	main, @function
main:
//==> {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     DDRB |= (1<<DDB0);                        // Port B0 as output
	sbi 0x17,0
.L2:
//==>         if (!synchMode_ON)        // NOT in synchronous torch on mode
	sbic 0x16,2
	rjmp .L3
//==>             if (M3_ON)      // CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE
	sbis 0x16,3
	rjmp .L6
	rjmp .L7
.L3:
//==>             if (M10_ON)            // Requires M10 for torch to fire (and M10 requires M3 in any case)
	sbis 0x16,4
	rjmp .L6
.L7:
//==>                 Torch_ON;
	sbi 0x18,0
	rjmp .L2
.L6:
//==>                 Torch_OFF;
	cbi 0x18,0
	rjmp .L2
	.size	main, .-main
	.text
.Letext0:

The .s before processing looked like:

	.file	"avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.Ltext0:
	.cfi_sections	.debug_frame
	.section	.text.startup,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB0:
	.file 1 "avr.c"
	.loc 1 13 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 14 0
	sbi 0x17,0
.L2:
.LBB2:
	.loc 1 20 0
	sbic 0x16,2
	rjmp .L3
.LBB3:
	.loc 1 25 0
	sbis 0x16,3
	rjmp .L6
	rjmp .L7
.L3:
.LBE3:
.LBB4:
	.loc 1 41 0
	sbis 0x16,4
	rjmp .L6
.L7:
	.loc 1 43 0
	sbi 0x18,0
	rjmp .L2
.L6:
	.loc 1 48 0
	cbi 0x18,0
	rjmp .L2
.LBE4:
.LBE2:
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.text
.Letext0:

 

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

Please post your Build Messages so we can see the avr-gcc command line with -mmcu=  plus any other flags etc.

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

clawson wrote:

My ultimate view of Asm is those assembly (.s) files. The only thing is that by default they only contain directives/opcodes but no visible link to the C that created them. For that reason I wrote this:

 

https://spaces.atmel.com/gf/proj...

 

Thanks Cliff,

 

that's a great little gold nugget.

 

No doubt with time I'll start to understand WHY there are different files with the assembly instructions. Up until yesterday I'd have thought having more than one file with the assembly code would not be needed and all you'd have is different files with the same assembly instructions. Obviously there's more to it than that.

 

How can people get bored when they retire ???? I wish I was retired so I could spend all my time learning this one "little" thing LOL.

 

Keith.

Last Edited: Tue. Nov 15, 2016 - 08:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:

Please post your Build Messages so we can see the avr-gcc command line with -mmcu=  plus any other flags etc.

 

Here it is. 

So where do I learn about what all this means. Will it all be explained in the GCC documentation ?

Keith

------ Build started: Project: M3 M10 CONTROL (2), Configuration: Debug AVR ------
Build started.
Project "M3 M10 CONTROL (2).cproj" (default targets):
Target "PreBuildEvent" skipped, due to false condition; ('$(PreBuildEvent)'!='') was evaluated as (''!='').
Target "CoreBuild" in file "C:\Program Files\Atmel\Studio\7.0\Vs\Compiler.targets" from project "C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\M3 M10 CONTROL (2).cproj" (target "Build" depends on it):
	Using "RunCompilerTask" task from assembly "C:\Program Files\Atmel\Studio\7.0\Extensions\Application\AvrGCC.dll".
	Task "RunCompilerTask"
		Shell Utils Path C:\Program Files\Atmel\Studio\7.0\shellUtils
		C:\Program Files\Atmel\Studio\7.0\shellUtils\make.exe all --jobs --output-sync 
		Building file: .././main.c
		Invoking: AVR/GNU C Compiler : 4.9.2
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe"  -x c -funsigned-char -funsigned-bitfields -DDEBUG  -I"C:\Program Files\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.106\include"  -O1 -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -g2 -Wall -mmcu=atmega328p -B "C:\Program Files\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.106\gcc\dev\atmega328p" -c -std=gnu99 -MD -MP -MF "main.d" -MT"main.d" -MT"main.o"   -o "main.o" ".././main.c" 
		Finished building: .././main.c
		Building target: M3 M10 CONTROL (2).elf
		Invoking: AVR/GNU Linker : 4.9.2
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe" -o "M3 M10 CONTROL (2).elf"  main.o   -Wl,-Map="M3 M10 CONTROL (2).map" -Wl,--start-group -Wl,-lm  -Wl,--end-group -Wl,--gc-sections -mmcu=atmega328p -B "C:\Program Files\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.0.106\gcc\dev\atmega328p"  
		Finished building target: M3 M10 CONTROL (2).elf
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O ihex -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures  "M3 M10 CONTROL (2).elf" "M3 M10 CONTROL (2).hex"
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -j .eeprom  --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0  --no-change-warnings -O ihex "M3 M10 CONTROL (2).elf" "M3 M10 CONTROL (2).eep" || exit 0
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objdump.exe" -h -S "M3 M10 CONTROL (2).elf" > "M3 M10 CONTROL (2).lss"
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O srec -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures "M3 M10 CONTROL (2).elf" "M3 M10 CONTROL (2).srec"
		"C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-size.exe" "M3 M10 CONTROL (2).elf"
		   text	   data	    bss	    dec	    hex	filename
		    162	      0	      0	    162	     a2	M3 M10 CONTROL (2).elf
	Done executing task "RunCompilerTask".
	Using "RunOutputFileVerifyTask" task from assembly "C:\Program Files\Atmel\Studio\7.0\Extensions\Application\AvrGCC.dll".
	Task "RunOutputFileVerifyTask"
				Program Memory Usage 	:	162 bytes   0.5 % Full
				Data Memory Usage 		:	0 bytes   0.0 % Full
	Done executing task "RunOutputFileVerifyTask".
Done building target "CoreBuild" in project "M3 M10 CONTROL (2).cproj".
Target "PostBuildEvent" skipped, due to false condition; ('$(PostBuildEvent)' != '') was evaluated as ('' != '').
Target "Build" in file "C:\Program Files\Atmel\Studio\7.0\Vs\Avr.common.targets" from project "C:\Users\BEEFY\Documents\CNC RELATED\THC\AA - BEEFYS THC FILES\M10_M11 METHODS\M3 M10 CONTROL (AS-OK) - Copy\M3 M10 CONTROL (2)\M3 M10 CONTROL (2).cproj" (entry point):
Done building target "Build" in project "M3 M10 CONTROL (2).cproj".
Done building project "M3 M10 CONTROL (2).cproj".

Build succeeded.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

 

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

Cliff,

 

I'll never look at "that silly yellow line" in the C simulator the same ever again. I recompiled the code with no volatile and single stepped in the C window in the simulator. The code APPEARED stuck in that loop I mentioned. 

 

So then I went to the dissassembly window and single stepped each line of assembly, and the code worked perfectly, it went where I expected it to, and responded to the different input variations correctly.

 

So then I went back to the C single stepping and found out that the line "if (!synchMode_ON)" is not being shown with the "silly yellow line" but it is actually being tested for. This is what has led me up the garden path and to my thoughts that the code was broken.

This is the line of assembly code representing the "if (!synchMode_ON)" 

00000041  SBIC 0x03,2		Skip if bit in I/O register cleared 

and it's looped to every time, so I don't know why the C single stepping does NOT show that step every time. I sort of look at that as a flaw in the simulator, but anyway that's the way it is and now I know I can't trust it, it will "tell me lies".

 

This little experience is also changing my view of C. When I started with Arduino that came to look like a sugar coating for C. Now C is starting to look like a sugar coating for assembly language LOL.

 

Thanks again (everyone),

 

Keith.

Last Edited: Tue. Nov 15, 2016 - 09:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Keith, the processor executes machine code - so whatever the language involved it ends up as machine code. So, just like Sara Lee, it's layer upon layer upon layer. Similarly with the code we write - you start with the low level details and work it up layer by layer to the desired result.

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

Kartman wrote:

Keith, the processor executes machine code - so whatever the language involved it ends up as machine code. So, just like Sara Lee, it's layer upon layer upon layer. Similarly with the code we write - you start with the low level details and work it up layer by layer to the desired result.

 

I think I've been wanting to bury my head under a rock and hope I can get by with just the high level C (too many tasks to accomplish), but I think I'm just shooting myself in the foot with that hope. The A4 folder with the assembly language tutorial I printed out years ago is back out on the table.

 

Hopefully this is all good groundwork for when I move on to something like an ARM micro.

 

Keith.

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

Keith,

For the most part you can be blissfully unaware of assembler - you just need to use different techniques for debug to suit what you're trying to do. Just about every test instrument I have can lie to you if you don't use it carefully. Even our eyes can lie to us, so it is a matter of knowing what technique to use in a given circumstance.

With your example I would've just put in some printf's and I would've been able to see the logic in operation in real time - probably all in less time than stepping through with the debugger. I also used to use a variety of 'tricks' to trigger my old analog oscilloscope before I had a digital one. My digital one is also fairly ancient and doesn't have a lot of the features the newer and cheaper ones have today, so even now I will toggle port bits to trigger on certain cases so I can observe stuff. With the likes of the saleae logic clones I can see uart, i2c and spi data. This weekend I was debugging some code I wrote on a cortex m0 ARM with no debugger - even though I had one connected, it was only downloading the code. I had a i2c sensor outputting strange values, well actually the sensor itself was ok but my code wasn't doing the right thing. I had the saleae logic clone sniffing the i2c, my finger adding temperature to the sensor and serial data streaming out with what my code was thinking the temperature was. I was able to validate that I was reading the sensor, the sensor was responding, that I was reading the correct registers and the data I was getting. I also found out the sensor didn't like reading a number of registers in one go and that I had to read them one by one via i2c. So I had zero exposure to the underlying machine code and all my work was in C. Note that I was working on a framework of known good code - the i2c library worked as did the serial and others. I was able to debug my sensor code and now that is known good. Using these techniques I was able to diagnose some problems I had with interrupts on the same platform. I was able to narrow down the problems to my isr not being called but I knew the interrupt was being hit as the code crashed. The problem turned out to be name mangling in C++. The tools I was using was MBED - which has no debugging and no access to the generated files apart from the hex file, so pretty well running blind, but my debug techniques are suited to such an arrangement.

Try it yourself - get your posted code and add printf's to critical parts of the code and see how quickly you are able to 'see' what is happening.

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

Thanks very much Kartman.

 

You seem like a very smart and streetwise guy when it comes to problem solving. I'll take good note of all that.

 

Just have to read up on printf now LOL. Yep I've never used that although I've seen it in code plenty times. 

 

I've got an 8 channel logic analyser and used it about 3 weeks ago to monitor the parameter bytes I was sending back and forth between my PC and an AVR, and I think it has to be one of the best test/debug tools I've bought. I'd like to get a 16 channel one now.

 

I'm guessing you already know but just in case you don't, Keil Microvision is supposed to be free and fully featured for M0/M0+ ARM Cortex chips, but code size limited for the rest. Don't know if that's of any benefit to you. I was not aware of the MBED limitations you mentioned.

 

Keith.

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

Keith, i haven't been referred to as 'streetwise' before! But as an engineer, my job is solving problems be it mechanical, philosophical, electrical or chemical. I try to avoid emotional problems.....
I have Keil on my computer but that wouldn't have made a difference to my debug technique. I think you'd be surprised how widespread the technique of putting printf's, serial.print(), echo, cout <<, console.log etc is in many languages. In many cases, putting a breakpoint and inspecting variables interrupts the real time operation, so it is of little use. Don't get me wrong - in my day job i have the debugger always connected and i've got the added benefit of trace, but i'll mainly use its features for nasty bugs like hard faults, stack corruption or monitoring the rtos.

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

The first thing I get up running is the UART, and a 1 byte protocol, so I can do everything that way, from dump special structures, states of all state machines, emulate key press, control IO's etc.

 

And I should say that I don't own a ICE.  

 

add:

I should add that I never use C's printf for debug, but a small routine that don't call anything (use UDR direct) 

Last Edited: Wed. Nov 16, 2016 - 08:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From this dump I can determine:

 

  1. The AVR chosen: (-mmcu=atmega328p)
  2. Compiler Version: (Invoking: AVR/GNU C Compiler : 4.9.2)
  3. Optimisation Level: (-O1)
  4. Default Extra Optimisations: (-ffunction-sections -fdata-sections -Wl,--gc-sections)
  5. Final Code Size: (162 bytes)

 

I was able to completely reproduce your build and single step in Simulator2 and of course the yellow arrow moves from

 

    if (!synchMode_ON)        // NOT in synchronous torch on mode

to

    if (M3_ON)		// CODE GETS STUCK IN A PERPETUAL LOOP FROM HERE

to

        Torch_OFF;	// TO HERE

and loops around again.

 

{Edit}

As for documentation: This it is spread widely but the majority of GNU stuff is installed into the toolchain folder.

 

Probably the best place to start is avr-libc-user-manual.pdf located here:

C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\doc\avr-libc\

 

In fact that whole doc folder contains lots of useful stuff - examples and binutils documentation.

Once you've read all that you can look in:

C:\Program Files\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\share\doc\gcc\ for the really nitty gritty of gcc.

 

Last Edited: Wed. Nov 16, 2016 - 10:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Keith, the keil free unlimited is for ST cm0 micros only. The main reason you'd go for Keil or IAR is the compiler and debugger -the editors are pretty average by today's standards. I try to void cortex m0 micros - at the hobbyist level for one or two dollars more you get a m4 with 100+MHz, a stack of flash and ram and floating point coprocessor.
As an aside, i got my pcbs via hackvana today. Very nice finish. Silly me decided to use some very small surface mount temp/humidity/pressure sensors -small as in 2x2mm. I was not hopeful of soldering these little babies successfully, but they worked first time! Am i chuffed? Hopefully i can repeat my success on the second board.