www.mikrocontroller.net

Forum: Compiler & IDEs Variable an Register binden - ich kanns nicht oder compilerbug :)


Autor: Roland Praml (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ich hab grad ein Problem mit meinem GCC

ich hab folgendes Progrämmchen (Attiny24)
#include <AVR/signal.h>

volatile unsigned int register currData asm("r2");

int main(void) {
  MCUCR = 1 << ISC00 | 1 << ISC01;        // int0 - rising edge
  GIMSK = 1 << INT0;                // enable int0
  DDRB  = 1 << PB1;
  PORTB = 0 << PB1;
  sei();
  for (;;) {
    while( PINB & (1 << PB0) );   // Wait until chip is selected
    currData = 0x2123;
    while(! (PINB & (1 << PB0)) );   // Wait until chip is deselected
  }
}

ISR(INT0_vect) {
  if (currData & 0x4000) {
    PORTB |= 1 << PB1;
  } else {
    PORTB &= ~(1 << PB1);
  }
  currData = currData << 1;
}

hier hab ich mir gedacht, ich binde die Variable currData fest an das 
Register R2 (aus Performancegründen, damit die ISR direkt drauf 
zugreifen kann), denkste, dies wird in der Main gleich mal wegoptimiert 
auf:
  for (;;) {
    while( PINB & (1 << PB0) );   // Wait until chip is selected
  40:  b0 99         sbic  0x16, 0  ; 22
  42:  fe cf         rjmp  .-4        ; 0x40 <__SREG__+0x1>
    currData = 0x2123; <-- HIER SOLLTE R2/R3 GESETZT WERDEN!
    while(! (PINB & (1 << PB0)) );   // Wait until chip is deselected
  44:  b0 9b         sbis  0x16, 0  ; 22
  46:  fe cf         rjmp  .-4        ; 0x44 <__SREG__+0x5>
  48:  fb cf         rjmp  .-10       ; 0x40 <__SREG__+0x1>

Hab ich hier wirklich einen Bug gefunden, oder kann man das nicht so 
machen wie ich es vorhabe.
Die ISR sieht im Listing i.O. aus, hier greift er immer brav auf r2/r3 
zu.
Mit der Deklaration:
  volatile unsigned int currData ;
(also ohne Register) funktioniert mein Programm dann auch, allerdings 
ist die ISR dann anstatt 17 Instruktionen 27 lang und das möchte ich 
nicht :-P

Gruß
Roland

Autor: Roland Praml (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
übrigens, GCC hab ich mir grad die neueste Version runter geladen. 
Optimierung ist -Os (tritt aber auch bei -O1 -O2 und -O3 auf)

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist ein 8bit Register nicht etwas klein für eine 16bit Variable?

Ich habe ehrlich gesagt keine Ahnung was der Compiler in diesem Fall 
macht, bzw. ob das überhaupt geht. Die FAQ zeigt als Beispiele nur 8bit 
Variablen.

Autor: Roland Praml (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bei geraden Variablen wird die nächste ungerade dazugebunden, also R2/R3

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Füg mal in die Schleife irgendeinen Funktionsaufruf ein, dann setzt er 
r2/r3. Es scheint also so zu sein, als wenn er das volatile ignoriert.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Benedikt K. schrieb:
> Es scheint also so zu sein, als wenn er das volatile ignoriert.

Ja, volatile & register kann GCC nicht behandeln.  Früher[tm] gab
es dafür eine Warnung, die wurde aber irgendwann mal rausgeworfen,
da sie an manchen Stellen nicht sinnvoll war.  Auf meinen Protest
hin:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34351

ist sie nun wieder drin, aber erst mit GCC 4.4.

Summa summarum: don't do it.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohne volatile ist ja das Hauptanwendunsgebiet vom dem Registerbinden 
weg.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon K. schrieb:
> Ohne volatile ist ja das Hauptanwendunsgebiet vom dem Registerbinden
> weg.

Ja, leider.

Beim AVR könnte man noch tricksen (außer Xmega), indem man zusätzlich
einen Zeiger auf das Register anlegt und den als auf ein volatile-
Objekt markiert, da der AVR ja die CPU-Register in die ersten 32
RAM-Adressen abbildet.

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder man könnte die Register in inline asm Beschreiben. Ist nicht schön, 
aber sollte gehen.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Das volatile an register sollte überflüssig sein, wundert mich, daß gcc 
da nicht meckert...

Als Optionen solltest du zusätzlich -ffixed-2 -ffixed-3 angeben bei 
allen Modulen, die zur Applikation gelinkt werden.

Davon ab sieht's nach nem Bug aus.

Testfall anbei.

Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen? Die 
Insn wird in Pass 141r.cse1 von avr-gcc 4.3.2 als tot erkannt und 
gelöscht.

Johann

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen?

Schlecht, ich habe immer nur gepatchte davon...  Gerade bei der
4.3er Serie sind ja einige Patches notwendig (hab mir noch nicht
angesehen, was davon bei 4.3.3 alles bereits integriert ist).

Die Warnung scheint ja erst bei 4.4 wieder dabei zu sein.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen?
>
> Schlecht, ich habe immer nur gepatchte davon...  Gerade bei der
> 4.3er Serie sind ja einige Patches notwendig (hab mir noch nicht
> angesehen, was davon bei 4.3.3 alles bereits integriert ist).

Ok, dann müsste ich die Tage mal nen ungepatchten 4.3.2 bauen und 
nachschauen, ob's da auch hakt.

Folgendes wird jedoch korrekt übersetzt:
register unsigned int currData asm ("r2");
void foo (void) 
{
    currData = 0x2123;
}

Der bekannten Bug von WinAVR ist es mal nicht, weil der hier resistent 
gegen -fno-split-wide-types ist...

Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser 
natürlich der Code ist tot. Oder lassen wir und von "register" in die 
Irre führen?

Johann

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zuweisungen an globale Variablen können, wenn sie unabhändig vom 
Schleifeninhalt sind und der Schleifencode vollständig bekannt ist, ggf. 
verschoben oder wie hier wegoptimiert werden. Ob "register" oder nicht 
ändert daran nichts. Daran würde nur "volatile" etwas ändern, und das 
kann GCC eben nicht zusammen mit "register".

Insofern ist das auch kein Bug, denn das ist sowieso kein Standard-C, 
also ist das Verhalten nirgendwo definiert.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser
> natürlich der Code ist tot.

Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
Register, was in einer ISR geändert wird, pollst:
#include <stdint.h>
#include <avr/interrupt.h>

register uint8_t flag asm("r2");

ISR(INT0_vect)
{
  flag = 42;
}

int
main(void)
{
  flag = 0;

  while (flag == 0)
    /* wait */;

  return 42;
}

Das übersetzen alle mir vorliegenden Versionen von GCC falsch, indem
sie die while-Schleife zur Endlosschleife machen.  (OK, ist sie auch,
aber nur, weil ich keine Interruptquelle für den Externinterrupt
aktiviere. ;-)

So würde es funktionieren:
int
main(void)
{
  volatile uint8_t *flagptr = (uint8_t *)0x02;

  flag = 0;

  while (*flagptr == 0)
    /* wait */;

  return 42;
}

Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:

> Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
> benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

Und funktioniert bei den Xmegas nicht, denn die haben diese 
Speicheradressen nicht mehr.

Autor: Roland Praml (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also denkt ihr ich soll das mit den Registern bleiben lassen und auf die 
paar Befele & Bytes die ich mir spare, verzichten, weil das Verhalten 
des GCC an dieser Stelle eben undefiniert ist.

Irgendwie juckts mich jetzt das Ganze dann komplett in ASM zu machen :)

Gruß
Roland

Autor: Roland Praml (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
> Register, was in einer ISR geändert wird, pollst:

Die (schnelle) Kommunikation zwischen Main() und ISR() wär ja meiner 
Meinung ein WESENTLICHER Vorteil. Wenn ich eine normale Variable als 
"volatile" deklariere, dann bastelt der Compiler da noch jede Menge 
"Zeug" dazu. z.B.:
  currData = currData << 1;
 174:  80 91 60 00   lds  r24, 0x0060
 178:  90 91 61 00   lds  r25, 0x0061
 17c:  88 0f         add  r24, r24
 17e:  99 1f         adc  r25, r25
 180:  90 93 61 00   sts  0x0061, r25
 184:  80 93 60 00   sts  0x0060, r24
vs:
  currData = currData << 1;
 14a:  22 0c         add  r2, r2
 14c:  33 1c         adc  r3, r3

Leider optimiert er die Registerzugriffe in der Main weg. Und die 
Register in der Main direkt beschreiben ist ja auch "quatsch" :-(

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser
>> natürlich der Code ist tot.
>
> Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
> Register, was in einer ISR geändert wird, pollst:
>
> Das übersetzen alle mir vorliegenden Versionen von GCC falsch, indem

Es ist anders als ich erwarten würde. Aber um zu beurteilen, ob's falsch 
ist, weiss ich offen gestanden zu wenig über die Semantik von 
"register".

Jedenfalls zeit ein Blick in rtl.h, daß sowas wie "volatile register" 
garnicht ausgedrückt werden kann in RTL-Flags:
  /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile.
     1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE
     if it has been deleted.
     1 in a REG expression if corresponds to a variable declared by the user,
     0 for an internally generated temporary.
     1 in a SUBREG with a negative value.
     1 in a LABEL_REF, REG_LABEL_TARGET or REG_LABEL_OPERAND note for a
     non-local label.
     In a SYMBOL_REF, this flag is used for machine-specific purposes.  */
  unsigned int volatil : 1;

> So würde es funktionieren:
>
>   volatile uint8_t *flagptr = (uint8_t *)0x02;
> 
> Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
> benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

UAAA seh ich jetzt erst. Hack as Hack can!
Wenn das raschelt, dann zurecht ;-)

Was mir als Hack einfallen würde ist
currData = 0x2123;
asm volatile ("" : "+r" (currData));

Damit macht zumindest der Testfall das, was er soll...

Johann

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Roland Praml schrieb:
> Also denkt ihr ich soll das mit den Registern bleiben lassen und auf die
> paar Befele & Bytes die ich mir spare, verzichten, weil das Verhalten
> des GCC an dieser Stelle eben undefiniert ist.

Ich verwende in einer Anwendung auch Registervariablen für Flags und für 
Daten. Ohne Register wäre das ganze empfindlich langsamer, und pro 
Sekunde hätte ich über 300000 Speicherzugriffe mehr...

Allerdings hatte ich bemerkt, daß avr-gcc 4.x damit Zicken macht. Bei 
der Echtzeitanwendung seh ich jede kleine Zeitabweichung und hab es 
darauf zurückgefügrt, das der Code langsamer (und zudem auch größer) ist 
als beim 3.4.6, den ich immer noch einsetze.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Es ist anders als ich erwarten würde. Aber um zu beurteilen, ob's falsch
> ist, weiss ich offen gestanden zu wenig über die Semantik von
> "register".

Stammt aus der Zeit, als man einem Compiler noch explizit sagen musste, 
welche Variable er in Registern vorhalten sollte. Wenn er genug davon 
hatte. Immerhin musste so ein Compiler in einer PDP-11 mit 64KB 
Adressraum arbeiten, da war für grössere Statement-übergreifende 
Optimizer kein Platz.

Das ist also ein Synonym zu "auto" mit einem freundlichen Hinweis 
obendrauf. Und interessiert heutzutage keinen halbwegs ernstzunehmenen 
Compiler mehr. Bei globalen Variablen ist das aus dieser historischen 
Sicht heraus folglich irgendwas zwischen undefiniert und Blödsinn.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ggf. helfen kann: Einige AVRs haben ein paar freie Bytes im 
bitadressierbaren I/O-Raum. GPIOR0-2 oder so. Ideal für Statusbits.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Was ggf. helfen kann: Einige AVRs haben ein paar freie Bytes im
> bitadressierbaren I/O-Raum. GPIOR0-2 oder so. Ideal für Statusbits.

Stimmt, die Stati hab ich tatsächlich in GPIOR0 (schon was her seit ich 
das gemacht hab), weil das SFR bitadressierbar ist und man Bits 
setzen/löschen kann; im Gegensatz zu "l"-Regs. Allerdings sind GPRIO1/2 
blöderweise nicht bitadressierbar (ATmega168).

Aber für Pixel-Koordinaten nehm ich GPRs, was auch prima geht im 3.4.6. 
Hier würde GPRIO1/2 nix sparen.

Johann

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.