Forum: Compiler & IDEs Funktionsaufruf an fester Adresse


von Axel Barkow (Gast)


Lesenswert?

Hallo,

ich möchte aus meiner Software für einen Mega88 eine Funktion an einer
festen Adresse im Bootloaderbereich anspringen, die nicht Teil meiner
Software ist. Ich habe das über einen Funktionszeiger versucht, jedoch
bringt das nicht den gewünschten Erfolg.

void (*jump_to_bootloader)( void ) = BOOTLDRSTART;
//Aufruf über jump_to_bootloader()

Nach langem Probieren habe ich eine Lösung gefunden, indem ich eine
Dummy-Funktion an diese fixe Adresse gebunden habe und den für diese
Funktion entstehenden Code nicht ins Flash übertrage.

void jump_to_bootloader(void) _attribute_ ((section
(".bootldrstart")));
void jump_to_bootloader(void)
{
  asm("nop");
}
//Aufruf über jump_to_bootloader()


Diese Lösung finde ich nur etwas unschön. Hat jemad einen besseren
Vorschlag oder eine Lösung für das Funktionszeigerproblem?

Gruß,

Axel

von Stefan (Gast)


Lesenswert?

Wie ist dein BOOTLDRSTART definiert?

Ist das vielleicht eine Datenadresse im ROM von der der Bootlader erst
an eine eigentliche Ablaufadresse geladen wird?

von Peter D. (peda)


Lesenswert?

Du machst ne Zuweisung und keinen Funktionsaufruf.

Die Syntax für nen Funktionsaufruf sieht z.B. so aus:
1
#define CALL(addr)      (((void(*)(void))(char *)addr)())
2
...
3
CALL( 0xFFF0 );               // API call


Peter

von Axel Barkow (Gast)


Lesenswert?

@Stefan
BOOTLDRSTART ist eine Konstante (0x1e2a).

@Peter
Die Zuweisung ist nur die Initialisierung einer globalen Variablen. Der
eigentliche Aufruf im Programm erfolgt über

...
msg.data[0] = 0x02;
can_send_message(&msg);
//jump_to_bootloader at 0x1e2a;
jump_to_bootloader();
...

Werde dein Makro aber auch noch einmal testen.

@all
Der Assembler-Code, der bei beiden Varianten entsteht, ist auch
unterschiedlich.

Die Dummy-Funktion erzeugt folgendes:
        msg.data[0] = 0x02;
 b4e:  82 e0         ldi  r24, 0x02  ; 2
 b50:  8a 87         std  Y+10, r24  ; 0x0a
        can_send_message(&msg);
 b52:  ce 01         movw  r24, r28
 b54:  01 96         adiw  r24, 0x01  ; 1
 b56:  67 dc         rcall  .-1842     ; 0x426

        //jump_to_bootloader = 0x1e2a;
        jump_to_bootloader();
 b58:  78 d9         rcall  .-3344     ; 0xfffffe4a

Der Funktionszeiger folgendes:

        msg.data[0] = 0x02;
 b4e:  82 e0         ldi  r24, 0x02  ; 2
 b50:  8a 87         std  Y+10, r24  ; 0x0a
        can_send_message(&msg);
 b52:  ce 01         movw  r24, r28
 b54:  01 96         adiw  r24, 0x01  ; 1
 b56:  67 dc         rcall  .-1842     ; 0x426
        //jump_to_bootloader = 0x1e2a;
        jump_to_bootloader();
 b58:  e0 91 00 01   lds  r30, 0x0100
 b5c:  f0 91 01 01   lds  r31, 0x0101
 b60:  09 95         icall

Den Aufruf über icall hatte ich auch schon über inline-Assembler
probiert, hat aber auch nicht funktioniert. Ich hatte das Z-Register
allerdings direkt mit ldi beschrieben.

Gruß,

Axel

von Axel Barkow (Gast)


Lesenswert?

@Stefan
BOOTLDRSTART ist natürlich 0x1e4a, ich hatte da nur einige Kommentare
noch nicht aktualisiert

von Stefan (Gast)


Lesenswert?

So verkehrt ist die erste Variante nicht bis auf die Tatsache, dass
0x1e4a von 0x0b58 aus nicht erreichbar ist. Wieso?

In
http://www.atmel.com/dyn/resources/prod_documents/doc2537.pdf
liest man, dass rcall nur 2K words in either direction (= +- 0x1000)
adressieren kann. Möglicherweise ist das bei dem AtMega88 auch so.

Es gibt eine ähnliche Diskussion darüber:
http://www.mikrocontroller.net/forum/read-2-240145.html

Ich vermute der Compiler ist einmal zu schlau, sieht dass sich der
Funktionspointer nicht ändert und setzt direkt den zugewiesenen Wert
ein. Dafür ist er dann aber zu blöd und weiss nix von der 2K Grenze.

Welche Compilerversion benutzt du?

von Stefan (Gast)


Lesenswert?

Ähm - Habe ich jetzt Quatsch geschrieben oder sind die Beispiele
gedreht? Muss mir mal selbst einen kompletten C-Code zusammenbasteln
und schauen, was jeweils rauskommt. Melde mich wieder...

von Peter D. (peda)


Lesenswert?

Ich denke die Adresse ist fest ?

Dann kostet der Umweg über ne globale Variable nur unnötig Flash und
SRAM.


Ältere WINAVR-Versionen hatten nen Bug bei RCALL/RJMP, die haben dann
relativ zum Modul gesprungen.
Z.B. ein Call nach 0x0000 für zur Startadresse des Moduls und nicht zum
Resetvektor.


Peter

von Axel B. (abarkow)


Lesenswert?

Ein avr-gcc -dumpversion ergibt 3.4.6, installiert ist WinAVR 20060421

@Peter
Gut, die globale Variable kostet zwar Speicher, trotzdem sollte der
Aufruf funktionieren. Die Adresse ist im Prinzip fix, ich halte mir so
aber die Möglichkeit offen, diese Adresse von extern zu beeinflussen.

@Stefan
Ja, die Beispiele sind gedreht, sorry. Also der rcall funktioniert, der
icall nicht. Wobei ich das so nicht verstehe.

Gruß,

Axel

von JojoS (Gast)


Lesenswert?

ich probiere z.Zt. das gleiche, im Elektronik Forum gibt es einen
ähnlichen Thread. Da ich das auch mit dem gcc realisieren möchte passt
es hier aber besser.
1
/* external for Bootloader --------------------- */
2
3
typedef void (*pFn)(void);
4
const pFn Bootloader = (pFn)0x1234;
5
.....
6
7
if (rcvBuffer[0] == 'B')
8
   Bootloader();      // exit to Bootloader

Der Bootloader Aufruf ist natürlich kein echter Call sondern ein
Never-come-back Jump, der Bootloader macht einen Reset nach dem
Blitzdingsen, damit nicht wieder die alte Diskussion losgeht... Im AVR
Studio habe ich das durchgesteppt, der Call springt an die 0x1234.
Unschön ist nur das die Bootloader adresse absolut angegeben wird. Ich
hätte hier lieber ein external und die Adresse vom Linker aufgelöst,
aber das habe ich nicht hinbekommen (kann der Linker eine .sym Datei
nutzen?).
Als Bootloader benutze ich den von P.Fleury, steht hier auch in der
Codesammlung. Der muss noch etwas umsortiert werden damit der
'Warmstart' auch die nötigen Initialisierungen macht.

von JojoS (Gast)


Lesenswert?

Nachtrag: ich hatte auch probiert den Pointer mit dem Attribut PROGMEM
zu versehen und in den Codespeicher zu legen, aber dann klappt der
Bootloader() call nicht weil der Compiler trotzdem LDS statt LPM
Befehle erzeugt.

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.