Hallo Allerseits,
es geht hier darum, Daten in einem AVR jenseits der 64kB Grenze im Flash
zu platzieren und darauf zuzugreifen.
https://www.mikrocontroller.net/articles/Kategorie:AVR-GCC-Tutorial#Variablenzugriff_.3E64kB
Das geht so nicht. Mein Testprogramm (siehe Anhang) hat nur 704Bytes,
nicht ~128kB. Die Parameter für die .far_section stehen unter custom
options / Linker.
Wie geht's richtig? Wie kann man dem Compiler und Linker verclickern,
daß die Daten so wie bei PROGMEM in der .far_section landen. Die
Zugriffe über die _far Funktionen von pgmspace.h gibt es ja schon.
Mein Ziel ist es, das erstmal im old school AVR Studio und später im
Atmel Studio zum laufen zu bringen, dann vielleicht sogar in der Arduino
IDE. Ich fürchte aber, daß ich das allein nicht gebacken kriege, u.a.
weil ich definitiv NICHT in die Untiefen der Linkerscripts absteigen
kann und will. Das ist mir ne Nummer zu kryptisch.
Wer kann helfen? Wie geht es im Atmelstudio?
Hmm, hier mein Test mit Atmel Studio 6.2 (jaja, schon ein wenig
angestaubt)
Wie es scheint, werden die Daten in der far_section zwar eingebunden,
aber bei der Berechnung der Flash-Nutzung vergessen. Nicht schön, aber
schonmal ein Fortschritt. Könnte es sein, daß das auch beim AVR-Studio
passiert und die Daten doch im Hexfile landen? Gleich mal testen.
Tatsächlich, auch im AVR-Studio landen die Daten der far_section im
Flash, werden aber nicht bei der Berechnung der Flash-Ausnutzung
einbezogen. Wie kann man das ändern?
Hier das Ausschnitt aus dem .map File
Oliver S. schrieb:> avr-size kurz etwas umschreiben sollte reichen. Die Sourcen sind> auffindbar.
Außerdem, avr-size macht es anscheinend richtig, nur die nachfolgende
Auswertung durch Atmel Studio geht schief.
Hmmm, wie es scheint, ist das in der Arduino-IDE leichter als gedacht!
Es funktioniert!
Man muss nur 2 Zeilen in
C:\Programme\Arduino\hardware\arduino\avr\plattform.txt
anpassen. Die Arduino-IDE darf dabei NICHT laufen! Erst schließen, dann
ändern und neu starten!
1
## Combine gc-sections, archives, and objects
2
* recipe.c.combine.pattern= .... viele Parameter..... -Wl,--section-start=.far_section=0x10000
also ans Ende der Zeile
1
-Wl,--section-start=.far_section=0x10000
anfügen. Damit wird dem Linker die Section mitgeteilt.
Hiermit wird die far_section in die Berechnung der Flash-Größe
einbezogen! Und damit kann man den Flash nicht überladen! Ebenso kommt
eine Fehlermeldung, wenn man >64kB PROGMEM + Programmcode compilieren
will, weil der sich dann mit der far_section überlagert. Super! Genau so
soll es sein!
Das kann man leicht selber prüfen, indem man die Arrays dummy6 sowie
dummy_PGM_B mal ein bisschen größer macht. Mehr als 32767 Bytes sind
beim avr gcc so oder so nicht möglich, das ist die Maximalgröße für ein
Array (wegen der Indexberechung mit 16 Bit Integer).
Das Testprogramm macht nix sinnvolles außer die Konstanten im Flash
anlegen und durch eine Zuweisung auf PORTA das Wegoptimieren verhindern.
Es wird auch gezeigt, wie man eine 32 Bit Variable als Adresse verwenden
kann, um die Arrays zu lesen. Ein Zugriff über den Index ist hier NICHT
möglich! Adressberechnungen müssen die Größe des Datentyps
berücksichtigem, der 32 Bit Pointer ist immer ein Byte-Pointer!
Viel Spaß beim Ausschöpfen der 256kB des ArduinoMEGA!
P S Ja, der Zugriff auf die Daten >64kB ist natürlich sowohl was den
Schreibaufwand im C-Code als auch real auf Assemblerebene angeht etwas
mühsam und deutlich langsamer als unterhalb von 64kB. Ein Byte-Zugriff
dauert mal fix 9 Takte, 4 zum Laden der 32 Bit Adresse (FAR_ADR()
Makro), 5 zum Zugriff via ELPM (pgm_read_byte_far() Makro). Naja, aber
besser als nix, denn ohne diese Funktionen kann man mit dem Flash >64kB
für Daten nix anfangen!
Weitere Möglichkeit sind die Address-Spaces __flash1 etc, die man
genauso verwendne kann wie __flash. Es sind 16-Bit Adressen, und für
den Zugriff wird RAMPZ mit 1 geladen bzw. mit N für __flashN.
Das einzige, was zu beachne ist, ist dass das default Linker-Skript die
entsprechenden Sections nicht lokatiert; das muss der Anwender machen je
nachdem wie er es braucht. Für __flash1 könnte das zum Beispiel so
aussehen:
1
SECTIONS
2
{
3
.flash1 :
4
{
5
. = MAX (ABSOLUTE(0x10000), .);
6
PROVIDE (__flash1_start = .);
7
. = ALIGN(2);
8
*(.text.flash1.*)
9
*(.progmem1.data*)
10
PROVIDE (__flash1_end = .);
11
ASSERT (__flash1_end <= 0x20000,
12
"__flash1 data in .progmem1.data exceeds 0x20000");
13
}
14
}
15
16
INSERT AFTER .text
Als Textdatei speichern und beim Linken mit -T <datei> als Ergänzung zum
Linker-Skript angeben.
So wird dann auch die Verwaltung etwas einfacher, weil mit einem
einzigen Makro Ablage und Zugriff auf einen anderen Address-Space
umgestellt werden kann:
1
#ifdef __FLASH1
2
#define SPACE_A __flash1
3
#else
4
#define SPACE_A __flash
Wenn man nicht für jeden Space eigene Funktionen vorhalten will, dann
kann man die Zugriffsfunktionen für __memx schreiben.
Johann L. schrieb:> ASSERT (__flash1_end <= 0x20000,> "__flash1 data in .progmem1.data exceeds 0x20000");
Hmm, sind das 16 Bit Word oder Byte Adressen? Für 256kB müßte dort
eigentlich 0x40000 stehen.
Johann L. schrieb:> Wenn man nicht für jeden Space eigene Funktionen vorhalten will,
Das würde ich nicht wollen, denn damit wird das Kuddelmuddel noch
größer. Außerdem erlauben die Makros pgm_read_xxx_far() den Zugriff auf
den vollen Adressraum, wenn gleich natürlich mit aufwändigerer
Schreibweise.
> dann> kann man die Zugriffsfunktionen für __memx schreiben.
Dazu braucht man aber wieder Inline-Assembler Makros. Richtig?
Gibt es da Ansätze, Beispiele?
Falk B. schrieb:> Dazu braucht man aber wieder Inline-Assembler Makros. Richtig?> Gibt es da Ansätze, Beispiele?
In nacktem Assembler ist das aber absolut trivial. Ich stelle das
Prinzip einfach mal im Vergleich zum Zugriff auf die ersten 64k dar.
;16Bit-Adresse, wahlfrei
ldi ZH,Byte2(Flashaddress)
ldi ZL,Byte1(Flashaddress)
lpm reg,Z
;16Bit-Adresse, sequenziell
ldi ZH,Byte2(Flashaddress)
ldi ZL,Byte1(Flashaddress)
lpm reg,Z+
lpm reg,Z+
lpm reg,Z+
...
;22Bit-Adresse, wahlfrei
ldi ZH,Byte3(Flashaddress)
out RAMPZ,ZH
ldi ZH,Byte2(Flashaddress)
ldi ZL,Byte1(Flashaddress)
elpm reg,Z
;22Bit-Adresse, sequenziell
ldi ZH,Byte3(Flashaddress)
out RAMPZ,ZH
ldi ZH,Byte2(Flashaddress)
ldi ZL,Byte1(Flashaddress)
elpm reg,Z+
elpm reg,Z+
elpm reg,Z+
...
Man kann das Zeug natürlich auch in Makros verpacken. Habe ich in
Assembler auch getan. Meine Macros sehen so aus:
;->@0: address somewhere in flashspace
;<-[RAMPZ:]ZH:ZL: set to given address
.MACRO LEAPM
.IFDEF RAMPZ
ldi ZH,Byte3(@0)
out RAMPZ,ZH
.ENDIF
ldi ZH,Byte2(@0)
ldi ZL,Byte1(@0)
.ENDMACRO
;->@0: target register
; @1: source (Z or Z+)
;<-target register set to flash content
.MACRO ULPM
.IFDEF RAMPZ
elpm @0,@1
.ELSE
lpm @0,@1
.ENDIF
Damit ist das Thema schon vollständig abgehandelt.
Der einzige Nachteil ist, wenn man nicht nachdenkt und auf Devices mit
>64k für eine Adresse unterhalb von 64k das LEAPM-Macro benutzt, dann
hat man zwei (meist) unnötige Extra-Ticks an der Backe. Das aber zum
Glück nur beim Setup einer Adresse, danach sind dann beliebig viele
sequentielle Zugriffe via ULPM ohne Verlust möglich, da elpm genau so
viele Takte wie lpm benötigt.
Der Vorteil ist: die 64k-Grenze verschwindet als solche. Ein Array kann
problemlos diese Grenze überschreiten, zumindest solange es halt bloß
sequentiell gelesen wird.
c-hater schrieb:> Damit ist das Thema schon vollständig abgehandelt.
Nicht ganz, da fehlte noch ein .ENDMACRO. Hab' ich wohl beim Kopieren
nicht mit erwischt.
Falk B. schrieb:> Johann L. schrieb:>> ASSERT (__flash1_end <= 0x20000,>> "__flash1 data in .progmem1.data exceeds 0x20000");>> Hmm, sind das 16 Bit Word oder Byte Adressen? Für 256kB müßte dort> eigentlich 0x40000 stehen.
Byte-Adressen. __flash1 reicht von 0x10000 bis 0x1ffff.
Du kannst also __flash1 bis __flash3 verwenden für 0x10000 bis 0x3ffff
mit entsprechenden Lokatierungen im Linker-Skript. Das braucht man aber
eigentlich nur, wenn man händlisch auf die einzelnen Bänke verteilen
will.
Falk B. schrieb:> Johann L. schrieb:>> dann kann man die Zugriffsfunktionen für __memx schreiben.>> Dazu braucht man aber wieder Inline-Assembler Makros. Richtig?
Nö, eigentlich nicht. Außer du brauchst Äquivalente zu PSTR bzw. FSTR.
Die __flashN und __memx Adressen können alle via __memx zugegriffen
werden, und __memx verdaut auch RAM-Adressen, ist allerdings aufwändiger
mit seinen 3-Byte Adressen. Eine Linkerskipt-Erweiterung braucht man
mit __memx nicht; .progmemx.data ist in den Binutils abgehandelt.
Mit __memx kann man auch Objekte über Segmentgrenzen hinweg lesen.
1
#include<stdio.h>
2
3
voidout(const__memxchar*str)
4
{
5
charc;
6
while((c=*str++))
7
putchar(c);
8
}
9
10
const__flashcharflash[]="Flash 0";
11
const__flash1charflash1[]="Flash 1";
12
const__memxcharmemx[]="MemX";
13
14
intmain(void)
15
{
16
out(memx);
17
out(flash);
18
out(flash1);
19
out("\n");
20
}
Bei gleichzeitiger Verwendung von __memx und __flashN muss man sich
natürlich entscheiden, wie man .progmem1.data relativ zu .progmemx.data
lokatiert; beim obigen Beispiel mit der Linkerskript-Ergänzung liegt
.progmem1.data nach .text, also auch nach .progmemx.data. Nicht so
sinnvoll, und der Grund, warum .progmemN nicht im default Linkerskript
behandelt wird und der Anwender das machen muss.
Wenn aber ohnehin alles über __memx zugegriffen wird, kann man auch
gleich alles nach __memx lokatieren. Die Zugriffe per __memx sind aber
langsamer als bei bekanntem Address-Space, weil
1. es sich um 24-Bit Zeiger handelt
2. die Zugriffsfunktionen in der libgcc sind und
3. __memx auch RAM-Adressen verwurstet wie das "\n" oben.
Johann L. schrieb:>> Dazu braucht man aber wieder Inline-Assembler Makros. Richtig?>> Nö, eigentlich nicht. Außer du brauchst Äquivalente zu PSTR bzw. FSTR.
Falsch gedenkt: Auch für Äquivalente zu PSTR / FSTR braucht man
natürlich kein Inline-Asm.
Johann L. schrieb:> Wenn aber ohnehin alles über __memx zugegriffen wird, kann man auch> gleich alles nach __memx lokatieren.
Danke für die Hinweise, vor allem mit dem Linkerscript. Aber ich werde
eher nicht __memx umsetzen. Ersten gibt es noch gar keine Anwendung
dafür und 2. ist es eher Overkill. Der Lesezugriff per pgm_read_xx_far()
reicht mir.
Es gibt aber noch ein paar Problemchen.
1.) Die Berechnung des belegten Flash-Speichers ist zwar richtig, aber
nur netto! Sprich, meine far_section ab 0x10000 kann max. 192kB
aufnehmen. Außerdem muss man noch ~10kB für den Bootloader abziehen, der
am Ende vom Flash liegt. Wenn ich nun 200kB in far_section an Daten
platziere, meldet die Berechnung vom Arduino weniger als 256kB Daten im
Flash, was netto auch stimmt. Aber die Lücke zwischen .text section ab
0x0000 und far_section ab 0x10000 muss man je beim Brutto einrechnen.
Spätestes beim Programmiervorgang meckert avrdude, daß Daten oberhalb
von 0x40000 geschrieben werden sollen. Wenn es nur knapp darunter ist,
zerschießt man sich den Bootloader 8-0
Frage: Wie kriegt man die richtige Bruttorechnung in die Arduino-IDE?
Ich würde meinen, man muss die far_section wie von Johann gezeigt
platzieren und die Obergrenze auf 256-10kB festlegen.
2.) Problem ist, daß AVR-Dude anscheinend ein Problem mit HEX-Files mit
>64kB hat. Denn das Programmieren des Hex-Files mit nahezu vollem Flash
geht schief, oberhalb 64kB ist im AVR alles leer! Im Hexfile stehen aber
die richtigen Daten! Wenn man mittels Atmelstudio und Atmel-ICE
programmiert landen die auch im AVR. Was klemmt da? Falsche
Konfigurationsdaten für avr-dude? Hat avr-dude ein Problem mit der
Segmentadresse im Intel-HEX?
Oder liegt das am Arduino-Bootloader?
https://www.nongnu.org/avrdude/user-manual/avrdude_4.html
(**) Flash addressing above 128 KB is not supported by all programming
hardware. Known to work are jtag2, stk500v2, and bit-bang programmers.
Aber ich meine, der Arduino-Bootloader basiert doch auf STK500V2, denn
die Datei dafür heißt ja stk500boot_v2_mega2560.hex
address=(((msgBuffer[3])<<8)|(msgBuffer[4]))<<1;//convert word to byte address
6
#endif
Das heißt wohl, daß >64kB unterstützt werden, dazu gibt es auch an
mehreren Stellen Kommentare. Außerdem ist der Support für den Mega2560
seit 2010 drin 8-0
Wo klemmt es dann?
c-hater schrieb:> In nacktem Assembler ist das aber absolut trivial.
Was nützen mir die dollsten Assemblermacros, wenn ich die Daten nicht
bequem und lesbar mit Structs aus Strings, Funktionspointern, 64bit,
float usw. vorbelegen kann.
Falk B. schrieb:> Wo klemmt es dann?
wenn du mich meinst, ich habs vergessen weil es ewig her ist wo ich
Bilder im m2560 flash ablegte, irgendwie ging es, oder doch nur in den
ersten 64k, wie gesagt vergessen, ist aber auch Schnee von gestern,
witzig dabei meine ersten Erfahrungen mit einer WS2812B LIB die auf dem
m1284p im Timing um 10% langsamer war als mit dem m328p, das sollte auch
auf dem erweiterten Adressbit/byte zurückzuführen sein, so ganz klären
konnte ich das nie, habe aber auch nicht mehr spätere Versionen probiert
weil mein eigener Patch für mich funktioniert.
Joachim B. schrieb:> Falk B. schrieb:>> Wo klemmt es dann?>> wenn du mich meinst,
Nö, ich fragte allgemein wo der Download >64kB beim ATmega 2560 über
avrdude und wiring Protokoll klemmt.
Peter D. schrieb:> Was nützen mir die dollsten Assemblermacros, wenn ich die Daten nicht> bequem und lesbar mit Structs aus Strings, Funktionspointern, 64bit,> float usw. vorbelegen kann.
Kann man doch. Auch in Assembler. Wenn man es kann. Und wenn man es
kann, kann man es auch problemlos lesen.
Ist halt so, wie in jeder anderen Sprache auch...
Veit D. schrieb:> vielleicht hilft das hier weiter.
Nein, tut es leider nicht. Die Beiträge sind recht alt. Das Grundproblem
ist schon gelöst, der Compiler erzeugt korrekten Code. Es bleibt noch
das Problem, daß avrdude das KORREKTE HEX-File nicht korrekt per
Bootloader auf den ArduinoMEGA schreibt. Per ATMEL-ICE und ISP geht es.
Falk B. schrieb:> ist schon gelöst, der Compiler erzeugt korrekten Code. Es bleibt noch> das Problem, daß avrdude das KORREKTE HEX-File nicht korrekt per> Bootloader auf den ArduinoMEGA schreibt.
Da ich meinen Arduino Mega eh gerade am Rechner hängen hatte, ein kurzer
Versuch im Atmel Studio:
1
start:
2
ldi R30, 00
3
ldi R31, 0xB8
4
ldi R26, 0x1
5
out EIND, R26
6
EIJMP
7
8
.ORG 0x1B800-1
9
loop:
10
rjmp loop
11
12
led:
13
ldi r16, (1<<PB7)
14
out PORTB, r16
15
out DDRB, r16
16
end:
17
rjmp end
avrdude -pm2560 -cwiring -Pcom8 -F -D ...
Die LED leuchtet.
Oliver
Oliver S. schrieb:> Die LED leuchtet.
Die leuchtet auch wenn gar kein Code drin ist. Die LED wird
von einem OP gespeist der an PB7 hängt. Wenn PB7 floatet
hängt der OP irgenwo zwischen 0 und 5V. Das reicht damit
die LED leuchtet.
RTFS (Read the fucking schematic)
LED blinken lassen wäre mindestens angesagt.
jo mei schrieb:> Die leuchtet auch wenn gar kein Code drin ist. Die LED wird> von einem OP gespeist der an PB7 hängt.
Dann hast du einen anderen Mega als der Rest der Welt. Beim Original
(und auch bei meiner Kopie) hängt die LED direkt an Port7, über
Widerstand gegen Masse. Da leuchtet ohne Ansteuerung des Ports gar nix.
> RTFS (Read the fucking schematic)
So isses.
Oliver
Oliver S. schrieb:> Dann hast du einen anderen Mega als der Rest der Welt.
Ich denke viele Leute haben einen anderen als du.
Wird schon seinen Grund haben dass man die LED gepuffert hat.
Leider keine Version im Schematic verfügbar.
YMMV
Ich hab jetzt mal meine Lupe rausgekramt, und meine Mega-Kopie ist doch
tatsächlich auch ein R3.
UNd ja, die LED leuchtet da tatsächlich auch ohne Prgramm.
Und nein, avrdude lädt das oben gezeigte Programm tatsächlich nicht
richtig. Den Verification-Error hatte ich schlicht übersehen.
Da ich aber ganz sicher war, daß ich den Mega mit avrdude mit einem
ähnlichen Programm schonmal laufen hatte, hab ich in meinen alten
Projekten gekramt. Ergebnis: Das hatte ich mit der gcc-toolchain gebaut
(gelinkt mit -nostartfiles -nodefaultlibs -nostdlib). Die erzeugt ein
hex-file, welches die ganzen 256kB des Speicher enthält, und das lädt
avrdude fehlerfrei hoch. (dauert halt etwas länger).
Das hex-file aus dem Studio dagegen ist nur ein paar Bytes groß, und
enthält nur die tasächlich Code enthaltenen Adressen.
Das wäre mal einen avrdude bug-report wert.
Oliver
Oliver S. schrieb:> Das hex-file aus dem Studio dagegen ist nur ein paar Bytes groß, und> enthält nur die tasächlich Code enthaltenen Adressen.
Hmm, beim Upload sieht man an dem Fortschrittsbalken, wie der bei 1/3
mal kurz springt. Das wäre bei MEINEM HEX-File OK, denn das hat ca. 20kB
Lücke unterhalb 64kB.
Ok, ich werde heute abend mal das rückgelesene HEX-File mit avrdude
brennen, das ist vollständig.
Oliver S. schrieb:> Das hex-file aus dem Studio dagegen ist nur ein paar Bytes groß, und> enthält nur die tasächlich Code enthaltenen Adressen.
So, ich hab es jetzt mal getestet. Es ist so, wie du es beschrieben
hast.
Das originale Hex-File aus dem avr gcc enthält die korrekten Daten, aber
eben auch nur die, da ist eine Lücke. Per Atmel-ICE kann man das brennen
und es funktioniert auch so, wie es soll. das gleiche Hex-File per
avrdude funktioniert nicht, Oberhalb 64kB fehlen die Daten. Wenn man
aber ein korrekt programmiertes File aus dem Flash zurück liest, kann
man das per avrdude korrekt brennen. Hmmm.
>> Das wäre mal einen avrdude bug-report wert.
Wo kann man das tun? Wird das noch bearbeitet? Gibt es da jemanden, der
die Software pflegt?
Oliver S. schrieb:> Dann hast du einen anderen Mega als der Rest der Welt. Beim Original> (und auch bei meiner Kopie) hängt die LED direkt an Port7, über> Widerstand gegen Masse.> Oliver
Hallo,
da muss ich Einspruch erheben. Beim Original ist immer ein OPV
dazwischen. Dein Schaltplan wird nicht vom Originalen sein. Hole dir
nochmal einen frischen Schaltplan.
@ Falk,
welchen Programmer verwendest du wenn du mit avrdude flashst?
Mit welchen Tool flashst du wenn du mit Atmel ICE flashst?
Kann mich dunkel an einen älteren Thread erinnern mit ähnlichen Problem,
da lag es Programmer, war aber kein Atmel Ice sondern ein mkII Nachbau.
Da musste die Firmware aktualisiert werden.
Veit D. schrieb:> @ Falk,>> welchen Programmer verwendest du wenn du mit avrdude flashst?
mit dem Arduino bootloader per virtuellem COM-Port.
> Mit welchen Tool flashst du wenn du mit Atmel ICE flashst?
Atmelstudio 6.2
> Kann mich dunkel an einen älteren Thread erinnern mit ähnlichen Problem,> da lag es Programmer, war aber kein Atmel Ice sondern ein mkII Nachbau.> Da musste die Firmware aktualisiert werden.
Nein.
Das Atmel-ICE macht alles richtig. Das Problem liegt bei avrdude. Ist
das so unverständlich in den Beiträgen beschrieben?
@ Oliver:
Will ja nichts sagen, aber der R3 ist schon seit Jahren aktuell, da kann
man nicht mit einem uralten Schaltplan um die Ecke kommen. Da kann der
noch so original sein. :-) :-) :-) Deswegen solltest du dir ja
einen frischen Schaltplan runterladen.
@ Falk:
avrdude habe ich verstanden. Konnte mir nur keinen Zusammenhang bilden
mit deinen Testmethoden. Jetzt ist alles klar.
avrdude wird doch von Jörg W. betreut?
Edit:
Doofe Frage. Nimmt Atmel Studio nicht auch avrdude zum flashen?
Veit D. schrieb:> Will ja nichts sagen, aber der R3 ist schon seit Jahren aktuell, da kann> man nicht mit einem uralten Schaltplan um die Ecke kommen. Da kann der> noch so original sein. Ä
Sag das mal google...
Veit D. schrieb:> Doofe Frage. Nimmt Atmel Studio nicht auch avrdude zum flashen?
Freiwillig nicht ;)
Oliver
Hallo,
ich sehe das anders. Arduino stellt zu jedem Board alle Dokus zur
Verfügung. Auch zu denen die nicht mehr im Handel sind. Deswegen findet
man auch Schaltpläne zu älteren Boardversionen. Ich gehe immer auf die
aktuelle Produktseite, da gibts im Falle vom Mega2560 nur den R3, dann
dort zur Doku und man hat aktuelle Dateien zu dem Teil.
Seht es mal so. Sucht nach "VW Golf" und ihr werdet auch nicht direkt
zum aktuellen Modell verwiesen. :-)
Das es funktioniert beweist aber auch, dass man nicht alles so
haarspalterisch betrachten muss wie es hier im Forum manchmal ausartet -
für die reine Funktion. Es sind schließlich keine! Industrieboards. Du
verwendest sie ja auch. Also können sie nicht so schlecht sein ... ;-)
:-)
Johann L. schrieb:> Für __flash1 könnte das zum Beispiel so> aussehen:
Moin,
ich versuche gerade, die Section mittels dieser extra Datei zu
definieren. Es klemmt aber noch. Hier meine Version, angepaßt auf die
far_section. Diese soll bis zu 256kB aufnehmen und nach allen anderen
sektionen liegen.
1
SECTIONS
2
3
{
4
5
.far_section :
6
7
{
8
9
. = MAX (ABSOLUTE(0x10000), .);
10
11
PROVIDE (__far_section_start = .);
12
13
. = ALIGN(2);
14
15
*(.text.far_section.*)
16
17
*(.progmem1.data*)
18
19
PROVIDE (__far_section_end = .);
20
21
ASSERT (__far_section_end <= 0x30000,
22
23
"__far_section data in .progmem1.data exceeds 0x30000");
24
25
}
26
27
}
28
29
INSERT AFTER .text
Was bedeutet
= MAX (ABSOLUTE(0x10000), .);
Ist das eine feste Startadresse oder maximal Länge? Ich glaube erstes.
Wie kann man erreichen, daß diese section unmittelbar nach allen anderen
sections platziert wird? Ich dachte, INSERT AFTER .text macht das. Ist
da so?
Warum fängt meine far_section exakt bei 0x10000 an? Ich hab das doch gar
nicht angegeben. Ich teste jettz mit der extra Section im Linkerscript,
nicht mit der festen Adresse 0x10000.
Auzug aus dem .map File
00010000 l d .far_section 00000000 .far_section
000277c6 l O .far_section 00007d42 fire_1
0001fa84 l O .far_section 00007d42 fire_2
00017d42 l O .far_section 00007d42 fire_3
00010000 l O .far_section 00007d42 fire_4
Gibt es in den Linkerscripts eine Einstellung, welche den Platz bis
0xFFFF reserviert?
Hmm, irgendwie hat sich der Compiler jetzt die Definition der
far_section bei 0x10000 gemerkt. Denn wenn ich den Zusatz beim Linker
wieder entferne, läuft die Compilierung auch vollständig durch, obwohl
die großen Datenobjekte in der far_section landen sollen, die der
Compiler aber gar nicht kennt!
Also, wo hat sich diese Info eingenistet?
Falk B. schrieb:> Also, wo hat sich diese Info eingenistet?
Ich weiss es auch nicht, aber ich freue mich wie du dich abmühst,
nicht weil ich Schadenfreude übe sondern hoffe dass du uns irgend-
wann eine Lösung präsentierst die wir auch nutzen dürfen/können.
Mich würde dann später auch experimentell interessieren ab
welcher (alter) Compilerversion das Ganze funktioniert ....
Danke (ausdrücklich) für's Kopf zerbrechen.
OK, mein Fehler, da hatte ich wohl die section noch in eine andere Zeile
mit Linker-Flags kopiert. Oder war es die Arduino-IDE? Egal.
Jedenfalls, es geht jetzt! Und zwar deutlich einfacher als gedacht! Man
muss dem Linker gar keine Anweisung geben, wo die far_section hin soll.
Er fügt die automatisch ans Ende der bestehenden sections an! Genau das
wollte ich ja, damit gibt es keine Lücke im Flash und auch der Upload
per avrdude geht jetzt! Es kann so einfach sein!
So, ich denke jetzt hab ich es wirklich. Hier nochmal alles
zusammengefaßt.
Wenn man Daten im Flash >64kB ablegen und nutzen will, geht das so.
Siehe Anhang. Außerdem muss man nur 1 Zeile in
C:\Programme\Arduino\hardware\arduino\avr\plattform.txt
anpassen. Die Arduino-IDE darf dabei NICHT laufen! Erst schließen, dann
ändern und neu starten!
## Compute size
recipe.size.regex=^(?:\.text|\.data|\.bootloader|\.far_section)\s+([0-9]
+).*
Hiermit wird die far_section in die Berechnung der Flash-Größe
einbezogen und man kann den Flash nicht überladen! Das kann man leicht
selber prüfen, indem man die Arrays dummy6 sowie dummy_PGM_B mal ein
bisschen größer macht. Mehr als 32767 Bytes sind beim avr gcc so oder so
nicht möglich, das ist die Maximalgröße für ein Array (wegen der
Indexberechung mit 16 Bit Integer). Das Testprogramm macht nix
sinnvolles außer die Konstanten im Flash anlegen und durch eine
Zuweisung auf PORTA das Wegoptimieren verhindern. Es wird auch gezeigt,
wie man eine 32 Bit Variable als Adresse verwenden kann, um die Arrays
zu lesen. Ein Zugriff über den Index ist hier NICHT möglich!
Adressberechnungen müssen die Größe des Datentyps berücksichtigem, der
32 Bit Pointer ist immer ein Byte-Pointer!
Viel Spaß beim Ausschöpfen der 256kB des ArduinoMEGA!
P S Ja, der Zugriff auf die Daten >64kB ist natürlich sowohl was den
Schreibaufwand im C-Code als auch real auf Assemblerebene angeht etwas
mühsam und deutlich langsamer als unterhalb von 64kB. Ein Byte-Zugriff
dauert mal fix 9 Takte, 4 zum Laden der 32 Bit Adresse (FAR_ADR()
Makro), 5 zum Zugriff via ELPM (pgm_read_byte_far() Makro). Naja, aber
besser als nix, denn ohne diese Funktionen kann man mit dem Flash >64kB
für Daten nix anfangen!
P S Die far_section muss man dem Compiler bzw. Linker nicht mitteilen,
der packt die automatisch ans Ende aller Sections. genau dort hin, wo
sie hingehört, ohne die bestehende Ordnung zu stören.
Danke sehr, werde ich bei Gelegenheit ausprobieren.
Falk B. schrieb:> Die Arduino-IDE darf dabei NICHT laufen! Erst schließen, dann> ändern und neu starten!
Wie gut dass ich so gut wie nie diese Krücke verwende, für
solche Spezialitäten dann sowieso nicht. Möchte ja irgendwas
Sinnvolles tun bei der sich die Krücke mir nicht in den Weg
stellt.
Hallo Falk,
Glückwunsch auch von mir. Wo darf ich den Fleißbienchenstempel
reindrücken? :-)
Noch ein Tipp am Rande. Lege dir dort wo die platform.txt liegt noch
eine platform.local.txt an. Dort rein schreibste deine Spezialzeile. In
der platform.txt nimmste Zeile wieder raus. Änderungen in der
platform.local.txt werden ohne schließen/öffnen der IDE übernommen.
Zufälligerweise stand ich gerade auch vor dem Problem, bei einem
AT90CAN128 Daten in den Bereich im Flash > 64kB abzulegen. Da ich
bereits für die unteren 64kB den __flash Qualifier bei der Definition
von Konstanten verwende, damit beim Zugriff keine pgm-Makros benötigt
werden, stand die Lösung mit pgm_far-Makros nicht zur Debatte.
Mit der Definition von
hab ich das für avr-gcc größtmögliche Datenarray angelegt. Die
Startadresse gibt man dem Compiler z.B. über
1
LDFLAGS += -Wl,--section-start=.flash1tab=0x18000
mit, der das dann an den Linker weiterreicht. Hier im Beispiel liegt das
Array dann ab 96kB im Flash. Es kommt auch eine Fehlermeldung, wenn es
mit anderen Sections überlappt.
Am besten platziert man das ganz ans Ende oder, falls es einen
Bootloader gibt, vor den Bootloader. Dann können die .text und .data
Sections weiter wachsen, ohne dass es sofort zu Überlappungen kommt. Man
muss nur beachten, dass bei der Definition des Arrays kein __flash1
Qualifier verwendet wird. Erst beim Zugriff auf die Daten kann ein 16bit
__flash1 Pointer verwendet werden. Die im Vergleich dazu langsameren und
etwas mehr Flash belegenden pgm_far-Makros funktionieren auch. __memx
Pointer funktionieren bei dieser Variante nicht.
Das mit dem weiter oben im Thread von Johann angegebene zusätzliche
Linker Script hat mich aber interessiert, so dass ich das ebenfalls
ausprobiert habe. Um es vorweg zunehmen, wer sich da nicht extrem gut
auskennt, sollte die Finger davon lassen, wenn man nicht die letzten
Haare verlieren will ;-P
Aufgabenstellung:
Füge eine Section .flash1 hinter .text und vor .data ein.
Zusätzliche Bedingungen:
Startadresse von .flash1 muss >= 0x10000 sein.
Endadresse von .flash1 muss < 0x20000 sein.
Nach vielen Versuchen habe ich zwei Linker Script Varianten gefunden,
die zumindest für meine Fälle und dem AT90CAN128 funktionieren. Hier
zuerst die in einer Datei flash1section.ld abgespeicherte Variante auf
Basis von Johanns Script, die man dem Compiler über
1
LDFLAGS += -Wl,-Tflash1section.ld
mitgibt, der die -T Option dann an den Linker weiterreicht.
1
SECTIONS
2
{
3
.flash1 :
4
{
5
. = MAX (0x10000, ABSOLUTE(.));
6
7
PROVIDE (__flash1_start = .);
8
9
. = ALIGN(2);
10
11
*(.flash1.text*)
12
*(.progmem1.data*)
13
14
PROVIDE (__flash1_end = .);
15
16
ASSERT (ABSOLUTE(__flash1_start) >= 0x10000,
17
"__flash1 data in .progmem1.data starts below 0x10000");
18
19
ASSERT (ABSOLUTE(__flash1_end) < 0x20000,
20
"__flash1 data in .progmem1.data exceeds 0x20000");
21
}
22
}
23
INSERT AFTER .text;
Dieses Script setzt den .flash1 Section relativen Location Counter "."
auf die Adresse 0x10000, falls der absolute Location Counter nach der
.text Section < 0x10000 ist. Sonst wird der Location Counter beibehalten
und die .flash1 Section fängt dann eben erst entsprechend mehr oder
weniger weit hinter 0x10000 an. Als Ergebnis erhält man eine .flash
Section, die nahtlos an .text anschließt. Falls der absolute Location
Counter nach der .text Section allerdings < 0x10000 ist, gibt es am
Anfang der .flash1 Section eine Lücke, die mit 0x00 gefüllt wird, bevor
ab 0x10000 tatsächliche Daten folgen. Das sieht man auch schön im
generierten Hexfile, das diese 0x00 bis 0x10000 enthält. Wer mit anderen
Werten füllen will z.B. 0xFF oder auch längeren Mustern, kann dazu am
Anfang der Section .flash1 den FILL(0xFF); Befehl einfügen.
Der erste Fallstrick war hier der Location Counter ".". Innerhalb einer
Section ist der Location Counter Section relativ, fängt also immer bei 0
an. Deshalb wurde die ursprüngliche Zeile ". = MAX (0x10000, .);" immer
zu ". = MAX (0x10000, 0);" und das zu ". = 0x10000", was Falk ja weiter
oben schon festgestellt hat.
Ein weiterer Fallstrick sind die ASSERTs im zusätzlichen LD Script. Auf
Variablen, die in der Output Section definiert wurden, kann im ASSERT
nicht zugegriffen werden, außer die Variablen enthalten nur den Location
Counter ".". Und selbst wenn die Variable nur den Location Counter
enthält, ist das dann der Section relative Location Counter. Deshalb
muss da noch ein ABSOLUTE(variable) um den jeweiligen Variablennamen,
damit da das tatsächlich Gewünschte geprüft wird.
Meine zweite Variante des zusätzlichen LD Scripts sieht so aus:
1
SECTIONS
2
{
3
. = MAX (0x10000, .);
4
5
.flash1 . :
6
{
7
PROVIDE (__flash1_start = .);
8
9
. = ALIGN(2);
10
11
*(.flash1.text*)
12
*(.progmem1.data*)
13
14
PROVIDE (__flash1_end = .);
15
16
ASSERT (ABSOLUTE(__flash1_start) >= 0x10000,
17
"__flash1 data in .progmem1.data starts below 0x10000");
18
19
ASSERT (ABSOLUTE(__flash1_end) < 0x20000,
20
"__flash1 data in .progmem1.data exceeds 0x20000");
21
} > text
22
}
23
INSERT AFTER .text;
Hier wird der Location Counter außerhalb der Section gesetzt. Dort ist
der Location Counter absolut. Das hat aber Nebenwirkungen, die
zusätzliche Script Änderungen nötig machen. Das Setzen des Location
Counters außerhalb der Section hat laut Mapfile keine Auswirkungen auf
die tatsächliche Startadresse von .flash1. Die Section wird immer noch
direkt hinter .text platziert, auch wenn das Ende von .text < 0x10000
ist. Abhilfe schafft die explizite Angabe der VMA (Virtual Memory
Address, das ist die virtuelle Adresse zur Laufzeit des Programms) in
Abhängigkeit vom Location Counter vor dem ":" in der Zeile ".flash1 .
:". Wenn man hinter dem ":" keine LMA (Load Memory Address, das ist die
physikalische Adresse) angibt, wird LMA automatisch auf VMA gesetzt. Man
könnte das auch explizit angeben ".flash1 . : AT (ADDR (.flash1) )".
Wenn jetzt das Ende von .text < 0x10000 ist, fängt .flash1 wie gewünscht
bei 0x10000 an. Aber es gibt dazwischen eine Lücke, in die der Linker
.data platziert. Wenn die Lücke kleiner ist als die Größe der .data
Section, dann überlappt das mit der .flash1 Section und beim Linken
kommt eine entsprechende Fehlermeldung: "section .data overlaps section
.flash1".
Der Grund liegt im default LD Script vergraben. Das kann man sich, ohne
es im Filesystem zu suchen, vom Linker anzeigen lassen, wenn man die
"LDFLAGS += -Wl,--verbose" erweitert. Da gibt es am Ende der Deklaration
der .data Output Section die Zeile "} data AT> text". Das bedeutet,
weise die .data Section der im default LD Script deklarierten Memory
Region "data" zu und setze die LMA von .data auf die nächste freie
Adresse nach der Memory Region "text". Und wenn dann eine Lücke zwischen
.text und .flash1 ist, startet .data am Ende von .text und leider nicht
erst am Ende von .flash1.
Das kann man mit der Angabe von "} > text" am Ende der Deklaration der
Ouput Section .flash1 verhindern. Damit wird .flash1 der Memory Region
text zugewiesen und das Ende von text liegt dann immer hinter .flash1.
Leider führt das wieder zu neuen Komplikationen. Beim Linken kommen zwei
Warnungen:
1. Beim Lesen des zusätzlichen LD Scripts kommt: warning: memory region
'text' not declared.
2. Beim Lesen des default LD Scripts kommt: warning: redeclaration of
memory region 'text'.
Der Grund ist, dass das zusätzliche LD Script zuerst gelesen und
bearbeitet wird. Aber zu dem Zeitpunkt sind die Memory Regions noch
nicht deklariert worden. Das geschieht erst am Anfang vom default LD
Script. Die erste Warnung kann man umgehen, wenn man die Memory Region
"text" im zusätzlichen LD Script definiert. Dann kommt aber immer noch
die zweite.
Trotz der ein oder zwei Warnungen kommt zumindest bei mir das Gewünschte
heraus. Die .data Output Section liegt immer hinter .flash1, auch wenn
es eine Lücke zwischen .text und .flash1 gibt, wenn die unteren 64kB
Flash noch nicht ausgereizt sind. Wer also diese Variante lieber mag,
sollte besser das default LD Script kopieren und die Änderung in der
Kopie eintragen. Die Zeile "INSERT AFTER .text" muss man dann natürlich
weglassen, damit das zusätzliche LD Script, das default LD Script
komplett ersetzt.
Der Unterschied zwischen den oben angegebenen Scripten ist die Lücke im
Hexfile, wenn das Ende von .text < 0x10000 ist. Das erste Script führt
im Hexfile dazu, dass die Lücke mit 0x00 gefüllt enthalten ist. Das
zweite Script führt zu einem kleineren Hexfile. Die Lücke taucht im
Hexfile nicht auf und wird beim Flashen nicht beschrieben. In der ersten
Variante kann die .flash1 Section vor 64kB anfangen, enthält dann aber
0x00 bis 0x10000. In der zweiten Variante fängt die .flash1 Section
immer erst bei 0x10000 oder weiter hinten an.
Beide LD Scripts führen dazu, dass Konstanten, die mit
1
const__flash1<Datentyp>name=...;
definiert sind, im Flash im Bereich 64-128kB abgelegt werden. Auf diese
Konstanten kann entweder direkt oder über einen 16bit __flash1 Pointer
zugegriffen werden, so dass man die sperrigen und langsameren
pgm_far-Makros aus avr/pgmspace.h nicht benötigt. Im Gegensatz zu meiner
Lösung von ganz oben funktioniert auch der Zugriff über __memx Pointer.
Da __flash1 nur beim avr-gcc existiert, muss man die Zugriffe und Daten
in C-Files auslagern, wenn man das mit avr-g++, z.B. in der Arduino
Welt, benutzen will.
1
// array tab located in .flash1
2
staticconst__flash1uint8_ttab[]=
3
{0xDE,0xAD,0x00,0xBE,0xEF};
4
5
// access function for array tab located in .flash1