Forum: Mikrocontroller und Digitale Elektronik Daten mittels Linker an bestimmte Stelle schreiben


von Peter P. (uncle-sam7)


Lesenswert?

Hallo NG,

habe eben versucht, die Daten fest nach 0x20a000 zu bekommen (sozusagen 
rein in der Binary, die in den Speicher geladen wird.)

fogelndes habe ich gemacht:

im .ld:

MEMORY {    /* memory map of AT91SAM7S256 */
    ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 256k
    RAM (rwx) : ORIGIN = 0x00202000, LENGTH = 32k
    SID (rwx) : ORIGIN = 0x0020A000, LENGTH = 1k
}

...

SECTIONS {

...

    .sid : {
    } >SID

...

im Quelltext dann folgendes:

#pragma DATA_SECTION(".sid");
static unsigned char* mysid = "êêêêêêêêêêêêêêêêêêêê";

die Daten befinden sich aber "vogelwild" bei 0x480c in der binary 
(Offset Dateibeginn = 0x00). Ich möchte aber gerne, dass nach einem 
gewissen "Leeraum" die Daten von Oben in diesem Beispiel bei Offset 
0x8000 liegen, so dass diese dann nach dem Hochladen ins SRAM (0x202000) 
dann automatisch bei 0x20a000 liegen.

Ich will nicht die Daten zuerst in ein Array speichern und dann von dort 
aus "kopieren". Bei so wenig Speicher wäre das Verschwendung.

Vielen Dank an alle, die hier Licht ins Dunkle bringen ;-)

MfG
Peter

von Reinhard Kern (Gast)


Lesenswert?

Peter Pippinger schrieb:
> habe eben versucht, die Daten fest nach 0x20a000 zu bekommen (sozusagen
> rein in der Binary, die in den Speicher geladen wird.)
> MEMORY {    /* memory map of AT91SAM7S256 */
>     ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 256k
>     RAM (rwx) : ORIGIN = 0x00202000, LENGTH = 32k
>     SID (rwx) : ORIGIN = 0x0020A000, LENGTH = 1k
> }

Hallo,

du kannst keine Daten per Linker in ein RAM schreiben - wie sollte das 
im Stand alone Betrieb denn gehen? Die Daten stehen beim Einschalten 
ausschliesslich im ROM, das RAM ist zufällig. Also musst du die Daten 
erst vom ROM ins RAM kopieren. Entweder gleich beim Booten mit ein paar 
Assembler-Befehlen oder die Runtime Library erledigt das, weil es sich 
um initialisierte Variablen handelt, oder im Startcode der Hochsprache, 
das ist so ziemlich immer das Gleiche.

Das RAM ohne Programm mit Daten zu füllen geht nur mit Hilfe des 
Debuggers - willst du den und den PC mitliefern? Ausserdem, nach dem 
gesagten ist das nur zu Testzwecken sinnvoll.

Gruss Reinhard

von Tretet den Trog (Gast)


Lesenswert?

Doch. Man kann Code & Konstaten fest an eine Adresse schreiben. Die 
noetige Anweisung nennt sich ORG .

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nein. Damit kann man zwar festlegen, daß Variablen an bestimmten 
Adressen liegen - deren Wert aber ist, wenn sie im RAM liegen, nach dem 
Einschalten des Systems undefiniert, und muss daher erst mal 
initialisiert werden.

von Andreas B. (Gast)


Lesenswert?

Ok, ich sehe du bist doch mit Linker Scripten etwas vertraut, dann kann 
ich das aus dem anderen Thread etwas ausbreiten hier.

Erst mal streich die SID aus der MEMORY raus, dass macht keinen Sinn. 
Alle zusammengehörigen Segmente bekommen einen Eintrag, also RAM wieder 
LENGTH = 64K.

In den SECTIONS, mach ins Ende von dem Block für .data (vor etwaigen 
aligns) die Einträge
1
. = 0x8000;
2
*(.sid)
Genauer könnte ich es sagen, wenn ich das ganze Script sehen könnte. 
Wobei ich statt dem Namen .sid eher .data.sid vorschlagen würde, da weiß 
man dass es eine Sektion ist, die zu .data gehört.

Diese Lösung funktioniert ganz ohne Änderungen am Startup. Problem ist 
halt, dass das restliche .data des ganzen Projekts nicht länger als 32 
kB sein darf. Zweitens wird das Vorspulen mittels ". = 0x8000;" mit 
nutzlosen Füllbytes erreicht, die auch im ROM im Abbild der .data 
Sektion Platz belegen.


Und an "Tretet den Trog": ORG (bzw. .org im GNU Assembler) funktioniert 
nicht, da es relativ zum Sektionsstart in dieser Datei ist. Werden 
andere Dateien mit einem .data davor gelinkt, verschiebt sich das 
entsprechend.

von Peter P. (uncle-sam7)


Lesenswert?

Hallo Andreas B.,

In mein Linkerskript habe ich nun folgendes eingebaut:

MEMORY {    /* memory map of AT91SAM7S256 */
    ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 256k
    RAM (rwx) : ORIGIN = 0x00202000, LENGTH = 64k
}

   .data : {
        __data_load = LOADADDR (.data);
        __data_start = .;
        KEEP(*(.jcr))
        *(.got.plt) *(.got)
        *(.shdata)
        *(.data .data.* .gnu.linkonce.d.*)
  . = 0x3000;
  *(.data.sid)
         . = ALIGN (4);
        _edata = .;
    } >RAM AT>RAM

Allerdings siehts in meiner .bin-Datei immer noch so aus:
00480C0 EA EA EA EA EA EA EA EA EA EA ...

Was allerdings schon passiert, ist, dass die Datei größer wird...

Muss ich das Segment im C-Quelltext anders definieren?

#pragma DATA_SECTION(".data.sid");
static unsigned char* mysid = "êêêêêêêêêêêêêêêêêêêê";

Was ich eben noch gesehen habe: Der Linker spuckt folgendes aus:
src/main.c:869:0: warning: ignoring #pragma DATA_SECTION

Gruß
Peter

von Lasse S. (cowz) Benutzerseite


Lesenswert?

Hi,

versuchs mal mit
1
static unsigned char* mysid __attribute__((section(".sid"))) = "êêêêêêêêêêêêêêêêêêêê";

Aber auch dann würde ich nur davon ausgehen, dass er den Pointer an der 
Adresse speichert, nicht aber das Array.

Gruß
Lasse

von Andreas B. (Gast)


Lesenswert?

Peter Pippinger schrieb:
> Muss ich das Segment im C-Quelltext anders definieren?
>
> #pragma DATA_SECTION(".data.sid");
> static unsigned char* mysid = "êêêêêêêêêêêêêêêêêêêê";
>
> Was ich eben noch gesehen habe: Der Linker spuckt folgendes aus:
> src/main.c:869:0: warning: ignoring #pragma DATA_SECTION

Ja, wenn es sich um den gcc handelt dann ignoriert der Pragmas.

Wie Lasse S. geschrieben hat, musst du da die attribute-Syntax von gcc 
verwenden und die Variable besser nicht als Pointer deklarieren:
1
static unsigned char mysid[] __attribute__((section(".data.sid"))) = "êêêêêêêêêêêêêêêêêêêê";
Der String wird sonst in .rodata und damit im ROM  abgelegt. Auf jeden 
Fall ist .rodata normalerweise nach .text abgelegt und damit sicher 
woanders.


Außerdem, nur um sicher zu gehen:
>  } >RAM AT>RAM

Das funktioniert nur, solange das komplette Programm für die Ausführung 
im RAM gelinkt wird, also nicht geflasht werden kann. Wenn das Absicht 
war, in Ordnung.


Das Ganze ist als Einführung in die Linkerscript Erstellung sicher 
interessant, aber die Frage bleibt: Ist hinter der festen Position für 
den String ein tieferer Sinn?

von Peter P. (uncle-sam7)


Lesenswert?

Hallo Lasse S,
hallo Andreas B.,

ich habe eure Versionen mal getestet. Aber leider kommen die Chars immer 
nach Datei-Offset 0x480c.

@Andreas:

>>  } >RAM AT>RAM
> Das funktioniert nur, solange das komplette Programm für die Ausführung
> im RAM gelinkt wird, also nicht geflasht werden kann. Wenn das Absicht
> war, in Ordnung.

Naja, irgendwie habe ich es nie geschafft mir einen Debugger 
anzuschaffen. In der Arbeit - so blöd sich das anhört - verwende ich 
auch immer den "Poor Man's Debugger" (PHP :-)

RAM ist also in diesem Fall ok. Habe mir eine Batch geschrieben, die 
zuerst aufräumt, dann kompiliert und linkt und das Ergebnis direkt via 
SAM-BA ins RAM legt und dort auch ausführt. Dadurch ist das alles schon 
ok, zumal ja RS232 schon super hinhaut...

Zu Deiner Frage, warum ich das im RAM an einer bestimmten Adresse 
ablegen möchte:

Ich bin ja gerade dabei einen SID-Player zu bauen. Dieser soll die alten 
Musikstücke vom C64 abspielen können. Dazu muss die CPU vom C64 emuliert 
werden (was jetzt schon funktioniert). Eine der wenigen Gemeinsamkeiten 
vom AT91SAM7P256 sind 64KB RAM. Diese 64KB möchte ich später auch als 
Hauptspeicher der Emulation verwenden. Und jetzt zum Testen habe ich das 
Problem, dass sich der ARM mit der Emulation des C64 den Speicher teilen 
müssen, bis alles fertig ist und ich es ins EEPROM verfrachten kann. Den 
Stack für den ARM mit ein paar Variablen werde ich dann an Stellen im 
RAM versetzen, die ich für die Emulation nicht brauche (z.B. $0400+)...

Die Sid-Musikfiles liegen alle an verschiedenen Stellen im Speicher - je 
nachdem, wie sie programmiert wurden. Mein Teststück, mit dem ich alles 
teste sollte am Ende in $C000 (also übertragen 0x20C000) landen.

Naja, zum Testen werde ich wohl nicht herumkommen, als dass ich die 
Daten dann doch beim Init an die passende Stelle schaufle...

Falls ihr doch noch eine Idee habt, dann bitte gerne her damit. Wäre 
echt cool.

MfG
Peter

von Peter P. (uncle-sam7)


Lesenswert?

So, jetzt habe ich diese Hürde auch genommen :-)
War zwar ein lustiges Raten, aber es geht...

im Linkerskript doch wieder mit SID:

MEMORY {    /* memory map of AT91SAM7S256 */
    ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 256k
    RAM (rwx) : ORIGIN = 0x00202000, LENGTH = 56k
    SID (rwx) : ORIGIN = 0x0020F000, LENGTH = 1k
}

SECTIONS {
...
    .sid : {
        . = 0x00;
        *(.sid)
     } >SID AT>SID
...
}

und im Programm:

static unsigned char mysid[] __attribute__((section(".sid"))) = 
"êêêêêêêêêêêêêêêêêêêê";

Im Hexeditor stehen dann die EA EA EA... an 0xd000 wenn man das nun 
alles an 0x202000 läd, ergibt sich eine Adresse von 0xf000.

Ein Ausführen mit Programmcounter @ 0xf000 im Emulator liefert nun wie 
gewollt:

NOP
NOP
NOP
...

Alles gut, ich kann schlafen gehen ;-) Gute Nacht.

von Andreas B. (Gast)


Lesenswert?

Ok, wenn alles im RAM ist, gab es natürlich ein Problem mit meiner 
Version: Ich hatte den Offset relativ zum Start von .data, da aber .text 
und .rodata im Allgemeinen davor gelinkt werden, liegt .data nicht am 
Anfang vom RAM und es verschiebt sich alles... Dann nochmal eine bessere 
Version:

Peter Pippinger schrieb:
> im Linkerskript doch wieder mit SID:
>
> MEMORY {    /* memory map of AT91SAM7S256 */
>     ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 256k
>     RAM (rwx) : ORIGIN = 0x00202000, LENGTH = 56k
>     SID (rwx) : ORIGIN = 0x0020F000, LENGTH = 1k
> }

Würde ich wieder nicht empfehlen, das macht man alles in den SECTIONS. 
Außer es soll wirklich ein Bereich fester Länge fürs SID freigelassen 
werden und nicht für sonstige >RAM Sektionen genutzt werden dürfen.

> SECTIONS {
> ...
>     .sid : {
>         . = 0x00;
>         *(.sid)
>      } >SID AT>SID
> ...
> }

Zustimmung, eine eigene Sektion für .sid wird gebraucht. Aber die 
Startadresse einer Sektion lässt sich einfach vorgeben:
1
.sid 0x20f000 : {
2
        *(.sid)
3
}

Das >RAM kann man sich sparen wenn die Adresse vorgegeben ist. Für den 
Fall, dass es dann doch ins Flash soll, braucht die .sid Sektion ein 
AT>ROM und analog zu .data definierte start und size Symbole und das 
Startup muss die Daten analog zur .data Sektion kopieren. Das Ganze nur 
falls die Daten initialisiert sein müssen und nicht sowieso erst im 
Betrieb geladen werden, versteht sich.

Die Zeile mit ". = 0x00;" macht keinen Sinn, der Zähler ist am Anfang 
einer Sektion sowieso Null. Außerdem ist AT> nur nötig, wenn die 
Ausführregion und Laderegion unterschiedlich sind. Damit man nachher 
einfach zwischen Linken für RAM und Linken für ROM umschalten kann, 
verwendet man REGION_ALIAS.

von Peter (Gast)


Lesenswert?

*** PERFEKT!!! ***

Endlich mal jemand, der sich mit Linkern auskennt :-)

Gruß
Peter

von Peter (Gast)


Lesenswert?

...eine Frage hätte ich da noch:

ist es eigentlich auch möglich, in dieser Sektion binäre Daten einer 
Datei "einzuhängen"? Das mit dem Array finde ich ein bisschen unschön...

MfG
Peter

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.