Forum: Compiler & IDEs frage zu rjmp beim GCC/ATmega162


von Martin Raffelsieper (Gast)


Lesenswert?

hallo!

Ich habe eine Frage zu relativen Sprüngen:
im define steht ein rjmp +4, d.h. er soll dem ProgramCounter 4 byte vom
Aktuellen Platz weitersetzen:


#define  PA5_TOG   _asm__ __volatile_ ( "sbis 0x1B, 5  \n\t"  \
                                          "sbi  0x1B, 5  \n\t"  \
                                          "rjmp  +4      \n\t"  \
                                          "cbi  0x1B, 5  \n\t"  \
                                        )

wenn ich aber folgenden Code schreibe:
int main(void)
{
   PA5_TOG;
   PA5_TOG;
   PA5_TOG;
   ::::::::::::




kommt folgendes heraus:

int main(void)
{
      b2:  cf ef         ldi  r28, 0xFF  ; 255
      b4:  d4 e0         ldi  r29, 0x04  ; 4
      b6:  de bf         out  0x3e, r29  ; 62
      b8:  cd bf         out  0x3d, r28  ; 61


   PA5_TOG;
      ba:  dd 9b         sbis  0x1b, 5  ; 27
      bc:  dd 9a         sbi  0x1b, 5  ; 27
      be:  fb cf         rjmp  .-10       ; 0xb6
      c0:  dd 98         cbi  0x1b, 5  ; 27
   PA5_TOG;
      c2:  dd 9b         sbis  0x1b, 5  ; 27
      c4:  dd 9a         sbi  0x1b, 5  ; 27
      c6:  f7 cf         rjmp  .-18       ; 0xb6
      c8:  dd 98         cbi  0x1b, 5  ; 27
   PA5_TOG;
      ca:  dd 9b         sbis  0x1b, 5  ; 27
      cc:  dd 9a         sbi  0x1b, 5  ; 27
      ce:  f3 cf         rjmp  .-26       ; 0xb6
      d0:  dd 98         cbi  0x1b, 5  ; 27



das heisst der rjump landet stets 4 plätze Relativ zur Main-Routine.
er soll aber 4 plätze relativ zum PC springen. Wie muss ich das
formulieren?


Gruß Martin

von Matthias (Gast)


Lesenswert?

Hi

ich frage mich erstens warum Inline-ASM für sowas wie ein Pin-Toggle
anwenden? Der GCC macht aus
PORTA^=1<<5;

 254:  8b b3         in  r24, 0x1b  ; 27
 256:  90 e2         ldi  r25, 0x20  ; 32
 258:  89 27         eor  r24, r25
 25a:  8b bb         out  0x1b, r24  ; 27

was mindestens genauso schnell ist wie deine Lösung.

BTW:
Bei deinem Ansatz wird
cbi  0x1B, 5
nie ausgeführt (sagt mir zumindest meine Interpretation von sbis :)

Matthias

von Martin Raffelsieper (Gast)


Lesenswert?

Der Assemblercode in meinem Beispiel ist falsch, das hast Du völlig
richtig erkannt - das mache ich auch noch besser.

Aber:
der Code ist nicht interruptfest, d.h. es kann zu konflikten in einer
ISR kommen.


mit meinem Assemblercode werden nämlich nichtunterbrechbare
bitoperationen ausgeführt - die sind bei meuiner anwendung vonnöten

Meine Frage zielt vielmehr auf Sprungmarken in Makros - Thema verfehlt
- trotzdem danke für die antwort

Gruß Martin

von Matthias (Gast)


Lesenswert?

Hi

wieso ist dein Code interruptfest? Kommt nach dem sbis ein INT kannst
du auch nicht mehr zu 100% sagen wie der Zustand des Ports ist. Du
solltest also entweder:

Während des toggelns die INT's ausschalten oder besser den Port nur im
INT bzw. nur in main() ändern.

Matthias

von Joerg Wunsch (Gast)


Lesenswert?

RJMP +4 ist falsch geschrieben.  RJMP .+4, aber ich würde an Deiner
Stelle besser einen lokalen label benutzen.  Das sind die, die nur aus
Ziffern bestehen und deren Referenz ein `f' oder `b' angehängt
bekommt.  Ist in der avr-libc Doku ein bißchen mit beschrieben.

Ansonsten stimme ich natürlich Matthias zu.  Wenn Du Assembler willst,
dann nimm ihn, ansonsten kannste das alles (einschließlich
Interruptfestigkeit) auch alles ordentlich in C schreiben.  Kann ja
sein, daß der Compiler manchmal einen Takt mehr braucht als Dein
handgefeilter Assembler, dafür kann man aber auch in 3 Jahren noch auf
den ersten Blick sagen, was es tut.

von Martin Raffelsieper (Gast)


Lesenswert?

@Matthias:
Du kannst natürlich niemals erwarten, dass eine Haupt- und eine
Int-Routine an dem Gleichen Port-Pin herumschalten und das dann keine
Konflikte gibt! Das geht weder mit Assembler noch mit C noch
überhaupt.

meine Assmblerroutine soll ja nur verhindern, dass sich die Port-Bits
eines Port-Bytes GEGENSEITIG stören

Gruß Martin


@Joerg:
( by the way:
RJMP .+4 ist das was ich eigentlich wissen wollte! Der punkt wars,
damit läufts so wie ich es brauche, der sorgt wohl für einen Sprung
RELATIV ZUM AKTUELLEN PC)

sicher ist die C-Formulierung in 3 Jahren noch lesbar und die
asseblerlösung nicht. Aber ich brauche halt eine interuptfeste Lösung,
die sowohl in einem Haupt-als auch in einer interuptroutine an dem
ports herumschaltet.

die C-Variante
PORTA^=1<<5;

 254:  8b b3         in  r24, 0x1b  ; 27
 256:  90 e2         ldi  r25, 0x20  ; 32
 258:  89 27         eor  r24, r25
 25a:  8b bb         out  0x1b, r24  ; 27

ist halt nicht interuptfest.

Die Assemblervariante (nach Fehlerkorrektur):
       sbis 0x18,3
       rjmp PORT_H
    PORT_L:
       cbi  0x18,3
       rjmp PORT_END
    PORT_H:
            sbi  0x18,3
    PORT_END:

verträgt sicher einen interrupt, der an den anderen Port-Bits
herumfummelt.

Wie würdest Du das in C formulieren?  - Ich habe lange probiert, so was
in C zu schreiben, aber es gab immer den Fall, dass entweder etwas
wegoptimiert wurde oder die interuptfestigkeit nicht gegeben war.


Gruß Martin

von Joerg Wunsch (Gast)


Lesenswert?

> verträgt sicher einen interrupt, der an den anderen Port-Bits
> herumfummelt.

Das kann man zwar nicht allgemein als ,interruptfest' ansehen, aber
wenn Du diesen Fall wirklich brauchst:

#define TOGGLE do {\
if (PORTB & _BV(3)) \
  PORTB &= ~_BV(3); \
else \
  PORTB |= _BV(3); \
} while (0)

Dieser Makro compiliert bei mir exakt zu Deinem Assemblercode:

        sbis 56-0x20,3
        rjmp .L3
        cbi 56-0x20,3
        rjmp .L2
.L3:
        sbi 56-0x20,3
.L2:

Das hier würde ich als generell interruptfest bezeichnen:

#define TOGGLE do {\
  unsigned char sreg = SREG; \
  cli(); \
  PORTB ^= _BV(3); \
  SREG = sreg; \
} while (0)

von Martin Raffelsieper (Gast)


Lesenswert?

vieln Dank, ich werde wahrscheinlich letzteres verwenden, das sollte
wohl alles abdecken

Deine erste Variante (ohne d-while(0) hatte ich zeitweilig auch, aber
die hatte halt Aussetzer. warum auch immer, während die
Assemblervariante letztlich lief. Nur die Versteht ja kein Mensch....

Allerdings wird die variante 1 bei mir wie folgt kopmiliert, was aber
wohl aufs gleiche herausläuft, solange der Prozessor weit genug
springen kann:


 #define TOGGLE do {\
if (PORTB & _BV(3)) \
  PORTB &= ~_BV(3); \
else \
  PORTB |= _BV(3); \
} while (0)



      TOGGLE;
  b2:  c3 9b         sbis  0x18, 3  ; 24
  b4:  47 c0         rjmp  .+142      ; 0x144
  b6:  c3 98         cbi  0x18, 3  ; 24
      TOGGLE;
  b8:  c3 9b         sbis  0x18, 3  ; 24
  ba:  42 c0         rjmp  .+132      ; 0x140
  bc:  c3 98         cbi  0x18, 3  ; 24

:::::::::::::::::::::::::

 140:  c3 9a         sbi  0x18, 3  ; 24
 142:  bd cf         rjmp  .-134      ; 0xbe
 144:  c3 9a         sbi  0x18, 3  ; 24
 146:  b8 cf         rjmp  .-144      ; 0xb8





Gruß Martin

von Joerg Wunsch (Gast)


Lesenswert?

> Allerdings wird die variante 1 bei mir wie folgt kopmiliert,

Hmm, ekliges Disassemblerlisting, warum nimmst Du nicht den
Assembler-Output des Compilers? ;-)

Sieht nach Optimierung größer als 1 oder s aus.

> ... was aber wohl aufs gleiche herausläuft, solange der Prozessor
> weit genug springen kann

Diese Einschätzung würde ich dem Compiler zutrauen. ;-)

von Martin Raffelsieper (Gast)


Lesenswert?

ja wie sieht es denn bei dir aus, du wolltest sicher ne datei
anhängen????


wo/wie finde ich denn das assembleroutput des compilers ??


ich trau dem compiler mittlerweile auch - wenn er auc für meinen
Geschmack zuviel optimiert!

von Matthias (Gast)


Lesenswert?

Hi

du kannst dir den ASM-Output anschauen indem du dem Compiler -S
übergibst. Dann wird der Vorgang allerdings abgebrochen und du erhälst
kein .o das du dann dem Linker übergeben kannst. Ich erzeuge mir
deshalb aus der .elf-Datei das ASM-Listing per

avr-objdump.exe -t -h -S $(BIN).elf >$(BIN).lst

Matthias

von Martin Raffelsieper (Gast)


Lesenswert?

Das mit der objdump anweisung kannte ich schon, nicht aber den Tip mit
dem Compiler - das probier ich gleich aus

vielen Dank

von Joerg Wunsch (Gast)


Lesenswert?

Mit dem Disassemblieren bekommst Du insbesondere nicht mehr die
originalen Kommentare des Compilers zu sehen.

Tip: bei Benutzung von -S den Compiler besser ohne -g aufrufen, die
Debug-Pseudoanweisungen verwirren nur unnütz.

Das aktuelle WinAVR-Makefile-Template kann sowas schon automatisch
(bis auf das Weglassen des -g): für eine C-Datei foo.c kann man mit
»make foo.s« verlangen, daß die zugehörige Assemblerdatei erstellt
wird.  Auf Dateisystemen, die nicht zwischen Groß- und Kleinschreibung
unterscheiden, sollte man dann aber besser keine Quelldate mit dem
Namen foo.S haben... ;-)

von Martin Raffelsieper (Gast)


Lesenswert?

Hmmm

ich habe folgenden Compileraufruf gemacht:

avr-gcc -S -Wall -O2 -mmcu=atmega162 -c main.c

und es kommt tatsächlich ein Listing main.s heraus. Leider ist die
Zuordnung C-Quelltest -> Assembler nicht zu erkennen. Hab ich da wohl
noch ne Option vergessen?

Bei der Variante, die Matthias vorschlägt, ist das dagegen schön zu
sehen:

avr-objdump.exe -t -h -S $(BIN).elf >$(BIN).lst




Gruß Martin

von Joerg Wunsch (Gast)


Lesenswert?

> Leider ist die Zuordnung C-Quelltest -> Assembler nicht zu
> erkennen. Hab ich da wohl noch ne Option vergessen?

Nein, dafür gibt es keine Compileroption.  Allerdings kann man sich da
ganz gut reinlesen.  Mit bißchen Übung versteht man durchaus, welche
Assemblerteile zu welchen C-Statements gehören.

> Bei der Variante, die Matthias vorschlägt, ist das dagegen schön zu
> sehen:

Das wird dort nachträglich über die Debug-Statements hergestellt.  Die
müssen übrigens nicht immer richtig sein, optimierter Code ist oft
nicht mehr so eindeutig zuordenbar.  Das Problem hat man natürlich
beim Debuggen selbst ebenfalls.

Du kannst natürlich beim Compileraufruf auch ein -g mit reinnehmen,
dann hast Du alle Debug-Statements wieder drin, damit auch die
Zeilennummern.  Ich find's aber unübersichtlicher, als gleich den
reinen Assembler zu lesen.

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.