Hallo Holger!
1 | Ich glaube ich habe eine Antwort auf mein Problem gefunden; vieleicht
|
2 | hilft sie ja irgendwann einmal jemandem.
|
Und ob! ;)
Ich habe auch eine etwas unübliche Bootloaderkonstruktion gebaut, die
kein UART implementiert, sondern nur als Ausführ-Ort für das SPM
Kommando dient. Die Anwendungen selbst rufen den Bootloader auf. Um
Bootloader und Images trotzdem die Möglichkeit zu geben, schreiben zu
können (und der Ram sehr begrenzt ist >.<) , habe ich nach einer
sauberen Lösung für gemeinsam bekannten Puffer im Ram gesucht.
Nach studenlangem Einlesen ins GNU-LD Handbuch, hab ich meine
Wunschkonstellation wie du ewig nicht hingekriegt. Und exakt der Tip mit
den eigenartig identischen Symbol/Section-Namen hat die gewünschte
Symbolverteilung gebracht!
Ich hätte bei
1 | .bss : AT ( ADDR (.bss) ) { .. }
|
aber auch schon stutzig werden müssen ;).
Jedoch wird ein Hex-File erzeugt, bei dem explizit ein Adress-Sprung auf
0x0080 drin ist (Intel-Hex Zeile vom Typ 04). Der avr-dude sagt mir da,
dass er die Adresse nicht gefunden hat (so einen großen Flash hat der
avr nun mal nicht ;). Im Avr wird die Adresse 0x0080.xxxx offenbar nicht
verwertet und an den SRAM weitergeleitet (wäre ja auch eine 32bit
Adresse).
Das was wir wollen, ist ja, dass der Linker zur Compile-Zeit allen
Variablen die richtige Adresse zurordnet.
Avr-Dude kann soweit ich das verstanden habe, keinen Ram direkt
schreiben. Der wird ja sowieso beim Reset mit den initialisierten
Datenwerten geladen (__data_load_start .. __data_load_end).
Ich hab gerade nochmal probiert, eeprom-Werte mit reinzukompilieren. Da
passiert dasselbe...
Avr-Dude erkennt folgende Intel-Hex Zeile als ungültig an:
1 | :02000004008179
|
2 | :04000000FEA4AFFEAD
|
--> also vom Typ 04, und Adresse 0x0081 als obere 16bit für die
folgenden Zeilen. Das korreliert mit dieser Zeile aus dem Linkerscript:
1 | eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
|
(zugehöriger Code: uint8_t eeFooByteArray4[] EEMEM = { 0xFE, 0xA4,
0xAF,0xFE }; )
Wenn man mit Avr-Dude den eeprom schreiben will, braucht man eine andere
Kommandozeile (eeprom:w:e.hex) ... Offenbar scheint das Schreiben in
verschiedene Speicher mit verschiedenen SPI-Schreib-Befehlen codiert zu
sein.
Die Lösung des Problems war im Endeffekt, dass man eine globale Variable
anlegt (Position egal) und sie mit einer eigenen Section-Anweisung
versieht.
1 | uint8_t globalBuf[128] __attribute__ (( section(".globalBufSec")));
|
Im Linkerscript wird eine Zeile eingfügt, dass vor den .data Sections
aller Dateien, die eigenen Sections zu stehen haben.
1 |
|
2 | .data : AT( ADDR (.text) + SIZEOF(.text) )
|
3 | {
|
4 | /* hier werden SYMBOLE definiert! */
|
5 | PROVIDE (__data_start = .) ; /* The PROVIDE keyword may be used to define a symbol, such as `etext', only if it is referenced but not defined. */
|
6 |
|
7 | *(.globalBufSec) /*eigene sections*/
|
8 |
|
9 |
|
10 | *(.data)
|
11 | *(.data*)
|
12 | *(.rodata) /* We need to include .rodata here if gcc is used */
|
13 | *(.rodata*) /* with -fdata-sections. */
|
14 | *(.gnu.linkonce.d*)
|
15 |
|
16 | . = ALIGN(2); /* Linker location counter auf relativen Offset von 2 ggü Start der Section,
|
17 | dieser Wert gilt *MINDESTENS* --> wenn data-variablen: "_edata" direkt danach*/
|
18 | _edata = . ; /* SYMBOL: end data */
|
19 | PROVIDE (__data_end = .) ;
|
20 | } > data
|
Wenn man die .data und .bss Blöcke umbenennt, gibt es mit hoher
Wahrscheinlichkeit einen Fehler!! Zwar meckert weder avr-gcc noch
avr-dude, aaaber:
Aus dem Datenblatt:
1 | Data Memory Map for ATmega164P/324P/644P.
|
2 | 32 Registers ... 0x0000 - 0x001F
|
3 | 64 I/O Registers ... 0x0020 - 0x005F
|
4 | 160 Ext I/O Reg. ... 0x0060 - 0x00FF
|
5 | Internal SRAM ... 0x0100 - 0x04FF/0x08FF/0x10FF
|
Wenn man .data in .dataOut ändert (also den Block innerhalb von
SECTIONS{..}), starten die zugehörigen Daten bei 0x0080.0060
(entsprechend der Definition im Linkerscript von "data" in MEMORY. siehe
avr-objdump [..].elf -x). Und genau das ist tödlich beim atmega164!
Somit werden nämlich die "Externen I/O Register" überschrieben und nicht
der SRAM benutzt!
Zum Schluss habe ich noch kurz gesucht, was die ganze Toolchain dazu
bewogen hat ".data" anders als ".dataOut" (ausgedachter Name) zu
behandeln und den Inhalt einfach so auf 0x0080.0100 zu schieben:
In /usr/lib/avr/include/avr gibt es eine Menge config-Dateien, die wohl
vom avr-gcc je nach Auswahl des Controllers (-mmcu=..) geladen werden.
Dort steht zb "#define RAMSTART (0x100)" drin, was intern mit der
Ausgabe-Section ".data" des Linker-Scripts verknüpft sein muss.
(gefunden mit: grep "x100" -r /usr/lib/avr/* )
Soviel dazu ... ;)
Viele Grüße
Flo