Issues with loop_until_bit_is_clear

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

Hi all,

I am having some strange problems with the loop_until_bit_is_clear macro.

I am using it within a larger code, like this:

volatile uint8_t bit;
uint8_t i, byte, parity;

TCCR3A = 0x0C;		
DDRC &= ~(_BV(PC4));	
PORTC |= _BV(PC4);					

loop_until_bit_is_clear(PINC, PC4);   // BK1
PORTB |= ~(_BV(PB0));	              // BK2

This code is within a function. PINC4 is connected to an external input. At some point PINC4 will become 0 for a short period of time (9us) and I want my code to continue only at that point.

I am debugging using AVR Dragon. My chip is AT90USB1287.

I have two breakpoints, BK1 and BK2.

When I stop on the first breakpoint and I turn on the disassembler window, I get this:

+00000360:   92EF        PUSH      R14            Push register on stack
+00000361:   92FF        PUSH      R15            Push register on stack
+00000362:   930F        PUSH      R16            Push register on stack
+00000363:   931F        PUSH      R17            Push register on stack
+00000364:   93DF        PUSH      R29            Push register on stack
+00000365:   93CF        PUSH      R28            Push register on stack
+00000366:   920F        PUSH      R0             Push register on stack
+00000367:   B7CD        IN        R28,0x3D       In from I/O location
+00000368:   B7DE        IN        R29,0x3E       In from I/O location
+00000369:   2F08        MOV       R16,R24        Copy register
+0000036A:   01FB        MOVW      R30,R22        Copy register pair

280:      	TCCR3A = 0x0C;						
+0000036B:   E08C        LDI       R24,0x0C       Load immediate
+0000036C:   3480        CPI       R24,0x40       Compare with immediate
+0000036D:   8599        LDD       R25,Y+9        Load indirect with displacement
281:      	DDRC &= ~(_BV(PC4));				
+0000036E:   98B1        CBI       0x16,1         
282:      	PORTC |= _BV(PC4);					
+0000036F:   3444        CPI       R20,0x44       Compare with immediate

285:      	loop_until_bit_is_clear(PINC, PC4);
+00000370:   8599        LDD       R25,Y+9        Load indirect with displacement
+00000371:   CFB1        RJMP      PC-0x004E      Relative jump

The first issue that we can see is that the assembler code generated is not quite what I would expected, i.e.

SBIC      0x06,4
RJMP      PC-0x0001

Any ideas on this issue?

Then I disable BK1 and I let the program run until the external input becomes 0 and thus BK2 fires. Thus I have my program/chip stopped on top of BK2 (see above).

The first problem that I encounter is that PINC4 seems to be high in the AVR Studio 4 I/O view, even though my code should reach that point only if PINC4 was low previously. Maybe between the compare and the break instruction the external input already came back to 1, I don't know.

Secondly, opening the disassembler window again, I see this code:

+00000360:   92EF        PUSH      R14            Push register on stack
+00000361:   92FF        PUSH      R15            Push register on stack
+00000362:   930F        PUSH      R16            Push register on stack
+00000363:   931F        PUSH      R17            Push register on stack
+00000364:   93DF        PUSH      R29            Push register on stack
+00000365:   93CF        PUSH      R28            Push register on stack
+00000366:   920F        PUSH      R0             Push register on stack
+00000367:   B7CD        IN        R28,0x3D       In from I/O location
+00000368:   B7DE        IN        R29,0x3E       In from I/O location
+00000369:   2F08        MOV       R16,R24        Copy register
+0000036A:   01FB        MOVW      R30,R22        Copy register pair
280:      	TCCR3A = 0x0C;						
+0000036B:   E08C        LDI       R24,0x0C       Load immediate
+0000036C:   93800090    STS       0x0090,R24     Store direct to data space

281:      	DDRC &= ~(_BV(PC4));				
+0000036E:   853C        LDD       R19,Y+12       Load indirect with displacement
282:      	PORTC |= _BV(PC4);					
+0000036F:   9AB1        SBI       0x16,1         Set bit in I/O register

285:      	loop_until_bit_is_clear(PINC, PC4);
+00000370:   9934        SBIC      0x06,4         Skip if bit in I/O register cleared
+00000371:   85FE        LDD       R31,Y+14       Load indirect with displacement

As you can see the code somehow "magically" transformed in the meanwhile.

Can anybody please explain me what could have happened? Why am I seeing such changes using the disassembler window? Should I use something else to analyse each instruction?

I've seen some other posts related to the loop_until_bit_is_clear/set macros but I am not sure if I am under the same circumstances. Is my issue something known? Am I doing something wrong?

If I use this code instead:

while(bit_is_set(PINC, PC4));

then the assembler seems fine:

286:      	while(bit_is_set(PINC, PC4));
+00000370:   9934        SBIC      0x06,4         Skip if bit in I/O register cleared
+00000371:   CFFE        RJMP      PC-0x0001      Relative jump

Most importantly, the assembler code remains the same, even when I go to the second breakpoint, BK2. It does not "magically" modifies.

So I could say that using while(bit_is_set(..)) is better/safer than using loop_until_bit_is_clear(..). However I am not sure if I am doing something wrong, this is a known issue or if there is a bug somewhere. Maybe someone can clarify this to me.

Thank you,
Omar

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

Hmmm--those LDDs and CPIs look strange. Does this have to do with the inserted breakpoint, and that is what is being dissasembled?

What do you see when looking at the listing file for that function?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:

What do you see when looking at the listing file for that function?

What do you mean by the listing file? How do I get it? Isn't the output from the disassembler window what you are asking for (given above) ?

Sorry if the question seems rather stupid but I am quite a beginner with AVR.

Thanks.

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

The listing file is a file with a .lst extension. If you are using avr-gcc from within AVR Studio then there is a check box in the project options to get it generated.

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

For the same number of characters as your macro, you could write

while (PINC & (1 << 4));

, which DOES compile the way you want. Of course, it doesn't give the same level of documentation, until you add a comment explaining what you're waiting for. But unlike the macro, you have the opportunity to write the comments in terms of the specific signal that's actually wired to that pin, saying something like "Wait for echo pulse reception".

Have you looked at what the preprocessor turns your "loop_until_bit_is_clear(p,n)" macro into?

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

JohanEkdahl wrote:
The listing file is a file with a .lst extension. If you are using avr-gcc from within AVR Studio then there is a check box in the project options to get it generated.

Ok, so there is no ".lst" file. However I have a ".lss" file, may be this is the one. Its contents seem to be what we were expecting. The listing for the function mentioned above is this:

uint8_t GetByteTerminalNoParity(uint8_t inverse_conversion, uint8_t *r_byte, uint8_t stop)
{
     6f0:	cf 92       	push	r12
     6f2:	df 92       	push	r13
     6f4:	ff 92       	push	r15
     6f6:	0f 93       	push	r16
     6f8:	1f 93       	push	r17
     6fa:	df 93       	push	r29
     6fc:	cf 93       	push	r28
     6fe:	0f 92       	push	r0
     700:	cd b7       	in	r28, 0x3d	; 61
     702:	de b7       	in	r29, 0x3e	; 62
     704:	f8 2e       	mov	r15, r24
     706:	db 01       	movw	r26, r22
	volatile uint8_t bit;
	uint8_t i, byte, parity;

	TCCR3A = 0x0C;						
     708:	8c e0       	ldi	r24, 0x0C	; 12
     70a:	80 93 90 00 	sts	0x0090, r24
	DDRC &= ~(_BV(PC4));				     
     70e:	3c 98       	cbi	0x07, 4	; 7
	PORTC |= _BV(PC4);					// enable pull-up	
     710:	44 9a       	sbi	0x08, 4	; 8
	
	loop_until_bit_is_clear(PINC, PC4);	
     712:	34 99       	sbic	0x06, 4	; 6
     714:	fe cf       	rjmp	.-4      	; 0x712 

/* Helper functions */

void Write16bitRegister(volatile uint16_t *reg, uint16_t value)
{
	cli();
     744:	f8 94       	cli
	*reg = value;
     746:	84 e7       	ldi	r24, 0x74	; 116
     748:	91 e0       	ldi	r25, 0x01	; 1
     74a:	90 93 99 00 	sts	0x0099, r25
     74e:	80 93 98 00 	sts	0x0098, r24
	sei();
     752:	78 94       	sei

	// check result and set timer for next bit
	bit = bit_is_set(PINC, PC4);
	Write16bitRegister(&OCR3A, 372);
	*r_byte = 0;
     754:	1c 92       	st	X, r1
	byte = 0;
	parity = 0;	
	if(bit)	return -1;	
     756:	89 81       	ldd	r24, Y+1	; 0x01
     758:	88 23       	and	r24, r24
     75a:	09 f0       	breq	.+2      	; 0x75e 
     75c:	4b c0       	rjmp	.+150    	; 0x7f4 
     75e:	50 e0       	ldi	r21, 0x00	; 0
     760:	60 e0       	ldi	r22, 0x00	; 0
     762:	27 e0       	ldi	r18, 0x07	; 7
     764:	30 e0       	ldi	r19, 0x00	; 0

I don't understand why this listing interleaves code from the Write16bitRegister function within the code for the GetByteTerminalNoParity.

Anyway, does this information help? The code seems to be what I would expect. But:

1) why, while debugging I see different pieces of code?
2) why does this listing interleaves code from different functions? what does this mean?

Thanks.

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

Quote:

Ok, so there is no ".lst" file. However I have a ".lss" file

You are correct. I was in error.

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

No problem, thanks anyway for the information.

I find the listing file very useful indeed.

Regarding the problems mentioned above, I think I can now explain some of them:

1. The debugger might show different behaviour as it inserts break instructions only when needed. This might change the code. What I still cannot explain is why I have different code at the same address during the same run.

2. The listing interleaves code simply because the compiler is probably optimising the code and putting the function inline. That is, the code from Write16BitRegister is copied directly into the body of the GetByteTerminalNoParity function. That is why I think it appears interleaved in the listing.

3. The value of the PIN after a break instruction probably changes much faster than my JTAG debugger is able to read the values of the I/O ports.

Cheers.