Forum: Compiler & IDEs Adresse von Variabel festlegen


von Christan (Gast)


Lesenswert?

Hallo,

wie kann ich festlegen an welcher Speicheradressse eine Variable
gespeichert wird?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

In Assembler sollte das möglich sein.

von inoffizieller WM-Rahul (Gast)


Lesenswert?

In C kannst du den Speicherbereich (RAM, EEPROM oder FLASH) festlegen.
AFAIK kannst du aber die genaue Speicherstelle nicht festlegen (Warum
auch...).

von Heiko Thole (Gast)


Lesenswert?

Hallo,

ich mach das so:
1
unsigned char ucVariable @ 0x3FF3;

von Christan (Gast)


Lesenswert?

@Heiko:
Danke, genau so habe ich mir das Vorgestellt!
Und wie kann ich Sicherstellen, daß mein Programm oder der Stack diese
Adresse nicht überschreibt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> ich mach das so:
>
1
> unsigned char ucVariable @ 0x3FF3;
2
>

Aber dann bist du in diesem Forum falsch. ;-)  Beim GCC macht man
das, indem man es dem Linker mitteilt.  Compiler und Assembler
haben bei einem Model mit verschieblichen Objektdateien keinen
sinnvollen Einfluss auf die Zielposition im Speicher.  Das war
übrigens schon bei M80/L80 unter CP/M so...  .org ist eine
Direktive, die nur bei absoluter Assemblierung Sinn hat (also
wenn man keinen Linker hat).

> Und wie kann ich Sicherstellen, daß mein Programm oder der Stack
> diese Adresse nicht überschreibt?

Da du es beim GCC-Modell ja dem Linker mitteilen musst (das geht
über sections, für AVR-GCC kannst du darüber im avr-libc-Manual
was nachlesen), stellt er zumindest sicher, dass es keine
Überlappungen mit statischen Speicherzuteilungen gibt.  Die
Kollisionsfreiheit mit dynamischen Speicherzuteilungen (Stack,
Heap) kann dir aber keiner abnehmen, die musst du selbst
sicher stellen.

von A.K. (Gast)


Lesenswert?

@Heiko: in GCC (Forum)?

@Christian: erklär lieber mal wozu du das benötigst. Und Plattform,
Compiler usw. wär auch nicht übel.

von Christan (Gast)


Lesenswert?

@ A.K.:
Ok, habe einen AVR (dürfte ja prinzipiel egal sein welcher) evtl.
Mega8.
benutze GCC (WinAVR) und will mittels inline Assembler einer Variablen
einen Wert zuweisen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> will mittels inline Assembler einer Variablen
> einen Wert zuweisen

Dafür hat der inline-Assembler aber 1023 vernünftige Optionen,
da muss man nicht mit absoluter Adressierung arbeiten.

von Christan (Gast)


Lesenswert?

@Jörg:
>Dafür hat der inline-Assembler aber 1023 vernünftige Optionen

währe super, wenn du mir ein paar nennen könntest (wenn es so viele
gibt)!

von A.K. (Gast)


Lesenswert?

Warum muss die Variable an einem bestimmten Platz liegen? Das ist eine
eher unübliche Forderung. Manchmal stellt sich heraus, dass eine
Antwort auf die falsche Frage nicht wirklich weiterhilft.

von Christan (Gast)


Lesenswert?

@A.K.:
ich will einer im C-Code definierter Variable im Assembler Code einen
Wert zuweisen.

von Karl heinz B. (kbucheg)


Lesenswert?

Ev. führt das jetzt zu weit:
Wozu brauchst du ein Assembler-Modul?

(Das hatten wir schon öfter, dass Leute meinten sie muessen
unbedingt einen Teil in Assembler schreiben. Und dann stellte
sich heraus, dass sie einfach nur das Pferd falsch rum aufgezäumt
hatten)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> währe super, wenn du mir ein paar nennen könntest (wenn es so
> viele gibt)!

Das Inline-Assembler-Kochbuch (in der avr-libc-Doku) nennt dir
alle sogenannten constraints, die der Inline-Assembler im AVR-GCC
kennt.  Da gibt's auch Beispiele.

Für eine einfache globale Variable genügt es, auf diese per
Namen zuzugreifen.  Das geht im inline-Assembler genauso wie
im ,,richtigen'' Assembler.

Wusstest du, dass 99 % der Fälle, in denen die Leute den
Inline-Assembler benutzen, besser gleich in C geschrieben worden
wären? ;-)

von A.K. (Gast)


Lesenswert?

Kooperation von C und Assembler macht man nicht über feste Adressen,
sondern indem man entweder den Assembler-Code in den C-Quellcode
einbaut, oder dass Assembler-Module hinzulinkt. Im GCC Kontext heisst
das natürlich, dass man den GNU-Assembler an Stelle des Atmel
Assemblers verwendet (ist recht ähnlich).

von Stefan (Gast)


Lesenswert?

...ist zwar schon etwas älter, aber mir fällt gerade aus aktuellem 
Anlass noch ein Grund ein, warum man eine Variable an einer ganz 
bestimmten Adresse haben will:

Wenn man das XMEM Interface benutzt, und daran neben SRAM noch andere 
Hardware (LCD etc) betreibt, müsste ich eine Pointer Variable anlegen, 
z.B. uin8_t *lcd_port, und dann immer mit *lcd_port = data; darauf 
zugreifen. Wenn ich einfach eine Variable anlege (uint8_t lcd) und dem 
Linker sage dass diese Variable bei Adresse 0xFFFF (darauf liegt das 
LCD) liegt, spare ich mir Code und Speicher.
So, nun muss ich nur noch rausfinden wie man das ganze mit einer Section 
so hinbekommt, denn in diesem Zusammenhang habe ich Sections noch nicht 
benutzt.

Gruß
Stefan

von Peter D. (peda)


Lesenswert?

Stefan wrote:

> Wenn man das XMEM Interface benutzt, und daran neben SRAM noch andere
> Hardware (LCD etc) betreibt, müsste ich eine Pointer Variable anlegen,
> z.B. uin8_t *lcd_port, und dann immer mit *lcd_port = data; darauf
> zugreifen. Wenn ich einfach eine Variable anlege (uint8_t lcd) und dem
> Linker sage dass diese Variable bei Adresse 0xFFFF (darauf liegt das
> LCD) liegt, spare ich mir Code und Speicher.

Warum soll das Code und Speicher sparen ?

Es ist dem Compiler völlig egal, ob er die Variable erst vom Linker 
aufgelöst kriegt oder direkt die Adresse. Beides wird in ein LDS oder 
STS übersetzt.

Der Trick ist einfach nur ein cast der Adresse (16Bit-Wert) als Pointer 
auf ein Byte. Irgendwo ein define dafür hingeschreiben und es sieht 
genau so aus, wie ne Variable.

Ich mache memory mapped Peripherie (z.B. SJA1000, LAN91C96) schon seit 
Ewigkeiten so (cast der Basisadresse als Pointer auf ne Struktur).


Peter

von Stefan (Gast)


Lesenswert?

Hallo Peter,

könntest du mir das etwas näher erläutern ? Ich blick da gerade noch 
nicht durch. Angenommen ich deklariere mir eine Pointer-Variable:

uint8_t *lcd_data = (uint8_t*) 0xFFFF;

Dann werden doch 2 Byte RAM gebraucht, weil dieser Pointer im Speicher 
gehalten wird (den Pointer könnte ich ja auch jederzeit umbiegen).

Wenn ich nun (wie das geht hab ich immer noch nicht rausgefunden) eine 
Variable anlegen kann und dem Linker gleich irgendwie eintrichtere, dass 
diese bei 0xFFFF liegt - Pseudocode etwa so:
uint8_t sack _attribute_ ((section (".ficken")));
uint8_t lcd_data; HALLO_LINKER, DIE HIER LIEGT IM RAM BEI 0xFFFF;

Hab ich doch in diesem Fall 2 Byte RAM gespart ?

>Es ist dem Compiler völlig egal, ob er die Variable erst vom Linker
>aufgelöst kriegt oder direkt die Adresse. Beides wird in ein LDS oder
>STS übersetzt.

Aber der Compiler weiß doch bei *lcd_data = 5; zur Compilezeit noch gar 
nicht was für eine Adresse in lcd_data steckt ? Und das kann sich ja 
auch ständig ändern ?

>Der Trick ist einfach nur ein cast der Adresse (16Bit-Wert) als Pointer
>auf ein Byte. Irgendwo ein define dafür hingeschreiben und es sieht
>genau so aus, wie ne Variable.

Könntest du dazu ein Beispiel geben ? Sieht das dann so ähnlich aus wie 
(uint8_t*) (0xFFFF) = 5 ? (Was selbstverständlich so nicht funktioniert)

>cast der Basisadresse als Pointer auf ne Struktur

Das verstehe ich so dass du eine Variable anlegst wie
struct port_struct *ports = 0xFFFE
In dem Beispiel hat die struct zwei uint8_t Member, womit ich einmal auf 
0xFFFE und auf 0xFFFF zugreifen kann. D.h. für einen zusammenhängenden 
Port Bereich brauche ich insgesamt nur einen Pointer, und somit 2 Byte 
RAM.
Hab ich das soweit richtig verstanden ?

Ich hab schon öfters mit Hilfe von Sections Funktionen an feste Adressen 
gelinkt (--Wl,-section......), z.B. für Bootloader oder Versionsnummer, 
die immer am Ende des Flash stehen soll etc.
Ich habe vorhin versucht meine lcd Variable an eine feste Adresse 
(0xFFFF) im RAM zu setzten mit
LDFLAGS += -Wl,--section-start=.lcdport=0x80FFFE
aber das haut leider nicht hin, die HexDatei passt dann nicht mehr in 
den µC, weil es über 8 MB sind. Der Linker scheint also nicht zu 
verstehen dass ich das im RAM haben will.

Liebe Grüße
Stefan




von Ronny (Gast)


Lesenswert?

> Aber der Compiler weiß doch bei *lcd_data = 5; zur Compilezeit noch gar
> nicht was für eine Adresse in lcd_data steckt ? Und das kann sich ja
> auch ständig ändern ?

Doch,das weiss er.Aber trotzdem muss zur Laufzeit erst die Adresse aus 
dem Pointer geladen werden,es kann ja sein das zwischendurch die 0x0005 
durch einen anderen Wert überschrieben wurde.An welcher Adresse der 
Pointer lcd_data letztendlich steht,kann der Compiler jedoch nicht 
wissen.Das Symbol wird nämlich erst vom Linker durch eine Adresse 
ersetzt.

Wenn du mit einem Pointer auf einen externen Speicherbereich 
zugreifst,brauchst du mindestens die 2 byte RAM für den Pointer.Falls du 
mit einer Controllerarchitektur arbeitest,die den Speicher in 
irgendeiner Weise segmentiert,ist es eventuell sogar nötig einen FAR 
Zeiger zu benutzen,was dann nochmal mindestens 1,typischerweise 2 byte 
benötigt.

Viele Compiler unterstützen aber Objektformate in denen man 
benutzerdefinierte Sections anlegen kann.Über Linkerparameter kann man 
diese dann an bestimmte Adressen nageln.Andere Compiler haben bestimmte 
pragmas oder Schlüsselwörter (at) um eine Variable an einer Adresse zu 
erzeugen.
Wie das geht steht in der Doku zu deinem Compiler/Linker.

von Peter D. (peda)


Lesenswert?

Stefan wrote:

> könntest du mir das etwas näher erläutern ? Ich blick da gerade noch
> nicht durch.

kein Problem:
1
#define MMIO_ADDR       0x1234
2
3
#define myiovar1        (*(unsigned char volatile *)MMIO_ADDR)
4
5
6
unsigned char test( void )
7
{
8
  myiovar1 = 0x12;
9
10
  return myiovar1;
11
}

Der Compiler macht daraus das hier:
1
unsigned char test( void )
2
{
3
  myiovar1 = 0x12;
4
  b8:   82 e1           ldi     r24, 0x12       ; 18
5
  ba:   80 93 34 12     sts     0x1234, r24
6
7
  return myiovar1;
8
  be:   80 91 34 12     lds     r24, 0x1234
9
}
10
  c2:   99 27           eor     r25, r25
11
  c4:   08 95           ret

Man nimmt also ne Zahl

 0x1235,

sagt dem Comiler, das ist ne Adresse eines Bytes

 (unsigned char volatile *)

und nun greif drauf zu

 *


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Wenn du mit einem Pointer auf einen externen Speicherbereich
> zugreifst,brauchst du mindestens die 2 byte RAM für den Pointer.

Nein.  Wenn die Adresse zur Compilezeit bereits bekannt ist, dann
mutiert der Pointer lediglich zu einem Hilfskonstrukt des C-Programms,
der Compiler kann stattdessen sofort die numerische Adresse in die
entsprechenden STS/LDS-Anweisungen einsetzen.

von Karl H. (kbuchegg)


Lesenswert?

Ne andere Möglichkeit ohne Makro wäre:

uint8_t * const lcd_data = (uint8_t * const) 0xFFFF;

OK. Das hängt jetzt ein bischen auch vom Compiler ab,
ob er Konstante ausdrücke durch den entsprechenden
Zahlenwert ersetzt. Ich, für meinen Teil, würde mal
davon ausgehen, dass der Optimizer diese simple
Substitution macht.

> Aber der Compiler weiß doch bei *lcd_data = 5; zur Compilezeit noch gar
> nicht was für eine Adresse in lcd_data steckt ? Und das kann sich ja
> auch ständig ändern ?

Deswegen teile ich ihm mit dem const mit, dass sich genau der
Teil nicht ändert: Die Pointerinhalt ist fix und lautet immer
0xFFFF. Dann weiß der Compiler das, und kann das benutzen um
mein Programm zu optimieren und die Pointervariable effektiv
aus dem Pgm rausschmeissen weil er sie nie wirklich als Variable
braucht.


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Es geht sogar ohne const, wenn man den Scope der Variablen einfach
klein genug macht, dass der Compiler feststellen kann, dass sich
der Wert des Zeigers gar nicht ändern kann.

von Stefan (Gast)


Lesenswert?

Die Möglichkeit hier von Peter hat mir am meisten zugesagt:
1
#define LCD_PORT (*(uint8_t volatile *) 0xFFFF)

Nun kann ich einfach mit LCD_PORT = 0xbla Daten zum LCD schieben, super!

Aber rein interessehalber, was immer noch nicht geklärt wurde: Wie kann 
ich dem Linker beibringen dass eine bestimmte Variable bei 0xFFFF ist 
? Ich konnte den Linker bislang immer nur im Zusammenhang mit Adressen 
die auf das Flash, nicht auf's RAM zeigen, überredern.

Gruß
Stefan

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Geht das auch in die andere Richtung?
Mich würde interessierenan welche Speicherplätze der Compiler die Daten 
hinschreibt, die ich ins EEPROM schreiben lasse. Ich hab bis jetzt nur 
die .lst und .lss gefunden, in der ich das allerdings nicht finden 
konnte.
Vielen Dank im Vorraus.

von Karl heinz B. (kbucheg)


Lesenswert?

> Geht das auch in die andere Richtung?

Wie meinst du das?

  uint8_t a;

  a = LCD_PORT;

Ja klar, das geht. Mach die Makrosubstitution:

  a = (*(uint8_t volatile *) 0xFFFF);

Ein perfekter Zugriff.


von inoffizieller WM-Rahul (Gast)


Lesenswert?

ich meinte eher

unsigned char eeVar EEMEM = 0x7F;

An welcher Speicherstelle steht das Ding im EEPROM?
Das wäre eine Debug-Information...

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.