Hallo, wie kann ich festlegen an welcher Speicheradressse eine Variable gespeichert wird?
In C kannst du den Speicherbereich (RAM, EEPROM oder FLASH) festlegen. AFAIK kannst du aber die genaue Speicherstelle nicht festlegen (Warum auch...).
@Heiko: Danke, genau so habe ich mir das Vorgestellt! Und wie kann ich Sicherstellen, daß mein Programm oder der Stack diese Adresse nicht überschreibt?
> 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.
@Heiko: in GCC (Forum)? @Christian: erklär lieber mal wozu du das benötigst. Und Plattform, Compiler usw. wär auch nicht übel.
@ 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
> 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.
@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)!
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.
@A.K.: ich will einer im C-Code definierter Variable im Assembler Code einen Wert zuweisen.
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)
> 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? ;-)
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).
...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
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
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
> 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.
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
> 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.
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.
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.
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
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.
> 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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.