Forum: Compiler & IDEs avr-gcc Bug, oder weshalb Behandlung als signed?


von ein Gast (Gast)


Lesenswert?

Ich habe eine Schleife geschrieben, welche, falls die normale 
Ausführungsbedingung innerhalb einer bestimmten Zeit nicht false wird, 
durch ein Timeout beendet werden soll. Diese ist folgendermaßen 
realisiert:
1
...
2
uint16_t timeout = 0;
3
...  
4
while ((!(TWCR & (1<<TWINT)))&&(timeout<65000U)){timeout++;}      
5
...
Beim Debugging in Assembler sieht dies folgenermaßen aus:
1
00000296  ADIW R24,0x01    Add immediate to word 
2
00000297  LDD R18,Z+0    Load indirect with displacement 
3
--- C:\Users\User\Desktop\Firmware\Debug/.././i2c_functions.c 
4
00000298  TST R18    Test for Zero or Minus 
5
00000299  BRLT PC+0x06    Branch if less than, signed 
6
--- Keine Quelldatei -----------------------------------------------------------
7
0000029A  CPI R24,0xE8    Compare with immediate 
8
0000029B  LDI R18,0xFD    Load immediate 
9
0000029C  CPC R25,R18    Compare with carry 
10
0000029D  BRNE PC-0x07    Branch if not equal 
11
0000029E  RJMP PC+0x0004    Relative jump

Die Schleife wird offenbar nicht beendet, wenn timeout>=65000 ist.
So ich den Assembler Code richtig interpretiere zeigt sich mir als Grund 
hierfür, dass die Überprüfung ob timeout<65000 ist als signed 
durchgeführt wird, obwohl beide Werte 16-Bit unsigned sein sollten. Da 
der Wertebereicht bei 16-Bit signed Zahlen nur bis 32767 reicht, 
erscheint es mir plausiebel dass timeout immer kleiner als 65000 ist.

Relativiert sich dies wieder da eventuell beide Zahlen als signed 
behandelt werden?
Ich habe wenig Erfahrung mit Assembler, jedoch iritiert mich diese 
Codestelle in Verbindung mit dem Verhalten beim Debugging.
Vielleicht könnte jemand mit etwas mehr Erfahrung in Assembler eine 
Einschätzung geben ob dies eine korrekte Optimierung des Compilers ist 
oder ob hier wirklich etwas faul ist oder Fehler meinerseits auffallen.

Ich verende Atmel Studio 6.1.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

ein Gast schrieb:
> So ich den Assembler Code richtig interpretiere

Ich denke nicht, dass du ihn richtig interpretierst.

Gib aber bitte mal ein vollständiges, compilierbares Beispiel.

von Oliver (Gast)


Lesenswert?

ein Gast schrieb:
> 0000029A  CPI R24,0xE8    Compare with immediate
> 0000029B  LDI R18,0xFD    Load immediate
> 0000029C  CPC R25,R18    Compare with carry
> 0000029D  BRNE PC-0x07    Branch if not equal
> 0000029E  RJMP PC+0x0004    Relative jump
> [/avrasm]
>
> Die Schleife wird offenbar nicht beendet,

offenbar doch.
doc0856.pdf RTFM...


Oliver

von (prx) A. K. (prx)


Lesenswert?

ein Gast schrieb:
> So ich den Assembler Code richtig interpretiere zeigt sich mir als Grund
> hierfür, dass die Überprüfung ob timeout<65000 ist als signed
> durchgeführt wird,

Nein. Der BRLT Befehl testet TWINT von TWCR. Ist nämlich Bit 7.

timeout wird dahinter mit BRNE getestet, weil der Compiler merkt, dass 
dieser Test hier äquivalent zu U< ist.

von ein Gast (Gast)


Lesenswert?

>Gib aber bitte mal ein vollständiges, compilierbares Beispiel.
Ich habe zusammenkopiert, was ich für relevant halte.
Der Timeout in in den weiteren I²C-Funktionen identisch realisiert.
1
#include <avr/io.h>
2
#define I2C_PORT  PORTC
3
#define SDA      PC1
4
#define SCL      PC0
5
6
char i2c_init(void)
7
{
8
  I2C_PORT |= (1<<SDA)|(1<<SCL);          //use internal pullup resistors instead of external ones. In external ones are present this line has to be removed / commented out.
9
  TWBR  = 100;                  //set SCL speed to F_CPU/(16+2*TWBR)
10
  TWSR  = TWSR & 252;                //make sure prescaler bits are cleared to keep the additional prescaler out of the equation above.
11
  return 0;
12
}
13
14
char i2c_start(void)
15
{
16
  uint16_t timeout = 0;
17
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);    //generate START condition (TWINT ~ do command, TWSTA ~ command for Startcondition, TWEN ~ enable I2C unit)
18
  while ((!(TWCR & (1<<TWINT)))&&(timeout<65000U)){timeout++;}    //wait as long as I2C unit is busy   
19
  if(timeout >= 65000U)
20
  {
21
    TWCR |=(1<<TWSTO) | (1<<TWINT);
22
    TWCR =(1<<TWEA) | (1<<TWINT) | (1<<TWEN); 
23
  }
24
  if (!(TWSR & 0x18)) return 1;        //check if start condition actually occurred, otherwise return 1          
25
  return 0;                  //if everything is allright return 0
26
}
27
28
int main(void)
29
{
30
    i2c_init();
31
  while(1)
32
    {
33
        i2c_start();
34
    }
35
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

ein Gast schrieb:
> Der Timeout in in den weiteren I²C-Funktionen identisch realisiert.

Der funktioniert aber auch, wie dir andere schon erklärt haben.

Hier mal der generierte Assemblercode, den ich für -mmcu=atmega16
bekomme.  Ich habe mal ein paar Kommentare eingefügt.
1
i2c_start:
2
/* prologue: function */
3
/* frame size = 0 */
4
/* stack size = 0 */
5
.L__stack_usage = 0
6
        ldi r24,lo8(-92)
7
        out 0x36,r24     ; TWCR = ...
8
        ldi r24,0        ; timeout = 0 // timeout liegt in r[25:24]
9
        ldi r25,0
10
        rjmp .L3
11
.L5:
12
        adiw r24,1       ; timeout++
13
.L3:
14
        in __tmp_reg__,0x36  ; Test von TWINT in TWCR
15
        sbrc __tmp_reg__,7   ; sieht hier etwas anders aus als bei
16
                             ; deiner Version, funktioniert aber genauso
17
        rjmp .L4             ; wenn TWINT nicht (mehr) gesetzt, dann
18
                             ; springe nach .L4
19
        cpi r24,-24          ; -3 => 0xFD, -24 => 0xE8; 0xFDE8 = 65000
20
        ldi r18,-3
21
        cpc r25,r18
22
        brne .L5             ; solange timeout ungleich 65000, zurück nach .L5
23
        rjmp .L6             ; wenn timeout erreicht, nach .L6 (Fehlerbehandlung)
24
.L4:
25
        cpi r24,-24          ; nochmal Test auf 65000
26
        sbci r25,-3
27
        brne .L7
28
.L6:
29
        in r24,0x36
30
        ori r24,lo8(-112)
31
        out 0x36,r24
32
        ldi r24,lo8(-60)
33
        out 0x36,r24
34
.L7:
35
        in r25,0x1
36
        andi r25,lo8(24)
37
        ldi r24,lo8(1)
38
        breq .L8
39
        ldi r24,0
40
.L8:
41
        ret

von ein Gast (Gast)


Lesenswert?

Danke, ich kann es jetzt nachvollziehen.
Es hatte vermutlich das Dragon nur wieder Probleme beim Debugging.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.