Forum: Compiler & IDEs AVR Studio 5 meckert sehr viel


von Knut (Gast)


Lesenswert?

Also ich habe jetzt mit AVR Studio 5 ein altes Projekt versucht zu 
kompilieren und irgendwie ist der Wurm drinn.

Bei 4.XX konnte man z.B. ohne Problem Code generieren, wenn man z.B. mit 
der UART Schnittstelle gearbeitet hat, es gab lediglich ne Warnung wenn 
die Datentypen anders waren als vorgesehen. In Version 5 bricht der 
sofort ab, wenn z.B. eine Variable uint8_t sein soll, aber volatile 
uint8_t ist.

Wie kann man dem Compiler das austreiben? Gibts da n Kommando/Befehl?
Warum ist Atmel so eine ungeheure Bastelbude was das angeht?


Knut

: Verschoben durch Moderator
von Jasch (Gast)


Lesenswert?

Knut schrieb:
> Also ich habe jetzt mit AVR Studio 5 ein altes Projekt versucht zu
> kompilieren und irgendwie ist der Wurm drinn.

???

> Bei 4.XX konnte man z.B. ohne Problem Code generieren, wenn man z.B. mit

Ich glaube nicht dass Du wirklich generieren meinst, eher wohl 
schreiben?

> der UART Schnittstelle gearbeitet hat, es gab lediglich ne Warnung wenn
> die Datentypen anders waren als vorgesehen. In Version 5 bricht der
> sofort ab, wenn z.B. eine Variable uint8_t sein soll, aber *volatile*
> uint8_t ist.

Ist doch gut, das ist potentiell ein Bug?

> Wie kann man dem Compiler das austreiben? Gibts da n Kommando/Befehl?
> Warum ist Atmel so eine ungeheure Bastelbude was das angeht?

Ist es nicht.

Das ist der benutzte GCC und vermutlich die von der IDE per Default 
benutzten Optionen. Wenn Du das wirklich abstellen willst: 
Projektoptionen im AVR Studio und die Doku zu den GCC-Optionen ansehen.

Hinweis: Du willst die Warnungen/Fehler nicht wirklich einfach 
deaktivieren. Das sind fast(!) alles potentielle Bugs, man kommt 
wesentlich besser weg wenn man die Ursachen verstehen lernt und den Code 
fixt. Gerade bei volatile...

von Micha (Gast)


Lesenswert?

Knut schrieb:
> In Version 5 bricht der sofort ab, wenn z.B. eine Variable uint8_t sein
> soll, aber volatile uint8_t ist.
Bist du dir da sicher? Poste mal den code dazu.

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


Lesenswert?

Knut schrieb:
> Warum ist Atmel so eine ungeheure Bastelbude was das angeht?

Weil du deinem Compiler offenbar Bastelbuden-Code vorlegst. ;-)

Nicht alles, was compiliert, muss auch notwendigerweise korrekter
Code sein.  Man kann lediglich sagen, dass ein Compiler aus korrektem
Code auch ein korrektes Programm bauen muss, aber er ist nicht
verpflichtet, jede Unkorrektheit wirklich festzustellen und mit einem
Fehler oder einer Warnung zu honorieren.  Da der GCC stetig immer
besser wird, Unkorrektheiten zu erkennen, moniert er nun halt Dinge
an, die in deinem Code zwar bereits früher unkorrekt waren, was er
aber in einer älteren Version noch nicht erkannt hat.

Die Firma Atmel hat damit weiter nichts zu tun, als dass sie einen
recht guten Compiler zusammen mit ihrer IDE ausliefert.

von Knut (Gast)


Lesenswert?

Hmm... seit der neuen Toolchain meckert nun auch 4.19.
Ist vlt. auch gar nicht schelcht. Aber wenn ich eine Variable volatile 
mache, weil sie in einer ISR benutzt wird, aber als "normal" vorgesehen 
ist, wie kann ich das Problem umgehen, dass er meckert?


Knut

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


Lesenswert?

Knut schrieb:
> wie kann ich das Problem umgehen, dass er meckert?

Du darfst erstmal "volatile" verstehen.  Nur, weil die Variable in
einer ISR benutzt wird, muss sie nicht volatile sein.

Wenn der Code trotz der Warnung funktioniert, ist es ein gutes
Zeichen, dass das volatile gar nicht nötig war.

von Peter D. (peda)


Lesenswert?

Man muß Variablen nicht volatile machen.
Man kann sie mit eine Macro im Main-Kontext nach volatile casten:
1
// force access of interrupt variables
2
#define IVAR(x)         (*(volatile typeof(x)*)&(x))


Peter

von Εrnst B. (ernst)


Lesenswert?

Peter Dannegger schrieb:
> Man kann sie mit eine Macro im Main-Kontext nach volatile casten

Bei 8bit-Werten klappt das Makro noch, aber bei größeren Werten ist es 
eine recht unangenehm zu debuggende Fehlerquelle, sollte man als Warnung 
dazuschreiben.

Im allgemeinen wär's besser, vorher "volatile" zu verstehen, und sich zu 
überlegen, ob man atomaren Zugriff oder eine memory barrier braucht, und 
ob man dies durch ein schreibfaules einfaches "volatile" simulieren 
kann.

von Micha (Gast)


Lesenswert?

Vielleicht sollte er mal ein Minimal-Beispiel posten, das seiner Meinung 
nach compilieren sollte, aber den Fehler wirft.

von Charly (Gast)


Lesenswert?

AVR Studio reicht doch bei C nur an WinAVR zum Compilieren weiter.

von Stefan E. (sternst)


Lesenswert?

Εrnst B✶ schrieb:
> Bei 8bit-Werten klappt das Makro noch, aber bei größeren Werten ist es
> eine recht unangenehm zu debuggende Fehlerquelle, sollte man als Warnung
> dazuschreiben.

Was genau meinst du damit?

von Εrnst B. (ernst)


Lesenswert?

Stefan Ernst schrieb:
> Was genau meinst du damit?

Beispiel:
1
volatile uint16_t counter;
2
3
ISR:
4
  counter++;
5
6
7
Main:
8
  uint16_t x=counter; // oder x=IVAR(counter), wenn counter nicht volatile
9
  print_uint16(x);

Denkbare Ausgabe:
... 0x00FF, 0x0000, 0x0101, ...

oder
... 0x00FF, 0x01FF, 0x0101, ...

Einfach weil während dem umkopieren von counter in x ein Interrupt 
auftreten kann (erstes Byte kopiert, ISR verändert beide, zweites Byte 
kopiert)


Lösung:
interrupts zwischendurch abschalten.
#include <util/atomic.h> hat dafür passende Makros.

von Stefan E. (sternst)


Lesenswert?

Εrnst B✶ schrieb:
> Einfach weil während dem umkopieren von counter in x ein Interrupt
> auftreten kann (erstes Byte kopiert, ISR verändert beide, zweites Byte
> kopiert)

Und was bitte hat das jetzt mit volatile und dem Makro zu tun?

von Εrnst B. (ernst)


Lesenswert?

Stefan Ernst schrieb:
> Und was bitte hat das jetzt mit volatile und dem Makro zu tun?

Nichts. Außer das Programmieranfängern hier im Zusammenhang mit ISRs 
immer sofort "volatile","volatile","volatile" an den Kopf geworfen wird, 
als wäre es die Allheillösung. Ist es nicht. Es ist Anfängern nur 
leichter zu erklären, und macht im einfachen Fall (Flag in ISR setzen, 
in main auswerten) ja auch das richtige.

von Stefan E. (sternst)


Lesenswert?

Εrnst B✶ schrieb:
> Nichts. Ausser das Programmieranfängern hier im Zusammenhang mit ISRs
> immer sofort "volatile","volatile","volatile" an den Kopf geworfen wird,
> als wäre es die Allheillösung. Ist es nicht. Es ist Anfängern nur
> leichter zu erklären.

Und warum folgt daraus jetzt, dass das Makro bei Variablen größer als 8 
Bit nicht funktioniert?

Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile 
zu machen, ohne dass die Variable selbst global volatile sein muss. 
Niemand hat behauptet, dass der Zugriff damit auch automatisch atomar 
wäre, oder dass das Thema "atomarer Zugriff" bei Verwendung des Makros 
nicht beachtet zu werden braucht.

"Dein Auto ist kaputt!"
"Ja? Wieso, was ist denn damit?"
"Es kann nicht fliegen."

von Oliver (Gast)


Lesenswert?

Εrnst B✶ schrieb:
> Es ist Anfängern nur
> leichter zu erklären,

Das ist erstens falsch, und zweitens auch...

Es gibt beim Zusammenspiel von ISR's mit dem Restprogramm zwei Probleme 
zu lösen. Das eine löst volatile, das andere atomic. Die Probleme (und 
auch die Lösungen) haben nichts miteinander zu tun, und sind einzeln 
oder auch kombiniert anzuwenden.

Oliver

von Εrnst B. (ernst)


Lesenswert?

Stefan Ernst schrieb:
> "Dein Auto ist kaputt!"
> "Ja? Wieso, was ist denn damit?"
> "Es kann nicht fliegen."

Typisches "Beratungsgespräch" hier im Forum:
"Ich hab da eine ISR ...."
"mach Volatile"
"Ich hab da eine ISR und volatile"
"mach Volatile"
"hab ich doch schon"
"erm.... mach noch mehr Volatile, Alle variablen"
"Häh?"
"und schalt die Optimierung aus!"
"Huh? Aber ich will doch nur meinen 16Bit-Zähler aus der ISR 
verwenden..."

Stefan Ernst schrieb:
> Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile
> zu machen, ohne dass die Variable selbst global volatile sein muss.

Dafür ist es ja gut und toll. Aber man sollte darauf hinweisen, dass es 
eben bei 16Bit-Variablen nicht für atomaren Zugriff sorgt, und es 
deswegen nicht alleine zur Lösung aller Interrupt-Sorgen führt.

Was im Gegensatz dazu die ATOMIC_BLOCK-Makros wesentlich besser 
erreichen, denn da wird beim "cli"/"sei" per Memory-Barrier ebenfalls 
ein neu-laden der Variable aus dem Ram erzwungen, und das volatile somit 
in fast allen Fällen überflüssig.

von der A. (the_a)


Lesenswert?

wie weiter oben beschrieben MUSS eine Varaible, die in einem anderen 
Kontext (aka ISR) verwendet wird, volatile sein.

Der Compiler denkt dass im obigen Beispiel in der main nie schreibend 
auf counter zugegriffen wird. Und folglich wird er den Zugriff bei 
Optimierung auch einsparen - er "kennt" ja den Wert.

volatile erzwingt das das erneute Lesen aus der globalen Variable.
Egal, ob der Interrupt ein- oder ausgeschaltet ist.

probier doch mal aus:
uint16_t counter;
ISR:
counter++;
main:
counter = 0;
while(counter > 100)
{
  counter = 0;
}

Ohne Optimierung steht der Code noch im erzeugten Object-File. Aber mit 
Optimierung (-Os) wird der Code der while Schleife "brutal" gelöscht - 
weil die Bedingung sonst nie erfüllt worden wäre.

Adib.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Atomic und Volatile sind 2 völlig verschiedenen Dinge.
Ihre einzige Gemeinsamkeit ist, daß beide in Zusammenhang mit Interrupts 
auftreten können.

Meistens macht das Atomic auch implizit ein Volatile.
Aber allein es kann Nachteile bezüglich der Laufzeit haben.
Es macht schon einen Unterschied, ob eine Interruptsperre 6 Zyklen 
dauert oder über 1000 Zyklen:
1
#include <util/atomic.h>
2
3
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
4
5
uint16_t foo;
6
7
void test( float fval )
8
{
9
  uint16_t val;
10
  
11
  val = fval / 123.4;
12
  ATOMIC_BLOCK(ATOMIC_FORCEON){
13
    foo = val;
14
  }
15
}
16
17
void test1( float fval )
18
{
19
  uint16_t val;
20
  
21
  val = fval / 123.4;
22
  ATOMIC_BLOCK(ATOMIC_FORCEON){
23
    IVAR(foo) = val;
24
  }
25
}


Peter

von Reiner F. (reiner)


Lesenswert?

Oliver schrieb:
> Es gibt beim Zusammenspiel von ISR's mit dem Restprogramm zwei Probleme
> zu lösen. Das eine löst volatile, das andere atomic. Die Probleme (und
> auch die Lösungen) haben nichts miteinander zu tun, und sind einzeln
> oder auch kombiniert anzuwenden.

Für einen ambitionierter Anfänger, der sich hier auf den Seiten bewegt, 
ist es nicht einfach, diese beiden Probleme auseinander zu halten. 
Wahrscheinlich wurde schon zu viel darüber geschrieben. Wo findet man 
die anfängertauglichen Merksätze "volatile muss man verwenden, wenn...", 
"Interruptsperren muss man wegen nicht atomarer Zugriffe sperren, 
wenn..."?

Ich habe mir das wie folgt zusammengereimt. Bin mir aber nicht sicher 
und bitte um Verbesserung:

------------------------------------------------------------------------ 
---
Globale Variablen, die innerhalb einer ISR und auch vom übrigen Programm 
verwendet werden, müssen als volatile gekennzeichnet werden.

Bei Zugriff und Veränderung von Variablen und Registern, die
- sowohl in einer ISR als auch im übrigen Programm verwendet werden
- und mehrere MC-Takte brauchen,
müssen Interrupts gesperrt werden. Wenn der MC keine verschachtelten 
Interrupts zulässt, sind diese Interruptsperren nur im übrigen Programm 
und nicht in den ISRn notwendig.

von Peter D. (peda)


Lesenswert?

Reiner Franke schrieb:
> Wenn der MC keine verschachtelten
> Interrupts zulässt, sind diese Interruptsperren nur im übrigen Programm
> und nicht in den ISRn notwendig.

Es dürfte die absolute Ausnahme sein, daß sich Interrupts gegenseitig 
Daten austauschen.
Daher braucht auch bei Verschachtelung der Interrupt (in der Regel) kein 
volatile oder atomic.


Peter

von Reiner F. (reiner)


Lesenswert?

Danke Peter für's durchschauen der vorgeschlagenen "Merksätze". Ich gehe 
jetzt mal davon aus, dass bis auf den einen Einwand alles gepasst hat.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:

> Es macht schon einen Unterschied, ob eine Interruptsperre 6 Zyklen
> dauert oder über 1000 Zyklen:

Aber nicht mit zweierlei Maß messen, gell?
1
#include <util/atomic.h>
2
3
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
4
5
uint16_t foo;
6
uint16_t volatile vfoo;
7
8
void test( float fval )
9
{
10
  uint16_t val;
11
  
12
  val = fval / 123.4;
13
  ATOMIC_BLOCK(ATOMIC_FORCEON){
14
    vfoo = val;
15
  }
16
}
17
18
void test1( float fval )
19
{
20
  uint16_t val;
21
  
22
  val = fval / 123.4;
23
  ATOMIC_BLOCK(ATOMIC_FORCEON){
24
    IVAR(foo) = val;
25
  }
26
}

...und nochmal vergleichen ;-)

Zumindest mit 4.3, 4.5, 4.6 und 4.7 sehe ich identische Ergebnisse.

von Peter D. (peda)


Lesenswert?

Johann L. schrieb:
> Aber nicht mit zweierlei Maß messen, gell?

Es ging darum, daß Atomic auch automatisch Volatile ersetzt.
Und mein Beispiel zeigt, das tut es nicht.

Da Du nun gleich die Variable volatile machst, ist logisch, daß dann der 
Code zum volatile-Cast identisch sein muß.

Ich benutze lieber den Cast.
Dann kann der Interrupt optimieren und im Main sehe ich sofort, daß es 
sich um eine Interruptvariable handelt.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Εrnst B✶ schrieb:
> Stefan Ernst schrieb:
>> Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile
>> zu machen, ohne dass die Variable selbst global volatile sein muss.
>
> Dafür ist es ja gut und toll. Aber man sollte darauf hinweisen, dass es
> eben bei 16Bit-Variablen nicht für atomaren Zugriff sorgt, und es
> deswegen nicht alleine zur Lösung aller Interrupt-Sorgen führt.

Achtung: Es sorgt bei 8-Bit Variablen auch nicht (unbedingt) für 
atomaren Zugriff!. Es ist eine völlig andere Baustelle. Nämlich die, 
dass der Variablenzugriff nicht wegoptimiert wird.

> Was im Gegensatz dazu die ATOMIC_BLOCK-Makros wesentlich besser
> erreichen, denn da wird beim "cli"/"sei" per Memory-Barrier ebenfalls
> ein neu-laden der Variable aus dem Ram erzwungen, und das volatile somit
> in fast allen Fällen überflüssig.

Das halte ich für eine SEHR gewagte These. Der ATOMIC_BLOCK sorgt mit 
Sicherheit nicht für das Neu-Laden einer Variable innerhalb dieses 
Blockes!

Du darfst die beiden Probleme nicht miteinander vertauschen. Das sind 
(wie schon mehrfach erwähnt) zwei verschieden Probleme und manchmal ist 
es nötig, dass beides angewendet wird. Um Atomizität herzustellen und 
gleichzeitig das Wegoptimieren eines Variablenzugriffes zu verhindern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Johann L. schrieb:
>> Aber nicht mit zweierlei Maß messen, gell?
>
> Es ging darum, daß Atomic auch automatisch Volatile ersetzt.

Nein, atomic ersetzt nicht volatile und auch nicht umgekehrt.

> Und mein Beispiel zeigt, das tut es nicht.
>
> Da Du nun gleich die Variable volatile machst, ist logisch, daß dann der
> Code zum volatile-Cast identisch sein muß.

> [...] und im Main sehe ich sofort, daß es
> sich um eine Interruptvariable handelt.

Wenn es darum geht, den eigenen Code zu verstehen, sind manchmal auch 
Kommentare dienlich ;-)

von tobi (Gast)


Lesenswert?

Hallo,

das geht aber am eigentlichen Thema vorbei. Das Problem waren ja Fehler 
beim Compilieren von aus Studio 4 in Studio 5 übernommenen Code.

Was mir fehlt ist eine Aussage wie der Code von 4 nach 5 gekommen ist.

Ich habe da andere Erfahrungen. Ich musste AVR Studio 5 einsetzen, weil 
Studio 4 und WINAVR nicht unter Win7 zum laufen zu kriegen waren. Ich 
habe alle Projekte (>100) und 90% davon mit UART-Funktion in Studio 5 
über die Import-Funktion nach Studio 5 konvertiert. Alle Projekte ließen 
sich auf Anhieb kompilieren und liefen auch in der Schaltung so wie sie 
sollten.

von Karl H. (kbuchegg)


Lesenswert?

tobi schrieb:
> Hallo,
>
> das geht aber am eigentlichen Thema vorbei. Das Problem waren ja Fehler
> beim Compilieren von aus Studio 4 in Studio 5 übernommenen Code.

Jein.
Das 'Problem' sind erst mal unterschiedliche Compilerversionen bzw. 
Einstellungen in der Commandline des Compileraufrufes.

Die sind insofern ein 'Problem', weil eine neuere Compilerversion oft 
pingeliger ist und Dinge anmerkt, die die alte Version durchgehen liess. 
Das bedeutet nicht automatisch, dass der geschriebene Code in der 
vorhergehenden Version korrekt gewesen wäre. Es bedeutet lediglich, dass 
der Compiler damals noch nicht die notwendigen Tests mit hatte um diese 
Problemkreise zu erkennen.

D.h. die 'Fehler' waren auch vorher schon drinnen, nur sind sie vom 
Compiler nicht angemerkt worden. Und die letzte Compilerversion (bze. 
die geänderten Command line Argumente) tun das eben.

> über die Import-Funktion nach Studio 5 konvertiert. Alle Projekte ließen
> sich auf Anhieb kompilieren und liefen auch in der Schaltung so wie sie
> sollten.

Und so sollte es auch sein, wenn man qualitativ guten C Code schreibt.

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.