Forum: Mikrocontroller und Digitale Elektronik GCC Compiler + ATXMEGA E = Schrott?


von Christoph M. (chrito)


Lesenswert?

Hallöchen!

Ist das ein Compiler- oder Controller-Bug? Mache folgendes:
Verwende einen ATXMEGA8E5 mit dem AVR GNU C Compiler in Atmel Studio 7. 
Nehme eine nacktes (neues) Projekt (file>new: GCC Executable C project). 
Compiler-Optimierungen schalte ich ab.  Dann hacke ich folgenden simplen 
Code ein:
1
#include <avr/io.h>
2
uint8_t a=1;
3
4
int main(void)
5
{  
6
  while (1)
7
  { 
8
   if (a)
9
   {  
10
    a=0;
11
    PORTA.OUTTGL=0b00000001;  // LED blinken lassen
12
   }
13
  }
14
}

Was sollte passieren? Einmal sollte der if-Teil ausgeführt werden, 
danach nicht mehr. Es sollte der Compiler in der Endlosschleife laufen 
und ständig if(a) testen.

Was passiert? Das Programm schmiert ab und landet außerhalb des C-Codes 
in der Assembler-Anweisung:
  000000AF  RJMP PC-0x0000    Relative jump

Eigentlich möchte ich diese Struktur nutzen, um über einen Trigger a=1 
zu setzen und dann den if Teil auszuführen. Doch das Sch***teil 
produziert Ausschuss.

Kann sich jemand einen Reim drauf machen? Ist das ein Bug oder bin ich 
zu blöd?
Danke!
Grüße Christoph

: Bearbeitet durch Moderator
von Walter S. (avatar)


Lesenswert?

sieh dir mal das Assemblerlisting an

von Jagan (Gast)


Lesenswert?

> Ist das ein Bug oder bin ich zu blöd?

Du bist zu blöd!

von Foren-tourist (Gast)


Lesenswert?

Compiler erkennen dass "a" hier niemals wieder ungleich 0 wird und 
optimieren dein Schleigfenkonstrukt mit der Bedingung einfach weg -- und 
laesst nur eine enge Endlosschleife ohne Bedingung uebig.


Loesung:

Sage dem Compiler dass "a" von aussen veraendert wird und deklariere es 
als
"volatile":

  volatile int a = 1


... siehe https://de.wikipedia.org/wiki/Volatile_(Informatik)

von Peter II (Gast)


Lesenswert?

Foren-tourist schrieb:
> Compiler erkennen dass "a" hier niemals wieder ungleich 0 wird und
> optimieren dein Schleigfenkonstrukt mit der Bedingung einfach weg -- und
> laesst nur eine enge Endlosschleife ohne Bedingung uebig.

und? deswegen würde ich noch kein ungültiger Sprung entstehen und auch 
das Verhalten ( LED schaltet einmal um ) bleibt gleich

von Christoph M. (chrito)


Lesenswert?

Hola! Danke für eure Vorschläge!

@Foren-Tourist: Gute Idee. Hatte ich auch so ziemlich am Anfang. Die 
Variable als volatile zu deklarieren bringt leider keine Veränderung. 
Die Optimierungen sind auch wie gesagt abgeschaltet. Hier wird auch 
nüscht wegoptimiert eigentlich...

von Stefan K. (stefan64)


Lesenswert?

Das ist kein Bug, sondern Optimierung.
Benutze volatile für a, dann sieht Dein Ergebnis anders aus.

von Peter II (Gast)


Lesenswert?

Stefan K. schrieb:
> Das ist kein Bug, sondern Optimierung.
> Benutze volatile für a, dann sieht Dein Ergebnis anders aus.

nein, eine endlosschleife darf nicht wegoptimiert werden.

von Christoph M. (chrito)


Lesenswert?

Hier das dazugehörende Assembly Listing.
Im letzen Assembler-Befehl (RJMP)
verreckt der Controller:
1
00000056  CLR R1    Clear Register 
2
00000057  OUT 0x3F,R1    Out to I/O location 
3
00000058  SER R28    Set Register 
4
00000059  OUT 0x3D,R28    Out to I/O location 
5
0000005A  LDI R29,0x23    Load immediate 
6
0000005B  OUT 0x3E,R29    Out to I/O location 
7
0000005C  LDI R17,0x20    Load immediate 
8
0000005D  LDI R26,0x00    Load immediate 
9
0000005E  LDI R27,0x20    Load immediate 
10
0000005F  LDI R30,0xFE    Load immediate 
11
00000060  LDI R31,0x00    Load immediate 
12
00000061  RJMP PC+0x0003    Relative jump 
13
00000062  LPM R0,Z+    Load program memory and postincrement 
14
00000063  ST X+,R0    Store indirect and postincrement 
15
00000064  CPI R26,0x02    Compare with immediate 
16
00000065  CPC R27,R17    Compare with carry 
17
00000066  BRNE PC-0x04    Branch if not equal 
18
00000067  CALL 0x0000006D    Call subroutine 
19
00000069  JMP 0x0000007D    Jump 
20
0000006B  JMP 0x00000000    Jump 
21
--- c:\users\admin\Documents\Atmel Studio\7.0\GccApplication3\GccApplication3\Debug/.././main.c 
22
{
23
0000006D  PUSH R28    Push register on stack 
24
0000006E  PUSH R29    Push register on stack 
25
0000006F  IN R28,0x3D    In from I/O location 
26
00000070  IN R29,0x3E    In from I/O location 
27
    if (a)
28
00000071  LDS R24,0x2000    Load direct from data space 
29
00000073  TST R24    Test for Zero or Minus 
30
00000074  BREQ PC+0x08    Branch if equal 
31
      a=0;
32
00000075  STS 0x2000,R1    Store direct to data space 
33
      PORTA.OUTTGL=0b00000001;  // LED blinken lassen
34
00000077  LDI R24,0x00    Load immediate 
35
00000078  LDI R25,0x06    Load immediate 
36
00000079  LDI R18,0x01    Load immediate 
37
0000007A  MOVW R30,R24    Copy register pair 
38
0000007B  STD Z+7,R18    Store indirect with displacement 
39
  }
40
0000007C  RJMP PC-0x000B    Relative jump 
41
--- No source file -------------------------------------------------------------
42
0000007D  CLI     Global Interrupt Disable 
43
0000007E  RJMP PC-0x0000    Relative jump

: Bearbeitet durch Moderator
von Timmo H. (masterfx)


Lesenswert?

Also als erstes würde ich mal den Port Pin auch auf Ausgang setzen.
Das Listing für ein atxmegaE unterscheidet sich bei mir nicht von einem 
A:
1
000000da <main>:
2
3
#include <avr/io.h>
4
uint8_t a=1;
5
6
int main(void)
7
{
8
  da:  cf 93         push  r28
9
  dc:  df 93         push  r29
10
  de:  cd b7         in  r28, 0x3d  ; 61
11
  e0:  de b7         in  r29, 0x3e  ; 62
12
  while (1)
13
  {
14
    if (a)
15
  e2:  80 91 00 20   lds  r24, 0x2000
16
  e6:  88 23         and  r24, r24
17
  e8:  39 f0         breq  .+14       ; 0xf8 <main+0x1e>
18
    {
19
      a=0;
20
  ea:  10 92 00 20   sts  0x2000, r1
21
      PORTA.OUTTGL=0b00000001;  // LED blinken lassen
22
  ee:  80 e0         ldi  r24, 0x00  ; 0
23
  f0:  96 e0         ldi  r25, 0x06  ; 6
24
  f2:  21 e0         ldi  r18, 0x01  ; 1
25
  f4:  fc 01         movw  r30, r24
26
  f6:  27 83         std  Z+7, r18  ; 0x07
27
    }
28
  }
29
  f8:  f4 cf         rjmp  .-24       ; 0xe2 <main+0x8>

von Draco (Gast)


Lesenswert?

Woher weißt du das dein µC "abschmiert"? Was läßt dich das vermuten?

von Christoph M. (chrito)


Lesenswert?

Timmo H. schrieb:
> Also als erstes würde ich mal den Port Pin auch auf Ausgang setzen.
Ja, stimmt. Etwas schlampig. Wenn ich ihn setze bleibt der Programmfluss 
jedoch immernoch derselbe.

> Das Listing für ein atxmegaE unterscheidet sich bei mir nicht von einem
A:
Und? Schmiert dein XMEGA A auch ab wie der E?

von Christoph M. (chrito)


Lesenswert?

Draco schrieb:
> Woher weißt du das dein µC "abschmiert"? Was läßt dich das vermuten?
Der Controller hängt danach im Befehl
 0000007E  RJMP PC-0x0000    Relative jump
Wenn ich einen Haltepunkt auf if(a) setze, bleibt er dort nicht mehr 
stehen (nur einmal läuft er in diesen Haltepunkt, nämlich das erste 
Mal).

von Timmo H. (masterfx)


Lesenswert?

Naja ich würde es nicht unbedingt "abschmieren" nennen. Ich meine am 
ende passiert doch genau das was man erwartet. Toggle einmal den 
Port-Pin und mach dann nichts mehr.
Bei mir springt er von 0xe8 nach 0xF8 und von dort gleich wieder nach 
0xe2 um dann gleich wieder nach 0xF8 zu springen. Also alles gut oder?

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


Lesenswert?

Christoph M. schrieb:
> Der Controller hängt danach im Befehl
>  0000007E  RJMP PC-0x0000    Relative jump

Diesen Befehl kann er allerdings gar nicht offiziell erreichen.

Irgendwas anderes muss bei dir foul sein.

Lad' doch bitte mal das generierte ELF-File hoch.

Und: benutz' bitte [c]- oder [avrasm]-Tags, um deine Codeschnipsel
zu markieren.  Leute, die hier mit einem Smartphone/Tablet browsen,
bekommen Proportionalfonts, da ist das sonst völlig unleserlich.

von Christoph M. (chrito)


Angehängte Dateien:

Lesenswert?

Hier die generierte .elf-Datei.

Kleiner vielleicht wichtiger Zusatz: Wenn ich im if-Teil Dummy-Code 
einfüge, der etwas rechnet und mit ansonsten nicht verwendete Variablen 
arbeitet, dann geht's. Dann ist der Programmfluss wie erwartet. 
Allerdings muss der Dummy-Code offenbar eine Mindestlänge haben.

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


Lesenswert?

Ich würde das für einen Fehler im Debugger des Studios halten.

Der Code sieht völlig ordentlich aus.  Hier nochmal das Disassembly
der GNU binutils:
1
000000da <main>:
2
  da:   cf 93           push    r28
3
  dc:   df 93           push    r29
4
  de:   cd b7           in      r28, 0x3d       ; 61
5
  e0:   de b7           in      r29, 0x3e       ; 62
6
  e2:   80 91 00 20     lds     r24, 0x2000
7
  e6:   88 23           and     r24, r24
8
  e8:   39 f0           breq    .+14            ; 0xf8 <main+0x1e>
9
  ea:   10 92 00 20     sts     0x2000, r1
10
  ee:   80 e0           ldi     r24, 0x00       ; 0
11
  f0:   96 e0           ldi     r25, 0x06       ; 6
12
  f2:   21 e0           ldi     r18, 0x01       ; 1
13
  f4:   fc 01           movw    r30, r24
14
  f6:   27 83           std     Z+7, r18        ; 0x07
15
  f8:   f4 cf           rjmp    .-24            ; 0xe2 <main+0x8>

Man sieht deutlich, dass am Ende von main() ein unbedingter Rücksprung
nach 0xe2 (main+8) ist.  Da du nicht optimieren lassen hast, wird
die Variable "a" auf Adresse 0x2000 danach auch wirklich wieder
abgefragt und getestet.  Wenn sie 0 ist, erfolgt ein Sprung ans Ende
von main() (0xf8, also zum Rücksprung).

Die Wahrscheinlichkeit, dass der Debugger hier Mist macht, halte ich
für sehr viel größer als die, dass der Controller den Code nicht
richtig ausführt.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Christoph M. schrieb:
> Der Controller hängt danach im Befehl
>  0000007E  RJMP PC-0x0000    Relative jump
> Wenn ich einen Haltepunkt auf if(a) setze, bleibt er dort nicht mehr
> stehen (nur einmal läuft er in diesen Haltepunkt, nämlich das erste
> Mal).

 Nein, da bleibt er nicht hängen, die 2 Linien:
1
0000007D  CLI     Global Interrupt Disable 
2
0000007E  RJMP PC-0x0000    Relative jump

 hast du in jedem Program, aber da gelangt dein Program nie hin.

 Wie schon mehrfach erwähnt, ATMEL Studio ist Mist, Debugger unter
 aller Sau.

: Bearbeitet durch User
von Bernhard R. (barnyhh)


Lesenswert?

Ich denke, daß der Compiler völlig korrekt arbeitet. Er berücksichtigt 
offensichtlich sogar mehr, als ich erwartet hätte, und das erklärt den 
generierten Code:

Beim Betreten der Funktion "main" sind Interrupts disabled!
Dementsprechend kann unter keinen Umständen die Variable "a" außerhalb 
der Funktion "main" beschrieben werden. Der Compiler darf also annehmen, 
daß a nich außerhalb der main verändert wird.

Das Resultat ist der generierte - m.E. vollkommen korrekte - Code.

Bernhard

von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> Draco schrieb:
>> Woher weißt du das dein µC "abschmiert"? Was läßt dich das vermuten?
> Der Controller hängt danach im Befehl
>  0000007E  RJMP PC-0x0000    Relative jump
> Wenn ich einen Haltepunkt auf if(a) setze, bleibt er dort nicht mehr
> stehen (nur einmal läuft er in diesen Haltepunkt, nämlich das erste
> Mal).

Ist doch korrekt! Das hat genau die selbe Außenwirkung ("Observable 
Behavior"¹) wie erst umständlich zu dem if zu springen von dem er 
beweisen kann dass es eh immer false gibt, also braucht er da gar nicht 
erst hinzuspringen und kann die Endlosschleife kürzer machen.

__________________
¹) was man von außen beobachten kann, also Eingabe und Ausgabe. Was Du 
im Debugger siehst gehört NICHT zum observable behavior, im Debugger 
kann man auch mal rosa Elefanten sehen, alles was zählt ist Eingabe und 
Ausgabe des Programms.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> Hier das dazugehörende Assembly Listing.
> Im letzen Assembler-Befehl (RJMP)
> verreckt der Controller:

> 0000007C  RJMP PC-0x000B    Relative jump

Da steht RJMP PC-0x000B und nicht RJMP PC-0x0000 wie von Dir im ersten 
post behauptet. Also mal lieber erstmal die Brille aufsetzen! Er 
springt 11 zurück ungefähr dorthin wo das if sich befindet.

von Bastian W. (jackfrost)


Lesenswert?

> Wenn ich einen Haltepunkt auf if(a) setze, bleibt er dort nicht mehr
> stehen (nur einmal läuft er in diesen Haltepunkt, nämlich das erste
> Mal).

Bei mir hällt er immer bei der if Abfrage wenn die Optimierung auf -O0 
oder -O1 steht.

Ich verwende das Ateml Studio 7.0.582

Gruß JackFrost

: Bearbeitet durch User
von Christoph M. (chrito)


Lesenswert?

Bastian W. schrieb:
> Bei mir hällt er immer bei der if Abfrage wenn die Optimierung auf -O0
> oder -O1 steht.
Ich hab auch die Optimierung auf -O0 ...
Atmel Studio ist 7.0.790.
Mit welchem Controller hast du das getestet?

: Bearbeitet durch User
von Bastian W. (jackfrost)


Lesenswert?

Ich hab das mit einem ATXMEGA8E5 im Simulator geprüft da ich keinen 
xMega E zum testen habe

Gruß JackFrost

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Da steht RJMP PC-0x000B und nicht RJMP PC-0x0000 wie von Dir im ersten
> post behauptet.

Ja, hab mich vertan. Es ist allerdings genau umgekehr:
Er hängt in
 RJMP PC-0x0000
fest und springt dann (nicht verwunderlich) nicht mehr zu if(a) zurück.

Der Controller ist ein XMEGA8E5-U 1604B.

von Christoph M. (chrito)


Lesenswert?

Bastian W. schrieb:
> Ich hab das mit einem ATXMEGA8E5 im Simulator geprüft da ich keinen
> xMega E zum testen habe

Naja, wenn Jörg Wunsch Recht haben sollte, dann ist ein Bug im Debugger:
> Die Wahrscheinlichkeit, dass der Debugger hier Mist macht, halte ich
> für sehr viel größer als die, dass der Controller den Code nicht
> richtig ausführt.
 Mit dem Simulator nutzt du dann höchstwahrscheinlich ein anderes 
Debug-System, und das funktioniert korrekt...

: Bearbeitet durch User
von Bastian W. (jackfrost)


Lesenswert?

Ich hab mal auf 7.0.790 geupgradet. Da ist es das gleiche.

Ich hab es dann auf meinem Xplaind A1 mit dem JtagICE Mark 3 probiert. 
Da ist es auch das gleiche. Bei -O0 und -O1 hält er bei der Abfrage.

Bei -O0 ist auch a in der Watchlist 0. Bei -O1 bleibt a 1 aber der Teil 
in der Schleife wird nur einmal ausgeführt.

Gruß JackFrost

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bastian W. schrieb:
> Bei -O1 bleibt a 1 aber der Teil in der Schleife wird nur einmal
> ausgeführt.

Was für eine nicht-volatile Variable auch zu erwarten war.

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Ist doch korrekt! Das hat genau die selbe Außenwirkung ("Observable
> Behavior"¹) wie erst umständlich zu dem if zu springen von dem er
> beweisen kann dass es eh immer false gibt, also braucht er da gar nicht
> erst hinzuspringen und kann die Endlosschleife kürzer machen.

Ne, also was kürzer machen darf er nicht. Die Optiemierung steht auf 
-O0, ist also aus.
1
#include <avr/io.h>
2
uint8_t a=1;
3
4
int main(void)
5
{  
6
  while (1)
7
  { 
8
   if (a)
9
   {  
10
    a=0;
11
    PORTA.OUTTGL=0b00000001;  // LED blinken lassen
12
   }
13
  }
14
  asm("NOP");  // Assembler-Nop, dann wird Code richtig ausgeführt!?
15
}

Ein Nop hinter dem if sorgt dafür, dass es geht. Warum auch immer. 
Wahrscheinlich ist der generierte Assembler dann doch nicht ganz 
korrekt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christoph M. schrieb:
> Ein Nop hinter dem if sorgt dafür, dass es geht.

Das NOP wird aber nie ausgeführt.  Es bewirkt lediglich, dass ein 
grottiger Debugger nicht abschmiert — warum auch himmer.  Frag Atmel.

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


Lesenswert?

Christoph M. schrieb:
> Wahrscheinlich ist der generierte Assembler dann doch nicht ganz
> korrekt.

Der generierte Code war doch oben zu sehen, und ist völlig
nachvollziehbar korrekt.

> Ne, also was kürzer machen darf er nicht. Die Optiemierung steht auf
> -O0, ist also aus.

Auch das bedeutet aber nicht, dass du einen irgendwie gearteten
Anspruch auf einen konkreten generierten Code hättest.  Du kannst
nur hoffen, dass er damit alle Variablen so behandelt, als wären sie
"volatile" (macht er ja auch, wie oben im Code gezeigt), aber eine
Garantie gibt es auch dafür nicht:
1
5.1.2.3 Program execution
2
The semantic descriptions in this International Standard describe the behavior of an
3
abstract machine in which issues of optimization are irrelevant.

von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> Ne, also was kürzer machen darf er nicht.

Wo steht das geschrieben? Er darf alles wegkürzen was nicht erforderlich 
ist um das erwünschte Verhalten (Eingabe, Ausgabe) zu erzielen. Dein 
Programm sieht nach Kürzung aller unnötigen Verrenkungen so aus:
1
#include <avr/io.h>
2
3
/**
4
 * LED einschalten,
5
 * handoptimierte Version
6
 */
7
int main(void) {  
8
    PORTA.OUTTGL=0b00000001;  // LED einschalten und...
9
    while (1);                // ...Däumchen drehen.
10
}

Es tätigt die selben Eingaben (nämlich keine) und tätigt die selben 
Ausgaben (nämlich LED genau einmal einschalten) und danach niemals enden 
in genau der selben Reihenfolge. Das ist also eine gültige Optimierung.

: Bearbeitet durch User
von Christoph M. (chrito)


Lesenswert?

Johann L. schrieb:
> Das NOP wird aber nie ausgeführt.

Doch, hab gerad geguckt. Er führt es aus und ich kann auch einen 
Haltepunkt darauf setzen.

Jörg W. schrieb:
> Der generierte Code war doch oben zu sehen, und ist völlig
> nachvollziehbar korrekt.

Und wo steckt dann der Bug? Code ist ok, wird auf den Controller geladen 
und ausgeführt... der schmiert dann aber ab, d.h. Programm läuft nicht 
mehr weiter. Auch ohne Debugger und Haltepunkte.

von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> der schmiert dann aber ab,

Woran siehst Du das?

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Wo steht das geschrieben? Er darf alles wegkürzen was nicht erforderlich
> ist um das erwünschte Verhalten (Eingabe, Ausgabe) zu erzielen.

Ne, darf er nicht. Und der Fehler tritt auch auf, wenn "a" als volatile 
deklariert wird. In einer komplexeren Variante dieses Programmes setze 
ich a im Timer wieder auf 1. Das funktioiniert nur mit der Zeile
1
 asm("NOP");
und das kann ja wohl eigentlich keinen Unterschied machen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christoph M. schrieb:
> Code ist ok, wird auf den Controller geladen
> und ausgeführt... der schmiert dann aber ab, d.h. Programm läuft nicht
> mehr weiter. Auch ohne Debugger und Haltepunkte.

Und wie erhennst du das?

Was ist mit:

- Watchdog
- Pest auf der Spannungsversorgung
- Chinaschrott

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Christoph M. schrieb:
>> der schmiert dann aber ab,
>
> Woran siehst Du das?

1. Verwende keinen Debugger, a wird über Timer auf 1 gesetzt:
=> LED blinkt nicht mehr.
2. Ohne Debugger, ohne Timer und mit
1
 PORTA.OUT=0b00000001;
 => LED wird nie gesetzt
3. Mit Debugger:
=> Steht im Assembler-Befehl
1
 0000007E  RJMP PC-0x0000    Relative jump
und läuft nicht mehr weiter.

Mit dem Dummy-Nop gehen alle drei Fälle wie erwartet.

von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> Bernd K. schrieb:
>> Wo steht das geschrieben? Er darf alles wegkürzen was nicht erforderlich
>> ist um das erwünschte Verhalten (Eingabe, Ausgabe) zu erzielen.
>
> Ne, darf er nicht.

Doch das darf er. Und das soll er auch, das wünscht man sich nämlich von 
einem guten Compiler.

> Und der Fehler tritt auch auf, wenn "a" als volatile
> deklariert wird.

welcher Fehler?

> In einer komplexeren Variante dieses Programmes setze
> ich a im Timer wieder auf 1. Das funktioiniert nur mit der Zeile
> asm("NOP");

Dann deklariere (in der komplexeren Variante die Du nicht gepostet hast) 
die Variable a als volatile, dann zählt nämlich jeder Zugriff darauf im 
Sinne der obigen Definition als *von außen sichtbare Eingabe/Ausgabe* 
(observable bahavior) und muss vom Programm in genau in der 
vorgeschriebenen Anzahl und Reihenfolge durchgeführt werden, also darf 
der Compiler Zugriffe darauf nicht mehr wegoptimieren.

Fertig.

Wenns dann immer noch nicht geht ist was anderes in Deinem Programm 
fehlerhaft. Ein Compilerfehler ist es jedenfalls garantiert nicht, eher 
gewinnst Du im Lotto als mit einem Hello-World-Blinky in einem der 
meistgenutzten Compiler der Welt noch einen bis dato unentdeckten 
Compilerfehler zu finden.

von Christoph M. (chrito)


Lesenswert?

Johann L. schrieb:
> - Watchdog
> - Pest auf der Spannungsversorgung
> - Chinaschrott

- Watchdog ist aus
- Spannung ist sauber 3,3V
- Chinaschrott? Hab das Ding bei mouser bestellt.

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Dann deklariere (in der komplexeren Variante die Du nicht gepostet hast)
> die Variable a als volatile, dann zählt nämlich jeder Zugriff darauf im
> Sinne der obigen Definition als *von außen sichtbare Eingabe/Ausgabe*
> (observable bahavior) und muss vom Programm in genau in der
> vorgeschriebenen Anzahl und Reihenfolge durchgeführt werden, also darf
> der Compiler Zugriffe darauf nicht mehr wegoptimieren.

hab ich ja als volatile deklariert. Bringt nüscht.

von Christoph M. (chrito)


Lesenswert?

Bernd K. schrieb:
> Wenns dann immer noch nicht geht ist was anderes in Deinem Programm
> fehlerhaft. Ein Compilerfehler ist es jedenfalls garantiert nicht, eher
> gewinnst Du im Lotto als mit einem Hello-World-Blinky in einem der
> meistgenutzten Compiler der Welt noch einen bis dato unentdeckten
> Compilerfehler zu finden.

Ja, find ich ja auch ungewöhnlich. Hardware-Bug? Also auf nem XMega A 
geht es, auf nem XMega E nicht.

von Bernd K. (prof7bit)


Lesenswert?

Poste die komplette Version, das komplette Minimal-Projekt das den 
Fehler zeigt (also inclusive Timer-Interrupt und allem drum und dran) so 
daß es die Leute hier mal selbst ausprobieren und/oder sehen können was 
Du da falsch gemacht hast.

von tf (Gast)


Lesenswert?

Ich sehe da kein Bug.
Der Ausgang wird einmal umgeschaltet und dann macht das Programm nichts 
mehr. Es ist doch egal ob er die While-Schleife durchläuft oder ob er 
immer am "RJMP PC" kreist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christoph M. schrieb:
> a wird über Timer auf 1 gesetzt:

Hä? Dein Programm besteht doch i.w. nur aus einer Zeile:

> PORTA.OUTTGL=0b00000001;

Oder steckt das Problem wie so häufig in der Obfuscation

// Some code

von Christoph M. (chrito)


Lesenswert?

Bitte schön:
1
#include <avr/io.h>
2
#include <avr/sleep.h>
3
#include <avr/interrupt.h>
4
5
uint8_t a=1;
6
uint16_t Millisekunden;
7
uint8_t Sekunden;
8
uint8_t Trigger_Sekunde;
9
uint8_t Trigger_Millisekunden;
10
11
void initialisiere_System()            // CPU auf 8 MHz laufen lassen
12
{  
13
  asm("LDI R24,0xD8");            // (1.) 0xD8 in temporäres Register R24 laden
14
  asm("OUT 0x34,R24");            // (1.) 0xD8 nach 0x34 (=CCP) schreiben
15
  asm("LDI R24,0x02");            // (2.) 0x02 in temporäres Register R24 laden
16
  asm("STS 0x0050,R24");            // (2.) 0x02 nach 0x0050 (==OSC.CTRL) schreiben
17
  while(!(OSC.STATUS & OSC_RC32MRDY_bm));    // Warten, bis Oszillator bereit ist
18
19
  asm("LDI R24,0xD8");            // (1.) 0xD8 in temporäres Register R24 laden
20
  asm("OUT 0x34,R24");            // (1.) 0xD8 nach 0x34 (=CCP) schreiben
21
  asm("LDI R24,0x01");            // (2.) 0x01 in temporäres Register R24 laden
22
  asm("STS 0x0040,R24");            // (2.) 0x01 nach 0x0040 (==CLK.CTRL) schreiben (S.87)
23
  
24
  PMIC.CTRL |= PMIC_HILVLEN_bm |PMIC_MEDLVLEN_bm|PMIC_LOLVLEN_bm;  // Interrupts High-,Medium- und Lowlevel freigeben
25
  sei();                    // Interrupts aktivieren
26
}
27
28
void initialisiere_Timer4()        // initialisiere mit einem Zeitintervall von 1mSek
29
{  
30
  cli();                    // Interrupts deaktivieren
31
  TCC4.CTRLA|= 0b00000110;
32
  TCC4.CTRLA&= 0b11110110;          // Prescaler auf 1:256          1/8MHz = 0,125µSek ;  0,125µSek * 64 = 8µSek
33
  TCC4.CTRLB = 0x00;              // select Modus: Normal
34
  TCC4.PER = 125;                // Zählerwert bis Überlauf = 125 ;    8µSek * 125 = 1mSek (Zeitintervall für Timer0)
35
  TCC4.CNT = 0x00;              // Zähler zurücksetzen
36
  TCC4.INTCTRLA = 0b00000011;          // Interrupt Highlevel
37
}
38
39
ISR(TCC4_OVF_vect)                              // ISR wird einmal pro Millisekunde ausgeführt
40
{  
41
  TCC4.INTFLAGS = TC4_OVFIF_bm;                      // Interrupt-Flags müssen bei
42
  Millisekunden++;                            // diesem Typen gelöscht werden! ^^
43
  Trigger_Millisekunden=1;
44
  
45
  if (Millisekunden>1000)
46
  {                                    
47
    Millisekunden=0;                      
48
    Trigger_Sekunde=1;                  // jede Sekunde
49
  }
50
}
51
52
void initialisiere_Ports(void)
53
{
54
  PORTA.DIR = 0b11111111;    // 1 für Ausgang, 0 für Eingang, Opto-Aus
55
  PORTC.DIR = 0b00000000;    // 1 für Ausgang, 0 für Eingang
56
  PORTR.DIR = 0b00000000;    // 1 für Ausgang, 0 für Eingang
57
  PORTD.DIR = 0b00000000;    // 1 für Ausgang, 0 für Eingang, Opto-Ein
58
}
59
60
int main(void)
61
{  
62
  initialisiere_Timer4();
63
  initialisiere_System();
64
  initialisiere_Ports();
65
66
  while (1)
67
  {
68
    if (Trigger_Sekunde)
69
    {
70
      Trigger_Sekunde=0;
71
      PORTA.OUTTGL=0b00000001;  // LED blinken lassen
72
    }
73
    asm("NOP"); // Dummi-NOP
74
  }
75
}

von Walter T. (nicolas)


Lesenswert?

Wieso hat main() eigentlich kein return?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Wieso hat main() eigentlich kein return?

Spielt hier keine Rolle.

von Steffen R. (steffen_rose)


Lesenswert?

Walter T. schrieb:
> Wieso hat main() eigentlich kein return?

wegen dem while(1)

von Steffen R. (steffen_rose)


Lesenswert?

Auch wenn es deiner Meinung nichts bringt, aber Trigger_Sekunde sollte 
volatile sein. Aus meiner Sicht bringt die Fehlersuche an einem formal 
fehlerhaften Programm wenig.

Da der Compiler für Trigger_Sekunde den Interrupt nicht beachtet und 
dieser Variable vorab 0 ist, wird der Teil völlig ignoriert.
Der Code ist leicht anders als dein obiges Beispiel, bei dem die 
relevante Variable mit 1 initialisiert wurde.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

> Wieso hat main() eigentlich kein return?

Spielt keine Rolle. Wenn main() nicht in einer Endlosschleife endet, 
dann fügt der Compiler eine ein. Main endet beim avr-gcc niemals.

Ist eine der wenigen Abweichungen vom C Standard.

von Stefan F. (Gast)


Lesenswert?

Ich habe es gerade ohne den dummy NOP unter Ubuntu 15.04 mit -O0 
compiliert und gelistet:
1
0000023c <main>:
2
 23c:  cf 93         push  r28
3
 23e:  df 93         push  r29
4
 240:  cd b7         in  r28, 0x3d  ; 61
5
 242:  de b7         in  r29, 0x3e  ; 62
6
 244:  0e 94 9b 00   call  0x136  ; 0x136 <initialisiere_Timer4>
7
 248:  0e 94 75 00   call  0xea  ; 0xea <initialisiere_System>
8
 24c:  0e 94 06 01   call  0x20c  ; 0x20c <initialisiere_Ports>
9
 250:  80 91 01 20   lds  r24, 0x2001
10
 254:  88 23         and  r24, r24
11
 256:  41 f0         breq  .+16       ; 0x268 <main+0x2c>
12
 258:  10 92 01 20   sts  0x2001, r1
13
 25c:  80 e0         ldi  r24, 0x00  ; 0
14
 25e:  96 e0         ldi  r25, 0x06  ; 6
15
 260:  21 e0         ldi  r18, 0x01  ; 1
16
 262:  fc 01         movw  r30, r24
17
 264:  27 83         std  Z+7, r18  ; 0x07
18
 266:  f4 cf         rjmp  .-24       ; 0x250 <main+0x14>
19
 268:  f3 cf         rjmp  .-26       ; 0x250 <main+0x14>
Ich kann hier keinen Fehler erkennen. Am Ende von main() sehe zwei rjmp 
Befehle die zurück zu dem if Ausdruck springen.

Und jetzt nochmal mit -O1:
1
000001aa <main>:
2
 1aa:  0e 94 8b 00   call  0x116  ; 0x116 <initialisiere_Timer4>
3
 1ae:  0e 94 75 00   call  0xea  ; 0xea <initialisiere_System>
4
 1b2:  0e 94 cb 00   call  0x196  ; 0x196 <initialisiere_Ports>
5
 1b6:  80 91 01 20   lds  r24, 0x2001
6
 1ba:  e0 e0         ldi  r30, 0x00  ; 0
7
 1bc:  f6 e0         ldi  r31, 0x06  ; 6
8
 1be:  91 e0         ldi  r25, 0x01  ; 1
9
 1c0:  81 11         cpse  r24, r1
10
 1c2:  01 c0         rjmp  .+2        ; 0x1c6 <main+0x1c>
11
 1c4:  ff cf         rjmp  .-2        ; 0x1c4 <main+0x1a>
12
 1c6:  97 83         std  Z+7, r25  ; 0x07
13
 1c8:  80 e0         ldi  r24, 0x00  ; 0
14
 1ca:  fa cf         rjmp  .-12       ; 0x1c0 <main+0x16>
Auch hier endet die main() Funktion mit einem Rücksprung zum 
if-Ausdruck.

Also wenn da was "abschmiert" dann ist es dein Simulator oder Debugger. 
Der Compiler ist in Ordnung.

von Bernd K. (prof7bit)


Lesenswert?

Stefan U. schrieb:
> Wieso hat main() eigentlich kein return?
>
> Spielt keine Rolle. Wenn main() nicht in einer Endlosschleife endet,
> dann fügt der Compiler eine ein. Main endet beim avr-gcc niemals.
>
> Ist eine der wenigen Abweichungen vom C Standard.

Ich glaube nicht dass das eine Abweichung ist sondern eher dass nach 
einer Rückkehr aus main() die Funktion _exit() aufgerufen wird welche 
aus einer Endlosschleife besteht und beim Optimieren kurzerhand 
geinlined wird.

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


Lesenswert?

Stefan U. schrieb:
> Ist eine der wenigen Abweichungen vom C Standard.

Nein, ist keine.  Der Startupcode macht das Äquivalent von:
1
exit(main());

Die Funktion exit() kannst du dabei bei Bedarf selbst überschreiben.
Die Vorgabe ist ein cli, gefolgt von einem Sprung nach _exit, welches
die besagte Endlosschleife ist.

Christoph M. schrieb:
> void initialisiere_System()            // CPU auf 8 MHz laufen lassen
> {
>   asm("LDI R24,0xD8");            // (1.) 0xD8 in temporäres Register
> R24 laden
>   asm("OUT 0x34,R24");            // (1.) 0xD8 nach 0x34 (=CCP)
> schreiben
>   asm("LDI R24,0x02");            // (2.) 0x02 in temporäres Register
> R24 laden
>   asm("STS 0x0050,R24");            // (2.) 0x02 nach 0x0050
> (==OSC.CTRL) schreiben

Dafür gibt es in <avr/xmega.h> den Makro _PROTECTED_WRITE().
1
_PROTECTED_WRITE(OSC_CTRL, OSC_RC32MEN_bm);

> void initialisiere_Timer4()        // initialisiere mit einem
> Zeitintervall von 1mSek
> {
>   cli();                    // Interrupts deaktivieren
>   TCC4.CTRLA|= 0b00000110;
>   TCC4.CTRLA&= 0b11110110;          // Prescaler auf 1:256
> 1/8MHz = 0,125µSek ;  0,125µSek * 64 = 8µSek
>   TCC4.CTRLB = 0x00;              // select Modus: Normal
>   TCC4.PER = 125;                // Zählerwert bis Überlauf = 125 ;
> 8µSek * 125 = 1mSek (Zeitintervall für Timer0)
>   TCC4.CNT = 0x00;              // Zähler zurücksetzen
>   TCC4.INTCTRLA = 0b00000011;          // Interrupt Highlevel
> }

Böse Falle!  Wenn eine Funktion Interrupts sperrt, dann sollte sie
sie auch wieder freigeben oder die Freigabe auf den vorherigen
Wert rücksetzen.

Dass Trigger_Sekunde "volatile" sein muss, wurde dir ja schon genannt.

Ansonsten vermisse ich die Initialisierung des PMIC.  Ich vermute,
dass dein Vektor deshalb gar nicht erst aufgerufen wird, weil du
keinen Interrupt im PMIC freigegeben hast.

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christoph M. schrieb:
>
1
>   while (1)
2
>   {
3
>     if (Trigger_Sekunde)
4
>     {
5
>       Trigger_Sekunde=0;
6
>       PORTA.OUTTGL=0b00000001;  // LED blinken lassen
7
>     }
8
>     asm("NOP"); // Dummi-NOP
9
>   }
10
> }
11
>

Vorhin stand das NOP noch außerhalb der while-Schleife...

von Bernd K. (prof7bit)


Lesenswert?

Christoph M. schrieb:
> void initialisiere_System()            // CPU auf 8 MHz laufen lassen
> {
>   asm("LDI R24,0xD8");            // (1.) 0xD8 in temporäres Register R24 laden
> [noch mehr asm]

Hat zwar direkt mit dem Problem des Themas nichts zu tun aber was um 
alles in der Welt soll das ganze unnötige asm da? Warum schreibst Du es 
nicht kürzer und lesbarer in C hin?

Und warum werden dort die Register und die Bits nicht beim Namen genannt 
sondern die nackten Zahlen in hex hingeschrieben? Willst Du einen 
Obfuscation-Contest gewinnen?

Und warum ist dieses unnötige asm auf 8 einzelne asm() statements 
verteilt, ohne clobber, ohne output, ohne volatile? Dir ist schon klar 
daß der Compiler das umsortieren oder wegoptimieren darf wenn er keine 
Verdachtsmomente darauf findet daß dieser Code zu irgendwas nütze sein 
könnte?

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Warum schreibst Du es nicht kürzer und lesbarer in C hin?

Weil du damit das Timing für das CCP nicht sicherstellen kannst.

Daher auch mein Hinweis auf _PROTECTED_WRITE(), darin ist der
entsprechende inline-asm-Code für CCP bereits enthalten.

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.