Forum: Ausbildung, Studium & Beruf linkerskript Frage (LPC2000er)


von sean paul (Gast)


Lesenswert?

Hallo zusammen,

ich möchte gerne auf einen externen SDRAM zugreifen, deswegen habe ich 
den Speicherbereich im linkerscript hinzugefügt:

MEMORY
{
  flash    (rx) : ORIGIN = 0x00000000, LENGTH = 512K
  ram      (rw) : ORIGIN = 0x40000000, LENGTH = 30720
  sdram     (rw) : ORIGIN = 0xA0000000, LENGTH = 33554432
}

...

sdramlayout :
  {
    _sdram_start_ = 0xA0000000;
    *(.sdramdata)
  _sdram_end_   = 0x02000000;
  } > sdram

und einen Macro definiert :
#define SDRAMDATA _attribute_ ((section (".sdramdata")))

1.das erste Problem ist, der Compiler baucht mehr Zeit als sonst um den 
Code zu übersetzen.

2.wenn im Map reinschaue, finde ich folgendes:

sdramlayout     0xa0000000  0x2000000
                0x40000000                _sdram_start_ = 0xa0000000
 *(.sdramdata)
 .sdramdata     0xa0000000  0x2000000 ./release/ringbuffer.o
                0xa0000000                ringbuffer
                0xa2000000                _sdram_end_ = 0x2000000
das ganze stimmt bis auf die zweite zeile (0x40000000), was hat diese 
Adresse hier zu suchen?

3.der Flash Magic verhält sich komisch; es dauert erstmal ewig bis er 
eine Verbindung mit dem Board erfolgt, und wenn die Sektore die er 
braucht gelöscht hat, dauert es wieder ewig bis er angefangen hat zu 
fläshen.

muss ich vielleicht was anders im linkerskript hinzufügen?

ich bin für jeden Tip dankbar

von sean paul (Gast)


Lesenswert?

kann keiner was dazu schreiben..?

von sean paul (Gast)


Lesenswert?

gibt es keiner der sich mit Linkerskript auskennt..?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Auf http://en.mikrocontroller.net gibt es ein paar mehr, die davon 
Anhnung haben. Umgangssprache ist allerdings Englisch.

Ich persönlich traue mich nicht, was zu deinem Linkerskript zu 
beantworten, weil ich mit ... Zeilen in der Frage nichts anfangen kann. 
Und bevor ich mich mit Spekulationen ala "Da ist ... falsch oder ..." 
zum Affen mache, lasse ich es.

von Robert Teufel (Gast)


Lesenswert?

Welcher Baustein denn?
Von den LPC2000 unterstuetzen nur sehr wenige externes DRAM. Das waere 
die LPC24xx und evtl. der LPC28xx und sonst nichts. Ohne DRAM controller 
gestaltet sich die Sache ziemlich schwierig.
Robert

von mthomas (Gast)


Lesenswert?

Betr. Linker-script. Leider grade keine GNU arm Crosstoolchain zur Hand 
zum Testen, daher erstmal nur ein paar Fragen: Warum die absolute Angabe 
von Addressen in der linker-section? Benötigte Addresse steht bereits 
bei Memory, den Rest lässt man den Linker ausrechnen. Warum grade diese 
"end-addresse"? Was gibt der Linker als VMA und LMA für die neue section 
aus? Prinzip von LMA und VMA halbwegs verinnerlicht? Linker-manual 
durchgelesen (GNU bintutils)? Ist das binary/hex-file extrem gross? 
Initialisierung der Daten im externen RAM erforderlich? Wie ist 
ROM/RAM-copy und zero-init dafür implementiert? Die STR710-Beispiele von 
Anglia-Designs angeschaut (google anglia idealist)? In denen finden 
sich, wenn recht erinnert, ein paar Beispiele für internes und externes 
RAM für GNU toolchain inkl. Linker-Scripten. Auch Linkerscripte für GBA 
sind recht nützlich, da das Teil verschiedenste Speicher bietet.

Martin Thomas

von sean paul (Gast)


Lesenswert?

Danke für die Antworten.

@Robert teufel: es geht um LPC2468, ich benutze die 
EMC-Initialisierungsroutine von NXP.

@Martin Thomas: sorry vielleicht musste ich das ganze linkerskript 
posten:

* ------------------------------------ 0xFFFF FFFF
 * |                                  |
 * |  AHB Peripherals                 |
 * |                                  |
 * |----------------------------------| 0xF000 0000
 * |                                  |
 * |  APB Peripherals                 |
 * |                                  |
 * |----------------------------------| 0xE000 0000
 * |  [Reserved]                      |
 * |----------------------------------| 0x8000 0000
 * |                                  |
 * |  Boot block                      |
 * |                                  |
 * |----------------------------------|
 * |  [Reserved]                      |
 * |----------------------------------| 0x7FE0 4000
 * |  Ethernet RAM                    |   16384 bytes (0x4000)
 * |----------------------------------| 0x7FE0 0000
 * |  [Reserved]                      |
 * |----------------------------------| 0x7FD0 2000
 * |  USB RAM                         |    8192 bytes (0x2000)
 * |----------------------------------| 0x7FD0 0000
 * |  [Reserved]                      |
 * |----------------------------------| 0x4000 8000
 * |            | Reserved for IAP    |     256 bytes (0x100)
 * |            |---------------------| 0x4000 7F00
 * |            | UND Stack           |     256 bytes (0x100)
 * |  Internal  |---------------------| 0x4000 7E00
 * |  RAM       | FIQ Stack           |     256 bytes (0x100)
 * |            |---------------------| 0x4000 7D00
 * |            | IRQ Stack           |     256 bytes (0x100)
 * |            |---------------------| 0x4000 7C00
 * |            | SVC Stack           |    1024 bytes (0x400)
 * |            |---------------------| 0x4000 7800
 * |            | Variables RW and ZI |   30720 bytes (0x7800)
 * |----------------------------------| 0x4000 0000
 * |  [Reserved]                      |
 * |----------------------------------| 0x0008 0000
 * |                                  |
 * | Flash                            |    512K
 * |                                  |
 * |                                  |
 * ------------------------------------ 0x00000000
 *  UND Stack is used by HaltUndef, HaltSWI, HaltPabort and HaldAabort
 *
 ********************************************************************/

/*
 * Defines memory layout
 */

MEMORY
{
  flash    (rx) : ORIGIN = 0x00000000, LENGTH = 512K
  ram      (rw) : ORIGIN = 0x40000000, LENGTH = 30720
  ramstack (rw) : ORIGIN = 0x40007800, LENGTH = 2048
  usbram   (rw) : ORIGIN = 0x7FD00000, LENGTH = 8K
  ethram   (rw) : ORIGIN = 0x7FE00000, LENGTH = 16K
  sdram     (rw) : ORIGIN = 0xA0000000, LENGTH = 33554432
}

/*
 * Define entry point, found in startarm.s
 */
ENTRY(_reset_handler)

/*
 * Sections
 */
SECTIONS
{
  /* Place memory counter at address 0 */
  . = 0;

  /* Place startup code at start of flash */
  startup :
  {
    *(.startup)
  } > flash =0

  . = ALIGN(4);

  /* Place program code */
  prog :
  {
    *(.text)
    *(.rodata)
    *(.rodata*)
    *(glue_7)
    *(glue_7t)
  } > flash =0

  . = ALIGN(4);

  _end_of_text_  = .;
  _data_beg_src_ = .;
  PROVIDE(etext = .);

  /* Place initialized data */
  .data : AT(_data_beg_src_)
  {
    _data_beg_     = .;
    *(.data)
  } > ram

  . = ALIGN(4);
  _data_end_     = .;
  PROVIDE(edata = .);

  /* Place zero initialized data */
  .bss :
  {
    _bss_beg_ = .;
    *(.bss)
  } >ram


  /* Align here to ensure that the .bss section occupies space up to
     _end. Align after .bss to ensure correct alignment even if the
     .bss section disappears because there are no input sections. */
  . = ALIGN(32 / 8);

  . = ALIGN(32 / 8);

  /* Provide end markers */
  _end        = .;
  bss_end_  = .;
  _bss_end_ = .;
  _end_     = .;
  PROVIDE(end = .);

  stacklayout :
  {
    _stack_scv_bottom_ = .;
    . = . + 0x0400;
    _stack_scv_top_    = .;
    _stack_irq_bottom_ = .;
    . = . + 0x0100;
    _stack_irq_top_    = .;
    _stack_fiq_bottom_ = .;
    . = . + 0x0100;
    _stack_fiq_top_    = .;
    _stack_und_bottom_ = .;
    . = . + 0x0100;
    _stack_und_top_    = .;
    _stack_iap_bottom_ = .;
  } >ramstack

  usbramlayout :
  {
    _usbram_start_ = .;
    _usbram_end_   = 0x2000;
  } >usbram

  ethramlayout :
  {
    _ethram_start_ = .;
    _ethram_end_   = 0x4000;
  } > ethram

  sdramlayout :
  {
    _sdram_start_ = 0xA0000000;
    *(.sdramdata)
  _sdram_end_   = 0x02000000;
  } > sdram

Nun, wie soll ich jetzt das schreiben, damit das linkerscript zum SDRAM 
Startadresse springt?
Soweit ich weiss LMA ist die adresse wo der einzelnen Sections geladen 
werden, und VMA wo die dann ausgeführt werden, wie kann ich das jetzt 
für meinen Fall im Linkerscript umsetzen.

von sean paul (Gast)


Lesenswert?

vielleicht muss ich meine Frage deulicher ausdrücken;

ich will auf einen SDRAM zugreifen um Daten drauf zu speichern, stimmt 
dann mein Linkerskript daoben? wenn nicht was fehlt denn da?

Es wäre noch super wenn ihr einen Beispiel habt.

Danke im Voraus.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Es gibt bei NXP einen Beispielcode:

Sample Code Bundle for LPC23xx/LPC24xx Peripherals using Keil's μVision, 
V1.50

http://www.standardics.nxp.com/support/documents/microcontrollers/zip/code.bundle.lpc23xx.lpc24xx.uvision.zip

Darin ist im Verzeichnis EXTMEM/SDRAM ein Beispiel zum Ansteuern eines 
SDRAM an Basisadresse 0xA0000000 und Länge 0x02000000 inkl. 
Schreiben/Lesen.

Technisch ist das so gelöst, dass dem External Memory Controller (EMC) 
des LPC2468 die Basisadresse des SDRAM und die Länge und das Timing... 
bekannt gemacht wird (ex_sdram.c) und anschliessend Zugriffe über 
Pointer in den Adressraum erfolgen (sdram_test.c). Der EMC kümmert sich 
darum, dass diese Zugriffe korrekt ablaufen.

Dem Linker ist bei der dort verwendeten Methode das SDRAM schnurz, er 
kennt das nicht (als was besonderes).

Bei deiner Methode mit der eigenen Section (bei der du auch irgendwo 
z.B. im Startupcode eine Initialisierung brauchst!) hängen die langen 
Zeiten beim Linken + Löschen IMHO damit zusammen, dass die von dir 
bekannt gemachten 32 MB auch bearbeitet werden wollen z.B. vorm Flashen 
gelöscht werden und beim Flashen ggf. beschrieben werden wollen (wenn du 
irgendwas in der Sektion .sdramdata abgelegt hast).

Aber das ist nur meine Laienmeinung, warte mal ab, was die Experten dazu 
sagen, wenn sie Zeit haben. Nicht zu ungeduldig sein.

von sean paul (Gast)


Lesenswert?

Danke Stefan,

das Beispiel von NXP benutze ich zum EMC Initialisierung, ich dachte 
eigentlich, dass man den EMC-Controller benutzt, um die Zugriffszyklen, 
den Adress- und Databus zu initialisieren, die Speicherorganisation 
übernimmt dann der Linker, und dachte auch dass die NXP-examples mit 
Keil-uvision entwicklet wurde D.h. dass der linkerscript dementsprechend 
automatisch generiert wird. Es ist auf jeden fall ein Versuch Wert; ich 
werde es so probieren wie im "sdram_test.c" ohne den Linkerscript zu 
ändern und dann sehen wir was passiert.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Das ist grundsätzlich richtig gedacht.

Die Zuordnungen Variable => Adresse könnten vom Linker automatisch zur 
Compilezeit gemacht werden und müssten nicht explizit von dir zur 
Compile- oder Laufzeit gemacht werden. Das würde später einen deutlich 
aufgeräumteren Quellcode ergeben.

Nur - Ich kenne die Syntax nicht, um das linker control script zu 
schreiben. Deine Version legt ja die Sektion .sdramdata an der richtigen 
Stelle an und sieht deshalb für mich korrekt aus. Ich kann aber nicht 
sagen, woher die 0x40000000 kommt, die dich stutzig macht.

Gibt eigentlich der Compiler/Linker Fehler/Warnungen aus oder macht dich 
nur das Mapfile skeptisch? Wenn du eine Variable in .sdramdata 
definierst, wie sieht die Ausgabe im Mapfile aus?

von sean paul (Gast)


Lesenswert?

Ich bin nur nicht sicher, ob das so richtig ist, mit linkerskript habe 
ich überhaupt keine Erfahrung, was mich auch noch skiptisch gemacht hat, 
war dieses komische Verhalten vom Compiler und dem Flash-Programm, aber 
deine Erklärung klingt logisch, ich versuche es anderes und dann werde 
ich dir Bescheid sagen.

von Martin Thomas (Gast)


Lesenswert?

Folgendes nur als Vorschlag, grade nichts zur Hand um es in einem 
Simulator oder "in Hardware" auszuprobieren.

1. Im C-Code nutzt man zwei selbstdefinierte sections. Eine für zu 
initialisierende Daten im externen Speicher. Entspricht .data bei 
initialisierten Daten ohne Attribut. Die andere section für Variablen im 
externen Speicher, die mit 0 zu initialisieren sind (entspr. .bss). Also 
zum Beispiel:
1
#define EDATA __attribute__ ((section (".exdata_section")))
2
#define EBSS __attribute__ ((section (".exbss_section")))
3
int foo1 EDATA = 123;
4
int foo2 EBSS;
5
int foo3[] EDATA = { 1, 2, 3 };

2. Den Speicherbereich in MEMORY habe ich einfach mal EXTRAM genannt und 
testweise so definiert:
1
MEMORY
2
{
3
  ROM (rx)    : ORIGIN = 0x00000000, LENGTH = 0x0003E000
4
  RAM (rw)    : ORIGIN = 0x40000000, LENGTH = 0x00004000
5
  EXTRAM (RW) : ORIGIN = 0xA0000000, LENGTH = 32k
6
}

3. Nun ist dafür zu sorgen, dass die Initialisierungswerte für die 
Variablen in "EDATA" im FLASH stehen und man sowohl die Addresse der 
Initialiserungswerte (Kopierquelle) und die Zieladdress dieser Daten 
(Kopierziel) kennt. Weiterhin muss der Addressebereich von "EBSS" 
bekannt sein, um diesen mit 0 vorzubelegen. Habe in Linkerscript von 
einem anderen Projekt genutzt und zwischen "schnipp" und "schnapp" 
ergänzt:
1
  .data : AT (_etext)
2
  {
3
    _data = .;
4
    *(.data)
5
    SORT(CONSTRUCTORS)
6
  } > RAM
7
  . = ALIGN(4);
8
  _edata = . ;
9
  PROVIDE (edata = .);
10
11
  /* schnipp */
12
13
  _edata_LMA = LOADADDR(.data) + SIZEOF(.data);
14
  .extdata : AT (_edata_LMA)
15
  {
16
     _extdata_start = .;
17
     *(.exdata_section)
18
     _extdata_end = .;
19
  } > EXTRAM
20
  
21
  .extbss (NOLOAD) :
22
  {
23
     _exbss_start = .;
24
    *(.exbss_section)
25
     _exbss_end = .;
26
  } > EXTRAM
27
28
  /* schnapp */  
29
30
  /* .bss section which is used for uninitialized data */
31
  .bss (NOLOAD) :
32
  {
33
    __bss_start = . ;
34
    __bss_start__ = . ;
35
    *(.bss)
36
    *(COMMON)
37
    . = ALIGN(4);
38
  } > RAM
39
40
  . = ALIGN(4);
41
  __bss_end__ = . ;
42
  PROVIDE (__bss_end = .);

Das gibt in einem kleinen Beispiel mit ein paar Variablen folgende 
Addressen für die Symbole (Ausgabe vomn nm), die zumindest plausibel 
erscheinen:
1
...
2
000002a0 A _etext
3
000002a4 A _edata_LMA
4
00000400 A STACK_SIZE
5
40000000 D _data
6
40000000 d foodata2
7
40000004 B __bss_start
8
40000004 B __bss_start__
9
40000004 A _edata
10
40000004 b foobss2
11
40000008 A __bss_end__
12
40000500 A _end
13
40000500 B _stack
14
a0000000 D _extdata_start
15
a0000000 d foo3
16
a000000c d foo1
17
a0000010 B _exbss_start
18
a0000010 D _extdata_end
19
a0000010 b foo2
20
a0000014 B _exbss_end

4. Sobald man das Speicherinterface im Startupcode initialisiert hat, 
sind die Inhalte der initialiserten Variablen ab "_edata_LMA" nach 
"_exdata_start" zu kopieren. Anzahl dabei: ( _exdata_end - exdata_start 
). Weiterhin sind die Speicherstellen von _exbss_start bis _exbss_end 
mit 0 zu beschreiben. Man kann sich dabei an den Routinen für .data und 
.bss im "normalen" RAM-Bereich orientieren.

Hoffe, es hilft zumindest ein wenig weiter
Martin Thomas

von sean paul (Gast)


Lesenswert?

@Stefan: ich danke dir vielmals, der Zugriff hat geklappt, und der 
Linker hat überhaupt nichts mitbekommen.

@Mattin Thomas: vielen vielen Dank, das wird für alle fälle ein Unikat 
in meinem Tutorials-archiev sein, man weiss ja nie.. ;-)

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.