Forum: Compiler & IDEs Funktionspointer


von Kurt (Gast)


Lesenswert?

Hallo zusammen.

Frage: Warum kann ich beim gcc keinen Funktionspointer, wie in C
üblich, bei der Deklaration mit einer Adresse initialisieren?

unsigned char (*fu_pointer)(unsigned char) = 0x1f14;

Was ist an dieser Deklaration falsch?

Gruß, Kurt

von A.K. (Gast)


Lesenswert?

Weil man das auch sonst nicht kann, jedenfalls nicht so. Wenn doch und
ohne Warnung, gib von mir eine Ohrfeige an den Compiler weiter.

0x1f14 ist nunmal kein Pointer, also stimmt der Typ nicht.

Besser:
typedef unsigned char (*myfcnptr)(unsigned char);
fcnptr fu_pointer = (fcnptr)0x1f14;

Empfehlen kann ich es freilich nicht. Denn ob das eine Byte- oder
Wortadresse ist, mag je nach Compiler verschieden sein.

von A.K. (Gast)


Lesenswert?

Tippfehler. Richtig:
typedef unsigned char (*myfcnptr)(unsigned char);
myfcnptr fu_pointer = (myfcnptr)0x1f14;

von Kurt (Gast)


Lesenswert?

Hallo A.K.
Vielen Dank für die schnelle Antwort. Der Compiler meckert nicht mehr,
aber die Pointerinitialisierung funktioniert leider nicht richtig, da
beim Funktionsauruf im gestarteten Programm, die Funktion leider nicht
angesprungen wird sondern das Programm sich irgendwohin verirrt! Die
zugewiesene Funktions-Adresse (Funktion im Boot-Loader-Flash) ist
richtig (habe ich durch auslesen des Boot-Loader-Flash überprüft). Wie
kann ich die Zuweisung überprüfen (Mapfile oder sowas?).

Kurt

von A.K. (Gast)


Lesenswert?

Yep, ich sach noch, pass auf die Byte/Wort-Adressierung auf.
Flash-Adressen sind je nach Kontext Byte- oder Wort-Adressen. Kann also
sein dass du mit 0x1f14*2 oder 0x1f14/2 mehr Erfolg hast.

von Kurt (Gast)


Lesenswert?

Ja, aber ich bin doch hier im GCC-Forum und spreche doch auch vom GCC.
Also noch einmal meine zweite Frage: Wie kann ich überprüfen, ob die
initialisierung richtig vorgenommen wird (Mapfile oder so)? Übrigens,
laut C-Lehrbuch und auch Informationen aus dem Net, ist die Deklaration
eines Funktions-Pointers mit direkter Initialisierung in C wie:

unsigned char (*fu_pointer)(unsigned char) = 0x1f14;

richtig und dürfte vom Compiler nicht beanstandet werden!


HILFE..., Kurt

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


Lesenswert?

Dann wirf das C-Lehrbuch weg.

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


Lesenswert?

Übrigens würde ich dafür eine jump table an den Anfang des
Bootloaders setzen, damit sich die Adressen nicht mehr
verändern.  In der Applikation bekommen all die Einsprünge
da eine struct of function pointers in eine eigene .section,
die dann per --section-start im Linker auf die Adresse im
Bootloader gesetzt wird.

von Kurt (Gast)


Lesenswert?

Ich habe die Initialisierung des typedef-Funktions-Pointers (wie oben
von A.K. vorgeschlagen) mal innerhalb einer Funktion durchgeführt. Das
macht der Compiler dann aus dem Funktionsaufruf:

     fu_pointer(72);
  6e:  88 e4         ldi  r24, 0x48  ; 72
  70:  7f df         rcall  .-258      ; 0xffffff70
<__eeprom_end+0xff7eff70>

Der Funktions-Pointer ist mit der Adrese 0x1f14 initialisiert.

Wie kommt der Compiler darauf, dass sich die Funktion im EEPROM
befindet oder was bedeutet der angefügte eeprom-end-Kommentar? Muss ich
dem Compiler irgendwie mitteilen das es sich um eine Funktion im Flash
handelt?

Kurt

von Stefan (Gast)


Lesenswert?

Ich finde nicht, dass der Compiler die Funktion im EEPROM vermutet.

Wenn du deine normalen Daten im EEPROM liegen hast, liegt natürlich
auch die Variable fu_pointer im EEPROM.

Für den Funktionsaufruf sollte das keine Rolle spielen, wenn der Inhalt
der Variablen eine gültige Flash-Adresse einer Funktion ist.

D.h. wie kommst du auf den absoluten Wert 0x1f14?

von A.K. (Gast)


Lesenswert?

Dass hier EEPROM-Adressen angezeigt werden, hat weniger mit dem EEPROM
und viel dem etwas hilflosen Adressmapping vom Von-Neumann-GCC auf eine
Hardward-CPU zu tun, und dem verzweifelten Versuch des Disassemblers,
darin einen Sinn zu erkennen. Hilfreicher wäre es gewesen, den Typ vom
AVR und die Adresse den RJMP zu kennen. Wenn es sich um einen 8KB-Typen
handelt, ist 0x1Fxx eine Byteadresse im Bootbereich am Ende vom Flash
und GCC will eine Wortadresse, also ist dann 0x1Fxx/2 richtig.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

der GCC arbeitet AFAIK immer mit Byteadressen. Das was man da sieht ist
ein rjmp dessen negatives Argument dafür sorgt das es zu einem
Unterlauf kommt was der Dissasembler mit dieser riesigen Adresse
beantwortet. Was wirklich im Chip passiert ist ein Wrap-Around so das
der rcall dann irgendwo am oberen Ende des Flash endet.

Ich fürchte aber das sich Kurt irgendwie verrant hat und den Wald vor
lauter Bäumen nicht mehr sieht. Desshalb meine Frage an den OP: Was
soll das ganze werden?

Matthias

von m4444x (Gast)


Lesenswert?

Ich steh jetzt vor dem gleichen Problem:

#define APP_FLASH_SIZE 0x1800
typedef uint8_t (*CAN_FUNCTION)( CAN_MSG * );
#define rcvCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 2 ))( X ) )
#define sndCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 4 ))( X ) )

sndCan( msg );

daraus entsteht folgendes Listing:

  d0:  d5 db         rcall  .-2134     ; 0xfffff87c

wenn ich nun aber nachrechne ( 0xd0 - 2134 + 0x2000 ) ergibt das nicht 
APP_FLASH_SIZE + 4 sondern APP_FLASH_SIZE + 4 + 0x78. 0x78 ist 
zufälliger weise die Größe der .vectors und .init sections. Wie sag' ich 
dem GCC jetzt das die Adresse eine absolute Adresse ist und nicht eine 
im .text Segment?

von m4444x (Gast)


Lesenswert?

Jörg? Könntest Du vielleicht die Struct of Function Pointers Lösung 
etwas präzisieren (Beispiel)?

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


Lesenswert?

Das Beispiel ist ein wenig komplex, habe ich gerade nicht (getestet)
zur Hand.

Im Prinzip ungefähr so: statt des normalen Startups setzt du an den
Anfang des Bootloaders eine Sprungtabelle:
1
rjmp   bootload  ; reset vector
2
rjmp   thisfunc
3
rjmp   thatfunc
4
rjmp   whatever

Die daraus assemblierte Objektdatei wird beim Linken als erstes
angegeben.

Für die normale Applikation links du die folgende Datei mit hinzu,
um auf diese Sprungtabelle zuzugreifen:
1
#define bl_offset 0x1800
2
#define sizeof_rjmp 2
3
4
.equ bootload,  bl_offset + 0
5
.equ thisfunc,  bl_offset + 1 * sizeof_rjmp
6
.equ thatfunc,  bl_offset + 2 * sizeof_rjmp
7
.equ whatever,  bl_offset + 3 * sizeof_rjmp

Wie geschrieben, ist alles ungetestet, aber sollte dir zumindest die
Idee bringen.

von _sk_ (Gast)


Lesenswert?

Im Prinzip benutzt man auf diese Weise ja unbenutzte 
Bootloader-IR-Einsprünge.

Am Schönsten wäre es, wenn man ein Makro ähnlich wie SIGNAL oder 
INTERRUPT schreiben würde, welches den Vektor automatisch setzt, aber 
einen normalen Funktionsaufruf statt eines IRs generiert.

Auf diese Weise kommt man auch weniger mit vom Bootloader benutzten 
Interrupts in die Quere.

Gruß, Stefan

von A.K. (Gast)


Lesenswert?

Nochmal zu Byte- und Wortadressen: Es stimmt schon, dass GCC und 
insbesondere die Binutils mit Byteadressen rechnen. Die Maschine 
allerdings sieht das anders und daher muss man höllisch aufpassen wer 
da grad rechnet und ob er weiss womit er rechnet.

GCC erlaubt von Haus aus keine Rechnung mit Codeadressen und kennt auch 
keine numerischen Konstanten von Codeadressen (mit diesem Problem fing 
der Thread ja an). Wenn man nun per type-cast eine numerische Konstante 
in einen Funktionszeiger umwandelt, dann findet keine Umrechnung 
zwischen Byte- und Wortadresse statt und die angegebene Zahl wird direkt 
als (Wort-) Adresse verwendet.

Im Beispiel am Anfang dieses Threads wird also die Wortadresse 0x1f14 
angesprungen. Bei einem Mega8 existiert diese Adresse überhaupt nicht. 
Korrekt wäre also "... = 0x1f14/2" gewesen.

Analog muss auch beim Problem von m4444x in Worten gerechnet werden:

#define APP_FLASH_SIZE (0x1800/2)
typedef uint8_t (*CAN_FUNCTION)( CAN_MSG * );
#define rcvCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 1 ))( X ) )
#define sndCan( X ) ( ((CAN_FUNCTION)( APP_FLASH_SIZE + 2 ))( X ) )

von Unbekannter (Gast)


Lesenswert?

Kann man so etwas micht über den Linker machen?

Also eine Funktion deklarieren (aber nicht definieren) und beim Linken 
dann die Adresse dieser Funktion per Option setzen.

So, oder so ähnlich müsste das gehen.

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.