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.
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.
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.
> 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.
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
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!
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.
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).
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.
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.
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
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
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
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
> 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
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
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
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.
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.
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
> 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
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
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.
> Wozu sollte ich einen RESET brauchen?
Nöööööööööööö DU brauchst NIIIEEEmals einen Reset... :D
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
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
> Fehlerhafte Soft-/Hardware auszuliefern ist unprofessionell.
Dann nenne mir doch bitte mal eine professionelle Softwarefirma...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.