Forum: Mikrocontroller und Digitale Elektronik Was passiert bei der Stackpointer initialisierung?


von Jens J. (trial)


Lesenswert?

Aus Schulischen Gründen versuche ich das grade zu verstehen anstatt es 
nur auswendig zu lernen.

AVR Studio 7 - ATMEGA 16 - Assembler

ldi r16, low(ramend)
out SPL, r16

ldi r16, high(ramend)
out SPH, r16


Was sind SPL und SPH ? scheint mir als würden die Stackpointer 
Erklärungen im Netz dieses Wissen vorraussetzen.

Warum muss man über ein Register gehen und kann nicht direkt bescheid 
geben, das die Stackpointer Funktion im ramend aktiviert werden soll?

So wie ich das verstanden habe ist der ramend ja ein Speicherbereich der 
frei genutzt werden kann. Und wenn man es will kann man einen Teil davon 
als Stackpointer deklarieren. Warum man da dann nun erst einen Teil des 
Ramend ins Register laden, und dann irgendwo hinschicken muss, verstehe 
ich nicht.

von H.Joachim S. (crazyhorse)


Lesenswert?

SPH/SPL sind 2 8bit-Register, die gemeinsam einen 16bit-Adresspointer 
bilden und damit beliebig im 16bit-Adressraum gesetzt werden können. 
Allerdings sollte an der Stelle auch tatsächlich RAM vorhanden sein :-)

Er muss auch nicht zwangsläufig auf ramend gesetzt werden, jedoch ist 
das i.a. die sinnvollste Art.
Inzwischen wird der nach Hardware-Reset von vielen neueren AVRs 
automatisch auf ramend gesetzt, früher war das nicht so. Stand dann auf 
0 wenn ich mich recht erinnere, also auf einem völlig nutzlosen Wert.

von avr (Gast)


Lesenswert?

Jens M. schrieb:
> So wie ich das verstanden habe ist der ramend ja ein Speicherbereich der
> frei genutzt werden kann.

Das ist die letzte Adresse des Rams, kein Bereich.

Jens M. schrieb:
> Und wenn man es will kann man einen Teil davon
> als Stackpointer deklarieren.

Einen Teil (Rambereich) kannst du nicht als Stackpointer deklariereren. 
Der Stackpointer ist wie der Name schon sagt einfach nur ein Zeiger, 
also eine Adresse. Und die gibt an in welches Byte vom Ram bei der 
nächsten Stackoperation genutzt wird. Diese Adresse musst du dem 
Mikrocontroller irgendwie mitteilen.

Jens M. schrieb:
> Warum man da dann nun erst einen Teil des
> Ramend ins Register laden, und dann irgendwo hinschicken muss, verstehe
> ich nicht.

mit ldi lädst du genau nicht aus dem Ram. Der Befehl lädt eine Konstante 
in ein Register. SPL und SPH sind Register. Mit den 4 Zeilen schreibst 
du eine Konstante (ramend) hinein. Grundsätzlich kann man mit 
(RISC-)Assemblerbefehlen keine Speicherbereiche kopieren.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> das die Stackpointer Funktion im ramend aktiviert werden soll?
Der Teil ist kompletter Blödsinn.

Hast Du überhaupt schon verstanden, wie der Stack funktioniert?

Die vier Zeilen machen nichts weiter, als die Adresse RAMEND in das 
Register SP (Stack Pointer) zu schreiben, welches als 16-Bit-Register 
auf der 8-Bit-CPU in die beiden 8-Bit-Register SPH und SPL zerlegt wird. 
Genau das Gleiche geht mit den 16-Bit-Registern X,Y und Z, die aus den 
hohen Rxx-Registern gebildet werden (Z bspw. aus R31:R30).

Der Umweg über das R16-Register ist nötig, weil der AVR nicht alle 
Register mit direkten Daten laden kann. Diese Daten werden beim AVR mit 
in den Befehlscode geschrieben, dadurch bleiben bei 16 Bit breiten 
Befehlsworten nur 256 mögliche Befehlscodes, die 8 Bit Daten direkt 
mitführen können. "LDI R0,88" geht auch nicht, R0-R15 können ebenfalls 
nicht direkt geladen werden.

Daß man sich den Stack frei irgendwo hinlegen kann finde ich schon 
praktisch. Es bietet dem erfahrenen Programmierer einfach mehr 
Möglichkeiten, ggf. beim Debuggen oder wenn der Stack nicht gebraucht 
wird zum Ablegen von zwei Bytes Daten, auf die schnell zugegriffen 
werden kann. Man könnte auch Befehle zweckentfremden, z.B. mit POP Rxx 
ein Array lesen wenn man das nützlich findet.

von chris (Gast)


Lesenswert?


von spess53 (Gast)


Lesenswert?

Hi

>Was sind SPL und SPH ? scheint mir als würden die Stackpointer
>Erklärungen im Netz dieses Wissen vorraussetzen.

Stimmt nicht. In jedem AVR-Datenblatt findest du ein Kapitel "Stack 
Pointer".  Und das Kapitel sagt auch aus mit welchen Wert der 
Stackpointer nach dem RESET initialisiert ist. Das kann 0x0000 oder bei 
kleineren AVRs 0x00 oder, bei neueren AVRs, auch RAMEND sein. Im 
letzteren Fall kannst du die obige Programmsequenz sparen.

Vollständiger weise seien auch noch AVRs mit einem Hardwarestack 
genannt. Der ist relativ klein und liegt nicht im RAM. Auch hier 
entfällt eine Initialisierung.

MfG Spess

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Ich finde man sollte diese Initialisierung auf keinen Fall weglassen. 
Außer es ist die letzte Möglichkeit, um die dringend benötigten 8 Byte 
Flash zu sparen.

Wenn man es weglässt tritt garantiert irgendwann bei irgendwem eine 
Situation auf, wo die automatische Initialisierung versagt. Dann ist die 
Kacke am Dampfen und keiner weiß wieso. Nee, nicht wegen den 4 kurzen 
Befehlen!

von Peter D. (peda)


Lesenswert?

Ben B. schrieb:
> Wenn man es weglässt tritt garantiert irgendwann bei irgendwem eine
> Situation auf, wo die automatische Initialisierung versagt.

Du kannst es ruhig weglassen, die alten AT90Sxxxx werden schon viele 
Jahre nicht mehr hergestellt.

von Peter D. (peda)


Lesenswert?

Jens M. schrieb:
> Warum muss man über ein Register gehen und kann nicht direkt bescheid
> geben, das die Stackpointer Funktion im ramend aktiviert werden soll?

Der AVR ist RISC, d.h. nur die 32 Arbeitsregister sind direkt 
manipulierbar. IO und SRAM können nur über Register zugegriffen werden 
(mit wenigen Ausnahmen).

von Stefan F. (Gast)


Lesenswert?

Ich denke, dass der durchschnittliche Assembler Programmierer seinen µC 
sehr genau kennt, weil der mit dem Datenblatt arbeitet. Er wird schon 
wissen, ob der SP initialisiert werden muss, oder nicht.

Der durchschnittliche C Programmierer muss sich damit nicht 
beschäftigen.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Naja ich halte davon nichts. Ich bin ja sogar "so krank", daß ich im 
Reset-Part meiner Programme wo die Hardware/Timer/ADC usw. konfiguriert 
werden, selbst über mit laut Datenblatt 0x00 vorinitialisierte 
Register/Ports noch einmal 0x00 drüberbügle. Alle meine Programme 
löschen beim Start das RAM, so daß ich sicher sein kann, daß beim Start 
auch überall 0x00 drinsteht wie ich es erwarte, selbst wenn irgendein 
unerwarteter Reset auftritt. Den Absturz kann man in dem Fall sowieso 
nicht verhindern, aber die Schaltung bzw. das Programm erholt sich zu 
100 Prozent davon.

Vielleicht ist das sinnlos, vielleicht ist es aber auch ein Grund warum 
meine Produkte so lange und zuverlässig funktionieren.

von Karl B. (gustav)


Lesenswert?

Ben B. schrieb:
> Alle meine Programme
> löschen beim Start das RAM, so daß ich sicher sein kann, daß beim Start
> auch überall 0x00 drinsteht wie ich es erwarte, selbst wenn irgendein
> unerwarteter Reset auftritt.

Beitrag "Re: uC Aufhängung technisch möglich?"

Was noch nicht gesagt wurde: Der Stack ist wegen der Sicherung der 
Adressen beim Sprung wichtig und "zählt rückwärts". Auch und nicht nur 
deswegen an den Rand des RAMs.
Einige AtTinys brauchen auch nur SPL, werden aber trotzdem "indirekt" 
adressiert.

Beitrag "register-indirekte adressierung (assembler)"

ciao
gustav

von Oldmax (Gast)


Lesenswert?

Hi
Bisher war zwar alles richtig, was ich gelesen hab, aber der Stack ist 
nicht erklärt. Warum brauche ich einen Zeiger auf eine Speicheradresse? 
Angenommen, du rufst ein Unterprogramm auf. Das passiert in einem 
Programm ständig. Nun muß der Controller sich die Adresse des Befehls 
nach dem Aufruf des Unterprogramms merken, da der Befehlszeiger, 
ebenfalls ein Register (Programmcounter), den nächsten Befehl in der 
ersten Speicherstelle des Unterprogramms adressieren muß. Dazu nutzt er 
die Speicherstelle, auf die der Stackpointer zeigt. Da sich der Inhalt 
in den adressierten Speicherzellen ständig ändert, muß es eine 
Speicherzelle sein, die dass kann. Und das kann nur der Speicherbereich, 
in dem auch die Variablen stehen. Der Variablenbereich wird von der 
ersten Speicherzelle von unten deklariert.  Da die Größe vom Stack aber 
ständig variiert wird von oben nach unten belegt. Bei sehr vielen 
Variablen und geschlachteten Unterprogramms  kann der Stackpointer 
sogar bis in den Variablenbereich hinein adressieren und Variablenwerte 
überschreiben. Hier das Prinzip:
Der Programmcounter, auch ein Adressierregister, kopiert sich den Befehl 
aus der Adressieren Speicherzelle und schreibt ihn in die Befehlsmatrix. 
Danach wird der Programmcounter erhöht und auf die nächste Speicherzelle 
mit dem nächsten Befehl gesetzt. Findet der Controller nun
den Aufruf eines Unterprogramms, wird der Inhalt des Programmcounters in 
die durch den Stackpointer adressierte Speicherzelle geschrieben. Danach 
wird der Stackpointer heruntergezählt und auf die nächste freie 
Speicherzelle nach unten gesetzt. Erst dann kommt die Adresse vom 
Unterprogramm in den Programmcounter. Der Controller springt also nicht 
wirklich, sondern holt sich immer den nächsten Befehl aus der Adresse, 
auf die der Programmcounter zeigt. Findet  der Controller den Befehl 
"Rücksprung aus dem Unterprogramm" kopiert er nun den Inhalt der durch 
den Stackpointer adressierten Speicherzelle in den Programmcounter und 
erhöht den Inhalt vom Stackpointer auf die nächste abgelegte Adresse 
oder abgelegten Registerwert.  Somit gilt: first in, last out. Auch Push 
und Pop Befehle nutzen den Stack. Hier werden aber Inhalte von 
Arbeitsregistern abgelegt und wieder zurück geholt. Ich hab jetzt nicht 
im angegebenen Link nachgelesen, aber vielleicht steht es da noch etwas 
ausführlicher.
Gruß oldmax

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Wenn Du es schon erklären willst, dann erklär es richtig.

Der Stack ist ein Stapelspeicher, das bedeutet was zuletzt 
draufgeschmissen wurde, muß man zuerst wieder herunterholen.

Im RAM wächst der von oben nach unten, also von der höchsten Adresse 
beginnend und abwärts zählend. Daraus ergibt sich die Gefahr von Stack 
Overflows. Wird der Stack zu groß, wandert er in den Speicherbereich wo 
"normale Daten" abgelegt sind und überschreibt diese. Bei einer 
Von-Neumann-Architektur (was der AVR nicht ist) wird ggf. auch das 
Programm selbst überschrieben.

Edit:
Mist, Zweiter.

Aber:
> geschlachteten Unterprogramms
Sicher?
Ich glaube, das sollte geschachtelt oder verschachtelt heißen.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Alle meine Programme
>löschen beim Start das RAM, so daß ich sicher sein kann, daß beim Start
>auch überall 0x00 drinsteht wie ich es erwarte, selbst wenn irgendein
>unerwarteter Reset auftritt. Den Absturz kann man in dem Fall sowieso
>nicht verhindern, aber die Schaltung bzw. das Programm erholt sich zu
>100 Prozent davon.

Nö. Wenn der Controller auf NOPs im RAM trifft läuft er weiter nach oben 
und trifft zwangsläufig auf den Stackbereich und da stehen, nach 
Unterprogrammaufruf(en) und/oder PUSH-Befehlen, keine keine NOPs mehr 
drin.
Und dann ist nicht sicher was der Controller macht.

>Vielleicht ist das sinnlos, vielleicht ist es aber auch ein Grund warum
>meine Produkte so lange und zuverlässig funktionieren.

Da hast du etwas anderes 'falsch' gemacht.

MfG Spess

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Wenn der Controller auf NOPs im RAM trifft läuft er weiter nach
> oben und trifft zwangsläufig auf den Stackbereich
Äh ja. Das stelle ich mir in einer Harvard-Architektur dann doch eher 
schwierig vor.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Äh ja. Das stelle ich mir in einer Harvard-Architektur dann doch eher
>schwierig vor.

Da hast du wohl Recht. Also ignoriere mein obiges Geschreibsel.

Aber ich halte deine Maßnahmen trotzdem für recht sinnfrei. Ich 
initialisiere nur RAM den ich benutze und Register die <> 0 sind. Und 
das klappt seit 20 Jahren auch mit deaktivierten Watchdog fehlerfrei.

MfG Spess

von georg (Gast)


Lesenswert?

spess53 schrieb:
> Und das Kapitel sagt auch aus mit welchen Wert der
> Stackpointer nach dem RESET initialisiert ist.

Na und, das garantiert ja garnichts, wenn das Programm nicht durch einen 
Hardware-Reset startet. Es sollte alles korrekt initialisiert werden, 
auch wenn der Start durch einen JMP 0 erfolgt. Oder nach einem 
softwaremässigen Amoklauf, wenn die Ausführung wieder zum Anfang kommt. 
Oder oder...

Nennt man defensives Programmieren, hat sich bewährt.

Georg

von Stefan F. (Gast)


Lesenswert?

georg schrieb:
> auch wenn der Start durch einen JMP 0 erfolgt. Oder nach einem

Das ist doch schon falsch. Zwanghaft die ganze Hardware durch zu 
initialisieren ist danach nur ein hässlicher Workaround.

Wenn du schon neu starten willst, dann bitte durch einen richtigen 
Reset. Bei AVR löst man den z.B. durch den Watchdog aus.

> Oder nach einem softwaremässigen Amoklauf. Oder oder...

Erledigt auch der Watchdog.

von fop (Gast)


Lesenswert?

Also halten wir fest : der Stack arbeitet ähnlich wie so ein 
Posteingangsplastikdingens. Man kann Zettel mit Informationen drauf 
werfen. So wirklich ohne Probleme kommt man aber immer nur an den 
obersten. Man bekommt also seine Infos in umgekehrter Reihenfolge 
zurück, wie man sie abgelegt hat.
Das Initialisieren ist wir das Platzieren des Teils. Wichtig ist, dass 
weder irgendwas wie Kaffe, die Zettel beschädigt, noch dass der Stapel 
was anderes platt macht. Ausserdem muss es ein Ort sein, wo sich 
überhaupt was ablegen läßt.
In manchen Büros steht das Teil schon richtig, wenn man seinen Job 
anfängt. In anderen steht es total bescheuert und man kommt um eine 
Neuplatzierung nicht herum, wenn man es nutzen will.

von spess53 (Gast)


Lesenswert?

Hi

>Es sollte alles korrekt initialisiert werden,
>auch wenn der Start durch einen JMP 0 erfolgt.

So einen Blödsinn programmiere ich nicht.

>Oder nach einem
>softwaremässigen Amoklauf, wenn die Ausführung wieder zum Anfang kommt.

Dann hast du entweder fehlerhafte Soft- und/oder fehlerhafte Hardware. 
Beides behebbar.

>Nennt man defensives Programmieren, hat sich bewährt.

Ich stehe auf fehlerfreies Programmieren. Hat sich besser bewährt als an 
irgentwelchen Syptomen herumzudoktern.

Wir hatten in jedenfalls unserer Firma seit 2000 (Beginn der 
Geräteproduktion) keinen einzigen Fall, das ausgelieferte Geräte mit 
einem Softwareupdate versehen werden mußten.

MfG Spess

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> So einen Blödsinn programmiere ich nicht.
Wieso nennst Du das Blödsinn? Ein JMP RESET ist deutlich einfacher, als 
dem Chip erst vom Watchdog in den Arsch treten/beißen zu lassen...

> Ich stehe auf fehlerfreies Programmieren.
Das nenne ich dafür mal Blödsinn. Kein Programm (ausgenommen triviale) 
ist wirklich fehlerfrei. Irgendwo liegt immer der Hase im Pfeffer und 
wartet nur auf seine Gelegenheit.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Wieso nennst Du das Blödsinn? Ein JMP RESET ist deutlich einfacher, als
>dem Chip erst vom Watchdog in den Arsch treten zu lassen...

Ich hatte schon geschrieben, das ich keinen Watchdog benutze. Wozu 
sollte ich einen RESET brauchen?

MfG Spess

von Clemens L. (c_l)


Lesenswert?

Ben B. schrieb:
> Ein JMP RESET ist deutlich einfacher, als dem Chip erst vom Watchdog in
> den Arsch treten/beißen zu lassen.

Aber nur ein Hardware-Arschtritt kann auch alle internen Register oder 
einen verklemmten Zustandsautomaten zuverlässig zurücksetzen. (Und ist 
auch schneller.)

Und wenn die Software-Initialisierung normalerweise nur nach einem 
echten Reset ausgeführt wird, dann ist sie im Zweifelsfall auch nicht 
richtig getestet.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Wozu sollte ich einen RESET brauchen?
Nöööööööööööö DU brauchst NIIIEEEmals einen Reset... :D

von georg (Gast)


Lesenswert?

Ben B. schrieb:
> Ein JMP RESET ist deutlich einfacher

Beispielsweise nach Durchführung von Inbetriebnahme-Test, etwa ein 
RAM-Test. Aber wer behauptet, niemals Programmierfehler zu machen, muss 
natürlich auch die Hardware nicht testen, die funktioniert ja auch 
immer... Für mich nur Angeberei. Niemand kann garantieren, niemals 
Fehler zu machen.

Georg

von spess53 (Gast)


Lesenswert?

Hi

>Nöööööööööööö DU brauchst NIIIEEEmals einen Reset... :D

Der kommt automatisch beim Einschalten oder vom Programmer.

>Aber wer behauptet, niemals Programmierfehler zu machen, muss
>natürlich auch die Hardware nicht testen, die funktioniert ja auch
>immer... Für mich nur Angeberei. Niemand kann garantieren, niemals
>Fehler zu machen.

Habe ich nie behauptet. Aber Fehler kann man finden und beheben. 
Fehlerhafte Soft-/Hardware auszuliefern ist unprofessionell.

MfG Spess

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Fehlerhafte Soft-/Hardware auszuliefern ist unprofessionell.
Dann nenne mir doch bitte mal eine professionelle Softwarefirma...

von Jens J. (trial)


Lesenswert?

Danke für die Antworten.
Denke ich habe es nun verstanden.

An dem punkt an dem Ihr angefangen habt zu diskutieren habe ich mich 
jedoch ausgeklinkt...

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.