Forum: Compiler & IDEs _SFR_IO_ADDR(PORTB) verursacht T-error


von Hegy (Gast)


Lesenswert?

Hallo,

schon spät/früh aber ich hab nen Problem mit inline-Assembler und den 
GCC. Bei der Zeile "out _SFR_IO_ADDR(PORTB),r0\n\t" (s. u.)
1
#include <avr/io.h>
2
3
void funktion(unsigned char hi, unsigned char mid, unsigned char lo)
4
{
5
  [....]
6
7
  asm volatile
8
  (
9
    [....]
10
    "lpm   \n\t\"
11
>>  "out  _SFR_IO_ADDR(PORTB),r0\n\t"
12
    "rjmp  1b\n\t"
13
    [....]
14
  )
15
}

bekomme ich folgende Fehlermeldung vom avr-gcc:
avr-gcc -c -mmcu=atmega168 -I. -g -DF_CPU=7372800UL  -D__AVR_ATmega168__ 
-O -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 
-Wall -Wstrict-prototypes -save-temps -Wa,-adhlns=Stepper.lst 
-std=gnu99 -MD -MP -MF .dep/Stepper.o.d Stepper.c -o Stepper.o
StepperDDS.s: Assembler messages:
Stepper.s:102: Error: constant value required
Stepper.s:102: Error: `,' required
Stepper.s:102: Error: constant value required
Stepper.s:102: Error: garbage at end of line

In Stepper.s, Zeile 102 steht:
1
out  _SFR_IO_ADDR(PORTB),r0

Warum wird _SFR_IO_ADDR(PORTB) nicht in eine Adresse umgesetzt, wie 
einige Zeilen tiefer auch, in z.B.
1
out 95-0x20,r24
wobei der Code automatisch generiert wurde, also nicht im C-Code 
vorhanden ist.

von Rolf Magnus (Gast)


Lesenswert?

> Warum wird _SFR_IO_ADDR(PORTB) nicht in eine Adresse umgesetzt

Weil der Assembler diese Makros nicht kennt. Schau dir mal die 
avr-libc-Doku an. Da wird beschrieben, wie man genau das mit 
inline-Assembler macht. Das _SFR_IO_ADDR(PORTB) muß in die 
Parameterliste.

von Hegy (Gast)


Lesenswert?

Mit Parameterliste meinst du z.B. sowas hier?
1
asm volatile("sbi %0,%1" : : "I"(_SFR_IO_ADDR(EECR)) , "I"(EEMWE));

Abgeändert auf mein Problem mit "out ...." ergeben sich immernoch 
Fehler. Daher habe ich mal nen Bastelprogramm (s.u.) gebaut und folgende 
Möglichkeiten ausprobiert.
1
#include <avr/io.h>
2
3
char value;
4
5
int main (void)
6
{
7
  value = 11;
8
9
  asm volatile("out %0,r0" : "I" (_SFR_IO_ADDR(PORTB)));
10
11
  return 0;
12
}

Fehler:
tt.c: In function 'main':
tt.c:9: error: invalid lvalue in asm statement
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: invalid lvalue in asm output 0

Die betreffende Zeile geändert und die Var. "value" mit in die 
Parameterliste aufgenommen.
1
asm volatile("out %0,%1 " : "I" (_SFR_IO_ADDR(PORTD)) : "=r" (value));

tt.c: In function 'main':
tt.c:9: error: invalid lvalue in asm statement
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: input operand constraint contains '='
tt.c:9: error: output operand constraint lacks '='
tt.c:9: error: invalid lvalue in asm output 0
tt.c:9: error: input operand constraint contains '='

ander typische Fehlermeldung war auch öfters:
 error: expected ':' or ')' before ';' token

Vorallem, wenn nach dem "out ...." noch eine Zeile kommt (rjmp 1b) und 
dann erst die schließende Klammer ");" von "asm volatile ("

Irgendwo habe ich auch einen solchen Konstrukt mit dem Befehl "in" 
gesehen,  das ging, geändert auf "out und die Parameter umgedreht, auch 
in der Parameterliste, ging nicht mehr. Kann echt nicht sein, seit 3 
Tagen schraube ich daran rum....

von Rolf Magnus (Gast)


Lesenswert?

> das ging, geändert auf "out und die Parameter umgedreht, auch
> in der Parameterliste, ging nicht mehr.

Natürlich nicht. Die Adresse ist immer ein Eingangsparameter. Daher 
kommen vermutlich auch die Fehler. Außerdem ergibt das "=" für einen 
Eingangsparameter keinen Sinn, da es bedeutet, daß er nicht gelesen, 
sondern nur geschrieben wird. Also:

asm volatile("out %0,%1 " : : "I" (_SFR_IO_ADDR(PORTD)), "r" (value));

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


Lesenswert?

Das ist ein gängiges Misstverständnis:

Die Richtung "in" und "out" bezieht sich nicht etwa darauf, was der
Prozessor mit seiner Umwelt dabei anstellt -- das interessiert das
inline-Assembler-Interface des Compilers nämlich herzlich wenig.
Sie bezieht sich darauf, was in die inline-Assembler-Anweisung
hineingegeben wird und was diese in das umgebende C-Programm wieder
zurückgibt.

von Hegy (Gast)


Lesenswert?

Mit dem Vertauschen/umdrehen der Parameter zum 'in' bzw. 'out'-Befehl 
meinte ich so, wie es auch in der Assembler-Doku von Atmel steht.
1
in  r25,$16  ; Read Port B
2
out $18,r16  ; Write zeros to Port B

Da kommt bei 'in' erst das Register, wo die Daten reinwandern, und dann 
die Datenquelle. Umgekehrt ist es beim out-Befehl. Ich merke mir das so: 
in Register25 der Wert von $16 und für out, out nach $18 der Wert von 
Register16, in Anlehnung an das Bsp. oben.

Zu Rolfs Code:
Ich habe mal die Zeile in mein Bastelcode eingefügt und da eine Schleife 
drübergelegt. Dabei kommt es mir jetzt nicht auf den Sinn des Programms 
an, sondern darum, wie es nach dieser Zeile weitergeht. Alle Beispiele, 
die ich bisher gesehen habe, enden komischerweise immer nach dem 'in' 
oder 'out'-Befehl per ");" Daher hier mal mein gesamter Bastelcode:
1
#include <avr/io.h>
2
3
char value;
4
5
int main (void)
6
{
7
  value = 11;
8
9
  asm volatile
10
  (
11
    "L0: "
12
    "out %0,%1 " : : "I" (_SFR_IO_ADDR(PORTD)), "r" (value)
13
    "rjmp L0\n\t"
14
  );
15
16
  return 0;
17
}


Simple Fehlermeldung:
tt.c: In function 'main':
tt.c:12: error: expected ':' or ')' before string constant

Also mal geändert:
1
#include <avr/io.h>
2
3
char value;
4
5
int main (void)
6
{
7
  value = 11;
8
9
  asm volatile("L0: ");
10
  asm volatile("out %0,%1 " : : "I" (_SFR_IO_ADDR(PORTD)), "r" (value));
11
  asm volatile("rjmp L0\n\t");
12
13
  return 0;
14
}

So geht es! Aber es ist doch nicht Sinn und Zweck, jede einzelne Zeile 
mit asm volatile einzuläuten.

[Einiges Zeugs wieder gelöscht, weil --- tataa --- Lösung in Sicht.]

Woran es jetzt geklemmt hat, habe ich raus. Nicht nur die "constraints" 
waren nicht immer richtig, auch die Anordnung des Parameterblocks (die 
"Dingers" nach dem Doppelpunkt). Dieser Parameterblock darf nämlich 
nicht zwischen den Assembler Befehlszeilen stehen, sondern immer am Ende 
des Anweisungsblockes, den Assemblerbefehlen, und schließt mit dem ");" 
ab. Das Bastelproggi sieht dann so aus:
1
#include <avr/io.h>
2
3
int main (void)
4
{
5
  register unsigned char reg0 asm ("r0");
6
7
  asm volatile
8
  (
9
    "L0: \n\t"
10
    "out %0,%1 \n\t"
11
    "rjmp L0\n\t"
12
    : : "I" (_SFR_IO_ADDR(PORTD)), "r" (reg0)
13
  );
14
15
  return 0;
16
}

Der Compiler beanstandet garnix mehr und auch im Assemblerfile, welches 
dabei generiert wird (und wegen des avr-gcc-Parameters "-save-temps" 
nicht gelöscht wird), sieht es sehr gut aus!
1
[....]
2
.LM1:
3
/* #APP */
4
        L0:
5
        out 11,r0
6
        rjmp L0
7
8
        .stabn  68,0,36,.LM2-main
9
.LM2:
10
/* #NOAPP */
11
[....]
Und auch mit der 11 in "out 11,r0 " ist richtig, weil
#define PORTD   _SFR_IO8 (0x0B)

Damit hat das tagelange rumsuchen ein Ende. Dank euch und bis demnäxt.

 Hegy

von Rolf Magnus (Gast)


Lesenswert?

> Da kommt bei 'in' erst das Register, wo die Daten reinwandern, und dann
> die Datenquelle. Umgekehrt ist es beim out-Befehl.

Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.

> Dieser Parameterblock darf nämlich nicht zwischen den Assembler
> Befehlszeilen stehen, sondern immer am Ende des Anweisungsblockes, den
> Assemblerbefehlen, und schließt mit dem ");" ab.

Ja. Die einzelnen Assembler-Zeilen sind ja nur "string literals", die 
dann in einer frühen Übersetzungsphase zu einem kombiniert werden. Wenn 
da mittendrin auf einmal ein Parameterblock kommt, kann der Compiler 
damit natürlich nichts anfangen.

> Der Compiler beanstandet garnix mehr und auch im Assemblerfile, welches
> dabei generiert wird (und wegen des avr-gcc-Parameters "-save-temps"
> nicht gelöscht wird), sieht es sehr gut aus!

Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt 
er statt eines Object-Files ein Assembler-File, das du nicht erst in 
irgendeinem Temp-Verzeichnis suchen mußt.

von Hegy (Gast)


Lesenswert?

> Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.

Aus der Sicht schon, wir reden aneinander vorbei, ich meinte die Stelle, 
wo die Parameter Rd bzw Rr steht und I/O stehen.

IN
 Operation:   Rd ← I/O(A)

Syntax: IN Rd,A

OUT
Operation:    I/O(A) ← Rr

Syntax: OUT A,Rr

> Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt
> er statt eines Object-Files ein Assembler-File, das du nicht erst in
> irgendeinem Temp-Verzeichnis suchen mußt.

Das Assemblerfile (*.s (kleines 's')) steht im dem Vz., wo auch das 
C-File liegt und das Listing etc.

von Simon K. (simon) Benutzerseite


Lesenswert?

Hegy wrote:
>> Nein, wieso? Auch bei out kommt zuerst das Ziel, dann die Quelle.
>
> Aus der Sicht schon, wir reden aneinander vorbei,

Ich denke nicht ;)

> ich meinte die Stelle,
> wo die Parameter Rd bzw Rr steht und I/O stehen.

Ja, genau. Und wie üblich bei Assembler Mnemonics kommt zuerst das Ziel 
(Rd) und dann die Quelle (Rr). Ob jetzt eines von beiden ein I/O 
Register ist, ist ja wumpe.

>> Du kannst den Compiler auch mit dem Parameter -S aufrufen. Dann erzeugt
>> er statt eines Object-Files ein Assembler-File, das du nicht erst in
>> irgendeinem Temp-Verzeichnis suchen mußt.
>
> Das Assemblerfile (*.s (kleines 's')) steht im dem Vz., wo auch das
> C-File liegt und das Listing etc.

Der Compiler erzeugt ein komplettes Assembler-Listing und das ist meist 
in einem der Unterordner zu finden (Relase, Debug, default, was auch 
immer).

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.