Forum: Compiler & IDEs Inline Assambler compiler error


von B. H. (bluehazzard)


Lesenswert?

Hallo,
ich bin gerade dabei den Basich code des I2C-Slaves für Attiny13 aus 
elector in c unzuschreiben.
Da ich mit c in kombination mit assambler überhaupt keine Ahnung habe, 
habe ich mit das tutorial auf roboternetz und das inline asm cookbook 
angeschaut... und habe auch schon den ersten teil in c umgeschrieben:
1
unsigned char i2c_address = 123,i2c_b1 =12,i2c_b2 = 23,i2c_stop = 23;
2
unsigned char adress;
3
unsigned char cSCL = PB4, cSDA = PB3, cACK = cSDA;
4
5
asm volatile(  "1:      \n\t"   //i2c_get = 1
6
    "sbis %[_PINB],%[_SCL]\n\t"   //wait for SCL&SDA=1
7
       "rjmp 1b    \n\t"
8
       "sbis %[_PINB],%[_SDA]  \n\t"
9
       "rjmp 1b    \n\t"
10
11
    "2:      \n\t"  //I2c_wait_for_start = 2
12
       "sbis %[_PINB],%[_SCL]  \n\t"//wait for SCL=1,SDA=0 (START)
13
       "rjmp 1b    \n\t"
14
       "sbic %[_PINB],%[_SDA]  \n\t"
15
       "rjmp 2b    \n\t"
16
17
    "3:      \n\t"  //I2c_get_0 = 3
18
     "clr r22    \n\t"  //clear receive area
19
      "sts %[_i2c_address],r22\n\t"
20
      "sts %[_i2c_b1],r22  \n\t"
21
      "sts %[_i2c_b2],r22  \n\t"
22
      "sts %[_i2c_stop],r22  \n\t"
23
24
[....Gekürtzt....]
25
  
26
    :[_i2c_b1] "=r"  (i2c_b1),
27
      [_i2c_b2] "=r"  (i2c_b2)  
28
    /*output->*/
29
    :[_SCL] "I" (cSCL),
30
     [_SDA] "I" (cSDA),
31
     [_PINB] "I" (_SFR_IO_ADDR (PINB)),
32
     [_i2c_address] "r" (i2c_address),
33
       "0"  (i2c_b1),
34
       "1"  (i2c_b2),
35
             [_i2c_stop]"r" (i2c_stop) 
36
     /*input ->*/
37
           :"memory");

nun bekomme ich aber die Fehler:
1
\Attiny13\default/../I2C_Test.c:65: undefined reference to `r24'
2
\Attiny13\default/../I2C_Test.c:65: undefined reference to `r18'
3
\Attiny13\default/../I2C_Test.c:65: undefined reference to `r25'
4
\Attiny13\default/../I2C_Test.c:65: undefined reference to `r25'

was mache ich falsch?

mfg

von Jörg G. (joergderxte)


Lesenswert?

Könntest du zumindest entweder
a) Markieren in welcher Zeile die Fehler auftreten (Dein Post hat keine 
Zeilennummern ... ) oder
b) Kompilierbaren Code posten(=anhängen)

Ich frag mich gerade, ob Inline-Asm bei sowas so viele Vorteile hat, 
dass sich der Ärger mit dem Inline-Asm lohnt ...

hth, Jörg

von B. H. (bluehazzard)


Lesenswert?

zu a) der Fehler wird bei "asm volatile(" angezeigt, und der Code ist 
compilierbar (bis zu den Fehlern), wenn man ihn in eine einfache main- 
Funktion gibt.... das weg gekürzte hat nichts zu bedeuten... deswegen 
ist es ja gekürzt.... ;)
ich hab es nämlich auskommentiert beim kompilieren....

ich persönlich denke, dass der Fehler beim zuweisen der Variablen liegt, 
ich habe nämlich nicht genau verstanden wie das funktionieren soll, und 
einfach darauf los geraten(geschrieben)...

danke
mfg.

von Karl H. (kbuchegg)


Lesenswert?

1) Hast du den Teil nicht in einer Funktion stehen?
   Für inline Assembler gilt dasselbe, wie für jeden anderen C-Code
   auch: Ausführbarer Code gehört in eine Funktion

2) Wenn du das schon von Assembler auf C übersetzt, warum machst du
   das dann nicht gleich richtig, und schreibst equivalenten C-Code?
   Da hast du mehr davon und kommst dann auch nicht dem Compiler mit
   der Registerallokierung in die Quere

  Das hier
   "1:      \n\t"   //i2c_get = 1
    "sbis %[_PINB],%[_SCL]\n\t"   //wait for SCL&SDA=1
       "rjmp 1b    \n\t"
       "sbis %[_PINB],%[_SDA]  \n\t"
       "rjmp 1b    \n\t"

  übersetzt sich schon fast von alleine zu
1
     //wait for SCL&SDA=1
2
     while( ( ( PINB & ( 1 << SCL ) ) == 0 ) ||
3
            ( ( PINB & ( 1 << SDA ) ) == 0 ) )
4
       ;

und wird (mit eingeschalteter Optimierung) vom Compiler höchst 
wahrscheinlich zu ziemlich genau derselben Sequenz übersetzt, die du 
auch im Assembler hattest.

Die ganze von dir gezeigte Sequenz lautet in reinem C
1
  unsigned char i2c_address = 123,
2
                i2c_b1 = 12,
3
                i2c_b2 = 23,
4
                i2c_stop = 23;
5
  unsigned char adress;
6
7
  unsigned char cSCL = PB4,
8
                cSDA = PB3,
9
                cACK = cSDA;
10
11
     //wait for SCL&SDA=1
12
     while( ( ( PINB & ( 1 << SCL ) ) == 0 ) ||
13
            ( ( PINB & ( 1 << SDA ) ) == 0 ) )
14
       ;
15
16
     //wait for SCL=1,SDA=0 (START)
17
     while( ( ( PINB & ( 1 << SCL ) ) == 0 ) ||
18
            ( ( PINB & ( 1 << SDA ) ) != 0 ) )
19
       ;
20
21
     i2c_address = 0;
22
     i2c_b1 = 0;
23
     i2c_b2 = 0;
24
     i2c_stop = 0;

und das hat dann schon eine ganz andere Code-Qualität, als da mit 
Inline-Assembler rumzumachen. Und du bringst dem Compiler seine 
Registerallokierung auch nicht durcheinander.

von B. H. (bluehazzard)


Lesenswert?

Hallo,
natürlich ist das alles in einer Funktion verpackt, aber das ist hier 
belanglos.... die wichtigen Sachen habe ich gepostet....
natürlich könnte ich das alles auch in c schreiben, aber im beigelegten 
Artikel in der elektro steht, dass der Autor sich entschieden hat die 
wichtigen Funktionen in asm zu schreiben, da sie Zeitkritisch sein 
könnten... seine Meinung... ich wollte daran mal nicht zweifeln und 
damit ich sicher bin, dass es funktioniert wollte ich den code einfach 
von inline basic in inline c umwandeln...
aber leider bekomme ich die oben genannten Fehler... und ich bin mir 
ziemlich sicher, dass das daran liegt, wie ich die Variablen übergeben 
muss, aber wie auch oben schon geschrieben habe ich davon keine Ahnung, 
und die Tutorials sind auch keine große Hilfe
z.B.:
Auf bekannte globale Symbole kann man direkt von Assembler aus 
zugreifen:
1
   extern int einInt;
2
3
   asm volatile (
4
      "lds %A0, einInt"   "\n\t"
5
      "lds %B0, einInt+1"
6
         : "=r" (...)
7
   );
das funktioniert auch nicht und stammt von 
http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc


mfg.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Um zu sehen, was im asm falsch läuft, übersetzt du mit der gcc-option 
-save-temps.

Dann ist die s-Datei nicht temporär und wird gelöscht, sondern bleibt 
erhalten.

"undefined reference" ist eine Fehlermeldung des Linkers, nicht des 
Compilers. Also erzeugt der Compiler wohl den falsches Assembler-Code 
(bzw. es wird per inline asm falscher Code eingefühgt. Für i2c_address 
ist die Constraint "r", d.h. gcc wird ein GPR dafür allokieren, STS 
erwartet jedoch ein Immediate (also eine Linkzeit-Konstante) als 
Destination-Operand. Der Assembler erzeugt also zB ein Reloc für ein 
Symbol namens "r18". Ein Objekt dieses Namens gibt's aber nicht, also 
meckert der Linker.

Zudem ist hat der Inline Assembler andere Effekte auf die Maschine als 
sie behauptet (zb auf r22). Dazu ein Zitat aus dem usenet:

> If you lie to the compiler, the compiler will get its revenge.
> Abusing GCC's asms is certainly one way to hang yourself.


B. H. schrieb:
> muss, aber wie auch oben schon geschrieben habe ich davon keine Ahnung,
> und die Tutorials sind auch keine große Hilfe
> z.B.:
> Auf bekannte globale Symbole kann man direkt von Assembler aus
> zugreifen:
>
1
>    extern int einInt;
2
> 
3
>    asm volatile (
4
>       "lds %A0, einInt"   "\n\t"
5
>       "lds %B0, einInt+1"
6
>          : "=r" (...)
7
>    );
8
>
> das funktioniert auch nicht und stammt von
> http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc

klar geht das, aber ein zugehörenden Objekt muss auch irgendwo 
angelegt werden bzw. ein entsprechendes Symbol definiert sein (etwa 
per cmd-line).

Davon ab bist du -- wie Karl Heinz schon schrieb -- mit C portabler, 
korrekt und nicht langsamer.

Das hier ist ein reines Angst-asm oder dient dazu, das Getane 
geheimnisvoller erscheinen zu lassen.

Johann

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

> Das hier ist ein reines Angst-asm oder dient dazu, das Getane
> geheimnisvoller erscheinen zu lassen.

Kann auch sein, dass der Originalautor seinem Basic-Compiler (war wohl 
BASCOM) nicht so recht über den Weg traute. Mit gcc ist das aber wohl in 
90% aller Fälle unbegründet. Zur Not kann man immer noch den Assembler 
Output des Compilers studieren (so es denn tatsächlich zu langsam sein 
sollte) und dann im C-Source Hand anlegen um dem Compiler noch besseren 
Code zu entlocken.

> aber im beigelegten
> Artikel in der elektro steht, dass der Autor sich entschieden hat
> die wichtigen Funktionen in asm zu schreiben, da sie Zeitkritisch
> sein könnten
       *******

... und wahrscheinlich hat ihm das sein Bauchgefühl gesagt und er hat 
keinerlei Beleg, als sein Bauchgefühl, dass die Routinen in einer 
Hochsprache nicht schnell genug wären.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Das hier ist ein reines Angst-asm oder dient dazu, das Getane
>> geheimnisvoller erscheinen zu lassen.
>
> Kann auch sein, dass der Originalautor seinem Basic-Compiler (war wohl
> BASCOM) nicht so recht über den Weg traute. Mit gcc ist das aber wohl in
> 90% aller Fälle unbegründet. Zur Not kann man immer noch den Assembler
> Output des Compilers studieren (so es denn tatsächlich zu langsam sein
> sollte) und dann im C-Source Hand anlegen um dem Compiler noch besseren
> Code zu entlocken.

Jo, stimmt. Ich hab einmal mir BASCOM-Code angeschaut. Da müsste man 
dann alles in Assembler schreiben aus Zeitgründen :o)

> ... und wahrscheinlich hat ihm das sein Bauchgefühl gesagt und er hat
> keinerlei Beleg, als sein Bauchgefühl, dass die Routinen in einer
> Hochsprache nicht schnell genug wären.

Assembler ist ja immer noch von dem Nimbus umgeben, schneller zu sein 
als Hochsprachen bzw. als der Code, den Hochsprachen-Compiler erzeugen. 
Für BASCOM ist das uneingeschränkt der Fall. Für GCC muss man manchmal 
staunen, daß der Code besser ist als handgeschrieben. Vor allem dann, 
wenn er Optimierungen sieht, die einem verborgen blieben, oder wenn 
Cache und Pipelines im Spiel sind oder der Maschine die Register 
ausgehen...

Johann

von B. H. (bluehazzard)


Lesenswert?

Ok, danke,
na dann versuche ich mal dass alles in C zu schreiben...

mfg.

von Oliver (Gast)


Lesenswert?

Wenn es dann doch bei Assembler bleibt, mach es wenigsten pur, nicht 
inline. Schau dir als Beispiel die I²C-Routinen von Peter Fleury an. Da 
ist zwar nur der Master implementiert, aber als Vorlage zur Kombination 
von C und Assembler lässt sich das nutzen.

Oliver

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.