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
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.
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
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.
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
Ü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.
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
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?
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.
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
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?
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.
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
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 ) )
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.