Forum: Compiler & IDEs Zugriff auf SRAM Speicher in Assembler


von toti (Gast)


Lesenswert?

Hi,

ich habe gestern angefangen auf einem atmega32 etwas mit Assembler zu 
experimentieren. Ich hab zuvor bereits ein bisschen auf dem atmega32 
programmiert aber immer nur in C, möchte mich jetzt aber intensiver mit 
der Plattform beschäftigen und deswegen jetzt in Assembler 
programmieren.
Mit Hilfe des Datenblatts von Atmel geht das eigentlich auch ganz gut, 
jetzt habe ich aber ein Problem, bei dem ich leider auf dem Schlauch 
sehe.

Im Grunde möchte ich nur einen Wert aus dem SRAM also .data auslesen und 
diesen Mittels UART auf der Seriellen Schnittstelle ausgeben. Die 
Kommunikation über UART funktioniert mit konstanten Werten auch ohne 
Probleme. Versuche ich aber einen aus dem SRAM ausgelesen Wert zu 
senden, kommt immer ein falscher Wert auf meinem Terminal heraus.

Ich denke, dass das ganze wahrscheinlich ein grundlegender Fehler ist, 
dass ich irgendetwas nicht beachtet habe oder so. Da die meisten 
Assembler Beispiele und Erklärungen sind ja auch leider für den Atmel 
eigenen Assembler und lassen sich speziell in diesem Bereich scheinbar 
nicht direkt auf den avr-gcc übertragen, den ich verwende.
1
#define F_CPU 16000000UL
2
#define __SFR_OFFSET 0x00
3
#include <avr/io.h>
4
5
.org 0
6
reset:
7
    rjmp main
8
9
.org _VECTORS_SIZE
10
11
.data
12
13
test_byte:  .byte 0x3A ;; Ein Doppelpunkt
14
15
.text                        
16
17
.global main
18
.extern delay_ms, init_usart, usart_transmit_byte, usart_transmit_string        
19
20
21
main:  
22
                ldi r16, hi8(RAMEND)
23
    ldi r17, lo8(RAMEND)
24
    out SPL, r17
25
    out SPH, r16
26
27
    rcall init_usart
28
29
    ldi r16, 10
30
    rcall usart_transmit_byte
31
    ldi r16, 13
32
    rcall usart_transmit_byte
33
34
    lds r16, test_byte
35
    rcall usart_transmit_byte
36
37
loop:                    
38
        rjmp loop

Die UART Implementierung erfolgt in folgender Datei:
1
#define __SFR_OFFSET 0x00
2
#include <avr/io.h>
3
4
.text
5
.global init_usart, usart_transmit_byte
6
7
init_usart:
8
    ldi r16, lo8(51)
9
    out UBRRL, r16
10
    ldi r16, hi8(51)
11
    out UBRRH, r16
12
13
    ldi r16, (1<<RXEN) | (1<<TXEN) | (1<<RXCIE)
14
    out UCSRB, r16
15
16
    ldi r16, (1<<URSEL)|(1<<USBS)|(3<<UCSZ0)
17
    out UCSRC, r16
18
    ret
19
20
21
;; Parameter:
22
;; r16 <- byte zum senden
23
24
usart_transmit_byte:
25
    sbis UCSRA, UDRE
26
    rjmp usart_transmit_byte
27
    out UDR, r16
28
    ret

Da ja bei avr-objcopy auch angegeben wird, was letztendlich in das Hex 
File rein soll und ich mich damit auch noch nicht wirklich auskenne, 
hier auch noch mein Makefile:
1
CC=avr-gcc
2
LD=avr-ld
3
CFLAGS=-mmcu=$(TARGET_PLATFORM)
4
LDFLAGS=
5
SOURCES=Main.S Utils.S usart.S
6
TARGET_PLATFORM=atmega32
7
OBJECTS=$(SOURCES:.S=.o)
8
OUTPUT=Main.hex
9
ELF=Main.elf
10
11
all: $(OUTPUT)
12
13
$(OUTPUT): $(SOURCES) $(ELF)
14
  avr-objcopy -O ihex -j .text -j .data $(ELF) $(OUTPUT)
15
16
$(ELF): $(OBJECTS)
17
  $(LD) $(LDFLAGS) $(OBJECTS) -o $@
18
19
%.o: %.S
20
  $(CC) $(CFLAGS) -c $< -o $@
21
22
load: $(OUTPUT)
23
  avrdude -p atmega32 -c avrisp2 -U flash:w:$(OUTPUT) -v -F -P usb
24
25
clean:
26
  rm $(EXECUTABLE) *.o *.hex

Also wie gesagt, mit konstanten Werten wie dem EOL und CR funktioniert 
es ohne Probleme, erst wenn ich auf den Speicher zugreifen will kommt 
anstellen des ":" ein "*", irgendwas läuft da also scheinbar beim 
Speicherzugriff falsch. Ich bin eigentlich davon ausgegangen, dass ich 
im .data Bereich einfach Daten definieren kann, die dann beim Start in 
den SRAM geladen werden und auf die ich dann über das Label zugreifen 
kann. Scheinbar scheint das ja leider nicht so einfach zu sein ^^.

Ich würde mich sehr freuen, wenn mir jemand dabei helfen könnte. Wenn 
irgendwelche anderen Informationen noch nötig sind werde ich die 
natürlich auch gerne nachreichen.

Gruß,
toti

von Konrad S. (maybee)


Lesenswert?

toti schrieb:
> die dann beim Start in
> den SRAM geladen werden

Wer sollte das bei einem Assembler-Programm tun? Bei C: ja, das macht 
der automatisch dazugelinkte Startup-Code.

von toti (Gast)


Lesenswert?

Konrad S. schrieb:
> toti schrieb:
>> die dann beim Start in
>> den SRAM geladen werden
>
> Wer sollte das bei einem Assembler-Programm tun? Bei C: ja, das macht
> der automatisch dazugelinkte Startup-Code.

Ich muss zugeben, dass ich mir das auch nicht ganz beantworten konnte. 
Heißt das, dass ich diesen Prozess von Hand anstoßen muss oder geht man 
dabei ganz anders vor?

von MWS (Gast)


Lesenswert?

Das bedeutet, was im SRam drin sein soll, muss man da per Programm 
reinladen. Alternativ Daten per LPM aus dem Flash lesen.

von Torsten W. (torsten_w)


Lesenswert?

MWS schrieb:
> Das bedeutet, was im SRam drin sein soll, muss man da per Programm
> reinladen. Alternativ Daten per LPM aus dem Flash lesen.

Das klingt so ganz einleuchtend, das einzige Problem ist nur, dass ich 
nichts dazu finden kann, wie beim gnu assembler festlegen kann, dass die 
Daten im flash gespeichert werden sollen. Ich finde da leider nur 
Beschreibungen für den Atmel Assembler.

von Stefan E. (sternst)


Lesenswert?

Ergänze Main.S um ".global __do_copy_data", und entferne deinen eigenen 
Reset-Vector. Das Initialisieren des Stack-Pointers kannst du dir dann 
auch sparen (das passiert schon im Startup-Code, den du im Augenblick 
einfach überspringst).

Außerdem fehlt beim Linken die -mmcu Option.

PS: Ach, mir fällt gerade auf, dass dein Reset-Vector eh wirkungslos 
ist, da das .org eh nicht das macht was du denkst.

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Ergänze Main.S um ".global __do_copy_data", und entferne deinen eigenen
> Reset-Vector. Das Initialisieren des Stack-Pointers kannst du dir dann
> auch sparen (das passiert schon im Startup-Code, den du im Augenblick
> einfach überspringst).
>
> Außerdem fehlt beim Linken die -mmcu Option.
>
> PS: Ach, mir fällt gerade auf, dass dein Reset-Vector eh wirkungslos
> ist, da das .org eh nicht das macht was du denkst.

mhhh, ok, ich hab den Reset-Vector gelöscht und .global __do_copy_data 
hinzugefügt aber leider verstehe ich das mit dem Startup-Code jetzt 
nicht genau. Von wo aus wird der aufgerufen und warum überspringe ich 
den Momentan?

von Stefan E. (sternst)


Lesenswert?

Torsten W. schrieb:
> leider verstehe ich das mit dem Startup-Code jetzt
> nicht genau. Von wo aus wird der aufgerufen

Der wird automatisch hinzugelinkt (sofern du das nicht unterbindest) und 
ein Sprung darauf als Reset-Vektor eingetragen.

Torsten W. schrieb:
> warum überspringe ich
> den Momentan?

Weil du direkt nach main springst. Aber (wie gesagt) da habe ich mich 
geirrt. Dein rjmp main landet eh nicht im Reset-Vektor, sondern ist 
einfach nur toter Code. Die Module werden gelinkt, da kannst du keine 
absoluten Adressen mit .org vorgeben. Das sind dann nur Modul-relative 
Adressen.

von Torsten W. (torsten_w)


Lesenswert?

Ahh ok, also nach dem Entfernen der .org _VECTORS_SIZE anweisung 
funktioniert es jetzt, aber wirklich befriedigend ist das nicht, weil 
ich es nicht ganz verstehe ^^. Könntest du mir dazu irgendeine Literatur 
empfehlen, um das ganz genau nachzulesen. Ich wäre auch generell an 
Tutorials für den GNU Assembler interessiert, weil ich da wie gesagt 
keine gefunden hab.

von Stefan E. (sternst)


Lesenswert?

Torsten W. schrieb:
> weil ich es nicht ganz verstehe

Was genau?

Torsten W. schrieb:
> Könntest du mir dazu irgendeine Literatur
> empfehlen, um das ganz genau nachzulesen.

Nicht wirklich. Ich wüsste jetzt nur das
http://www.nongnu.org/avr-libc/user-manual/assembler.html
aber da wird der Startup auch nicht ausführlich erklärt.

von MWS (Gast)


Lesenswert?

Torsten W. schrieb:
> wie beim gnu assembler festlegen kann

Zum gnu Assembler kann ich nicht viel sagen, aber ich nehme an, so wie 
Du test_byte angelegt hast, landete das tatsächlich im Flash. Das 
hättest Du dann per Z-Pointer und LPM in's SRam laden können. Was ich 
für den Beginn auch mal selbst machen, bevor ich das automatisiert 
stattfinden lassen würde.

von Stefan E. (sternst)


Lesenswert?

MWS schrieb:
> Zum gnu Assembler kann ich nicht viel sagen, aber ich nehme an, so wie
> Du test_byte angelegt hast, landete das tatsächlich im Flash.

Ne, das landet schon im RAM (aber natürlich steht der 
Initialisierungswert im Flash).

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Was genau?

Was genau .global __do_copy_data bewirkt. Handelt es sich dabei um das 
ganz am Anfang angesprochene Ladescript, was in den Code mit rein 
gelinkt wird und dann zu beginn die Daten vom Flash in den SRAM kopiert?

Ich hab jetzt mal versucht einen ganzen String auszugeben, bekomme dabei 
aber auch nur Müll heraus. Definiert hab ich den folgendermaßen:
1
.data
2
test_byte:  .byte 0x3A
3
text_tmp:  .string "Das ist ein test"

Ausgeben möchte ich den dann mit folgender Prozedur:
1
usart_transmit_string:      
2
.next:  lpm r16, Z+
3
    cpi r16, 0x20 ;; Ausgeben bis zum Leerzeichen
4
    breq .done
5
    rcall usart_transmit_byte
6
    rjmp .next
7
.done:  ret

Und aufgerufen hiermit:
1
    ldi ZL, lo8(text_tmp)
2
    ldi ZH, hi8(text_tmp)
3
    rcall usart_transmit_string

Ich möchte jetzt nicht jede Kleinigkeit hier vorschnell fragen, aber das 
wundert mich jetzt ein bischen, weil das einzelne Byte funktioniert.

von Stefan E. (sternst)


Lesenswert?

Torsten W. schrieb:
> Was genau .global __do_copy_data bewirkt. Handelt es sich dabei um das
> ganz am Anfang angesprochene Ladescript, was in den Code mit rein
> gelinkt wird und dann zu beginn die Daten vom Flash in den SRAM kopiert?

Ja, damit wird sicher gestellt, dass diese Kopier-Routine Bestandteil 
des Startup-Codes wird.

Torsten W. schrieb:
> Ich möchte jetzt nicht jede Kleinigkeit hier vorschnell fragen, aber das
> wundert mich jetzt ein bischen, weil das einzelne Byte funktioniert.

Und wo bei dem einzelnen Byte hast du lpm verwendet?

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Und wo bei dem einzelnen Byte hast du lpm verwendet?

Ich hatte den code:
1
lds r16, test_byte
2
    rcall usart_transmit_byte

durch den ersetzt:
1
    ldi ZH, hi8(test_byte)
2
    ldi ZL, lo8(test_byte)    
3
    lpm r16, Z
4
    rcall usart_transmit_byte

MWS hatte das ja zu beginn empfohlen um die Daten aus dem Flash zu 
laden.

von Stefan E. (sternst)


Lesenswert?

Wenn das funktioniert hat, hattest du schlicht Glück.
test_byte und text_tmp sind RAM-Adressen.

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Wenn das funktioniert hat, hattest du schlicht Glück.
> test_byte und text_tmp sind RAM-Adressen.

mhhh ok, also das heißt ja dann ich müsste sie einfach mittel
1
lds r16, test_byte
oder
1
    ldi ZH, hi8(test_byte)
2
    ldi ZL, lo8(test_byte)    
3
    ld  r16, Z
laden können, da ja durch das .global __do_copy_data die 
Speicherbereiche im SRAM ja durch den Startup-code initialisiert worden 
sein müssten oder? Ich bekomme in diesem Fall aber wieder ein * statt 
des Doppelpunkts.

von Peter II (Gast)


Lesenswert?

warum lässt du das nicht einfach mal im simulator laufen, dann siehst du 
immerhein ob das zeug im Ram drin steht.

von Stefan E. (sternst)


Lesenswert?

Torsten W. schrieb:
> Ich bekomme in diesem Fall aber wieder ein * statt
> des Doppelpunkts.

Hast du die Linker-Kommandozeile korrigiert?

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Torsten W. schrieb:
>> Ich bekomme in diesem Fall aber wieder ein * statt
>> des Doppelpunkts.
>
> Hast du die Linker-Kommandozeile korrigiert?

Ich bin jetzt ein bisschen verwirrt. Ich hatte dem Linker bis jetzt als 
Parameter "-m avr5" übergeben und dabei kam ja nicht das gewünschte 
Ergebnis heraus. Sowohl beim Byte als auch bei dem String wurden falsche 
Werte ausgegeben. Jetzt habe ich das Linken mal nicht direkt über avr-ld 
ausgeführt sondern mittel avr-gcc und als Parameter -mmcu=atmega32 
übergeben, wie beim Compilen. Und jetzt funktioniert das Ganze. Was mich 
jetzt aber wirklich irritiert ist, dass wenn ich nach dem erfolgreichen 
Linken wieder auf avr-ld umstelle und "-m avr5" oder auch gar nichts 
übergebe, funktioniert es auch. Wenn ich dann aber im .data bereich 
etwas verändere, z.B. einen Anderen String davor packe, dann werden 
teile von dem ausgegeben. Ich vermute jetzt, dass der Linker auf die 
Informationen vom letzten linken zugreift, wenn ich ihm nicht die 
richten Parameter übergebe?! Liegt da irgendwo eine Temporäre Datei rum, 
auf die avr-ld dann zugreift oder woran liegt das?

von Konrad S. (maybee)


Lesenswert?

Lass mal den avr-gcc linken und gib ihm die Option -v mit. Dann siehst 
du, wie der Linker aufgerufen wird.

von Torsten W. (torsten_w)


Lesenswert?

Konrad S. schrieb:
> Lass mal den avr-gcc linken und gib ihm die Option -v mit. Dann siehst
> du, wie der Linker aufgerufen wird.

Der Linker wird folgendermaßen aufgerufen:
1
/usr/libexec/gcc/avr/4.5.3/collect2 -m avr5 -o Main.elf /usr/lib/gcc/avr/4.5.3/../../../../avr/lib/crtm32.o -L/usr/lib/gcc/avr/4.5.3 -L/usr/lib/gcc/avr/4.5.3/../../../../avr/lib Main.o Utils.o usart.o -lgcc -lc -lgcc
ich gehe mal davon aus, dass collect2 und avr-ld identisch sind oder? Da 
collect2 mir bei einem Start mit -v folgendes angibt:
1
/usr/libexec/gcc/avr/4.5.3/collect2 -v
2
collect2 version 4.5.3 (GNU assembler syntax)
3
/usr/bin/avr-ld -v
4
GNU ld (GNU Binutils) 2.22
Der Unterschied müsste dann ja daran liegen, dass ich beim direkten 
Aufrufe von avr-ld die crtm32.o nicht übergebe und deswegen irgendwas im 
Programm falsch läuft. Ich verstehe jetzt aber immer noch nicht, warum 
es mit avr-ld auch direkt geht, wenn ich vorher einmal mit avr-gcc 
linken lasse. Selbst wenn ich dazwischen ein make clean mache.

von Stefan E. (sternst)


Lesenswert?

Torsten W. schrieb:
> Der Unterschied müsste dann ja daran liegen, dass ich beim direkten
> Aufrufe von avr-ld die crtm32.o nicht übergebe

Und was ist mit -lgcc und insbesondere -lc? Hast du die etwa bei deinem 
eigenen avr-ld Aufruf auch mit drin?

Torsten W. schrieb:
> Ich verstehe jetzt aber immer noch nicht, warum
> es mit avr-ld auch direkt geht, wenn ich vorher einmal mit avr-gcc
> linken lasse. Selbst wenn ich dazwischen ein make clean mache.

Ich wette du hast dem AVR zwischendurch den Saft nicht abgedreht. 
Vermutlich landet bei deinem eigenen Aufruf kein __do_copy_data in Code. 
Es funktioniert aber nach einem "richtigen" Durchlauf trotzdem, weil im 
RAM immer noch das richtige drin steht (ein Reset oder Chip-Erase löscht 
nicht das RAM).

von Torsten W. (torsten_w)


Lesenswert?

Stefan Ernst schrieb:
> Und was ist mit -lgcc und insbesondere -lc?

Huch, genau das hab ich grad eben noch bei meinem vorherigen Post 
hinzugefügt. Genau als ichs abgeschickt habe, hab ich die Mail von 
deinem Post bekommen und mein Post wurde wohl irgendwie nicht 
gespeichert. Hört sich jetzt wie ne doofe Ausrede an ^^.

Stefan Ernst schrieb:
> Vermutlich landet bei deinem eigenen Aufruf kein __do_copy_data in Code.
> Es funktioniert aber nach einem "richtigen" Durchlauf trotzdem, weil im
> RAM immer noch das richtige drin steht (ein Reset oder Chip Erase löscht
> nicht das RAM).

Hui, da muss ich mir jetzt aber selber vorn Kopf hauen, daran hab ich 
gar nicht gedacht. Aber es stimmt natürlich, ist ja auch total 
einleuchtend, bin ich aber so alleine nicht drauf gekommen.

Vielen Dank auf jeden Fall für die vielen und besonders schnellen 
Antworten, das hat mir sehr geholfen.

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.